aptible-cli 0.21.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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