aptible-cli 0.26.2 → 0.26.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/Gemfile.lock +1 -1
- data/lib/aptible/cli/agent.rb +7 -4
- data/lib/aptible/cli/helpers/app.rb +29 -16
- data/lib/aptible/cli/helpers/database.rb +49 -32
- data/lib/aptible/cli/helpers/environment.rb +5 -4
- data/lib/aptible/cli/helpers/log_drain.rb +3 -1
- data/lib/aptible/cli/helpers/metric_drain.rb +3 -1
- data/lib/aptible/cli/resource_formatter.rb +14 -1
- data/lib/aptible/cli/subcommands/backup.rb +1 -1
- data/lib/aptible/cli/subcommands/config.rb +2 -2
- data/lib/aptible/cli/subcommands/db.rb +4 -3
- data/lib/aptible/cli/subcommands/log_drain.rb +2 -0
- data/lib/aptible/cli/subcommands/logs.rb +5 -5
- data/lib/aptible/cli/subcommands/metric_drain.rb +2 -0
- data/lib/aptible/cli/version.rb +1 -1
- data/lib/aptible/cli.rb +14 -0
- data/spec/aptible/cli/subcommands/apps_spec.rb +34 -21
- data/spec/aptible/cli/subcommands/backup_retention_policy_spec.rb +3 -1
- data/spec/aptible/cli/subcommands/backup_spec.rb +28 -3
- data/spec/aptible/cli/subcommands/config_spec.rb +11 -3
- data/spec/aptible/cli/subcommands/db_spec.rb +49 -95
- data/spec/aptible/cli/subcommands/deploy_spec.rb +8 -3
- data/spec/aptible/cli/subcommands/endpoints_spec.rb +20 -4
- data/spec/aptible/cli/subcommands/environment_spec.rb +4 -0
- data/spec/aptible/cli/subcommands/log_drain_spec.rb +15 -0
- data/spec/aptible/cli/subcommands/logs_spec.rb +16 -4
- data/spec/aptible/cli/subcommands/maintenance_spec.rb +7 -2
- data/spec/aptible/cli/subcommands/metric_drain_spec.rb +14 -2
- data/spec/aptible/cli/subcommands/services_spec.rb +3 -4
- data/spec/fabricators/account_fabricator.rb +9 -1
- data/spec/fabricators/app_external_aws_rds_connection_fabricator.rb +1 -1
- data/spec/fabricators/app_fabricator.rb +1 -1
- data/spec/fabricators/backup_fabricator.rb +1 -1
- data/spec/fabricators/backup_retention_policy_fabricator.rb +1 -1
- data/spec/fabricators/certificate_fabricator.rb +1 -1
- data/spec/fabricators/configuration_fabricator.rb +1 -1
- data/spec/fabricators/database_credential_fabricator.rb +1 -1
- data/spec/fabricators/database_disk_fabricator.rb +1 -1
- data/spec/fabricators/database_fabricator.rb +10 -1
- data/spec/fabricators/database_image_fabricator.rb +1 -1
- data/spec/fabricators/external_aws_account_fabricator.rb +1 -1
- data/spec/fabricators/external_aws_database_credential_fabricator.rb +1 -1
- data/spec/fabricators/external_aws_resource_fabricator.rb +1 -1
- data/spec/fabricators/log_drain_fabricator.rb +1 -1
- data/spec/fabricators/maintenance_app_fabricator.rb +1 -1
- data/spec/fabricators/maintenance_database_fabricator.rb +1 -1
- data/spec/fabricators/metric_drain_fabricator.rb +15 -1
- data/spec/fabricators/operation_fabricator.rb +1 -1
- data/spec/fabricators/service_fabricator.rb +1 -1
- data/spec/fabricators/service_sizing_policy_fabricator.rb +1 -1
- data/spec/fabricators/stack_fabricator.rb +1 -1
- data/spec/fabricators/vhost_fabricator.rb +1 -1
- data/spec/spec_helper.rb +7 -0
- data/spec/support/stub_aptible_resource.rb +25 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 13c94bbbfe4b853700fec4e895ab0eebd02f0c5ffcddf6cfd0a597c42f665d37
|
|
4
|
+
data.tar.gz: c495f4582ca15271799caa1b71a3922c3e5bbaafa692eda9476862465971556e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb5b9e8adc78ba955f5125ca6c673894fea37fb8dd9a61775b4f44f87b9afc2c7fd9dbf8c645cd380af6fca7f754f03654a139539d9180060c6475544320b864
|
|
7
|
+
data.tar.gz: d86d87bb3c7d8dd6c57692c63c0592b99f950cd96b8b6bce1038678678a0c983c6b734ef380852b6edb8bb1e4e205fa6938b5045c8d5337f82fd4c40fb5aac2e
|
data/.rubocop.yml
ADDED
data/Gemfile.lock
CHANGED
data/lib/aptible/cli/agent.rb
CHANGED
|
@@ -92,10 +92,13 @@ module Aptible
|
|
|
92
92
|
level = Logger::WARN
|
|
93
93
|
debug_level = ENV['APTIBLE_DEBUG']
|
|
94
94
|
level = debug_level if debug_level
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
if ENV['BUNDLER_VERSION'] && \
|
|
96
|
+
ENV['APTIBLE_LOG_HTTP_REQUEST_RESPONSE'] && \
|
|
97
|
+
!ENV['APTIBLE_LOG_HTTP_REQUEST_RESPONSE'] \
|
|
98
|
+
.downcase.start_with?('f')
|
|
99
|
+
require 'httplog'
|
|
100
|
+
HttpLog.configure { |c| c.log_headers = true }
|
|
101
|
+
end
|
|
99
102
|
conf.logger.tap { |l| l.level = level }
|
|
100
103
|
end
|
|
101
104
|
warn_sso_enforcement
|
|
@@ -110,12 +110,9 @@ module Aptible
|
|
|
110
110
|
end
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
app = app_from_handle(s.app_handle, environment)
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
when 1
|
|
117
|
-
return apps.first
|
|
118
|
-
when 0
|
|
115
|
+
if app.nil?
|
|
119
116
|
err_bits = ['Could not find app', s.app_handle]
|
|
120
117
|
if environment
|
|
121
118
|
err_bits << 'in environment'
|
|
@@ -125,11 +122,9 @@ module Aptible
|
|
|
125
122
|
end
|
|
126
123
|
err_bits << s.explain
|
|
127
124
|
raise Thor::Error, err_bits.join(' ')
|
|
128
|
-
else
|
|
129
|
-
err = "Multiple apps named #{s.app_handle} exist, please specify " \
|
|
130
|
-
'with --environment'
|
|
131
|
-
raise Thor::Error, err
|
|
132
125
|
end
|
|
126
|
+
|
|
127
|
+
app
|
|
133
128
|
end
|
|
134
129
|
|
|
135
130
|
def ensure_service(options, type)
|
|
@@ -166,13 +161,20 @@ module Aptible
|
|
|
166
161
|
)
|
|
167
162
|
end
|
|
168
163
|
|
|
169
|
-
def
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
164
|
+
def app_from_handle(handle, environment)
|
|
165
|
+
url = "/find/app?handle=#{handle}"
|
|
166
|
+
url += "&environment=#{environment.handle}" unless environment.nil?
|
|
167
|
+
|
|
168
|
+
Aptible::Api::App.find_by_url(
|
|
169
|
+
url,
|
|
170
|
+
token: fetch_token
|
|
171
|
+
)
|
|
172
|
+
rescue HyperResource::ClientError => e
|
|
173
|
+
raise unless e.body.is_a?(Hash) &&
|
|
174
|
+
e.body['error'] == 'multiple_resources_found'
|
|
175
|
+
raise Thor::Error,
|
|
176
|
+
"Multiple apps named #{handle} exist, please specify " \
|
|
177
|
+
'with --environment'
|
|
176
178
|
end
|
|
177
179
|
|
|
178
180
|
def extract_env(args)
|
|
@@ -196,6 +198,17 @@ module Aptible
|
|
|
196
198
|
raise Thor::Error, "Invalid argument: #{k}" if v.nil?
|
|
197
199
|
end
|
|
198
200
|
|
|
201
|
+
def current_configuration(app)
|
|
202
|
+
conf_link = app.links['current_configuration']
|
|
203
|
+
return unless conf_link
|
|
204
|
+
|
|
205
|
+
Aptible::Api::Configuration.find_by_url(
|
|
206
|
+
conf_link.href,
|
|
207
|
+
token: fetch_token,
|
|
208
|
+
headers: { 'Prefer' => 'no_sensitive_extras=false' }
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
|
|
199
212
|
private
|
|
200
213
|
|
|
201
214
|
def handle_strategies
|
|
@@ -36,16 +36,10 @@ module Aptible
|
|
|
36
36
|
raise Thor::Error,
|
|
37
37
|
"Could not find environment #{environment_handle}"
|
|
38
38
|
end
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
when 0
|
|
44
|
-
raise Thor::Error, "Could not find database #{db_handle}"
|
|
45
|
-
else
|
|
46
|
-
err = 'Multiple databases exist, please specify with --environment'
|
|
47
|
-
raise Thor::Error, err
|
|
48
|
-
end
|
|
39
|
+
db = database_from_handle(db_handle, environment)
|
|
40
|
+
raise Thor::Error, "Could not find database #{db_handle}" if db.nil?
|
|
41
|
+
|
|
42
|
+
db
|
|
49
43
|
end
|
|
50
44
|
|
|
51
45
|
def databases_href
|
|
@@ -140,20 +134,27 @@ module Aptible
|
|
|
140
134
|
external_rds_databases_all.find { |a| a.handle == handle }
|
|
141
135
|
end
|
|
142
136
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
137
|
+
def database_from_handle(handle, environment)
|
|
138
|
+
url = "/find/database?handle=#{handle}"
|
|
139
|
+
url += "&environment=#{environment.handle}" unless environment.nil?
|
|
140
|
+
|
|
141
|
+
Aptible::Api::Database.find_by_url(
|
|
142
|
+
url,
|
|
143
|
+
token: fetch_token
|
|
144
|
+
)
|
|
145
|
+
rescue HyperResource::ClientError => e
|
|
146
|
+
raise unless e.body.is_a?(Hash) &&
|
|
147
|
+
e.body['error'] == 'multiple_resources_found'
|
|
148
|
+
raise Thor::Error,
|
|
149
|
+
'Multiple databases exist, please specify ' \
|
|
150
|
+
'with --environment'
|
|
150
151
|
end
|
|
151
152
|
|
|
152
153
|
def clone_database(source, dest_handle)
|
|
153
154
|
op = source.create_operation!(type: 'clone', handle: dest_handle)
|
|
154
155
|
attach_to_operation_logs(op)
|
|
155
156
|
|
|
156
|
-
|
|
157
|
+
database_from_handle(dest_handle, source.account)
|
|
157
158
|
end
|
|
158
159
|
|
|
159
160
|
def replicate_database(source, dest_handle, options)
|
|
@@ -177,7 +178,7 @@ module Aptible
|
|
|
177
178
|
op = source.create_operation!(replication_params)
|
|
178
179
|
attach_to_operation_logs(op)
|
|
179
180
|
|
|
180
|
-
replica =
|
|
181
|
+
replica = database_from_handle(dest_handle, source.account)
|
|
181
182
|
attach_to_operation_logs(replica.operations.last)
|
|
182
183
|
replica
|
|
183
184
|
end
|
|
@@ -185,6 +186,14 @@ module Aptible
|
|
|
185
186
|
# Creates a local tunnel and yields the helper
|
|
186
187
|
|
|
187
188
|
def with_local_tunnel(credential, port = 0, target_account = nil)
|
|
189
|
+
# Credential has the senstive header set, and for some reason
|
|
190
|
+
# credential.create_operation! _lists all operations_. This would
|
|
191
|
+
# generate a show activity for every previous tunnel operation.
|
|
192
|
+
# So, we strip the sensitive header first to prevent that from happening
|
|
193
|
+
# This will also strip the connection_url, but we don't need it from
|
|
194
|
+
# this point on.
|
|
195
|
+
credential = without_sensitive(credential)
|
|
196
|
+
# Twice by here??
|
|
188
197
|
op = if target_account.nil?
|
|
189
198
|
credential.create_operation!(
|
|
190
199
|
type: 'tunnel',
|
|
@@ -284,7 +293,7 @@ module Aptible
|
|
|
284
293
|
raise Thor::Error, 'This command only works for PostgreSQL'
|
|
285
294
|
end
|
|
286
295
|
|
|
287
|
-
credential = find_credential(database)
|
|
296
|
+
credential, _credentials = find_credential(database)
|
|
288
297
|
|
|
289
298
|
with_local_tunnel(credential) do |tunnel_helper|
|
|
290
299
|
yield local_url(credential, tunnel_helper.port)
|
|
@@ -304,7 +313,7 @@ module Aptible
|
|
|
304
313
|
remote_url = credential.connection_url
|
|
305
314
|
|
|
306
315
|
uri = URI.parse(remote_url)
|
|
307
|
-
domain = credential.database.account.stack.internal_domain
|
|
316
|
+
domain = without_sensitive(credential).database.account.stack.internal_domain
|
|
308
317
|
"#{uri.scheme}://#{uri.user}:#{uri.password}@" \
|
|
309
318
|
"localhost.#{domain}:#{local_port}#{uri.path}"
|
|
310
319
|
end
|
|
@@ -314,21 +323,25 @@ module Aptible
|
|
|
314
323
|
raise Thor::Error, "Database #{database.handle} is not provisioned"
|
|
315
324
|
end
|
|
316
325
|
|
|
326
|
+
# Get the database credentials, without going using `with_senstive(database)`, as that
|
|
327
|
+
# would get the embedded last_operation, and generate an extra show activity
|
|
328
|
+
creds_link = database.links['database_credentials']
|
|
329
|
+
database_credentials = Aptible::Api::DatabaseCredential.all(
|
|
330
|
+
href: creds_link.href,
|
|
331
|
+
token: fetch_token,
|
|
332
|
+
headers: { 'Prefer' => 'no_sensitive_extras=false' }
|
|
333
|
+
)
|
|
334
|
+
|
|
317
335
|
finder = proc { |c| c.default }
|
|
318
336
|
finder = proc { |c| c.type == type } if type
|
|
319
|
-
credential =
|
|
337
|
+
credential = database_credentials.find(&finder)
|
|
320
338
|
|
|
321
|
-
return credential
|
|
339
|
+
# It may be weird to return the credential and all the credentials, but the db:tunnel
|
|
340
|
+
# command lists all the credential types if you do not provide one, and we want to avoid
|
|
341
|
+
# generating more show activity than needed
|
|
342
|
+
return credential, database_credentials if credential
|
|
322
343
|
|
|
323
|
-
types =
|
|
324
|
-
|
|
325
|
-
# On v1, we fallback to the DB. We make sure to make --type work, to
|
|
326
|
-
# avoid a confusing experience for customers.
|
|
327
|
-
if database.account.stack.version == 'v1'
|
|
328
|
-
types << database.type
|
|
329
|
-
types.uniq!
|
|
330
|
-
return database if type.nil? || type == database.type
|
|
331
|
-
end
|
|
344
|
+
types = database_credentials.map(&:type)
|
|
332
345
|
|
|
333
346
|
valid = types.join(', ')
|
|
334
347
|
|
|
@@ -365,6 +378,10 @@ module Aptible
|
|
|
365
378
|
end
|
|
366
379
|
|
|
367
380
|
def render_database(database, account)
|
|
381
|
+
# Maybe reload with senstive data
|
|
382
|
+
# Definately don't load the embedded last_operation
|
|
383
|
+
database.href = database.href + '?no_embed=true'
|
|
384
|
+
database = with_sensitive(database) if database.connection_url.nil?
|
|
368
385
|
Formatter.render(Renderer.current) do |root|
|
|
369
386
|
root.keyed_object('connection_url') do |node|
|
|
370
387
|
ResourceFormatter.inject_database(node, database, account)
|
|
@@ -42,10 +42,11 @@ module Aptible
|
|
|
42
42
|
|
|
43
43
|
def environment_from_handle(handle)
|
|
44
44
|
return nil unless handle
|
|
45
|
-
|
|
46
|
-
Aptible::Api::Account.
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
|
|
46
|
+
Aptible::Api::Account.find_by_url(
|
|
47
|
+
"/find/account?handle=#{handle}",
|
|
48
|
+
token: fetch_token
|
|
49
|
+
)
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def environment_map(accounts)
|
|
@@ -65,7 +65,9 @@ module Aptible
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def ensure_log_drain(account, handle)
|
|
68
|
-
|
|
68
|
+
link = account.links['log_drains'].base_href
|
|
69
|
+
account_drains = Aptible::Api::LogDrain.all(href: link, token: fetch_token)
|
|
70
|
+
drains = account_drains.select { |d| d.handle == handle }
|
|
69
71
|
|
|
70
72
|
if drains.empty?
|
|
71
73
|
raise Thor::Error, "No drain found with handle #{handle}"
|
|
@@ -19,7 +19,9 @@ module Aptible
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def ensure_metric_drain(account, handle)
|
|
22
|
-
|
|
22
|
+
link = account.links['metric_drains'].base_href
|
|
23
|
+
account_drains = Aptible::Api::MetricDrain.all(href: link, token: fetch_token)
|
|
24
|
+
drains = account_drains.select { |d| d.handle == handle }
|
|
23
25
|
|
|
24
26
|
if drains.empty?
|
|
25
27
|
raise Thor::Error, "No drain found with handle #{handle}"
|
|
@@ -128,6 +128,10 @@ module Aptible
|
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
def inject_database(node, database, account)
|
|
131
|
+
# Some callers pass a database object with sensitive attributes already, others do not.
|
|
132
|
+
# Avoid creating extra 'show' activity if we already have the needed info
|
|
133
|
+
database = with_sensitive(database) if database.objects[:database_credentials].nil?
|
|
134
|
+
|
|
131
135
|
node.value('id', database.id)
|
|
132
136
|
node.value('handle', database.handle)
|
|
133
137
|
node.value('created_at', database.created_at)
|
|
@@ -243,6 +247,9 @@ module Aptible
|
|
|
243
247
|
log_drain.drain_ephemeral_sessions)
|
|
244
248
|
node.value('drain_proxies', log_drain.drain_proxies)
|
|
245
249
|
|
|
250
|
+
# These can be either optional for the drain type,
|
|
251
|
+
# or sensitive attributes we don't need to worry about
|
|
252
|
+
# in text output
|
|
246
253
|
optional_attrs = %w(drain_username drain_host drain_port url)
|
|
247
254
|
optional_attrs.each do |attr|
|
|
248
255
|
value = log_drain.attributes[attr]
|
|
@@ -257,7 +264,13 @@ module Aptible
|
|
|
257
264
|
node.value('handle', metric_drain.handle)
|
|
258
265
|
node.value('drain_type', metric_drain.drain_type)
|
|
259
266
|
node.value('created_at', metric_drain.created_at)
|
|
260
|
-
|
|
267
|
+
|
|
268
|
+
# Sensitive attributes we don't need to worry about being missing in text output
|
|
269
|
+
optional_attrs = %w(drain_configuration)
|
|
270
|
+
optional_attrs.each do |attr|
|
|
271
|
+
value = metric_drain.attributes[attr]
|
|
272
|
+
node.value(attr, value) unless value.nil?
|
|
273
|
+
end
|
|
261
274
|
|
|
262
275
|
attach_account(node, account)
|
|
263
276
|
end
|
|
@@ -15,7 +15,7 @@ module Aptible
|
|
|
15
15
|
telemetry(__method__, options)
|
|
16
16
|
|
|
17
17
|
app = ensure_app(options)
|
|
18
|
-
config = app
|
|
18
|
+
config = current_configuration(app)
|
|
19
19
|
env = config ? config.env : {}
|
|
20
20
|
|
|
21
21
|
Formatter.render(Renderer.current) do |root|
|
|
@@ -38,7 +38,7 @@ module Aptible
|
|
|
38
38
|
telemetry(__method__, options)
|
|
39
39
|
|
|
40
40
|
app = ensure_app(options)
|
|
41
|
-
config = app
|
|
41
|
+
config = current_configuration(app)
|
|
42
42
|
env = config ? config.env : {}
|
|
43
43
|
|
|
44
44
|
Formatter.render(Renderer.current) do |root|
|
|
@@ -335,13 +335,13 @@ module Aptible
|
|
|
335
335
|
return use_rds_tunnel(handle, desired_port) if aws_rds_db?(handle)
|
|
336
336
|
|
|
337
337
|
database = ensure_database(options.merge(db: handle))
|
|
338
|
-
credential = find_credential(database, options[:type])
|
|
338
|
+
credential, credentials = find_credential(database, options[:type])
|
|
339
339
|
|
|
340
340
|
m = "Creating #{credential.type} tunnel to #{database.handle}..."
|
|
341
341
|
CLI.logger.info m
|
|
342
342
|
|
|
343
343
|
if options[:type].nil?
|
|
344
|
-
types =
|
|
344
|
+
types = credentials.map(&:type)
|
|
345
345
|
unless types.empty?
|
|
346
346
|
valid = types.join(', ')
|
|
347
347
|
CLI.logger.info 'Use --type TYPE to specify a tunnel type'
|
|
@@ -481,7 +481,8 @@ module Aptible
|
|
|
481
481
|
telemetry(__method__, options.merge(handle: handle))
|
|
482
482
|
|
|
483
483
|
database = ensure_database(options.merge(db: handle))
|
|
484
|
-
|
|
484
|
+
|
|
485
|
+
credential, _credentials = find_credential(database, options[:type])
|
|
485
486
|
|
|
486
487
|
Formatter.render(Renderer.current) do |root|
|
|
487
488
|
root.keyed_object('connection_url') do |node|
|
|
@@ -44,6 +44,8 @@ module Aptible
|
|
|
44
44
|
account = acc_map[drain.links.account.href]
|
|
45
45
|
next if account.nil?
|
|
46
46
|
|
|
47
|
+
# JSON output format we potentially show sensitive attributes
|
|
48
|
+
drain = with_sensitive(drain) if Renderer.format == 'json'
|
|
47
49
|
node.object do |n|
|
|
48
50
|
ResourceFormatter.inject_log_drain(n, drain, account)
|
|
49
51
|
end
|
|
@@ -134,11 +134,11 @@ module Aptible
|
|
|
134
134
|
options[:string_matches]
|
|
135
135
|
)
|
|
136
136
|
elsif id_options.any?
|
|
137
|
-
if options[:container_id]
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
search_attrs = if options[:container_id]
|
|
138
|
+
{ container_id: options[:container_id] }
|
|
139
|
+
else
|
|
140
|
+
{ type: r_type, id: id_options.compact.first }
|
|
141
|
+
end
|
|
142
142
|
files = find_s3_files_by_attrs(
|
|
143
143
|
options[:region],
|
|
144
144
|
options[:bucket],
|
|
@@ -38,6 +38,8 @@ module Aptible
|
|
|
38
38
|
account = acc_map[drain.links.account.href]
|
|
39
39
|
next if account.nil?
|
|
40
40
|
|
|
41
|
+
# JSON output format we potentially show sensitive attributes
|
|
42
|
+
drain = with_sensitive(drain) if Renderer.format == 'json'
|
|
41
43
|
node.object do |n|
|
|
42
44
|
ResourceFormatter.inject_metric_drain(n, drain, account)
|
|
43
45
|
end
|
data/lib/aptible/cli/version.rb
CHANGED
data/lib/aptible/cli.rb
CHANGED
|
@@ -7,6 +7,20 @@ require 'aptible/cli/formatter'
|
|
|
7
7
|
require 'aptible/cli/renderer'
|
|
8
8
|
require 'aptible/cli/resource_formatter'
|
|
9
9
|
|
|
10
|
+
# Set no_sensitive_extras=true as the default for all API resources.
|
|
11
|
+
# This avoids returning sensitive embedded data unless explicitly requested.
|
|
12
|
+
Aptible::Api::Resource.headers = { 'Prefer' => 'no_sensitive_extras=true' }
|
|
13
|
+
|
|
14
|
+
def with_sensitive(resource)
|
|
15
|
+
resource.headers['Prefer'] = 'no_sensitive_extras=false'
|
|
16
|
+
resource.find_by_url(resource.href)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def without_sensitive(resource)
|
|
20
|
+
resource.headers['Prefer'] = 'no_sensitive_extras=true'
|
|
21
|
+
resource.find_by_url(resource.href)
|
|
22
|
+
end
|
|
23
|
+
|
|
10
24
|
module Aptible
|
|
11
25
|
module CLI
|
|
12
26
|
class TtyLogFormatter
|
|
@@ -20,13 +20,17 @@ describe Aptible::CLI::Agent do
|
|
|
20
20
|
before do
|
|
21
21
|
allow(subject).to receive(:save_token)
|
|
22
22
|
allow(subject).to receive(:attach_to_operation_logs)
|
|
23
|
-
allow(subject).to receive(:fetch_token) {
|
|
23
|
+
allow(subject).to receive(:fetch_token) { token }
|
|
24
|
+
allow(Aptible::Api::Account).to receive(:find_by_url)
|
|
25
|
+
.with("/find/account?handle=#{account.handle}", token: token)
|
|
26
|
+
.and_return(account)
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
let!(:account) { Fabricate(:account) }
|
|
27
30
|
let!(:app) { Fabricate(:app, handle: 'hello', account: account) }
|
|
28
31
|
let!(:service) { Fabricate(:service, app: app, process_type: 'web') }
|
|
29
32
|
let(:op) { Fabricate(:operation, status: 'succeeded', resource: app) }
|
|
33
|
+
let(:token) { double 'token' }
|
|
30
34
|
|
|
31
35
|
describe '#apps' do
|
|
32
36
|
it 'lists an app in an account' do
|
|
@@ -83,13 +87,14 @@ describe Aptible::CLI::Agent do
|
|
|
83
87
|
|
|
84
88
|
it 'lists filters down to one account' do
|
|
85
89
|
account2 = Fabricate(:account, handle: 'account2')
|
|
90
|
+
allow(Aptible::Api::Account).to receive(:find_by_url)
|
|
91
|
+
.with("/find/account?handle=#{account2.handle}", token: token)
|
|
92
|
+
.and_return(account2)
|
|
86
93
|
app2 = Fabricate(:app, account: account2, handle: 'app2')
|
|
87
94
|
allow(subject).to receive(:options)
|
|
88
95
|
.and_return(environment: account2.handle)
|
|
89
96
|
allow(Aptible::Api::App).to receive(:all).and_return([app2])
|
|
90
97
|
|
|
91
|
-
allow(Aptible::Api::Account).to receive(:all)
|
|
92
|
-
.and_return([account, account2])
|
|
93
98
|
subject.send('apps')
|
|
94
99
|
|
|
95
100
|
expect(captured_output_text)
|
|
@@ -215,8 +220,11 @@ describe Aptible::CLI::Agent do
|
|
|
215
220
|
|
|
216
221
|
describe '#apps:rename' do
|
|
217
222
|
before do
|
|
218
|
-
allow(Aptible::Api::App).to receive(:
|
|
219
|
-
|
|
223
|
+
allow(Aptible::Api::App).to receive(:find_by_url)
|
|
224
|
+
.and_return(nil)
|
|
225
|
+
allow(Aptible::Api::App).to receive(:find_by_url)
|
|
226
|
+
.with("/find/app?handle=#{app.handle}&environment=#{account.handle}", token: token)
|
|
227
|
+
.and_return(app)
|
|
220
228
|
end
|
|
221
229
|
|
|
222
230
|
before(:each) do
|
|
@@ -256,7 +264,6 @@ describe Aptible::CLI::Agent do
|
|
|
256
264
|
|
|
257
265
|
describe '#apps:scale' do
|
|
258
266
|
before do
|
|
259
|
-
allow(Aptible::Api::App).to receive(:all) { [app] }
|
|
260
267
|
allow(Aptible::Api::Account).to receive(:all) { [account] }
|
|
261
268
|
end
|
|
262
269
|
|
|
@@ -268,9 +275,9 @@ describe Aptible::CLI::Agent do
|
|
|
268
275
|
.with('foobar')
|
|
269
276
|
.and_return(account)
|
|
270
277
|
|
|
271
|
-
expect(subject).to receive(:
|
|
278
|
+
expect(subject).to receive(:app_from_handle)
|
|
272
279
|
.with('hello', account)
|
|
273
|
-
.and_return(
|
|
280
|
+
.and_return(app)
|
|
274
281
|
end
|
|
275
282
|
|
|
276
283
|
def stub_options(**opts)
|
|
@@ -330,7 +337,7 @@ describe Aptible::CLI::Agent do
|
|
|
330
337
|
allow(subject).to receive(:options) do
|
|
331
338
|
{ environment: 'foo', app: 'web', container_count: 2 }
|
|
332
339
|
end
|
|
333
|
-
allow(Aptible::Api::Account).to receive(:
|
|
340
|
+
allow(Aptible::Api::Account).to receive(:find_by_url).and_return(nil)
|
|
334
341
|
allow(service).to receive(:create_operation!) { op }
|
|
335
342
|
|
|
336
343
|
expect do
|
|
@@ -394,7 +401,14 @@ describe Aptible::CLI::Agent do
|
|
|
394
401
|
|
|
395
402
|
before do
|
|
396
403
|
account.apps = apps
|
|
397
|
-
allow(Aptible::Api::App).to receive(:
|
|
404
|
+
allow(Aptible::Api::App).to receive(:find_by_url)
|
|
405
|
+
.and_return(nil)
|
|
406
|
+
allow(Aptible::Api::App).to receive(:find_by_url)
|
|
407
|
+
.with("/find/app?handle=#{app.handle}&environment=#{account.handle}", token: token)
|
|
408
|
+
.and_return(app)
|
|
409
|
+
allow(Aptible::Api::App).to receive(:find_by_url)
|
|
410
|
+
.with("/find/app?handle=#{app.handle}", token: token)
|
|
411
|
+
.and_return(app)
|
|
398
412
|
end
|
|
399
413
|
|
|
400
414
|
it 'scopes the app search to an environment if provided' do
|
|
@@ -415,27 +429,21 @@ describe Aptible::CLI::Agent do
|
|
|
415
429
|
end
|
|
416
430
|
|
|
417
431
|
it 'fails if no app is found' do
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
strategies = [dummy_strategy_factory('hello', nil, true)]
|
|
432
|
+
strategies = [dummy_strategy_factory('goodbye', nil, true)]
|
|
421
433
|
allow(subject).to receive(:handle_strategies) { strategies }
|
|
422
434
|
|
|
423
|
-
expect { subject.ensure_app }.to raise_error(/not find app
|
|
435
|
+
expect { subject.ensure_app }.to raise_error(/not find app goodbye/)
|
|
424
436
|
end
|
|
425
437
|
|
|
426
438
|
it 'explains the strategy when it fails' do
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
strategies = [dummy_strategy_factory('hello', nil, true)]
|
|
439
|
+
strategies = [dummy_strategy_factory('goodbye', nil, true)]
|
|
430
440
|
allow(subject).to receive(:handle_strategies) { strategies }
|
|
431
441
|
|
|
432
442
|
expect { subject.ensure_app }.to raise_error(/from dummy/)
|
|
433
443
|
end
|
|
434
444
|
|
|
435
445
|
it 'indicates the environment when the app search was scoped' do
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
strategies = [dummy_strategy_factory('hello', 'aptible', true)]
|
|
446
|
+
strategies = [dummy_strategy_factory('goodbye', 'aptible', true)]
|
|
439
447
|
allow(subject).to receive(:handle_strategies) { strategies }
|
|
440
448
|
|
|
441
449
|
expect(subject).to receive(:environment_from_handle).with('aptible')
|
|
@@ -445,7 +453,12 @@ describe Aptible::CLI::Agent do
|
|
|
445
453
|
end
|
|
446
454
|
|
|
447
455
|
it 'fails if multiple apps are found' do
|
|
448
|
-
|
|
456
|
+
error = HyperResource::ClientError.new('multiple resources found')
|
|
457
|
+
allow(error).to receive(:body)
|
|
458
|
+
.and_return('error' => 'multiple_resources_found')
|
|
459
|
+
allow(Aptible::Api::App).to receive(:find_by_url)
|
|
460
|
+
.with('/find/app?handle=hello', token: token)
|
|
461
|
+
.and_raise(error)
|
|
449
462
|
|
|
450
463
|
strategies = [dummy_strategy_factory('hello', nil, true)]
|
|
451
464
|
allow(subject).to receive(:handle_strategies) { strategies }
|
|
@@ -13,7 +13,9 @@ describe Aptible::CLI::Agent do
|
|
|
13
13
|
|
|
14
14
|
before do
|
|
15
15
|
allow(subject).to receive(:fetch_token).and_return(token)
|
|
16
|
-
allow(Aptible::Api::Account).to receive(:
|
|
16
|
+
allow(Aptible::Api::Account).to receive(:find_by_url)
|
|
17
|
+
.with("/find/account?handle=#{account.handle}", token: token)
|
|
18
|
+
.and_return(account)
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
describe '#backup_retention_policy' do
|