aptible-cli 0.21.0 → 0.23.0

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.
@@ -21,6 +21,177 @@ module Aptible
21
21
  end
22
22
  end
23
23
  end
24
+
25
+ desc 'services:settings SERVICE'\
26
+ ' [--force-zero-downtime|--no-force-zero-downtime]'\
27
+ ' [--simple-health-check|--no-simple-health-check]',
28
+ 'Modifies the zero-downtime deploy setting for a service'
29
+ app_options
30
+ option :force_zero_downtime,
31
+ type: :boolean, default: false,
32
+ desc: 'Force zero downtime deployments.'\
33
+ ' Has no effect if service has an associated Endpoint'
34
+ option :simple_health_check,
35
+ type: :boolean, default: false,
36
+ desc: 'Use a simple uptime healthcheck during deployments'
37
+ define_method 'services:settings' do |service|
38
+ service = ensure_service(options, service)
39
+ updates = {}
40
+ updates[:force_zero_downtime] =
41
+ options[:force_zero_downtime] if options[:force_zero_downtime]
42
+ updates[:naive_health_check] =
43
+ options[:simple_health_check] if options[:simple_health_check]
44
+
45
+ service.update!(**updates) if updates.any?
46
+ end
47
+
48
+ desc 'services:sizing_policy SERVICE',
49
+ 'Returns the associated sizing policy, if any'
50
+ app_options
51
+ define_method 'services:sizing_policy' do |service|
52
+ service = ensure_service(options, service)
53
+ policy = service.service_sizing_policy
54
+
55
+ unless policy
56
+ raise Thor::Error, "Service #{service} does not have a " \
57
+ 'service sizing policy set'
58
+ end
59
+
60
+ Formatter.render(Renderer.current) do |root|
61
+ root.object do |node|
62
+ ResourceFormatter.inject_service_sizing_policy(
63
+ node, policy, service
64
+ )
65
+ end
66
+ end
67
+ end
68
+
69
+ desc 'services:sizing_policy:set SERVICE '\
70
+ '--autoscaling-type (horizontal|vertical) '\
71
+ '[--metric-lookback-seconds SECONDS] '\
72
+ '[--percentile PERCENTILE] '\
73
+ '[--post-scale-up-cooldown-seconds SECONDS] '\
74
+ '[--post-scale-down-cooldown-seconds SECONDS] '\
75
+ '[--post-release-cooldown-seconds SECONDS] '\
76
+ '[--mem-cpu-ratio-r-threshold RATIO] '\
77
+ '[--mem-cpu-ratio-c-threshold RATIO] '\
78
+ '[--mem-scale-up-threshold THRESHOLD] '\
79
+ '[--mem-scale-down-threshold THRESHOLD] '\
80
+ '[--minimum-memory MEMORY] '\
81
+ '[--maximum-memory MEMORY] '\
82
+ '[--min-cpu-threshold THRESHOLD] '\
83
+ '[--max-cpu-threshold THRESHOLD] '\
84
+ '[--min-containers CONTAINERS] '\
85
+ '[--max-containers CONTAINERS] '\
86
+ '[--scale-up-step STEPS] '\
87
+ '[--scale-down-step STEPS] ',
88
+ 'Sets the sizing (autoscaling) policy for a service.'\
89
+ ' This is not incremental, all arguments must be sent'\
90
+ ' at once or they will be set to defaults.'
91
+ app_options
92
+ option :autoscaling_type,
93
+ type: :string,
94
+ desc: 'The type of autoscaling. Must be either '\
95
+ '"horizontal" or "vertical"'
96
+ option :metric_lookback_seconds,
97
+ type: :numeric,
98
+ desc: '(Default: 1800) The duration in seconds for '\
99
+ 'retrieving past performance metrics.'
100
+ option :percentile,
101
+ type: :numeric,
102
+ desc: '(Default: 99) The percentile for evaluating metrics.'
103
+ option :post_scale_up_cooldown_seconds,
104
+ type: :numeric,
105
+ desc: '(Default: 60) The waiting period in seconds after '\
106
+ 'an automated scale-up before another scaling action can '\
107
+ 'be considered.'
108
+ option :post_scale_down_cooldown_seconds,
109
+ type: :numeric,
110
+ desc: '(Default: 300) The waiting period in seconds after '\
111
+ 'an automated scale-down before another scaling action can '\
112
+ 'be considered.'
113
+ option :post_release_cooldown_seconds,
114
+ type: :numeric,
115
+ desc: '(Default: 300) The time in seconds to wait '\
116
+ 'following a deploy before another scaling action can '\
117
+ 'be considered.'
118
+ option :mem_cpu_ratio_r_threshold,
119
+ type: :numeric,
120
+ desc: '(Default: 4.0) Establishes the ratio of Memory '\
121
+ '(in GB) to CPU (in CPUs) at which values exceeding the '\
122
+ 'threshold prompt a shift to an R (Memory Optimized) '\
123
+ 'profile.'
124
+ option :mem_cpu_ratio_c_threshold,
125
+ type: :numeric,
126
+ desc: '(Default: 2.0) Sets the Memory-to-CPU ratio '\
127
+ 'threshold, below which the service is transitioned to a '\
128
+ 'C (Compute Optimized) profile.'
129
+ option :mem_scale_up_threshold,
130
+ type: :numeric,
131
+ desc: '(Default: 0.9) Vertical autoscaling only - '\
132
+ 'Specifies the percentage of the current memory limit '\
133
+ 'at which the service’s memory usage triggers an '\
134
+ 'up-scaling action.'
135
+ option :mem_scale_down_threshold,
136
+ type: :numeric,
137
+ desc: '(Default: 0.75) Vertical autoscaling only - '\
138
+ 'Specifies the percentage of the current memory limit at '\
139
+ 'which the service’s memory usage triggers a '\
140
+ 'down-scaling action.'
141
+ option :minimum_memory,
142
+ type: :numeric,
143
+ desc: '(Default: 2048) Vertical autoscaling only - Sets '\
144
+ 'the lowest memory limit to which the service can be '\
145
+ 'scaled down by Autoscaler.'
146
+ option :maximum_memory,
147
+ type: :numeric,
148
+ desc: 'Vertical autoscaling only - Defines the upper '\
149
+ 'memory threshold, capping the maximum memory allocation'\
150
+ ' possible through Autoscaler. If blank, the container can'\
151
+ ' scale to the largest size available.'
152
+ option :min_cpu_threshold,
153
+ type: :numeric,
154
+ desc: 'Horizontal autoscaling only - Specifies the '\
155
+ 'percentage of the current CPU usage at which a '\
156
+ 'down-scaling action is triggered.'
157
+ option :max_cpu_threshold,
158
+ type: :numeric,
159
+ desc: 'Horizontal autoscaling only - Specifies the '\
160
+ 'percentage of the current CPU usage at which an '\
161
+ 'up-scaling action is triggered.'
162
+ option :min_containers,
163
+ type: :numeric,
164
+ desc: 'Horizontal autoscaling only - Sets the lowest'\
165
+ ' container count to which the service can be scaled down.'
166
+ option :max_containers,
167
+ type: :numeric,
168
+ desc: 'Horizontal autoscaling only - Sets the highest '\
169
+ 'container count to which the service can be scaled up to.'
170
+ option :scale_up_step,
171
+ type: :numeric,
172
+ desc: '(Default: 1) Horizontal autoscaling only - Sets '\
173
+ 'the amount of containers to add when autoscaling (ex: a '\
174
+ 'value of 2 will go from 1->3->5). Container count will '\
175
+ 'never exceed the configured maximum.'
176
+ option :scale_down_step,
177
+ type: :numeric,
178
+ desc: '(Default: 1) Horizontal autoscaling only - Sets '\
179
+ 'the amount of containers to remove when autoscaling (ex:'\
180
+ ' a value of 2 will go from 4->2->1). Container count '\
181
+ 'will never exceed the configured minimum.'
182
+ define_method 'services:sizing_policy:set' do |service|
183
+ service = ensure_service(options, service)
184
+ ignored_attrs = %i(autoscaling_type app environment remote)
185
+ args = options.except(*ignored_attrs)
186
+ args[:autoscaling] = options[:autoscaling_type]
187
+
188
+ sizing_policy = service.service_sizing_policy
189
+ if sizing_policy
190
+ sizing_policy.update!(**args)
191
+ else
192
+ service.create_service_sizing_policy!(**args)
193
+ end
194
+ end
24
195
  end
