Module: Aptible::CLI::Helpers::Database
Defined Under Namespace
Classes: MockRdsDatabaseAccountShell, RdsDatabase
Constant Summary
collapse
- UNATTACHED_RDS_ACCOUNT_ID =
using an ID that cannot be hit for visual segregation of unattached databases
-9999
Constants included
from Token
Token::TOKEN_ENV_VAR
Instance Method Summary
collapse
-
#accounts_external_rds_databases_map(rds_map) ⇒ Object
-
#aws_rds_db?(handle) ⇒ Boolean
-
#clone_database(source, dest_handle) ⇒ Object
-
#database_from_handle(handle, environment) ⇒ Object
-
#databases_all ⇒ Object
-
#databases_href ⇒ Object
-
#derive_account_from_conns(db, preferred_acct = nil) ⇒ Object
-
#ensure_database(options = {}) ⇒ Object
-
#external_rds_database_from_handle(handle) ⇒ Object
-
#external_rds_databases_all ⇒ Object
-
#external_rds_databases_map ⇒ Object
-
#fetch_rds_databases_with_accounts ⇒ Object
-
#find_credential(database, type = nil) ⇒ Object
-
#find_database_image(type, version) ⇒ Object
-
#local_rds_url(credential, local_port, forced_account) ⇒ Object
-
#local_url(credential, local_port) ⇒ Object
-
#map_of_accounts_to_rds(rds_map) ⇒ Object
-
#rds_shell_account ⇒ Object
-
#render_database(database, account) ⇒ Object
-
#replicate_database(source, dest_handle, options) ⇒ Object
-
#use_rds_dump(handle, filename, dump_options) ⇒ Object
-
#use_rds_execute(handle, sql_path, options) ⇒ Object
-
#use_rds_tunnel(handle, port) ⇒ Object
-
#validate_image_type(type) ⇒ Object
-
#with_local_tunnel(credential, port = 0, target_account = nil) ⇒ Object
Creates a local tunnel and yields the helper.
-
#with_postgres_tunnel(database) ⇒ Object
Creates a local PG tunnel and yields the url to it.
-
#with_rds_tunnel(handle, port = 0) ⇒ Object
Methods included from Ssh
#connect_to_ssh_portal, #exit_with_ssh_portal, #with_ssh_cmd
Methods included from ConfigPath
#aptible_config_path
#ensure_default_environment, #ensure_environment, #environment_from_handle, #environment_href, #environment_map, #scoped_environments
Methods included from Token
#current_token, #current_token_hash, #decode_token, #fetch_token, #save_token, #token_file, #whoami
Instance Method Details
#accounts_external_rds_databases_map(rds_map) ⇒ Object
74
75
76
77
78
|
# File 'lib/aptible/cli/helpers/database.rb', line 74
def accounts_external_rds_databases_map(rds_map)
return {} if rds_map.empty?
map_of_accounts_to_rds(rds_map)
end
|
#aws_rds_db?(handle) ⇒ Boolean
60
61
62
|
# File 'lib/aptible/cli/helpers/database.rb', line 60
def aws_rds_db?(handle)
handle.start_with? 'aws:rds::'
end
|
#clone_database(source, dest_handle) ⇒ Object
153
154
155
156
157
158
|
# File 'lib/aptible/cli/helpers/database.rb', line 153
def clone_database(source, dest_handle)
op = source.create_operation!(type: 'clone', handle: dest_handle)
attach_to_operation_logs(op)
database_from_handle(dest_handle, source.account)
end
|
#database_from_handle(handle, environment) ⇒ Object
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
# File 'lib/aptible/cli/helpers/database.rb', line 137
def database_from_handle(handle, environment)
url = "/find/database?handle=#{handle}"
url += "&environment=#{environment.handle}" unless environment.nil?
Aptible::Api::Database.find_by_url(
url,
token: fetch_token
)
rescue HyperResource::ClientError => e
raise unless e.body.is_a?(Hash) &&
e.body['error'] == 'multiple_resources_found'
raise Thor::Error,
'Multiple databases exist, please specify ' \
'with --environment'
end
|
#databases_all ⇒ Object
53
54
55
56
57
58
|
# File 'lib/aptible/cli/helpers/database.rb', line 53
def databases_all
Aptible::Api::Database.all(
token: fetch_token,
href: databases_href
)
end
|
#databases_href ⇒ Object
45
46
47
48
49
50
51
|
# File 'lib/aptible/cli/helpers/database.rb', line 45
def databases_href
href = '/databases'
if Renderer.format != 'json'
href = '/databases?per_page=5000&no_embed=true'
end
href
end
|
#derive_account_from_conns(db, preferred_acct = nil) ⇒ Object
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
# File 'lib/aptible/cli/helpers/database.rb', line 116
def derive_account_from_conns(db, preferred_acct = nil)
conns = db.raw.app_external_aws_rds_connections
return nil if conns.empty?
if preferred_acct.present?
valid_conns = conns.find do |conn|
conn.present? && conn.app.account.id == preferred_acct.id
end
return nil if valid_conns.nil?
return valid_conns.app.account
end
first_present_conn = conns.find(&:present?)
return nil if first_present_conn.nil?
first_present_conn.app.account
end
|
#ensure_database(options = {}) ⇒ Object
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# File 'lib/aptible/cli/helpers/database.rb', line 28
def ensure_database(options = {})
db_handle = options[:db]
environment_handle = options[:environment]
raise Thor::Error, 'Database handle not specified' unless db_handle
environment = environment_from_handle(environment_handle)
if environment_handle && !environment
raise Thor::Error,
"Could not find environment #{environment_handle}"
end
db = database_from_handle(db_handle, environment)
raise Thor::Error, "Could not find database #{db_handle}" if db.nil?
db
end
|
#external_rds_database_from_handle(handle) ⇒ Object
133
134
135
|
# File 'lib/aptible/cli/helpers/database.rb', line 133
def external_rds_database_from_handle(handle)
external_rds_databases_all.find { |a| a.handle == handle }
end
|
#external_rds_databases_all ⇒ Object
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
# File 'lib/aptible/cli/helpers/database.rb', line 100
def external_rds_databases_all
Aptible::Api::ExternalAwsResource
.all(
token: fetch_token
)
.select { |db| db.resource_type == 'aws_rds_db_instance' }
.map do |db|
RdsDatabase.new(
"aws:rds::#{db.resource_name}",
db.id,
db.created_at,
db
)
end
end
|
#external_rds_databases_map ⇒ Object
64
65
66
|
# File 'lib/aptible/cli/helpers/database.rb', line 64
def external_rds_databases_map
external_rds_databases_all.map { |rds| [rds[:id], rds] }.to_h
end
|
#fetch_rds_databases_with_accounts ⇒ Object
68
69
70
71
72
|
# File 'lib/aptible/cli/helpers/database.rb', line 68
def fetch_rds_databases_with_accounts
rds_map = external_rds_databases_map
accts_rds_map = accounts_external_rds_databases_map(rds_map)
[rds_map, accts_rds_map]
end
|
#find_credential(database, type = nil) ⇒ Object
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
|
# File 'lib/aptible/cli/helpers/database.rb', line 321
def find_credential(database, type = nil)
unless database.provisioned?
raise Thor::Error, "Database #{database.handle} is not provisioned"
end
creds_link = database.links['database_credentials']
database_credentials = Aptible::Api::DatabaseCredential.all(
href: creds_link.href,
token: fetch_token,
headers: { 'Prefer' => 'no_sensitive_extras=false' }
)
finder = proc { |c| c.default }
finder = proc { |c| c.type == type } if type
credential = database_credentials.find(&finder)
return credential, database_credentials if credential
types = database_credentials.map(&:type)
valid = types.join(', ')
err = 'No default credential for database'
err = "No credential with type #{type} for database" if type
raise Thor::Error, "#{err}, valid credential types: #{valid}"
end
|
#find_database_image(type, version) ⇒ Object
353
354
355
356
357
358
359
360
361
362
363
364
365
|
# File 'lib/aptible/cli/helpers/database.rb', line 353
def find_database_image(type, version)
available_versions = []
Aptible::Api::DatabaseImage.all(token: fetch_token).each do |i|
next unless i.type == type
return i if i.version == version
available_versions << i.version
end
err = "No Database Image of type #{type} with version #{version}"
err = "#{err}, valid versions: #{available_versions.join(' ')}"
raise Thor::Error, err
end
|
#local_rds_url(credential, local_port, forced_account) ⇒ Object
303
304
305
306
307
308
309
310
|
# File 'lib/aptible/cli/helpers/database.rb', line 303
def local_rds_url(credential, local_port, forced_account)
remote_url = credential.connection_url
uri = URI.parse(remote_url)
domain = forced_account.stack.internal_domain
"#{uri.scheme}://#{uri.user}:#{uri.password}@" \
"localhost.#{domain}:#{local_port}#{uri.path}"
end
|
#local_url(credential, local_port) ⇒ Object
312
313
314
315
316
317
318
319
|
# File 'lib/aptible/cli/helpers/database.rb', line 312
def local_url(credential, local_port)
remote_url = credential.connection_url
uri = URI.parse(remote_url)
domain = without_sensitive(credential).database.account.stack.internal_domain
"#{uri.scheme}://#{uri.user}:#{uri.password}@" \
"localhost.#{domain}:#{local_port}#{uri.path}"
end
|
#map_of_accounts_to_rds(rds_map) ⇒ Object
80
81
82
83
84
85
86
87
88
89
90
91
|
# File 'lib/aptible/cli/helpers/database.rb', line 80
def map_of_accounts_to_rds(rds_map)
accts_rds_map = {}
rds_map.each_value do |db|
account = derive_account_from_conns(db)
next if account.nil?
accts_rds_map[account.id] = [] if accts_rds_map[account.id].nil?
accts_rds_map[account.id] << db
end
accts_rds_map
end
|
#rds_shell_account ⇒ Object
#render_database(database, account) ⇒ Object
#replicate_database(source, dest_handle, options) ⇒ Object
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
# File 'lib/aptible/cli/helpers/database.rb', line 160
def replicate_database(source, dest_handle, options)
replication_params = {
handle: dest_handle,
container_size: options[:container_size],
disk_size: options[:size],
key_arn: options[:key_arn],
instance_profile: options[:instance_profile],
provisioned_iops: options[:provisioned_iops]
}.reject { |_, v| v.nil? }
if options[:logical]
replication_params[:type] = 'replicate_logical'
replication_params[:docker_ref] =
options[:database_image].docker_repo
else
replication_params[:type] = 'replicate'
end
op = source.create_operation!(replication_params)
attach_to_operation_logs(op)
replica = database_from_handle(dest_handle, source.account)
attach_to_operation_logs(replica.operations.last)
replica
end
|
#use_rds_dump(handle, filename, dump_options) ⇒ Object
272
273
274
275
276
277
278
|
# File 'lib/aptible/cli/helpers/database.rb', line 272
def use_rds_dump(handle, filename, dump_options)
with_rds_tunnel(handle) do |url|
CLI.logger.info "Dumping to #{filename}"
`pg_dump #{url} #{dump_options.shelljoin} > #{filename}`
exit $CHILD_STATUS.exitstatus unless $CHILD_STATUS.success?
end
end
|
#use_rds_execute(handle, sql_path, options) ⇒ Object
280
281
282
283
284
285
286
287
|
# File 'lib/aptible/cli/helpers/database.rb', line 280
def use_rds_execute(handle, sql_path, options)
with_rds_tunnel(handle) do |url|
CLI.logger.info "Executing #{sql_path} against #{handle}"
args = options[:on_error_stop] ? '-v ON_ERROR_STOP=true ' : ''
`psql #{args}#{url} < #{sql_path}`
exit $CHILD_STATUS.exitstatus unless $CHILD_STATUS.success?
end
end
|
#use_rds_tunnel(handle, port) ⇒ Object
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
|
# File 'lib/aptible/cli/helpers/database.rb', line 249
def use_rds_tunnel(handle, port)
with_rds_tunnel(handle, port) do |url, tunnel_helper|
CLI.logger.info "Connect at #{url}"
uri = URI(url)
db = uri.path.gsub(%r{^/}, '')
CLI.logger.info 'Or, use the following arguments:'
CLI.logger.info "* Host: #{uri.host}"
CLI.logger.info "* Port: #{uri.port}"
CLI.logger.info "* Username: #{uri.user}" unless uri.user.empty?
CLI.logger.info "* Password: #{uri.password}"
CLI.logger.info "* Database: #{db}" unless db.empty?
CLI.logger.info 'Connected. Ctrl-C to close connection.'
begin
tunnel_helper.wait
rescue Interrupt
CLI.logger.warn 'Closing tunnel'
end
end
end
|
#validate_image_type(type) ⇒ Object
367
368
369
370
371
372
373
374
375
376
377
378
|
# File 'lib/aptible/cli/helpers/database.rb', line 367
def validate_image_type(type)
available_types = []
Aptible::Api::DatabaseImage.all(token: fetch_token).each do |i|
return true if i.type == type
available_types << i.type
end
err = "No Database Image of type \"#{type}\""
err = "#{err}, valid types: #{available_types.uniq.join(', ')}"
raise Thor::Error, err
end
|
#with_local_tunnel(credential, port = 0, target_account = nil) ⇒ Object
Creates a local tunnel and yields the helper
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
# File 'lib/aptible/cli/helpers/database.rb', line 188
def with_local_tunnel(credential, port = 0, target_account = nil)
credential = without_sensitive(credential)
op = if target_account.nil?
credential.create_operation!(
type: 'tunnel',
status: 'succeeded'
)
else
credential.create_operation!(
type: 'tunnel',
status: 'succeeded',
destination_account: target_account.id
)
end
with_ssh_cmd(op) do |base_ssh_cmd, ssh_credential|
ssh_cmd = base_ssh_cmd + ['-o', 'SendEnv=ACCESS_TOKEN']
ssh_env = { 'ACCESS_TOKEN' => fetch_token }
socket_path = ssh_credential.ssh_port_forward_socket
tunnel_helper = Helpers::Tunnel.new(ssh_env, ssh_cmd, socket_path)
tunnel_helper.start(port)
yield tunnel_helper if block_given?
tunnel_helper.stop
end
end
|
#with_postgres_tunnel(database) ⇒ Object
Creates a local PG tunnel and yields the url to it
291
292
293
294
295
296
297
298
299
300
301
|
# File 'lib/aptible/cli/helpers/database.rb', line 291
def with_postgres_tunnel(database)
if database.type != 'postgresql'
raise Thor::Error, 'This command only works for PostgreSQL'
end
credential, _credentials = find_credential(database)
with_local_tunnel(credential) do |tunnel_helper|
yield local_url(credential, tunnel_helper.port)
end
end
|
#with_rds_tunnel(handle, port = 0) ⇒ Object
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
# File 'lib/aptible/cli/helpers/database.rb', line 223
def with_rds_tunnel(handle, port = 0)
external_rds = external_rds_database_from_handle(handle)
if external_rds.nil?
raise Thor::Error, "No rds db found with handle #{handle}"
end
credential = external_rds.raw.external_aws_database_credentials.first
if credential.nil?
raise Thor::Error, 'No rds credential found with handle ' \
"#{handle}. Check to see if you have run " \
'db:attach or a scan has properly completed.'
end
target_account = derive_account_from_conns(external_rds)
if target_account.nil?
raise Thor::Error,
"No env for rds found with handle #{handle}. Check to see " \
'if you have run db:attach or a scan has properly completed.'
end
with_local_tunnel(credential, port, target_account) do |tunnel_helper|
url = local_rds_url(credential, tunnel_helper.port, target_account)
yield url, tunnel_helper
end
end
|