Module: Aptible::CLI::Helpers::Database

Includes:
Environment, Token
Defined in:
lib/aptible/cli/helpers/database.rb

Instance Method Summary collapse

Methods included from Environment

#ensure_default_environment, #ensure_environment, #environment_from_handle, #scoped_environments

Methods included from Token

#current_token_hash, #fetch_token, #save_token, #token_file

Instance Method Details

#claim_remote_port(database) ⇒ Object



105
106
107
108
109
# File 'lib/aptible/cli/helpers/database.rb', line 105

def claim_remote_port(database)
  ENV['ACCESS_TOKEN'] = fetch_token

  `ssh #{common_ssh_args(database)} 2>/dev/null`.chomp
end

#clone_database(source, dest_handle) ⇒ Object



59
60
61
62
63
64
# File 'lib/aptible/cli/helpers/database.rb', line 59

def clone_database(source, dest_handle)
  op = source.create_operation(type: 'clone', handle: dest_handle)
  poll_for_success(op)

  databases_from_handle(dest_handle, source.).first
end

#common_ssh_args(database) ⇒ Object



111
112
113
114
115
116
117
118
119
# File 'lib/aptible/cli/helpers/database.rb', line 111

def common_ssh_args(database)
  host = database..bastion_host
  port = database..bastion_port

  opts = " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
         '-o UserKnownHostsFile=/dev/null'
  connection_args = "-p #{port} root@#{host}"
  "#{opts} #{connection_args}"
end

#databases_from_handle(handle, environment) ⇒ Object



32
33
34
35
36
37
38
39
# File 'lib/aptible/cli/helpers/database.rb', line 32

def databases_from_handle(handle, environment)
  if environment
    databases = environment.databases
  else
    databases = Aptible::Api::Database.all(token: fetch_token)
  end
  databases.select { |a| a.handle == handle }
end

#dump_database(database) ⇒ Object



66
67
68
69
70
71
72
# File 'lib/aptible/cli/helpers/database.rb', line 66

def dump_database(database)
  execute_local_tunnel(database) do |url|
    filename = "#{database.handle}.dump"
    say "Dumping to #{filename}"
    `pg_dump #{url} > #{filename}`
  end
end

#ensure_database(options = {}) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/aptible/cli/helpers/database.rb', line 10

def ensure_database(options = {})
  db_handle = options[:db]
  environment_handle = options[:environment]

  fail Thor::Error, 'Database handle not specified' unless db_handle

  environment = environment_from_handle(environment_handle)
  if environment_handle && !environment
    fail Thor::Error, "Could not find environment #{environment_handle}"
  end
  databases = databases_from_handle(db_handle, environment)
  case databases.count
  when 1
    return databases.first
  when 0
    fail Thor::Error, "Could not find database #{db_handle}"
  else
    fail Thor::Error,
         'Multiple databases exist, please specify environment'
  end
end

#establish_connection(database, local_port) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/aptible/cli/helpers/database.rb', line 47

def establish_connection(database, local_port)
  ENV['ACCESS_TOKEN'] = fetch_token
  ENV['APTIBLE_DATABASE'] = database.handle

  remote_port = claim_remote_port(database)
  ENV['TUNNEL_PORT'] = remote_port

  tunnel_args = "-L #{local_port}:localhost:#{remote_port}"
  command = "ssh #{tunnel_args} #{common_ssh_args(database)}"
  Kernel.exec(command)
end

#execute_local_tunnel(database) ⇒ Object

Creates a local tunnel and yields the url to it



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/aptible/cli/helpers/database.rb', line 75

def execute_local_tunnel(database)
  local_port = random_local_port
  pid = fork { establish_connection(database, local_port) }

  # TODO: Better test for connection readiness
  sleep 10

  auth = "aptible:#{database.passphrase}"
  host = "localhost:#{local_port}"
  yield "postgresql://#{auth}@#{host}/db"
ensure
  Process.kill('HUP', pid) if pid
end

#local_url(database, local_port) ⇒ Object



97
98
99
100
101
102
103
# File 'lib/aptible/cli/helpers/database.rb', line 97

def local_url(database, local_port)
  remote_url = database.connection_url
  uri = URI.parse(remote_url)

  "#{uri.scheme}://#{uri.user}:#{uri.password}@" \
  "127.0.0.1:#{local_port}#{uri.path}"
end

#present_environment_databases(environment) ⇒ Object



41
42
43
44
45
# File 'lib/aptible/cli/helpers/database.rb', line 41

def present_environment_databases(environment)
  say "=== #{environment.handle}"
  environment.databases.each { |db| say db.handle }
  say ''
end

#random_local_port ⇒ Object



89
90
91
92
93
94
95
# File 'lib/aptible/cli/helpers/database.rb', line 89

def random_local_port
  # Allocate a dummy server to discover an available port
  dummy = TCPServer.new('127.0.0.1', 0)
  port = dummy.addr[1]
  dummy.close
  port
end