25
196
  end
26
197
  end
@@ -1,5 +1,5 @@
1
1
  module Aptible
2
2
  module CLI
3
- VERSION = '0.21.0'.freeze
3
+ VERSION = '0.23.0'.freeze
4
4
  end
5
5
  end
@@ -452,6 +452,30 @@ describe Aptible::CLI::Agent do
452
452
  end
453
453
  end
454
454
 
455
+ describe 'endpoints:grpc:create' do
456
+ m = 'endpoints:grpc:create'
457
+ include_examples 'shared create app vhost examples', m
458
+ include_examples 'shared create tls vhost examples', m
459
+
460
+ it 'creates a gRPC Endpoint' do
461
+ expect_create_vhost(
462
+ service,
463
+ type: 'grpc',
464
+ platform: 'elb',
465
+ internal: false,
466
+ default: false,
467
+ ip_whitelist: []
468
+ )
469
+ subject.send(m, 'web')
470
+ end
471
+
472
+ it 'creates an Endpoint with a container Port' do
473
+ expect_create_vhost(service, container_port: 10)
474
+ stub_options(port: 10)
475
+ subject.send(m, 'web')
476
+ end
477
+ end
478
+
455
479
  shared_examples 'shared modify app vhost examples' do |m|
456
480
  it 'does not change anything if no options are passed' do
