aptible-cli 0.25.0 → 0.26.2
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 +53 -2
- data/README.md +83 -82
- data/aptible-cli.gemspec +5 -2
- data/docker-compose.yml +5 -1
- data/lib/aptible/cli/agent.rb +16 -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/renderer/text.rb +33 -2
- data/lib/aptible/cli/resource_formatter.rb +2 -2
- data/lib/aptible/cli/subcommands/apps.rb +2 -2
- data/lib/aptible/cli/subcommands/aws_accounts.rb +252 -0
- data/lib/aptible/cli/subcommands/config.rb +6 -6
- data/lib/aptible/cli/subcommands/db.rb +67 -3
- data/lib/aptible/cli/subcommands/deploy.rb +46 -12
- data/lib/aptible/cli/subcommands/organizations.rb +55 -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 +24 -12
- data/lib/aptible/cli/subcommands/ssh.rb +1 -1
- data/lib/aptible/cli/version.rb +1 -1
- data/spec/aptible/cli/agent_spec.rb +70 -0
- 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 +65 -7
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aptible
|
|
4
|
+
module CLI
|
|
5
|
+
module Subcommands
|
|
6
|
+
module AwsAccounts
|
|
7
|
+
def self.included(thor)
|
|
8
|
+
thor.class_eval do
|
|
9
|
+
include Helpers::Token
|
|
10
|
+
include Helpers::AwsAccount
|
|
11
|
+
include Helpers::Telemetry
|
|
12
|
+
|
|
13
|
+
desc 'aws_accounts', 'List external AWS accounts', hide: true
|
|
14
|
+
option :organization_id, aliases: '--org-id',
|
|
15
|
+
type: :string,
|
|
16
|
+
default: nil,
|
|
17
|
+
desc: 'Organization ID'
|
|
18
|
+
def aws_accounts
|
|
19
|
+
telemetry(__method__, options)
|
|
20
|
+
|
|
21
|
+
accounts = aws_accounts_all
|
|
22
|
+
|
|
23
|
+
Formatter.render(Renderer.current) do |root|
|
|
24
|
+
root.list do |list|
|
|
25
|
+
accounts.each do |ext|
|
|
26
|
+
list.object do |node|
|
|
27
|
+
node.value('id', ext.id) if ext.respond_to?(:id)
|
|
28
|
+
attrs = ext.respond_to?(:attributes) ? ext.attributes : {}
|
|
29
|
+
%w(
|
|
30
|
+
aws_account_id
|
|
31
|
+
account_name
|
|
32
|
+
aws_region_primary
|
|
33
|
+
status
|
|
34
|
+
discovery_enabled
|
|
35
|
+
discovery_role_arn
|
|
36
|
+
discovery_frequency
|
|
37
|
+
account_id
|
|
38
|
+
created_at
|
|
39
|
+
updated_at
|
|
40
|
+
).each do |k|
|
|
41
|
+
v = attrs[k]
|
|
42
|
+
node.value(k, v) unless v.nil?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
desc 'aws_accounts:add ' \
|
|
51
|
+
'[--account-name ACCOUNT_NAME] ' \
|
|
52
|
+
'[--aws-account-id AWS_ACCOUNT_ID] ' \
|
|
53
|
+
'[--org-id ORGANIZATION_ID] '\
|
|
54
|
+
'[--aws-region-primary AWS_REGION] ' \
|
|
55
|
+
'[--discovery-enabled|--no-discovery-enabled] ' \
|
|
56
|
+
'[--discovery-role-arn DISCOVERY_ROLE_ARN] ' \
|
|
57
|
+
'[--discovery-frequency FREQ]', \
|
|
58
|
+
'Add a new external AWS account', hide: true
|
|
59
|
+
option :account_name, type: :string, desc: 'Display name'
|
|
60
|
+
option :aws_account_id, type: :string, desc: 'AWS Account ID'
|
|
61
|
+
option :organization_id, aliases: '--org-id',
|
|
62
|
+
type: :string,
|
|
63
|
+
default: nil,
|
|
64
|
+
desc: 'Organization ID'
|
|
65
|
+
option :aws_region_primary, type: :string,
|
|
66
|
+
desc: 'Primary AWS region'
|
|
67
|
+
option :discovery_enabled, type: :boolean,
|
|
68
|
+
desc: 'Enable resource discovery'
|
|
69
|
+
option :discovery_role_arn, type: :string,
|
|
70
|
+
desc: 'IAM Role ARN that Aptible ' \
|
|
71
|
+
'will assume to discover ' \
|
|
72
|
+
'resources in your AWS account'
|
|
73
|
+
option :discovery_frequency,
|
|
74
|
+
type: :string,
|
|
75
|
+
desc: 'Discovery frequency (e.g., daily)'
|
|
76
|
+
define_method 'aws_accounts:add' do
|
|
77
|
+
telemetry(__method__, options)
|
|
78
|
+
|
|
79
|
+
resource = create_external_aws_account!(options)
|
|
80
|
+
|
|
81
|
+
Formatter.render(Renderer.current) do |root|
|
|
82
|
+
root.object do |node|
|
|
83
|
+
node.value('id', resource.id) if resource.respond_to?(:id)
|
|
84
|
+
rattrs =
|
|
85
|
+
if resource.respond_to?(:attributes)
|
|
86
|
+
resource.attributes
|
|
87
|
+
else
|
|
88
|
+
{}
|
|
89
|
+
end
|
|
90
|
+
%w(
|
|
91
|
+
aws_account_id
|
|
92
|
+
account_name
|
|
93
|
+
aws_region_primary
|
|
94
|
+
discovery_enabled
|
|
95
|
+
discovery_role_arn
|
|
96
|
+
discovery_frequency
|
|
97
|
+
account_id
|
|
98
|
+
created_at
|
|
99
|
+
updated_at
|
|
100
|
+
).each do |k|
|
|
101
|
+
v = rattrs[k]
|
|
102
|
+
node.value(k, v) unless v.nil?
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
desc 'aws_accounts:show ID',
|
|
109
|
+
'Show an external AWS account', \
|
|
110
|
+
hide: true
|
|
111
|
+
define_method 'aws_accounts:show' do |id|
|
|
112
|
+
telemetry(__method__, options.merge(id: id))
|
|
113
|
+
ext = ensure_external_aws_account(id)
|
|
114
|
+
Formatter.render(Renderer.current) do |root|
|
|
115
|
+
root.object do |node|
|
|
116
|
+
node.value('id', ext.id)
|
|
117
|
+
rattrs =
|
|
118
|
+
if ext.respond_to?(:attributes)
|
|
119
|
+
ext.attributes
|
|
120
|
+
else
|
|
121
|
+
{}
|
|
122
|
+
end
|
|
123
|
+
%w(
|
|
124
|
+
aws_account_id
|
|
125
|
+
account_name
|
|
126
|
+
aws_region_primary
|
|
127
|
+
discovery_enabled
|
|
128
|
+
discovery_role_arn
|
|
129
|
+
discovery_frequency
|
|
130
|
+
account_id
|
|
131
|
+
created_at
|
|
132
|
+
updated_at
|
|
133
|
+
).each do |k|
|
|
134
|
+
v = rattrs[k]
|
|
135
|
+
node.value(k, v) unless v.nil?
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
desc 'aws_accounts:delete ID',
|
|
142
|
+
'Delete an external AWS account', \
|
|
143
|
+
hide: true
|
|
144
|
+
define_method 'aws_accounts:delete' do |id|
|
|
145
|
+
telemetry(__method__, options.merge(id: id))
|
|
146
|
+
|
|
147
|
+
delete_external_aws_account!(id)
|
|
148
|
+
|
|
149
|
+
Formatter.render(Renderer.current) do |root|
|
|
150
|
+
root.object do |node|
|
|
151
|
+
node.value('id', id)
|
|
152
|
+
node.value('deleted', true)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
desc 'aws_accounts:update ID ' \
|
|
158
|
+
'[--account-name ACCOUNT_NAME] ' \
|
|
159
|
+
'[--aws-account-id AWS_ACCOUNT_ID] ' \
|
|
160
|
+
'[--aws-region-primary AWS_REGION] ' \
|
|
161
|
+
'[--discovery-enabled|--no-discovery-enabled] ' \
|
|
162
|
+
'[--discovery-role-arn DISCOVERY_ROLE_ARN] ' \
|
|
163
|
+
'[--remove-discovery-role-arn] ' \
|
|
164
|
+
'[--discovery-frequency FREQ]', \
|
|
165
|
+
'Update an external AWS account', hide: true
|
|
166
|
+
option :account_name, type: :string, desc: 'New display name'
|
|
167
|
+
option :aws_account_id, type: :string, desc: 'AWS Account ID'
|
|
168
|
+
option :aws_region_primary, type: :string,
|
|
169
|
+
desc: 'Primary AWS region'
|
|
170
|
+
option :discovery_enabled, type: :boolean,
|
|
171
|
+
desc: 'Enable resource discovery'
|
|
172
|
+
option :discovery_role_arn, type: :string,
|
|
173
|
+
desc: 'IAM Role ARN that Aptible ' \
|
|
174
|
+
'will assume to discover ' \
|
|
175
|
+
'resources in your AWS account'
|
|
176
|
+
option :remove_discovery_role_arn, type: :boolean,
|
|
177
|
+
desc: 'Remove the discovery ' \
|
|
178
|
+
'role ARN from this ' \
|
|
179
|
+
'account'
|
|
180
|
+
option :discovery_frequency,
|
|
181
|
+
type: :string,
|
|
182
|
+
desc: 'Discovery frequency (e.g., daily)'
|
|
183
|
+
define_method 'aws_accounts:update' do |id|
|
|
184
|
+
telemetry(__method__, options.merge(id: id))
|
|
185
|
+
|
|
186
|
+
ext = update_external_aws_account!(id, options)
|
|
187
|
+
|
|
188
|
+
Formatter.render(Renderer.current) do |root|
|
|
189
|
+
root.object do |node|
|
|
190
|
+
node.value('id', ext.id)
|
|
191
|
+
rattrs =
|
|
192
|
+
if ext.respond_to?(:attributes)
|
|
193
|
+
ext.attributes
|
|
194
|
+
else
|
|
195
|
+
{}
|
|
196
|
+
end
|
|
197
|
+
%w(
|
|
198
|
+
aws_account_id
|
|
199
|
+
account_name
|
|
200
|
+
aws_region_primary
|
|
201
|
+
discovery_enabled
|
|
202
|
+
discovery_role_arn
|
|
203
|
+
discovery_frequency
|
|
204
|
+
account_id
|
|
205
|
+
created_at
|
|
206
|
+
updated_at
|
|
207
|
+
).each do |k|
|
|
208
|
+
v = rattrs[k]
|
|
209
|
+
node.value(k, v) unless v.nil?
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
desc 'aws_accounts:check ID',
|
|
216
|
+
'Check the connection for an external AWS account', \
|
|
217
|
+
hide: true
|
|
218
|
+
define_method 'aws_accounts:check' do |id|
|
|
219
|
+
telemetry(__method__, options.merge(id: id))
|
|
220
|
+
|
|
221
|
+
response = check_external_aws_account!(id)
|
|
222
|
+
|
|
223
|
+
fmt_state = lambda do |state|
|
|
224
|
+
Renderer.format == 'json' ? state : format_check_state(state)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
Formatter.render(Renderer.current) do |root|
|
|
228
|
+
root.object do |node|
|
|
229
|
+
node.value('state', fmt_state.call(response.state))
|
|
230
|
+
node.list('checks') do |check_list|
|
|
231
|
+
response.checks.each do |check|
|
|
232
|
+
check_list.object do |check_node|
|
|
233
|
+
check_node.value('name', check.check_name)
|
|
234
|
+
check_node.value('state', fmt_state.call(check.state))
|
|
235
|
+
check_node.value('details', check.details) \
|
|
236
|
+
unless check.details.nil?
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
unless response.state == 'success'
|
|
244
|
+
raise Thor::Error, 'AWS account check failed'
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
@@ -9,7 +9,7 @@ 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)
|
|
@@ -31,7 +31,7 @@ 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|
|
|
@@ -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|
|
|
@@ -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|
|
|
@@ -20,7 +19,7 @@ module Aptible
|
|
|
20
19
|
include Helpers::App
|
|
21
20
|
include Helpers::Telemetry
|
|
22
21
|
|
|
23
|
-
desc 'deploy [OPTIONS] [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
22
|
+
desc 'deploy [--app APP] [OPTIONS] [VAR1=VAL1] [VAR2=VAL2] [...]',
|
|
24
23
|
'Deploy an app'
|
|
25
24
|
option :git_commitish,
|
|
26
25
|
desc: 'Deploy a specific git commit or branch: the ' \
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -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,19 +25,25 @@ 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
|
+
' [--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,18 +52,24 @@ 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
|
|
59
71
|
|
|
60
|
-
desc 'services:autoscaling_policy SERVICE',
|
|
72
|
+
desc 'services:autoscaling_policy [--app APP] SERVICE',
|
|
61
73
|
'Returns the associated sizing policy, if any'
|
|
62
74
|
app_options
|
|
63
75
|
define_method 'services:autoscaling_policy' do |service|
|
|
@@ -87,7 +99,7 @@ module Aptible
|
|
|
87
99
|
|
|
88
100
|
map 'services:sizing_policy' => 'services:autoscaling_policy'
|
|
89
101
|
|
|
90
|
-
desc 'services:autoscaling_policy:set SERVICE '\
|
|
102
|
+
desc 'services:autoscaling_policy:set [--app APP] SERVICE '\
|
|
91
103
|
'--autoscaling-type (horizontal|vertical) '\
|
|
92
104
|
'[--metric-lookback-seconds SECONDS] '\
|
|
93
105
|
'[--percentile PERCENTILE] '\
|