aptible-cli 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0a4dc9b3d5de431e99e8aef2e76fdf0a352c03f7
4
- data.tar.gz: eaaba80a605eff86cdddb3bec822a57ae928d871
3
+ metadata.gz: 82497046ef055642ef5009c32253a6ffdf7dfd5b
4
+ data.tar.gz: 791ede35111988aaf40ce86ff1ff7ebcf476a7a0
5
5
  SHA512:
6
- metadata.gz: d86fdfec7a75ba6aa0234551c0b7c326ee6b408175ea41d7fb8e44c96f0f2db2ecd9346029f4ac36295f1e68d9690f9928a74efdba79c6f8e63d88a6905d5253
7
- data.tar.gz: 7a115c06a8479359d96dade21f5467cdba4b50af226d828062b0b9a88722d46ef1772fc1e22c56f991e1d6e874bce0e0fb9105997d9a944d876c3fdd3df29852
6
+ metadata.gz: 090af05a44a38f86a746086df06a4c2d2531076752d8b819bcf9cfd9ef19f4bad03e4aa934ef93148d72e721b2f2cabade69184a817828300eaea39cc0e048be
7
+ data.tar.gz: 4ce62bac547f46a64d00b44d52f051205aaeb4cd40d5637d285a24bce278e1802083f00ce22343cc281a947fc7e30a225a74d32e6e1d1ff500cf1b685baf70dd
@@ -6,6 +6,7 @@ require_relative 'helpers/token'
6
6
  require_relative 'helpers/operation'
7
7
  require_relative 'helpers/environment'
8
8
  require_relative 'helpers/app'
9
+ require_relative 'helpers/database'
9
10
  require_relative 'helpers/env'
10
11
 
11
12
  require_relative 'subcommands/apps'
@@ -0,0 +1,123 @@
1
+ require 'aptible/api'
2
+
3
+ module Aptible
4
+ module CLI
5
+ module Helpers
6
+ module Database
7
+ include Helpers::Token
8
+ include Helpers::Environment
9
+
10
+ def ensure_database(options = {})
11
+ db_handle = options[:db]
12
+ environment_handle = options[:environment]
13
+
14
+ fail Thor::Error, 'Database handle not specified' unless db_handle
15
+
16
+ environment = environment_from_handle(environment_handle)
17
+ if environment_handle && !environment
18
+ fail Thor::Error, "Could not find environment #{environment_handle}"
19
+ end
20
+ databases = databases_from_handle(db_handle, environment)
21
+ case databases.count
22
+ when 1
23
+ return databases.first
24
+ when 0
25
+ fail Thor::Error, "Could not find database #{db_handle}"
26
+ else
27
+ fail Thor::Error,
28
+ 'Multiple databases exist, please specify environment'
29
+ end
30
+ end
31
+
32
+ def databases_from_handle(handle, environment)
33
+ if environment
34
+ databases = environment.databases
35
+ else
36
+ databases = Aptible::Api::Database.all(token: fetch_token)
37
+ end
38
+ databases.select { |a| a.handle == handle }
39
+ end
40
+
41
+ def present_environment_databases(environment)
42
+ say "=== #{environment.handle}"
43
+ environment.databases.each { |db| say db.handle }
44
+ say ''
45
+ end
46
+
47
+ def establish_connection(database, local_port)
48
+ ENV['ACCESS_TOKEN'] = fetch_token
49
+ ENV['APTIBLE_DATABASE'] = database.handle
50
+
51
+ remote_port = claim_remote_port(database)
52
+ ENV['TUNNEL_PORT'] = remote_port
53
+
54
+ tunnel_args = "-L #{local_port}:localhost:#{remote_port}"
55
+ command = "ssh #{tunnel_args} #{common_ssh_args(database)}"
56
+ Kernel.exec(command)
57
+ end
58
+
59
+ def clone_database(source, dest_handle)
60
+ op = source.create_operation(type: 'clone', handle: dest_handle)
61
+ poll_for_success(op)
62
+
63
+ databases_from_handle(dest_handle, source.account).first
64
+ end
65
+
66
+ def dump_database(database)
67
+ execute_local_tunnel(database) do |url|
68
+ filename = "#{database.handle}.dump"
69
+ say "Dumping to #{filename}"
70
+ `pg_dump #{url} > #{filename}`
71
+ end
72
+ end
73
+
74
+ # Creates a local tunnel and yields the url to it
75
+ def execute_local_tunnel(database)
76
+ local_port = random_local_port
77
+ pid = fork { establish_connection(database, local_port) }
78
+
79
+ # TODO: Better test for connection readiness
80
+ sleep 10
81
+
82
+ auth = "aptible:#{database.passphrase}"
83
+ host = "localhost:#{local_port}"
84
+ yield "postgresql://#{auth}@#{host}/db"
85
+ ensure
86
+ Process.kill('HUP', pid) if pid
87
+ end
88
+
89
+ def random_local_port
90
+ # Allocate a dummy server to discover an available port
91
+ dummy = TCPServer.new('127.0.0.1', 0)
92
+ port = dummy.addr[1]
93
+ dummy.close
94
+ port
95
+ end
96
+
97
+ def local_url(database, local_port)
98
+ remote_url = database.connection_url
99
+ uri = URI.parse(remote_url)
100
+
101
+ "#{uri.scheme}://#{uri.user}:#{uri.password}@" \
102
+ "127.0.0.1:#{local_port}#{uri.path}"
103
+ end
104
+
105
+ def claim_remote_port(database)
106
+ ENV['ACCESS_TOKEN'] = fetch_token
107
+
108
+ `ssh #{common_ssh_args(database)} 2>/dev/null`.chomp
109
+ end
110
+
111
+ def common_ssh_args(database)
112
+ host = database.account.bastion_host
113
+ port = database.account.bastion_port
114
+
115
+ opts = " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
116
+ '-o UserKnownHostsFile=/dev/null'
117
+ connection_args = "-p #{port} root@#{host}"
118
+ "#{opts} #{connection_args}"
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -7,6 +7,7 @@ module Aptible
7
7
  def self.included(thor)