457
481
  v = Fabricate(:vhost, service: service)
@@ -579,6 +603,20 @@ describe Aptible::CLI::Agent do
579
603
  end
580
604
  end
581
605
 
606
+ describe 'endpoints:grpc:modify' do
607
+ m = 'endpoints:grpc:modify'
608
+ include_examples 'shared modify app vhost examples', m
609
+ include_examples 'shared modify tls vhost examples', m
610
+
611
+ it 'allows updating the Container Port' do
612
+ v = Fabricate(:vhost, service: service)
613
+ expect_modify_vhost(v, container_port: 10)
614
+
615
+ stub_options(port: 10)
616
+ subject.send(m, v.external_host)
617
+ end
618
+ end
619
+
582
620
  describe 'endpoints:list' do
583
621
  it 'lists Endpoints across services' do
584
622
  s1 = Fabricate(:service, app: app)
@@ -8,45 +8,162 @@ describe Aptible::CLI::Agent do
8
8
  allow(subject).to receive(:fetch_token) { token }
9
9
  allow(Aptible::Api::App).to receive(:all).with(token: token)
10
10
  .and_return([app])
11
- allow(subject).to receive(:options).and_return(app: app.handle)
12
11
  end
13
12
 
14
- it 'lists a CMD service' do
15
- Fabricate(:service, app: app, process_type: 'cmd', command: nil)
16
- subject.send('services')
13
+ describe '#services' do
14
+ before do
15
+ allow(subject).to receive(:options).and_return(app: app.handle)
16
+ end
17
17
 
18
- expect(captured_output_text.split("\n")).to include('Service: cmd')
19
- expect(captured_output_text.split("\n")).to include('Command: CMD')
20
- end
18
+ it 'lists a CMD service' do
19
+ Fabricate(:service, app: app, process_type: 'cmd', command: nil)
20
+ subject.send('services')
21
+
22
+ expect(captured_output_text.split("\n")).to include('Service: cmd')
23
+ expect(captured_output_text.split("\n")).to include('Command: CMD')
24
+ end
25
+
26
+ it 'lists a service with command' do
27
+ Fabricate(:service, app: app, process_type: 'cmd', command: 'foobar')
28
+ subject.send('services')
29
+
30
+ expect(captured_output_text.split("\n")).to include('Service: cmd')
31
+ expect(captured_output_text.split("\n")).to include('Command: foobar')
32
+ end
33
+
34
+ it 'lists container size' do
35
+ Fabricate(:service, app: app, container_memory_limit_mb: 1024)
36
+ subject.send('services')
37
+
38
+ expect(captured_output_text.split("\n"))
39
+ .to include('Container Size: 1024')
40
+ end
41
+
42
+ it 'lists container count' do
43
+ Fabricate(:service, app: app, container_count: 3)
44
+ subject.send('services')
21
45
 
