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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +27 -0
  3. data/.github/workflows/test.yml +1 -1
  4. data/Dockerfile +8 -5
  5. data/Gemfile.lock +28 -17
  6. data/Makefile +53 -2
  7. data/README.md +83 -82
  8. data/aptible-cli.gemspec +5 -2
  9. data/docker-compose.yml +5 -1
  10. data/lib/aptible/cli/agent.rb +16 -0
  11. data/lib/aptible/cli/helpers/aws_account.rb +158 -0
  12. data/lib/aptible/cli/helpers/database.rb +182 -2
  13. data/lib/aptible/cli/helpers/token.rb +14 -0
  14. data/lib/aptible/cli/renderer/text.rb +33 -2
  15. data/lib/aptible/cli/resource_formatter.rb +2 -2
  16. data/lib/aptible/cli/subcommands/apps.rb +2 -2
  17. data/lib/aptible/cli/subcommands/aws_accounts.rb +252 -0
  18. data/lib/aptible/cli/subcommands/config.rb +6 -6
  19. data/lib/aptible/cli/subcommands/db.rb +67 -3
  20. data/lib/aptible/cli/subcommands/deploy.rb +46 -12
  21. data/lib/aptible/cli/subcommands/organizations.rb +55 -0
  22. data/lib/aptible/cli/subcommands/rebuild.rb +2 -1
  23. data/lib/aptible/cli/subcommands/restart.rb +2 -1
  24. data/lib/aptible/cli/subcommands/services.rb +24 -12
  25. data/lib/aptible/cli/subcommands/ssh.rb +1 -1
  26. data/lib/aptible/cli/version.rb +1 -1
  27. data/spec/aptible/cli/agent_spec.rb +70 -0
  28. data/spec/aptible/cli/helpers/database_spec.rb +118 -0
  29. data/spec/aptible/cli/helpers/token_spec.rb +70 -0
  30. data/spec/aptible/cli/subcommands/db_spec.rb +553 -0
  31. data/spec/aptible/cli/subcommands/deploy_spec.rb +42 -7
  32. data/spec/aptible/cli/subcommands/external_aws_accounts_spec.rb +737 -0
  33. data/spec/aptible/cli/subcommands/organizations_spec.rb +90 -0
  34. data/spec/aptible/cli/subcommands/services_spec.rb +77 -0
  35. data/spec/fabricators/app_external_aws_rds_connection_fabricator.rb +55 -0
  36. data/spec/fabricators/external_aws_account_fabricator.rb +49 -0
  37. data/spec/fabricators/external_aws_database_credential_fabricator.rb +46 -0
  38. data/spec/fabricators/external_aws_resource_fabricator.rb +72 -0
  39. 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
- DOCKER_IMAGE_DEPLOY_ARGS = Hash[%w(
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
- DOCKER_IMAGE_DEPLOY_ARGS.each_pair do |opt, var|
44
- option opt,
45
- type: :string, banner: var,
46
- desc: "Shorthand for #{var}=..."
47
- end
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
- DOCKER_IMAGE_DEPLOY_ARGS.each_pair do |opt, var|
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
- env[var] = val
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[:env].try(:[], 'APTIBLE_DOCKER_IMAGE'),
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', 'Rebuild an app, and restart its services'
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', 'Restart all services associated with an app'
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, default: false,
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, default: false,
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
- updates[:force_zero_downtime] =
51
- options[:force_zero_downtime] if options[:force_zero_downtime]
52
- updates[:naive_health_check] =
53
- options[:simple_health_check] if options[:simple_health_check]
54
- updates[:stop_timeout] =
55
- options[:stop_timeout] if options[:stop_timeout]
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] '\