8
8
  thor.class_eval do
9
9
  include Helpers::Operation
10
+ include Helpers::Database
10
11
  include Helpers::Token
11
12
  include Term::ANSIColor
12
13
 
@@ -40,24 +41,24 @@ module Aptible
40
41
  desc 'db:clone SOURCE DEST', 'Clone a database to create a new one'
41
42
  option :environment
42
43
  define_method 'db:clone' do |source_handle, dest_handle|
43
- environment = ensure_environment(options)
44
- dest = clone_database(source_handle, dest_handle, environment)
44
+ source = ensure_database(options.merge(db: source_handle))
45
+ dest = clone_database(source, dest_handle)
45
46
  say dest.connection_url
46
47
  end
47
48
 
48
49
  desc 'db:dump HANDLE', 'Dump a remote database to file'
49
50
  option :environment
50
51
  define_method 'db:dump' do |handle|
51
- environment = ensure_environment(options)
52
- dump_database(handle, environment)
52
+ database = ensure_database(options.merge(db: handle))
53
+ dump_database(database)
53
54
  end
54
55
 
55
56
  desc 'db:execute HANDLE SQL_FILE', 'Executes sql against a database'
56
57
  option :environment
57
58
  define_method 'db:execute' do |handle, sql_path|
58
- environment = ensure_environment(options)
59
- execute_local_tunnel(handle, environment) do |url|
60
- say "Executing #{sql_path} against #{handle}"
59
+ database = ensure_database(options.merge(db: handle))
60
+ execute_local_tunnel(database) do |url|
61
+ say "Executing #{sql_path} against #{database.handle}"
61
62
  `psql #{url} < #{sql_path}`
62
63
  end
63
64
  end
@@ -66,8 +67,7 @@ module Aptible
66
67
  option :environment
67
68
  option :port, type: :numeric
68
69
  define_method 'db:tunnel' do |handle|
69
- environment = ensure_environment(options)
70
- database = database_from_handle(handle, environment)
70
+ database = ensure_database(options.merge(db: handle))
71
71
  local_port = options[:port] || random_local_port
72
72
 
73
73
  say 'Creating tunnel...', :green
@@ -87,117 +87,11 @@ module Aptible
87
87
  desc 'db:deprovision HANDLE', 'Deprovision a database'
88
88
  option :environment
89
89
  define_method 'db:deprovision' do |handle|
90
- environment = ensure_environment(options)
91
- database = database_from_handle(handle, environment)
92
- say "Deprovisioning #{handle}..."
90
+ database = ensure_database(options.merge(db: handle))
91
+ say "Deprovisioning #{database.handle}..."
93
92
  database.update!(status: 'deprovisioned')
94
93
  database.create_operation!(type: 'deprovision')
95
94
  end