22
- it 'lists a service with command' do
23
- Fabricate(:service, app: app, process_type: 'cmd', command: 'foobar')
24
- subject.send('services')
46
+ expect(captured_output_text.split("\n")).to include('Container Count: 3')
47
+ end
25
48
 
26
- expect(captured_output_text.split("\n")).to include('Service: cmd')
27
- expect(captured_output_text.split("\n")).to include('Command: foobar')
49
+ it 'lists multiple services' do
50
+ Fabricate(:service, app: app, process_type: 'foo')
51
+ Fabricate(:service, app: app, process_type: 'bar')
52
+ subject.send('services')
53
+
54
+ expect(captured_output_text.split("\n")).to include('Service: foo')
55
+ expect(captured_output_text.split("\n")).to include('Service: bar')
56
+ end
28
57
  end
29
58
 
30
- it 'lists container size' do
31
- Fabricate(:service, app: app, container_memory_limit_mb: 1024)
32
- subject.send('services')
59
+ describe '#services:settings' do
60
+ let(:base_options) { { app: app.handle } }
61
+
62
+ it 'allows changing zero_downtime_deployment settings' do
63
+ stub_options(force_zero_downtime: true, simple_health_check: true)
64
+ service = Fabricate(:service, app: app, process_type: 'foo')
65
+
66
+ expect(service).to receive(:update!)
67
+ .with(force_zero_downtime: true, naive_health_check: true)
68
+
69
+ subject.send('services:settings', 'foo')
70
+ end
71
+
72
+ it 'allows changing only one of the options' do
73
+ stub_options(simple_health_check: true)
74
+ service = Fabricate(:service, app: app, process_type: 'foo')
75
+
76
+ expect(service).to receive(:update!).with(naive_health_check: true)
33
77
 
34
- expect(captured_output_text.split("\n")).to include('Container Size: 1024')
78
+ subject.send('services:settings', 'foo')
79
+ end
35
80
  end
36
81
 
37
- it 'lists container count' do
38
- Fabricate(:service, app: app, container_count: 3)
39
- subject.send('services')
82
+ describe '#services:sizing_policy' do
83
+ let(:base_options) { { app: app.handle } }
40
84
 
41
- expect(captured_output_text.split("\n")).to include('Container Count: 3')
85
+ it 'returns the sizing policy' do
86
+ stub_options
87
+ service = Fabricate(:service, app: app, process_type: 'foo')
88
+ sizing_policy = Fabricate(:service_sizing_policy)
89
+ expect(service).to receive(:service_sizing_policy)
90
+ .and_return(sizing_policy)
91
+
92
+ subject.send('services:sizing_policy', 'foo')
93
+
94
+ out = captured_output_text
95
+ expect(out).to match(/Autoscaling Type: vertical/i)
96
+ expect(out).to match(/Metric Lookback Seconds: 1800/i)
97
+ expect(out).to match(/Percentile: 99.0/i)
98
+ expect(out).to match(/Post Scale Up Cooldown Seconds: 60/i)
99
+ expect(out).to match(/Post Scale Down Cooldown Seconds: 300/i)
100
+ expect(out).to match(/Post Release Cooldown Seconds: 300/i)
101
+ expect(out).to match(/Mem Cpu Ratio R Threshold: 4/i)
102
+ expect(out).to match(/Mem Cpu Ratio C Threshold: 2/i)
103
+ expect(out).to match(/Mem Scale Up Threshold: 0.9/i)
104
+ expect(out).to match(/Mem Scale Down Threshold: 0.75/i)
105
+ expect(out).to match(/Minimum Memory: 2048/i)
106
+ expect(out).to match(/Maximum Memory:/i)
107
+ expect(out).to match(/Min Cpu Threshold: 0.4/i)
108
+ expect(out).to match(/Max Cpu Threshold: 0.9/i)
109
+ expect(out).to match(/Min Containers: 2/i)
110
+ expect(out).to match(/Max Containers: 5/i)
111
+ expect(out).to match(/Scale Up Step: 1/i)
112
+ expect(out).to match(/Scale Down Step: 1/i)
113
+ end
114
+
115
+ it 'raises an error if the environment has no policy' do
116
+ stub_options
117
+ service = Fabricate(:service, app: app, process_type: 'foo')
118
+ expect(service).to receive(:service_sizing_policy).and_return(nil)
119
+ expect { subject.send('services:sizing_policy', 'foo') }
120
+ .to raise_error(/does not have a service sizing policy set/)
121
+ end
42
122
  end
