aptible-cli 0.26.1 → 0.26.4
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 +2 -2
- data/Makefile +3 -0
- data/README.md +83 -83
- data/lib/aptible/cli/agent.rb +14 -4
- data/lib/aptible/cli/helpers/app.rb +11 -0
- data/lib/aptible/cli/helpers/database.rb +29 -13
- 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 +16 -3
- data/lib/aptible/cli/subcommands/apps.rb +2 -2
- data/lib/aptible/cli/subcommands/config.rb +8 -8
- data/lib/aptible/cli/subcommands/db.rb +4 -3
- data/lib/aptible/cli/subcommands/deploy.rb +1 -1
- 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/subcommands/rebuild.rb +2 -1
- data/lib/aptible/cli/subcommands/restart.rb +2 -1
- data/lib/aptible/cli/subcommands/services.rb +4 -4
- data/lib/aptible/cli/subcommands/ssh.rb +1 -1
- data/lib/aptible/cli/version.rb +1 -1
- data/lib/aptible/cli.rb +14 -0
- data/spec/aptible/cli/agent_spec.rb +70 -0
- data/spec/aptible/cli/subcommands/config_spec.rb +8 -0
- data/spec/aptible/cli/subcommands/db_spec.rb +3 -51
- data/spec/aptible/cli/subcommands/log_drain_spec.rb +6 -0
- data/spec/aptible/cli/subcommands/metric_drain_spec.rb +4 -0
- 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 +5 -0
- data/spec/support/stub_aptible_resource.rb +25 -0
- metadata +5 -2
|
@@ -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)
|
|
@@ -203,8 +207,8 @@ module Aptible
|
|
|
203
207
|
node.value('type', 'https')
|
|
204
208
|
node.value('port', port)
|
|
205
209
|
node.value('load_balancing_algorithm_type', vhost
|
|
206
|
-
.load_balancing_algorithm_type)
|
|
207
|
-
node.value('shared', vhost.shared)
|
|
210
|
+
.load_balancing_algorithm_type || 'round_robin')
|
|
211
|
+
node.value('shared', vhost.shared || 'false')
|
|
208
212
|
end
|
|
209
213
|
|
|
210
214
|
node.value('internal', vhost.internal)
|
|
@@ -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
|
|
@@ -65,7 +65,7 @@ module Aptible
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
desc 'apps:scale SERVICE ' \
|
|
68
|
+
desc 'apps:scale [--app APP] SERVICE ' \
|
|
69
69
|
'[--container-count COUNT] [--container-size SIZE_MB] ' \
|
|
70
70
|
'[--container-profile PROFILE]',
|
|
71
71
|
'Scale a service'
|
|
@@ -107,7 +107,7 @@ module Aptible
|
|
|
107
107
|
attach_to_operation_logs(op)
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
desc 'apps:deprovision', 'Deprovision an app'
|
|
110
|
+
desc 'apps:deprovision [--app APP]', 'Deprovision an app'
|
|
111
111
|
app_options
|
|
112
112
|
define_method 'apps:deprovision' do
|
|
113
113
|
telemetry(__method__, options)
|
|
@@ -9,13 +9,13 @@ module Aptible
|
|
|
9
9
|
include Helpers::App
|
|
10
10
|
include Helpers::Telemetry
|
|
11
11
|
|
|
12
|
-
desc 'config', "Print an app's current configuration"
|
|
12
|
+
desc 'config [--app APP]', "Print an app's current configuration"
|
|
13
13
|
app_options
|
|
14
14
|
def config
|
|
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|
|
|
@@ -31,14 +31,14 @@ module Aptible
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
desc 'config:get [VAR1]',
|
|
34
|
+
desc 'config:get [--app APP] [VAR1]',
|
|
35
35
|
"Print a specific key within an app's current configuration"
|
|
36
36
|
app_options
|
|
37
37
|
define_method 'config:get' do |*args|
|
|
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|
|
|
@@ -50,7 +50,7 @@ module Aptible
|
|
|
50
50
|
end
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
desc 'config:add [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
53
|
+
desc 'config:add [--app APP] [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
54
54
|
'Add an ENV variable to an app'
|
|
55
55
|
app_options
|
|
56
56
|
define_method 'config:add' do |*args|
|
|
@@ -64,7 +64,7 @@ module Aptible
|
|
|
64
64
|
attach_to_operation_logs(operation)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
-
desc 'config:set [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
67
|
+
desc 'config:set [--app APP] [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
68
68
|
'Add an ENV variable to an app'
|
|
69
69
|
app_options
|
|
70
70
|
define_method 'config:set' do |*args|
|
|
@@ -72,7 +72,7 @@ module Aptible
|
|
|
72
72
|
send('config:add', *args)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
desc 'config:rm [VAR1] [VAR2] [...]',
|
|
75
|
+
desc 'config:rm [--app APP] [VAR1] [VAR2] [...]',
|
|
76
76
|
'Remove an ENV variable from an app'
|
|
77
77
|
app_options
|
|
78
78
|
define_method 'config:rm' do |*args|
|
|
@@ -90,7 +90,7 @@ module Aptible
|
|
|
90
90
|
attach_to_operation_logs(operation)
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
-
desc 'config:unset [VAR1] [VAR2] [...]',
|
|
93
|
+
desc 'config:unset [--app APP] [VAR1] [VAR2] [...]',
|
|
94
94
|
'Remove an ENV variable from an app'
|
|
95
95
|
app_options
|
|
96
96
|
define_method 'config:unset' do |*args|
|
|
@@ -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|
|
|
@@ -19,7 +19,7 @@ module Aptible
|
|
|
19
19
|
include Helpers::App
|
|
20
20
|
include Helpers::Telemetry
|
|
21
21
|
|
|
22
|
-
desc 'deploy [OPTIONS] [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
22
|
+
desc 'deploy [--app APP] [OPTIONS] [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
23
23
|
'Deploy an app'
|
|
24
24
|
option :git_commitish,
|
|
25
25
|
desc: 'Deploy a specific git commit or branch: the ' \
|
|
@@ -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
|
|
@@ -8,7 +8,8 @@ module Aptible
|
|
|
8
8
|
include Helpers::App
|
|
9
9
|
include Helpers::Telemetry
|
|
10
10
|
|
|
11
|
-
desc 'rebuild
|
|
11
|
+
desc 'rebuild [--app APP]',
|
|
12
|
+
'Rebuild an app, and restart its services'
|
|
12
13
|
app_options
|
|
13
14
|
def rebuild
|
|
14
15
|
telemetry(__method__, options)
|
|
@@ -8,7 +8,8 @@ module Aptible
|
|
|
8
8
|
include Helpers::App
|
|
9
9
|
include Helpers::Telemetry
|
|
10
10
|
|
|
11
|
-
desc 'restart
|
|
11
|
+
desc 'restart [--app APP]',
|
|
12
|
+
'Restart all services associated with an app'
|
|
12
13
|
option :simulate_oom,
|
|
13
14
|
type: :boolean,
|
|
14
15
|
desc: 'Add this flag to simulate an OOM restart and test ' \
|
|
@@ -7,7 +7,7 @@ module Aptible
|
|
|
7
7
|
include Helpers::App
|
|
8
8
|
include Helpers::Telemetry
|
|
9
9
|
|
|
10
|
-
desc 'services', 'List Services for an App'
|
|
10
|
+
desc 'services [--app APP]', 'List Services for an App'
|
|
11
11
|
app_options
|
|
12
12
|
def services
|
|
13
13
|
telemetry(__method__, options)
|
|
@@ -25,7 +25,7 @@ module Aptible
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
desc 'services:settings SERVICE'\
|
|
28
|
+
desc 'services:settings [--app APP] SERVICE'\
|
|
29
29
|
' [--force-zero-downtime|--no-force-zero-downtime]'\
|
|
30
30
|
' [--simple-health-check|--no-simple-health-check]'\
|
|
31
31
|
' [--restart-free-scaling|--no-restart-free-scaling]'\
|
|
@@ -69,7 +69,7 @@ module Aptible
|
|
|
69
69
|
service.update!(**updates) if updates.any?
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
desc 'services:autoscaling_policy SERVICE',
|
|
72
|
+
desc 'services:autoscaling_policy [--app APP] SERVICE',
|
|
73
73
|
'Returns the associated sizing policy, if any'
|
|
74
74
|
app_options
|
|
75
75
|
define_method 'services:autoscaling_policy' do |service|
|
|
@@ -99,7 +99,7 @@ module Aptible
|
|
|
99
99
|
|
|
100
100
|
map 'services:sizing_policy' => 'services:autoscaling_policy'
|
|
101
101
|
|
|
102
|
-
desc 'services:autoscaling_policy:set SERVICE '\
|
|
102
|
+
desc 'services:autoscaling_policy:set [--app APP] SERVICE '\
|
|
103
103
|
'--autoscaling-type (horizontal|vertical) '\
|
|
104
104
|
'[--metric-lookback-seconds SECONDS] '\
|
|
105
105
|
'[--percentile PERCENTILE] '\
|
|
@@ -10,7 +10,7 @@ module Aptible
|
|
|
10
10
|
include Helpers::App
|
|
11
11
|
include Helpers::Telemetry
|
|
12
12
|
|
|
13
|
-
desc 'ssh [COMMAND]', 'Run a command against an app'
|
|
13
|
+
desc 'ssh [--app APP] [COMMAND]', 'Run a command against an app'
|
|
14
14
|
long_desc <<-LONGDESC
|
|
15
15
|
Runs an interactive command against a remote Aptible app
|
|
16
16
|
|
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
|
|
@@ -428,6 +428,76 @@ describe Aptible::CLI::Agent do
|
|
|
428
428
|
end
|
|
429
429
|
end
|
|
430
430
|
|
|
431
|
+
context 'failed_login_attempt_id handling' do
|
|
432
|
+
before do
|
|
433
|
+
allow(subject).to receive(:options)
|
|
434
|
+
.and_return(email: email, password: password)
|
|
435
|
+
expect(subject).to receive(:ask).with('2FA Token: ')
|
|
436
|
+
.once
|
|
437
|
+
.and_return(token)
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
it 'should extract failed_login_attempt_id when present' do
|
|
441
|
+
e = make_oauth2_error(
|
|
442
|
+
'otp_token_required',
|
|
443
|
+
'failed_login_attempt_id' => 'attempt-123'
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
expect(Aptible::Auth::Token).to receive(:create)
|
|
447
|
+
.with(email: email, password: password, expires_in: 1.week.seconds)
|
|
448
|
+
.once
|
|
449
|
+
.and_raise(e)
|
|
450
|
+
|
|
451
|
+
expect(Aptible::Auth::Token).to receive(:create)
|
|
452
|
+
.with(email: email, password: password, otp_token: token,
|
|
453
|
+
previous_login_attempt_id: 'attempt-123',
|
|
454
|
+
expires_in: 12.hours.seconds)
|
|
455
|
+
.once
|
|
456
|
+
.and_return(token)
|
|
457
|
+
|
|
458
|
+
subject.login
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
it 'should handle nil exception_context gracefully' do
|
|
462
|
+
parsed = { 'error' => 'otp_token_required' }
|
|
463
|
+
response = double('response',
|
|
464
|
+
parsed: parsed,
|
|
465
|
+
body: 'error otp_token_required')
|
|
466
|
+
allow(response).to receive(:error=)
|
|
467
|
+
e = OAuth2::Error.new(response)
|
|
468
|
+
|
|
469
|
+
expect(Aptible::Auth::Token).to receive(:create)
|
|
470
|
+
.with(email: email, password: password, expires_in: 1.week.seconds)
|
|
471
|
+
.once
|
|
472
|
+
.and_raise(e)
|
|
473
|
+
|
|
474
|
+
expect(Aptible::Auth::Token).to receive(:create)
|
|
475
|
+
.with(email: email, password: password, otp_token: token,
|
|
476
|
+
expires_in: 12.hours.seconds)
|
|
477
|
+
.once
|
|
478
|
+
.and_return(token)
|
|
479
|
+
|
|
480
|
+
subject.login
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
it 'should handle missing failed_login_attempt_id gracefully' do
|
|
484
|
+
e = make_oauth2_error('otp_token_required', {})
|
|
485
|
+
|
|
486
|
+
expect(Aptible::Auth::Token).to receive(:create)
|
|
487
|
+
.with(email: email, password: password, expires_in: 1.week.seconds)
|
|
488
|
+
.once
|
|
489
|
+
.and_raise(e)
|
|
490
|
+
|
|
491
|
+
expect(Aptible::Auth::Token).to receive(:create)
|
|
492
|
+
.with(email: email, password: password, otp_token: token,
|
|
493
|
+
expires_in: 12.hours.seconds)
|
|
494
|
+
.once
|
|
495
|
+
.and_return(token)
|
|
496
|
+
|
|
497
|
+
subject.login
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
431
501
|
context 'SSO logins' do
|
|
432
502
|
let(:token) { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzUxMiJ9.eyJpZCI6I' }
|
|
433
503
|
|
|
@@ -17,6 +17,9 @@ describe Aptible::CLI::Agent do
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
before { allow(subject).to receive(:options) { { app: app.handle } } }
|
|
20
|
+
before do
|
|
21
|
+
allow(subject).to receive(:current_configuration, &:current_configuration)
|
|
22
|
+
end
|
|
20
23
|
let(:operation) { Fabricate(:operation, resource: app) }
|
|
21
24
|
|
|
22
25
|
describe '#config' do
|
|
@@ -60,6 +63,11 @@ describe Aptible::CLI::Agent do
|
|
|
60
63
|
end
|
|
61
64
|
|
|
62
65
|
describe '#config:get' do
|
|
66
|
+
it 'shows nothing for an unconfigured app' do
|
|
67
|
+
subject.send('config:get', 'FOO')
|
|
68
|
+
expect(captured_output_text).to eq('')
|
|
69
|
+
end
|
|
70
|
+
|
|
63
71
|
it 'should show single environment variable specified' do
|
|
64
72
|
app.current_configuration = Fabricate(
|
|
65
73
|
:configuration, app: app, env: { 'FOO' => 'BAR', 'QUX' => 'two words' }
|
|
@@ -10,6 +10,9 @@ describe Aptible::CLI::Agent do
|
|
|
10
10
|
allow(subject).to receive(:ask)
|
|
11
11
|
allow(subject).to receive(:save_token)
|
|
12
12
|
allow(subject).to receive(:fetch_token) { token }
|
|
13
|
+
allow(Aptible::Api::DatabaseCredential).to receive(:all) do
|
|
14
|
+
database.database_credentials
|
|
15
|
+
end
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
let(:handle) { 'foobar' }
|
|
@@ -226,41 +229,6 @@ describe Aptible::CLI::Agent do
|
|
|
226
229
|
expect { subject.send('db:tunnel', handle) }
|
|
227
230
|
.to raise_error(/foobar is not provisioned/im)
|
|
228
231
|
end
|
|
229
|
-
|
|
230
|
-
context 'v1 stack' do
|
|
231
|
-
before do
|
|
232
|
-
allow(database.account.stack).to receive(:version) { 'v1' }
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
it 'falls back to the database itself if no type is given' do
|
|
236
|
-
expect(subject).to receive(:with_local_tunnel).with(database, 0)
|
|
237
|
-
subject.send('db:tunnel', handle)
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
it 'falls back to the database itself if type matches' do
|
|
241
|
-
subject.options = { type: 'bar' }
|
|
242
|
-
allow(database).to receive(:type) { 'bar' }
|
|
243
|
-
|
|
244
|
-
expect(subject).to receive(:with_local_tunnel).with(database, 0)
|
|
245
|
-
subject.send('db:tunnel', handle)
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
it 'does not fall back to the database itself if type mismatches' do
|
|
249
|
-
subject.options = { type: 'bar' }
|
|
250
|
-
allow(database).to receive(:type) { 'foo' }
|
|
251
|
-
|
|
252
|
-
expect { subject.send('db:tunnel', handle) }
|
|
253
|
-
.to raise_error(/no credential with type bar/im)
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
it 'does not suggest other types that do not exist' do
|
|
257
|
-
expect(subject).to receive(:with_local_tunnel).with(database, 0)
|
|
258
|
-
|
|
259
|
-
subject.send('db:tunnel', handle)
|
|
260
|
-
|
|
261
|
-
expect(captured_logs).not_to match(/use --type type/i)
|
|
262
|
-
end
|
|
263
|
-
end
|
|
264
232
|
end
|
|
265
233
|
end
|
|
266
234
|
|
|
@@ -845,22 +813,6 @@ describe Aptible::CLI::Agent do
|
|
|
845
813
|
expect { subject.send('db:url', handle) }
|
|
846
814
|
.to raise_error(/Multiple databases/)
|
|
847
815
|
end
|
|
848
|
-
|
|
849
|
-
context 'v1 stack' do
|
|
850
|
-
before do
|
|
851
|
-
allow(database.account.stack).to receive(:version) { 'v1' }
|
|
852
|
-
end
|
|
853
|
-
|
|
854
|
-
it 'returns the URL of a specified DB' do
|
|
855
|
-
connection_url = 'postgresql://aptible-v1:password@lega.cy:4242/db'
|
|
856
|
-
expect(database).to receive(:connection_url)
|
|
857
|
-
.and_return(connection_url)
|
|
858
|
-
|
|
859
|
-
subject.send('db:url', handle)
|
|
860
|
-
|
|
861
|
-
expect(captured_output_text.chomp).to eq(connection_url)
|
|
862
|
-
end
|
|
863
|
-
end
|
|
864
816
|
end
|
|
865
817
|
end
|
|
866
818
|
|
|
@@ -14,9 +14,15 @@ describe Aptible::CLI::Agent do
|
|
|
14
14
|
.with(token: token, href: '/log_drains?per_page=5000')
|
|
15
15
|
.and_return([log_drain])
|
|
16
16
|
|
|
17
|
+
allow(Aptible::Api::LogDrain).to receive(:all)
|
|
18
|
+
.with(token: token, href: "/accounts/#{account.id}/log_drains")
|
|
19
|
+
.and_return([log_drain])
|
|
20
|
+
|
|
17
21
|
allow(Aptible::Api::Account).to receive(:all)
|
|
18
22
|
.with(token: token, href: '/accounts?per_page=5000&no_embed=true')
|
|
19
23
|
.and_return([account])
|
|
24
|
+
|
|
25
|
+
allow(account).to receive(:reload).and_return(account)
|
|
20
26
|
end
|
|
21
27
|
|
|
22
28
|
describe '#log_drain:list' do
|
|
@@ -14,6 +14,10 @@ describe Aptible::CLI::Agent do
|
|
|
14
14
|
.with(token: token, href: '/metric_drains?per_page=5000')
|
|
15
15
|
.and_return([metric_drain])
|
|
16
16
|
|
|
17
|
+
allow(Aptible::Api::MetricDrain).to receive(:all)
|
|
18
|
+
.with(token: token, href: "/accounts/#{account.id}/metric_drains")
|
|
19
|
+
.and_return([metric_drain])
|
|
20
|
+
|
|
17
21
|
allow(Aptible::Api::Account).to receive(:all)
|
|
18
22
|
.with(token: token, href: '/accounts?per_page=5000&no_embed=true')
|
|
19
23
|
.and_return([account])
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class StubAccount <
|
|
1
|
+
class StubAccount < StubAptibleResource
|
|
2
2
|
def each_app(&block)
|
|
3
3
|
return enum_for(:each_app) if block.nil?
|
|
4
4
|
apps.each(&block)
|
|
@@ -34,6 +34,14 @@ Fabricator(:account, from: :stub_account) do
|
|
|
34
34
|
hash = {
|
|
35
35
|
self: OpenStruct.new(
|
|
36
36
|
href: "/accounts/#{attrs[:id]}"
|
|
37
|
+
),
|
|
38
|
+
metric_drains: OpenStruct.new(
|
|
39
|
+
base_href: "/accounts/#{attrs[:id]}/metric_drains",
|
|
40
|
+
href: "/accounts/#{attrs[:id]}/metric_drains"
|
|
41
|
+
),
|
|
42
|
+
log_drains: OpenStruct.new(
|
|
43
|
+
base_href: "/accounts/#{attrs[:id]}/log_drains",
|
|
44
|
+
href: "/accounts/#{attrs[:id]}/log_drains"
|
|
37
45
|
)
|
|
38
46
|
}
|
|
39
47
|
OpenStruct.new(hash)
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
class StubDatabase <
|
|
1
|
+
class StubDatabase < StubAptibleResource
|
|
2
2
|
def provisioned?
|
|
3
3
|
status == 'provisioned'
|
|
4
4
|
end
|
|
5
|
+
|
|
6
|
+
def objects
|
|
7
|
+
{
|
|
8
|
+
'database_credentials' => database_credentials
|
|
9
|
+
}
|
|
10
|
+
end
|
|
5
11
|
end
|
|
6
12
|
|
|
7
13
|
Fabricator(:database, from: :stub_database) do
|
|
@@ -23,6 +29,9 @@ Fabricator(:database, from: :stub_database) do
|
|
|
23
29
|
hash = {
|
|
24
30
|
account: OpenStruct.new(
|
|
25
31
|
href: "/accounts/#{attrs[:account].id}"
|
|
32
|
+
),
|
|
33
|
+
database_credentials: OpenStruct.new(
|
|
34
|
+
href: "/databases/#{attrs[:handle]}/database_credentials"
|
|
26
35
|
)
|
|
27
36
|
}
|
|
28
37
|
OpenStruct.new(hash)
|