96
-
97
- private
98
-
99
- def present_environment_databases(environment)
100
- say "=== #{environment.handle}"
101
- environment.databases.each { |db| say db.handle }
102
- say ''
103
- end
104
-
105
- def establish_connection(database, local_port)
106
- ENV['ACCESS_TOKEN'] = fetch_token
107
- ENV['APTIBLE_DATABASE'] = database.handle
108
-
109
- remote_port = claim_remote_port(database)
110
- ENV['TUNNEL_PORT'] = remote_port
111
-
112
- tunnel_args = "-L #{local_port}:localhost:#{remote_port}"
113
- command = "ssh #{tunnel_args} #{common_ssh_args(database)}"
114
- Kernel.exec(command)
115
- end
116
-
117
- def database_from_handle(handle,
118
- environment,
119
- options = { postgres_only: false })
120
- all = environment.databases
121
- database = all.find { |a| a.handle == handle }
122
-
123
- unless database
124
- fail Thor::Error, "Could not find database #{handle}"
125
- end
126
-
127
- if options[:postgres_only] && database.type != 'postgresql'
128
- fail Thor::Error, 'This command only works for PostgreSQL'
129
- end
130
-
131
- database
132
- end
133
-
134
- def clone_database(source_handle, dest_handle, environment)
135
- source = database_from_handle(source_handle, environment)
136
- op = source.create_operation(type: 'clone', handle: dest_handle)
137
- poll_for_success(op)
138
-
139
- database_from_handle(dest_handle)
140
- end
141
-
142
- def dump_database(handle, environment)
143
- execute_local_tunnel(handle, environment) do |url|
144
- filename = "#{handle}.dump"
145
- say "Dumping to #{filename}"
146
- `pg_dump #{url} > #{filename}`
147
- end
148
- end
149
-
150
- # Creates a local tunnel and yields the url to it
151
-
152
- def execute_local_tunnel(handle, environment)
153
- database = database_from_handle(handle,
154
- environment,
155
- postgres_only: true)
156
-
157
- local_port = random_local_port
158
- pid = fork { establish_connection(database, local_port) }
159
-
160
- # TODO: Better test for connection readiness
161
- sleep 10
162
-
163
- auth = "aptible:#{database.passphrase}"
164
- host = "localhost:#{local_port}"
165
- yield "postgresql://#{auth}@#{host}/db"
166
- ensure
167
- Process.kill('HUP', pid) if pid
168
- end
169
-
170
- def random_local_port
171
- # Allocate a dummy server to discover an available port
172
- dummy = TCPServer.new('127.0.0.1', 0)
173
- port = dummy.addr[1]
174
- dummy.close
175
- port
176
- end
177
-
178
- def local_url(database, local_port)
179
- remote_url = database.connection_url
180
- uri = URI.parse(remote_url)
181
-
182
- "#{uri.scheme}://#{uri.user}:#{uri.password}@" \
183
- "127.0.0.1:#{local_port}#{uri.path}"
184
- end
185
-
186
- def claim_remote_port(database)
187
- ENV['ACCESS_TOKEN'] = fetch_token
188
-
189
- `ssh #{common_ssh_args(database)} 2>/dev/null`.chomp
190
- end
191
-
192
- def common_ssh_args(database)
193
- host = database.account.bastion_host
194
- port = database.account.bastion_port
195
-
196
- opts = " -o 'SendEnv=*' -o StrictHostKeyChecking=no " \
197
- '-o UserKnownHostsFile=/dev/null'
198
- connection_args = "-p #{port} root@#{host}"
199
- "#{opts} #{connection_args}"
200
- end
201
95
  end
202
96
  end
203
97
  end
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module CLI
3
- VERSION = '0.6.1'
3
+ VERSION = '0.6.2'
4
4
  end
5
5
  end
@@ -30,16 +30,14 @@ describe Aptible::CLI::Agent do
30
30
 
31
31
  describe '#db:tunnel' do
32
32
  it 'should fail if database is non-existent' do
33
- allow(Aptible::Api::Account).to receive(:all) { [account] }
34
- allow(account).to receive(:databases) { [] }
33
+ allow(Aptible::Api::Database).to receive(:all) { [] }
35
34
  expect do
36
35
  subject.send('db:tunnel', 'foobar')
37
36
  end.to raise_error('Could not find database foobar')
38
37
  end
39
38
 
40
39
  it 'should print a message about how to connect' do
41
- allow(Aptible::Api::Account).to receive(:all) { [account] }
42
- allow(account).to receive(:databases) { [database] }
40
+ allow(Aptible::Api::Database).to receive(:all) { [database] }
43
41
  local_url = 'postgresql://aptible:password@127.0.0.1:4242/db'
44
42
  expect(subject).to receive(:say).with('Creating tunnel...', :green)
45
43
  expect(subject).to receive(:say).with("Connect at #{local_url}", :green)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aptible-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Frank Macreery
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-24 00:00:00.000000000 Z
11
+ date: 2016-02-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aptible-api
@@ -171,6 +171,7 @@ files:
171
171
  - lib/aptible/cli.rb
172
172
  - lib/aptible/cli/agent.rb
173
173
  - lib/aptible/cli/helpers/app.rb
174
+ - lib/aptible/cli/helpers/database.rb
174
175
  - lib/aptible/cli/helpers/env.rb
175
176
  - lib/aptible/cli/helpers/environment.rb
176
177
  - lib/aptible/cli/helpers/operation.rb