43
123
 
44
- it 'lists multiple services' do
45
- Fabricate(:service, app: app, process_type: 'foo')
46
- Fabricate(:service, app: app, process_type: 'bar')
47
- subject.send('services')
124
+ describe '#services:sizing_policy:set' do
125
+ let(:base_options) { { app: app.handle } }
126
+ let(:args) do
127
+ {
128
+ autoscaling_type: 'vertical',
129
+ mem_scale_down_threshold: 0.5,
130
+ scale_up_step: 2,
131
+ post_scale_down_cooldown_seconds: 3,
132
+ percentile: 93.2
133
+ }
134
+ end
135
+
136
+ it 'updates existing sizing policy' do
137
+ stub_options(**args)
138
+ service = Fabricate(:service, app: app, process_type: 'foo')
139
+ sizing_policy = double(sizing_policy)
140
+ expect(service).to receive(:service_sizing_policy)
141
+ .and_return(sizing_policy)
142
+
143
+ api_args = args.except(:autoscaling_type)
144
+ api_args[:autoscaling] = args[:autoscaling_type]
145
+
146
+ expect(sizing_policy).to receive(:update!)
147
+ .with(**api_args)
148
+
149
+ subject.send('services:sizing_policy:set', 'foo')
150
+ end
151
+
152
+ it 'creates a new sizing policy if necessary' do
153
+ stub_options(**args)
154
+ service = Fabricate(:service, app: app, process_type: 'foo')
155
+
156
+ api_args = args.except(:autoscaling_type)
157
+ api_args[:autoscaling] = args[:autoscaling_type]
158
+
159
+ expect(service).to receive(:create_service_sizing_policy!)
160
+ .with(**api_args)
161
+
162
+ subject.send('services:sizing_policy:set', 'foo')
163
+ end
164
+ end
48
165
 
49
- expect(captured_output_text.split("\n")).to include('Service: foo')
50
- expect(captured_output_text.split("\n")).to include('Service: bar')
166
+ def stub_options(**opts)
167
+ allow(subject).to receive(:options).and_return(base_options.merge(opts))
51
168
  end
52
169
  end
@@ -0,0 +1,22 @@
1
+ class StubServiceSizingPolicy < OpenStruct; end
2
+
3
+ Fabricator(:service_sizing_policy, from: :stub_service_sizing_policy) do
4
+ autoscaling 'vertical'
5
+ metric_lookback_seconds 1800
6
+ percentile 99.0
7
+ post_scale_up_cooldown_seconds 60
8
+ post_scale_down_cooldown_seconds 300
9
+ post_release_cooldown_seconds 300
10
+ mem_cpu_ratio_r_threshold 4
11
+ mem_cpu_ratio_c_threshold 2
12
+ mem_scale_up_threshold 0.9
13
+ mem_scale_down_threshold 0.75
14
+ minimum_memory 2048
15
+ maximum_memory nil
16
+ min_cpu_threshold 0.4
17
+ max_cpu_threshold 0.9
18
+ min_containers 2
19
+ max_containers 5
20
+ scale_up_step 1
21
+ scale_down_step 1
22
+ end