aptible-cli 0.24.10 → 0.26.1
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/.github/workflows/release.yml +27 -0
- data/.github/workflows/test.yml +1 -1
- data/Dockerfile +8 -5
- data/Gemfile.lock +28 -17
- data/Makefile +50 -2
- data/README.md +2 -1
- data/aptible-cli.gemspec +5 -2
- data/docker-compose.yml +5 -1
- data/lib/aptible/cli/agent.rb +9 -0
- data/lib/aptible/cli/helpers/aws_account.rb +158 -0
- data/lib/aptible/cli/helpers/database.rb +182 -2
- data/lib/aptible/cli/helpers/token.rb +14 -0
- data/lib/aptible/cli/helpers/vhost/option_set_builder.rb +8 -15
- data/lib/aptible/cli/renderer/text.rb +33 -2
- data/lib/aptible/cli/subcommands/aws_accounts.rb +252 -0
- data/lib/aptible/cli/subcommands/db.rb +67 -3
- data/lib/aptible/cli/subcommands/deploy.rb +45 -11
- data/lib/aptible/cli/subcommands/endpoints.rb +0 -2
- data/lib/aptible/cli/subcommands/organizations.rb +55 -0
- data/lib/aptible/cli/subcommands/services.rb +20 -8
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/helpers/database_spec.rb +118 -0
- data/spec/aptible/cli/helpers/token_spec.rb +70 -0
- data/spec/aptible/cli/subcommands/db_spec.rb +553 -0
- data/spec/aptible/cli/subcommands/deploy_spec.rb +42 -7
- data/spec/aptible/cli/subcommands/external_aws_accounts_spec.rb +737 -0
- data/spec/aptible/cli/subcommands/organizations_spec.rb +90 -0
- data/spec/aptible/cli/subcommands/services_spec.rb +77 -0
- data/spec/fabricators/app_external_aws_rds_connection_fabricator.rb +55 -0
- data/spec/fabricators/external_aws_account_fabricator.rb +49 -0
- data/spec/fabricators/external_aws_database_credential_fabricator.rb +46 -0
- data/spec/fabricators/external_aws_resource_fabricator.rb +72 -0
- metadata +64 -6
|
@@ -27,6 +27,21 @@ module Aptible
|
|
|
27
27
|
accounts = scoped_environments(options)
|
|
28
28
|
acc_map = environment_map(accounts)
|
|
29
29
|
|
|
30
|
+
# the below is done because an rds resource can belong to
|
|
31
|
+
# multiple accounts or none. we go through and iterate
|
|
32
|
+
# through all external_aws_resources and print them based
|
|
33
|
+
# on connections. To avoid repeated API calls, we keep
|
|
34
|
+
# them in these maps and only fetch relations when
|
|
35
|
+
# necessary.
|
|
36
|
+
rds_map = {}
|
|
37
|
+
accts_rds_map = {}
|
|
38
|
+
begin
|
|
39
|
+
rds_map, accts_rds_map = fetch_rds_databases_with_accounts
|
|
40
|
+
rescue StandardError => e
|
|
41
|
+
CLI.logger.warn 'Unable to fetch RDS databases: ' \
|
|
42
|
+
"#{e.message}"
|
|
43
|
+
end
|
|
44
|
+
|
|
30
45
|
if Renderer.format == 'json'
|
|
31
46
|
accounts.each do |account|
|
|
32
47
|
account.each_database do |db|
|
|
@@ -34,12 +49,23 @@ module Aptible
|
|
|
34
49
|
ResourceFormatter.inject_database(n, db, account)
|
|
35
50
|
end
|
|
36
51
|
end
|
|
52
|
+
next unless accts_rds_map.key? account.id
|
|
53
|
+
|
|
54
|
+
accts_rds_map[account.id].each do |rds_db|
|
|
55
|
+
rds_map.delete(rds_db.id)
|
|
56
|
+
node.object do |n|
|
|
57
|
+
ResourceFormatter.inject_database_minimal(
|
|
58
|
+
n,
|
|
59
|
+
rds_db,
|
|
60
|
+
account
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
37
64
|
end
|
|
38
65
|
else
|
|
39
66
|
databases_all.each do |db|
|
|
40
67
|
account = acc_map[db.links.account.href]
|
|
41
68
|
next if account.nil?
|
|
42
|
-
|
|
43
69
|
node.object do |n|
|
|
44
70
|
ResourceFormatter.inject_database_minimal(
|
|
45
71
|
n,
|
|
@@ -48,6 +74,34 @@ module Aptible
|
|
|
48
74
|
)
|
|
49
75
|
end
|
|
50
76
|
end
|
|
77
|
+
accounts.each do |account|
|
|
78
|
+
next unless accts_rds_map.key? account.id
|
|
79
|
+
|
|
80
|
+
accts_rds_map[account.id].each do |rds_db|
|
|
81
|
+
rds_map.delete(rds_db.id)
|
|
82
|
+
node.object do |n|
|
|
83
|
+
ResourceFormatter.inject_database_minimal(
|
|
84
|
+
n,
|
|
85
|
+
rds_db,
|
|
86
|
+
account
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Render unattached RDS databases, but exclude
|
|
94
|
+
# if environment filter set
|
|
95
|
+
unless options[:environment]
|
|
96
|
+
rds_map.each_value do |db|
|
|
97
|
+
node.object do |n|
|
|
98
|
+
ResourceFormatter.inject_database_minimal(
|
|
99
|
+
n,
|
|
100
|
+
db,
|
|
101
|
+
rds_shell_account
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
51
105
|
end
|
|
52
106
|
end
|
|
53
107
|
end
|
|
@@ -232,9 +286,13 @@ module Aptible
|
|
|
232
286
|
define_method 'db:dump' do |handle, *dump_options|
|
|
233
287
|
telemetry(__method__, options.merge(handle: handle))
|
|
234
288
|
|
|
289
|
+
filename = "#{handle}.dump"
|
|
290
|
+
if aws_rds_db?(handle)
|
|
291
|
+
return use_rds_dump(handle, filename, dump_options)
|
|
292
|
+
end
|
|
293
|
+
|
|
235
294
|
database = ensure_database(options.merge(db: handle))
|
|
236
295
|
with_postgres_tunnel(database) do |url|
|
|
237
|
-
filename = "#{handle}.dump"
|
|
238
296
|
CLI.logger.info "Dumping to #{filename}"
|
|
239
297
|
`pg_dump #{url} #{dump_options.shelljoin} > #{filename}`
|
|
240
298
|
exit $CHILD_STATUS.exitstatus unless $CHILD_STATUS.success?
|
|
@@ -252,6 +310,10 @@ module Aptible
|
|
|
252
310
|
)
|
|
253
311
|
telemetry(__method__, opts)
|
|
254
312
|
|
|
313
|
+
if aws_rds_db?(handle)
|
|
314
|
+
return use_rds_execute(handle, sql_path, options)
|
|
315
|
+
end
|
|
316
|
+
|
|
255
317
|
database = ensure_database(options.merge(db: handle))
|
|
256
318
|
with_postgres_tunnel(database) do |url|
|
|
257
319
|
CLI.logger.info "Executing #{sql_path} against #{handle}"
|
|
@@ -269,8 +331,10 @@ module Aptible
|
|
|
269
331
|
telemetry(__method__, options.merge(handle: handle))
|
|
270
332
|
|
|
271
333
|
desired_port = Integer(options[:port] || 0)
|
|
272
|
-
database = ensure_database(options.merge(db: handle))
|
|
273
334
|
|
|
335
|
+
return use_rds_tunnel(handle, desired_port) if aws_rds_db?(handle)
|
|
336
|
+
|
|
337
|
+
database = ensure_database(options.merge(db: handle))
|
|
274
338
|
credential = find_credential(database, options[:type])
|
|
275
339
|
|
|
276
340
|
m = "Creating #{credential.type} tunnel to #{database.handle}..."
|
|
@@ -2,9 +2,8 @@ module Aptible
|
|
|
2
2
|
module CLI
|
|
3
3
|
module Subcommands
|
|
4
4
|
module Deploy
|
|
5
|
-
|
|
5
|
+
DEPRECATED_ENV = Hash[%w(
|
|
6
6
|
APTIBLE_DOCKER_IMAGE
|
|
7
|
-
APTIBLE_PRIVATE_REGISTRY_EMAIL
|
|
8
7
|
APTIBLE_PRIVATE_REGISTRY_USERNAME
|
|
9
8
|
APTIBLE_PRIVATE_REGISTRY_PASSWORD
|
|
10
9
|
).map do |var|
|
|
@@ -40,11 +39,23 @@ module Aptible
|
|
|
40
39
|
desc: 'This option only affects new ' \
|
|
41
40
|
'services, not existing ones. ' \
|
|
42
41
|
'Examples: m c r'
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
|
|
43
|
+
option :docker_image,
|
|
44
|
+
type: :string,
|
|
45
|
+
desc: 'The docker image to deploy. If none specified, ' \
|
|
46
|
+
'the currently deployed image will be pulled again'
|
|
47
|
+
option :private_registry_username,
|
|
48
|
+
type: :string,
|
|
49
|
+
desc: 'Username for Docker images located in a private ' \
|
|
50
|
+
'repository'
|
|
51
|
+
option :private_registry_password,
|
|
52
|
+
type: :string,
|
|
53
|
+
desc: 'Password for Docker images located in a private ' \
|
|
54
|
+
'repository'
|
|
55
|
+
option :private_registry_email,
|
|
56
|
+
type: :string,
|
|
57
|
+
desc: 'This parameter is deprecated'
|
|
58
|
+
|
|
48
59
|
app_options
|
|
49
60
|
def deploy(*args)
|
|
50
61
|
telemetry(__method__, options)
|
|
@@ -62,20 +73,43 @@ module Aptible
|
|
|
62
73
|
|
|
63
74
|
env = extract_env(args)
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
DEPRECATED_ENV.each_pair do |opt, var|
|
|
66
77
|
val = options[opt]
|
|
78
|
+
dasherized = "--#{opt.to_s.tr('_', '-')}"
|
|
79
|
+
if env[var]
|
|
80
|
+
m = "WARNING: The environment variable #{var} " \
|
|
81
|
+
'will be deprecated. Use the option ' \
|
|
82
|
+
"#{dasherized}, instead."
|
|
83
|
+
CLI.logger.warn m
|
|
84
|
+
end
|
|
67
85
|
next unless val
|
|
68
86
|
if env[var] && env[var] != val
|
|
69
|
-
dasherized = "--#{opt.to_s.tr('_', '-')}"
|
|
70
87
|
raise Thor::Error, "The options #{dasherized} and #{var} " \
|
|
71
88
|
'cannot be set to different values'
|
|
72
89
|
end
|
|
73
|
-
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
settings = {}
|
|
93
|
+
sensitive_settings = {}
|
|
94
|
+
|
|
95
|
+
if options[:docker_image]
|
|
96
|
+
settings['APTIBLE_DOCKER_IMAGE'] = options[:docker_image]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if options[:private_registry_username]
|
|
100
|
+
sensitive_settings['APTIBLE_PRIVATE_REGISTRY_USERNAME'] =
|
|
101
|
+
options[:private_registry_username]
|
|
102
|
+
end
|
|
103
|
+
if options[:private_registry_password]
|
|
104
|
+
sensitive_settings['APTIBLE_PRIVATE_REGISTRY_PASSWORD'] =
|
|
105
|
+
options[:private_registry_password]
|
|
74
106
|
end
|
|
75
107
|
|
|
76
108
|
opts = {
|
|
77
109
|
type: 'deploy',
|
|
78
110
|
env: env,
|
|
111
|
+
settings: settings,
|
|
112
|
+
sensitive_settings: sensitive_settings,
|
|
79
113
|
git_ref: git_ref,
|
|
80
114
|
container_count: options[:container_count],
|
|
81
115
|
container_size: options[:container_size],
|
|
@@ -84,7 +118,7 @@ module Aptible
|
|
|
84
118
|
|
|
85
119
|
allow_it = [
|
|
86
120
|
opts[:git_ref],
|
|
87
|
-
opts[:
|
|
121
|
+
opts[:settings].try(:[], 'APTIBLE_DOCKER_IMAGE'),
|
|
88
122
|
app.status == 'provisioned'
|
|
89
123
|
].any? { |x| x }
|
|
90
124
|
|
|
@@ -160,7 +160,6 @@ module Aptible
|
|
|
160
160
|
port!
|
|
161
161
|
tls!
|
|
162
162
|
alb!
|
|
163
|
-
shared!
|
|
164
163
|
end
|
|
165
164
|
|
|
166
165
|
desc 'endpoints:https:create [--app APP] SERVICE',
|
|
@@ -180,7 +179,6 @@ module Aptible
|
|
|
180
179
|
port!
|
|
181
180
|
tls!
|
|
182
181
|
alb!
|
|
183
|
-
shared!
|
|
184
182
|
end
|
|
185
183
|
|
|
186
184
|
desc 'endpoints:https:modify [--app APP] ENDPOINT_HOSTNAME',
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aptible
|
|
4
|
+
module CLI
|
|
5
|
+
module Subcommands
|
|
6
|
+
module Organizations
|
|
7
|
+
def self.included(thor)
|
|
8
|
+
thor.class_eval do
|
|
9
|
+
include Helpers::Token
|
|
10
|
+
include Helpers::Telemetry
|
|
11
|
+
|
|
12
|
+
desc 'organizations', 'List all organizations'
|
|
13
|
+
def organizations
|
|
14
|
+
telemetry(__method__, options)
|
|
15
|
+
|
|
16
|
+
user_orgs_and_roles = {}
|
|
17
|
+
begin
|
|
18
|
+
roles = whoami.roles_with_organizations
|
|
19
|
+
rescue HyperResource::ClientError => e
|
|
20
|
+
raise Thor::Error, e.message
|
|
21
|
+
end
|
|
22
|
+
roles.each do |role|
|
|
23
|
+
user_orgs_and_roles[role.organization.id] ||= {
|
|
24
|
+
'org' => role.organization,
|
|
25
|
+
'roles' => []
|
|
26
|
+
}
|
|
27
|
+
user_orgs_and_roles[role.organization.id]['roles'] << role
|
|
28
|
+
end
|
|
29
|
+
Formatter.render(Renderer.current) do |root|
|
|
30
|
+
root.list do |list|
|
|
31
|
+
user_orgs_and_roles.each do |org_id, org_and_role|
|
|
32
|
+
org = org_and_role['org']
|
|
33
|
+
roles = org_and_role['roles']
|
|
34
|
+
list.object do |node|
|
|
35
|
+
node.value('id', org_id)
|
|
36
|
+
node.value('name', org.name)
|
|
37
|
+
node.list('roles') do |roles_list|
|
|
38
|
+
roles.each do |role|
|
|
39
|
+
roles_list.object do |role_node|
|
|
40
|
+
role_node.value('id', role.id)
|
|
41
|
+
role_node.value('name', role.name)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -28,16 +28,22 @@ module Aptible
|
|
|
28
28
|
desc 'services:settings SERVICE'\
|
|
29
29
|
' [--force-zero-downtime|--no-force-zero-downtime]'\
|
|
30
30
|
' [--simple-health-check|--no-simple-health-check]'\
|
|
31
|
+
' [--restart-free-scaling|--no-restart-free-scaling]'\
|
|
31
32
|
' [--stop-timeout SECONDS]',
|
|
32
33
|
'Modifies the deployment settings for a service'
|
|
33
34
|
app_options
|
|
34
35
|
option :force_zero_downtime,
|
|
35
|
-
type: :boolean,
|
|
36
|
+
type: :boolean,
|
|
36
37
|
desc: 'Force zero downtime deployments.'\
|
|
37
38
|
' Has no effect if service has an associated Endpoint'
|
|
38
39
|
option :simple_health_check,
|
|
39
|
-
type: :boolean,
|
|
40
|
+
type: :boolean,
|
|
40
41
|
desc: 'Use a simple uptime healthcheck during deployments'
|
|
42
|
+
option :restart_free_scaling,
|
|
43
|
+
type: :boolean,
|
|
44
|
+
desc: 'When enabled, scaling operations that only change '\
|
|
45
|
+
'the number of containers will not restart existing '\
|
|
46
|
+
'containers'
|
|
41
47
|
option :stop_timeout,
|
|
42
48
|
type: :numeric,
|
|
43
49
|
desc: 'The number of seconds to wait for the service '\
|
|
@@ -46,13 +52,19 @@ module Aptible
|
|
|
46
52
|
telemetry(__method__, options.merge(service: service))
|
|
47
53
|
|
|
48
54
|
service = ensure_service(options, service)
|
|
55
|
+
|
|
49
56
|
updates = {}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
updates[
|
|
55
|
-
|
|
57
|
+
vars = [
|
|
58
|
+
:force_zero_downtime, :simple_health_check,
|
|
59
|
+
:restart_free_scaling, :stop_timeout
|
|
60
|
+
]
|
|
61
|
+
vars.each { |v| updates[v] = options[v] unless options[v].nil? }
|
|
62
|
+
# The var we use with users is different than what the API
|
|
63
|
+
# expects, so we have to map it.
|
|
64
|
+
unless updates[:simple_health_check].nil?
|
|
65
|
+
updates[:naive_health_check] = \
|
|
66
|
+
updates.delete(:simple_health_check)
|
|
67
|
+
end
|
|
56
68
|
|
|
57
69
|
service.update!(**updates) if updates.any?
|
|
58
70
|
end
|
data/lib/aptible/cli/version.rb
CHANGED
|
@@ -33,4 +33,122 @@ describe Aptible::CLI::Helpers::Database do
|
|
|
33
33
|
expect(subject.validate_image_type(pg.type)).to be(true)
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
describe '#derive_account_from_conns' do
|
|
38
|
+
let(:stack) { Fabricate(:stack) }
|
|
39
|
+
let(:account1) { Fabricate(:account, handle: 'account1', stack: stack) }
|
|
40
|
+
let(:account2) { Fabricate(:account, handle: 'account2', stack: stack) }
|
|
41
|
+
let(:app1) { Fabricate(:app, account: account1) }
|
|
42
|
+
let(:app2) { Fabricate(:app, account: account2) }
|
|
43
|
+
|
|
44
|
+
let(:raw_rds_resource) do
|
|
45
|
+
Fabricate(:external_aws_resource, resource_type: 'aws_rds_db_instance')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
let(:rds_db) do
|
|
49
|
+
Aptible::CLI::Helpers::Database::RdsDatabase.new(
|
|
50
|
+
'aws:rds::test-db',
|
|
51
|
+
raw_rds_resource.id,
|
|
52
|
+
raw_rds_resource.created_at,
|
|
53
|
+
raw_rds_resource
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
let(:conn1) do
|
|
58
|
+
double('connection1', present?: true, app: app1)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
let(:conn2) do
|
|
62
|
+
double('connection2', present?: true, app: app2)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
before do
|
|
66
|
+
allow(app1).to receive(:account).and_return(account1)
|
|
67
|
+
allow(app2).to receive(:account).and_return(account2)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'when connections are empty' do
|
|
71
|
+
it 'returns nil' do
|
|
72
|
+
raw_rds_resource.instance_variable_set(
|
|
73
|
+
:@app_external_aws_rds_connections,
|
|
74
|
+
[]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
result = subject.derive_account_from_conns(rds_db)
|
|
78
|
+
expect(result).to be_nil
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context 'when no preferred account is specified' do
|
|
83
|
+
it 'returns the account from the first connection' do
|
|
84
|
+
raw_rds_resource.instance_variable_set(
|
|
85
|
+
:@app_external_aws_rds_connections,
|
|
86
|
+
[conn1, conn2]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
result = subject.derive_account_from_conns(rds_db)
|
|
90
|
+
expect(result).to eq(account1)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'handles a single connection' do
|
|
94
|
+
raw_rds_resource.instance_variable_set(
|
|
95
|
+
:@app_external_aws_rds_connections,
|
|
96
|
+
[conn2]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
result = subject.derive_account_from_conns(rds_db)
|
|
100
|
+
expect(result).to eq(account2)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context 'when a preferred account is specified' do
|
|
105
|
+
it 'returns the matching account when found' do
|
|
106
|
+
raw_rds_resource.instance_variable_set(
|
|
107
|
+
:@app_external_aws_rds_connections,
|
|
108
|
+
[conn1, conn2]
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
result = subject.derive_account_from_conns(rds_db, account2)
|
|
112
|
+
expect(result).to eq(account2)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
it 'returns nil when no matching connection is found' do
|
|
116
|
+
account3 = Fabricate(:account, handle: 'account3', stack: stack)
|
|
117
|
+
raw_rds_resource.instance_variable_set(
|
|
118
|
+
:@app_external_aws_rds_connections,
|
|
119
|
+
[conn1, conn2]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
result = subject.derive_account_from_conns(rds_db, account3)
|
|
123
|
+
expect(result).to be_nil
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'skips connections where conn.present? is false' do
|
|
127
|
+
conn_not_present = double('connection_not_present', present?: false)
|
|
128
|
+
raw_rds_resource.instance_variable_set(
|
|
129
|
+
:@app_external_aws_rds_connections,
|
|
130
|
+
[conn_not_present, conn2]
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
result = subject.derive_account_from_conns(rds_db, account2)
|
|
134
|
+
expect(result).to eq(account2)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it 'returns the first matching account when multiple matches exist' do
|
|
138
|
+
app1_duplicate = Fabricate(:app, account: account1)
|
|
139
|
+
allow(app1_duplicate).to receive(:account).and_return(account1)
|
|
140
|
+
conn1_duplicate = double('connection1_dup',
|
|
141
|
+
present?: true,
|
|
142
|
+
app: app1_duplicate)
|
|
143
|
+
|
|
144
|
+
raw_rds_resource.instance_variable_set(
|
|
145
|
+
:@app_external_aws_rds_connections,
|
|
146
|
+
[conn1, conn1_duplicate]
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
result = subject.derive_account_from_conns(rds_db, account1)
|
|
150
|
+
expect(result).to eq(account1)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
36
154
|
end
|
|
@@ -7,6 +7,10 @@ describe Aptible::CLI::Helpers::Token do
|
|
|
7
7
|
|
|
8
8
|
subject { Class.new.send(:include, described_class).new }
|
|
9
9
|
|
|
10
|
+
let(:token) { 'test-token' }
|
|
11
|
+
let(:user) { double('user', id: 'user-id', email: 'test@example.com') }
|
|
12
|
+
let(:auth_token) { double('auth_token', user: user) }
|
|
13
|
+
|
|
10
14
|
describe '#save_token / #fetch_token' do
|
|
11
15
|
it 'reads back a token it saved' do
|
|
12
16
|
subject.save_token('foo')
|
|
@@ -38,4 +42,70 @@ describe Aptible::CLI::Helpers::Token do
|
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
end
|
|
45
|
+
|
|
46
|
+
describe '#current_token' do
|
|
47
|
+
before do
|
|
48
|
+
subject.save_token(token)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'returns the current auth token' do
|
|
52
|
+
expect(Aptible::Auth::Token).to receive(:current_token)
|
|
53
|
+
.with(token: token)
|
|
54
|
+
.and_return(auth_token)
|
|
55
|
+
|
|
56
|
+
expect(subject.current_token).to eq(auth_token)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'raises Thor::Error on 401 unauthorized' do
|
|
60
|
+
response = Faraday::Response.new(status: 401)
|
|
61
|
+
error = HyperResource::ClientError.new(
|
|
62
|
+
'401 (invalid_token) Invalid Token', response: response
|
|
63
|
+
)
|
|
64
|
+
expect(Aptible::Auth::Token).to receive(:current_token)
|
|
65
|
+
.with(token: token)
|
|
66
|
+
.and_raise(error)
|
|
67
|
+
|
|
68
|
+
expect { subject.current_token }
|
|
69
|
+
.to raise_error(Thor::Error, /Invalid Token/)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'raises Thor::Error on 403 forbidden' do
|
|
73
|
+
response = Faraday::Response.new(status: 403)
|
|
74
|
+
error = HyperResource::ClientError.new('403 (forbidden) Access denied',
|
|
75
|
+
response: response)
|
|
76
|
+
expect(Aptible::Auth::Token).to receive(:current_token)
|
|
77
|
+
.with(token: token)
|
|
78
|
+
.and_raise(error)
|
|
79
|
+
|
|
80
|
+
expect { subject.current_token }
|
|
81
|
+
.to raise_error(Thor::Error, /Access denied/)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe '#whoami' do
|
|
86
|
+
before do
|
|
87
|
+
subject.save_token(token)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'returns the current user' do
|
|
91
|
+
expect(Aptible::Auth::Token).to receive(:current_token)
|
|
92
|
+
.with(token: token)
|
|
93
|
+
.and_return(auth_token)
|
|
94
|
+
|
|
95
|
+
expect(subject.whoami).to eq(user)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'raises Thor::Error on API error' do
|
|
99
|
+
response = Faraday::Response.new(status: 401)
|
|
100
|
+
error = HyperResource::ClientError.new(
|
|
101
|
+
'401 (invalid_token) Invalid Token', response: response
|
|
102
|
+
)
|
|
103
|
+
expect(Aptible::Auth::Token).to receive(:current_token)
|
|
104
|
+
.with(token: token)
|
|
105
|
+
.and_raise(error)
|
|
106
|
+
|
|
107
|
+
expect { subject.whoami }
|
|
108
|
+
.to raise_error(Thor::Error, /Invalid Token/)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
41
111
|
end
|