rollo 0.7.0 → 0.8.0.pre.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 +5 -5
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +4 -2
- data/Gemfile.lock +1127 -523
- data/LICENSE.txt +1 -1
- data/Rakefile +134 -10
- data/bin/console +4 -3
- data/lib/rollo.rb +3 -1
- data/lib/rollo/commands.rb +2 -0
- data/lib/rollo/commands/hosts.rb +125 -79
- data/lib/rollo/commands/main.rb +67 -52
- data/lib/rollo/commands/services.rb +92 -57
- data/lib/rollo/model.rb +2 -0
- data/lib/rollo/model/host.rb +4 -2
- data/lib/rollo/model/host_cluster.rb +32 -22
- data/lib/rollo/model/scaling_activity.rb +8 -6
- data/lib/rollo/model/service.rb +28 -19
- data/lib/rollo/model/service_cluster.rb +19 -14
- data/lib/rollo/version.rb +3 -1
- metadata +141 -39
- data/.envrc +0 -5
- data/.gitignore +0 -31
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/.travis.yml +0 -5
- data/exe/rollo +0 -5
- data/go +0 -61
- data/rollo.gemspec +0 -37
data/lib/rollo/commands/main.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require_relative '../model'
|
3
5
|
require_relative './hosts'
|
@@ -24,38 +26,44 @@ module Rollo
|
|
24
26
|
end
|
25
27
|
|
26
28
|
desc('roll REGION ASG_NAME ECS_CLUSTER_NAME',
|
27
|
-
|
29
|
+
'Rolls all hosts in the cluster')
|
28
30
|
method_option(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
:batch_size,
|
32
|
+
aliases: '-b',
|
33
|
+
type: :numeric,
|
34
|
+
default: 3,
|
35
|
+
desc:
|
36
|
+
'The number of hosts / service instances to add / remove at ' \
|
37
|
+
'a time.'
|
38
|
+
)
|
36
39
|
method_option(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
:maximum_service_instances,
|
41
|
+
aliases: '-mx',
|
42
|
+
type: :numeric,
|
43
|
+
desc: 'The maximum number of service instances to expand to.'
|
44
|
+
)
|
41
45
|
method_option(
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
46
|
+
:minimum_service_instances,
|
47
|
+
aliases: '-mn',
|
48
|
+
type: :numeric,
|
49
|
+
desc: 'The minimum number of service instances to contract to.'
|
50
|
+
)
|
51
|
+
# rubocop:disable Metrics/MethodLength
|
52
|
+
# rubocop:disable Metrics/AbcSize
|
47
53
|
def roll(region, asg_name, ecs_cluster_name)
|
48
54
|
host_cluster = Rollo::Model::HostCluster.new(asg_name, region)
|
49
55
|
service_cluster = Rollo::Model::ServiceCluster
|
50
|
-
|
56
|
+
.new(ecs_cluster_name, region)
|
51
57
|
|
52
58
|
initial_hosts = host_cluster.hosts
|
53
59
|
|
54
60
|
say(
|
55
|
-
|
56
|
-
|
61
|
+
"Rolling instances in host cluster #{host_cluster.name} for " \
|
62
|
+
"service cluster #{service_cluster.name}..."
|
63
|
+
)
|
64
|
+
# rubocop:disable Metrics/BlockLength
|
57
65
|
with_padding do
|
58
|
-
unless host_cluster.
|
66
|
+
unless host_cluster.desired_capacity?
|
59
67
|
say('ERROR: Host cluster is not in stable state.')
|
60
68
|
say('This may be due to scaling above or below the desired')
|
61
69
|
say('capacity or because hosts are not in service or are ')
|
@@ -64,46 +72,53 @@ module Rollo
|
|
64
72
|
end
|
65
73
|
|
66
74
|
invoke(
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
75
|
+
'hosts:expand',
|
76
|
+
[
|
77
|
+
region, asg_name, ecs_cluster_name,
|
78
|
+
host_cluster
|
79
|
+
]
|
80
|
+
)
|
72
81
|
|
73
82
|
invoke(
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
83
|
+
'services:expand',
|
84
|
+
[
|
85
|
+
region, asg_name, ecs_cluster_name,
|
86
|
+
service_cluster
|
87
|
+
],
|
88
|
+
maximum_instances: options[:maximum_service_instances]
|
89
|
+
)
|
80
90
|
|
81
91
|
invoke(
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
92
|
+
'hosts:terminate',
|
93
|
+
[
|
94
|
+
region, asg_name, ecs_cluster_name, initial_hosts.map(&:id),
|
95
|
+
host_cluster, service_cluster
|
96
|
+
]
|
97
|
+
)
|
87
98
|
|
88
99
|
invoke(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
100
|
+
'hosts:contract',
|
101
|
+
[
|
102
|
+
region, asg_name, ecs_cluster_name,
|
103
|
+
host_cluster, service_cluster
|
104
|
+
]
|
105
|
+
)
|
94
106
|
|
95
107
|
invoke(
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
108
|
+
'services:contract',
|
109
|
+
[
|
110
|
+
region, asg_name, ecs_cluster_name,
|
111
|
+
service_cluster
|
112
|
+
],
|
113
|
+
minimum_instances: options[:minimum_service_instances]
|
114
|
+
)
|
102
115
|
end
|
103
|
-
|
104
|
-
say("Instances in host cluster #{host_cluster.name} rolled "
|
105
|
-
|
116
|
+
# rubocop:enable Metrics/BlockLength
|
117
|
+
say("Instances in host cluster #{host_cluster.name} rolled " \
|
118
|
+
'successfully.')
|
106
119
|
end
|
120
|
+
# rubocop:enable Metrics/MethodLength
|
121
|
+
# rubocop:enable Metrics/AbcSize
|
107
122
|
end
|
108
123
|
end
|
109
|
-
end
|
124
|
+
end
|
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require_relative '../model'
|
3
5
|
|
4
6
|
module Rollo
|
5
7
|
module Commands
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
6
9
|
class Services < Thor
|
7
10
|
namespace :services
|
8
11
|
|
@@ -11,128 +14,160 @@ module Rollo
|
|
11
14
|
end
|
12
15
|
|
13
16
|
desc(
|
14
|
-
|
15
|
-
|
17
|
+
'expand REGION ASG_NAME ECS_CLUSTER_NAME',
|
18
|
+
'Expands the service cluster by one batch.'
|
19
|
+
)
|
16
20
|
method_option(
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
:batch_size,
|
22
|
+
aliases: '-b',
|
23
|
+
type: :numeric,
|
24
|
+
default: 3,
|
25
|
+
desc: 'The number of service instances to add at a time.'
|
26
|
+
)
|
22
27
|
method_option(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
:startup_time,
|
29
|
+
aliases: '-t',
|
30
|
+
type: :numeric,
|
31
|
+
default: 2,
|
32
|
+
desc: 'The number of minutes to wait for services to start up.'
|
33
|
+
)
|
28
34
|
method_option(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
35
|
+
:maximum_instances,
|
36
|
+
aliases: '-mx',
|
37
|
+
type: :numeric,
|
38
|
+
desc: 'The maximum number of service instances to expand to.'
|
39
|
+
)
|
40
|
+
# rubocop:disable Metrics/AbcSize
|
41
|
+
# rubocop:disable Metrics/MethodLength
|
34
42
|
def expand(
|
35
|
-
|
36
|
-
|
43
|
+
region, _, ecs_cluster_name,
|
44
|
+
service_cluster = nil
|
45
|
+
)
|
37
46
|
batch_size = options[:batch_size]
|
38
47
|
maximum_instances = options[:maximum_instances]
|
39
48
|
service_start_wait_minutes = options[:startup_time]
|
40
49
|
service_start_wait_seconds = 60 * service_start_wait_minutes
|
41
50
|
|
42
|
-
service_cluster
|
43
|
-
|
51
|
+
service_cluster ||= Rollo::Model::ServiceCluster.new(ecs_cluster_name,
|
52
|
+
region)
|
44
53
|
|
45
54
|
say("Increasing service instance counts by #{batch_size}...")
|
55
|
+
# rubocop:disable Metrics/BlockLength
|
46
56
|
with_padding do
|
47
57
|
service_cluster.with_replica_services do |on|
|
48
58
|
on.start do |services|
|
49
59
|
say(
|
50
|
-
|
51
|
-
|
60
|
+
'Service cluster contains services:' \
|
61
|
+
"\n\t\t[#{services.map(&:name).join(",\n\t\t ")}]"
|
62
|
+
)
|
52
63
|
end
|
53
64
|
on.each_service do |service|
|
54
65
|
say(
|
55
|
-
|
56
|
-
|
66
|
+
"Increasing instance count by #{batch_size} " \
|
67
|
+
"for #{service.name}"
|
68
|
+
)
|
69
|
+
# rubocop:disable Lint/ShadowingOuterLocalVariable
|
57
70
|
with_padding do
|
58
71
|
service.increase_instance_count_by(
|
59
|
-
|
72
|
+
batch_size, maximum_instances: maximum_instances
|
73
|
+
) do |on|
|
60
74
|
on.prepare do |current, target|
|
61
75
|
say(
|
62
|
-
|
63
|
-
|
76
|
+
"Changing instance count from #{current} " \
|
77
|
+
"to #{target}..."
|
78
|
+
)
|
64
79
|
end
|
65
80
|
on.waiting_for_health do |attempt|
|
66
81
|
say(
|
67
|
-
|
68
|
-
|
82
|
+
'Waiting for service to reach a steady state ' \
|
83
|
+
"(attempt #{attempt})..."
|
84
|
+
)
|
69
85
|
end
|
70
86
|
end
|
71
87
|
end
|
88
|
+
# rubocop:enable Lint/ShadowingOuterLocalVariable
|
72
89
|
end
|
73
90
|
end
|
74
91
|
end
|
92
|
+
# rubocop:enable Metrics/BlockLength
|
75
93
|
say(
|
76
|
-
|
77
|
-
|
94
|
+
"Waiting #{service_start_wait_minutes} minute(s) for " \
|
95
|
+
'services to finish starting...'
|
96
|
+
)
|
78
97
|
with_padding do
|
79
98
|
sleep(service_start_wait_seconds)
|
80
99
|
say(
|
81
|
-
|
82
|
-
|
100
|
+
"Waited #{service_start_wait_minutes} minute(s). " \
|
101
|
+
'Continuing...'
|
102
|
+
)
|
83
103
|
end
|
84
104
|
say('Service instance counts increased, continuing...')
|
85
105
|
end
|
106
|
+
# rubocop:enable Metrics/AbcSize
|
107
|
+
# rubocop:enable Metrics/MethodLength
|
86
108
|
|
87
109
|
desc(
|
88
|
-
|
89
|
-
|
110
|
+
'contract REGION ASG_NAME ECS_CLUSTER_NAME',
|
111
|
+
'Contracts the service cluster by one batch.'
|
112
|
+
)
|
90
113
|
method_option(
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
114
|
+
:batch_size,
|
115
|
+
aliases: '-b',
|
116
|
+
type: :numeric,
|
117
|
+
default: 3,
|
118
|
+
desc: 'The number of service instances to remove at a time.'
|
119
|
+
)
|
96
120
|
method_option(
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
121
|
+
:minimum_instances,
|
122
|
+
aliases: '-mn',
|
123
|
+
type: :numeric,
|
124
|
+
desc: 'The minimum number of service instances to contract to.'
|
125
|
+
)
|
126
|
+
# rubocop:disable Metrics/AbcSize
|
127
|
+
# rubocop:disable Metrics/MethodLength
|
102
128
|
def contract(
|
103
|
-
|
104
|
-
|
129
|
+
region, _, ecs_cluster_name,
|
130
|
+
service_cluster = nil
|
131
|
+
)
|
105
132
|
batch_size = options[:batch_size]
|
106
133
|
|
107
|
-
service_cluster
|
108
|
-
|
134
|
+
service_cluster ||= Rollo::Model::ServiceCluster.new(ecs_cluster_name,
|
135
|
+
region)
|
109
136
|
|
110
137
|
say("Decreasing service instance counts by #{batch_size}...")
|
111
138
|
with_padding do
|
112
139
|
service_cluster.with_replica_services do |on|
|
113
140
|
on.each_service do |service|
|
114
141
|
say(
|
115
|
-
|
116
|
-
|
142
|
+
"Decreasing instance count by #{batch_size} " \
|
143
|
+
"for #{service.name}"
|
144
|
+
)
|
145
|
+
# rubocop:disable Lint/ShadowingOuterLocalVariable
|
117
146
|
with_padding do
|
118
147
|
service.decrease_instance_count_by(batch_size) do |on|
|
119
148
|
on.prepare do |current, target|
|
120
149
|
say(
|
121
|
-
|
122
|
-
|
150
|
+
"Changing instance count from #{current} " \
|
151
|
+
"to #{target}..."
|
152
|
+
)
|
123
153
|
end
|
124
154
|
on.waiting_for_health do |attempt|
|
125
155
|
say(
|
126
|
-
|
127
|
-
|
156
|
+
'Waiting for service to reach a steady state ' \
|
157
|
+
"(attempt #{attempt})..."
|
158
|
+
)
|
128
159
|
end
|
129
160
|
end
|
130
161
|
end
|
162
|
+
# rubocop:enable Lint/ShadowingOuterLocalVariable
|
131
163
|
end
|
132
164
|
end
|
133
165
|
end
|
134
|
-
say(
|
166
|
+
say('Service instance counts decreased, continuing...')
|
135
167
|
end
|
168
|
+
# rubocop:enable Metrics/AbcSize
|
169
|
+
# rubocop:enable Metrics/MethodLength
|
136
170
|
end
|
171
|
+
# rubocop:enable Metrics/ClassLength
|
137
172
|
end
|
138
173
|
end
|
data/lib/rollo/model.rb
CHANGED
data/lib/rollo/model/host.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rollo
|
2
4
|
module Model
|
3
5
|
class Host
|
@@ -13,11 +15,11 @@ module Rollo
|
|
13
15
|
@instance.terminate(should_decrement_desired_capacity: false)
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
18
|
+
def in_service?
|
17
19
|
@instance.lifecycle_state == 'InService'
|
18
20
|
end
|
19
21
|
|
20
|
-
def
|
22
|
+
def healthy?
|
21
23
|
@instance.health_status == 'Healthy'
|
22
24
|
end
|
23
25
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'aws-sdk'
|
2
4
|
require 'hollerback'
|
3
5
|
require 'wait'
|
@@ -14,7 +16,7 @@ module Rollo
|
|
14
16
|
@region = region
|
15
17
|
@asg_name = asg_name
|
16
18
|
@asg_resource = asg_resource ||
|
17
|
-
|
19
|
+
Aws::AutoScaling::Resource.new(region: region)
|
18
20
|
@asg = @asg_resource.group(@asg_name)
|
19
21
|
record_latest_scaling_activity
|
20
22
|
|
@@ -34,30 +36,30 @@ module Rollo
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def desired_capacity=(capacity)
|
37
|
-
@asg.set_desired_capacity({desired_capacity: capacity})
|
39
|
+
@asg.set_desired_capacity({ desired_capacity: capacity })
|
38
40
|
end
|
39
41
|
|
40
|
-
def
|
42
|
+
def desired_capacity?
|
41
43
|
hosts.size == desired_capacity &&
|
42
|
-
|
44
|
+
hosts.all? { |h| h.in_service? && h.healthy? }
|
43
45
|
end
|
44
46
|
|
45
47
|
def scaling_activities
|
46
|
-
@asg.activities.collect {|a| ScalingActivity.new(a)}
|
48
|
+
@asg.activities.collect { |a| ScalingActivity.new(a) }
|
47
49
|
end
|
48
50
|
|
49
|
-
def
|
51
|
+
def started_changing_capacity?
|
50
52
|
scaling_activities
|
51
|
-
|
52
|
-
|
53
|
+
.select { |a| a.started_after_completion_of?(@last_scaling_activity) }
|
54
|
+
.size.positive?
|
53
55
|
end
|
54
56
|
|
55
|
-
def
|
56
|
-
scaling_activities.all?(&:
|
57
|
+
def completed_changing_capacity?
|
58
|
+
scaling_activities.all?(&:complete?)
|
57
59
|
end
|
58
60
|
|
59
61
|
def hosts
|
60
|
-
@asg.instances.collect {|h| Host.new(h)}
|
62
|
+
@asg.instances.collect { |h| Host.new(h) }
|
61
63
|
end
|
62
64
|
|
63
65
|
def increase_capacity_by(capacity_delta, &block)
|
@@ -65,7 +67,8 @@ module Rollo
|
|
65
67
|
increased = initial + capacity_delta
|
66
68
|
|
67
69
|
callbacks_for(block).try_respond_with(
|
68
|
-
|
70
|
+
:prepare, initial, increased
|
71
|
+
)
|
69
72
|
|
70
73
|
ensure_capacity_changed_to(increased, &block)
|
71
74
|
end
|
@@ -75,7 +78,8 @@ module Rollo
|
|
75
78
|
decreased = initial - capacity_delta
|
76
79
|
|
77
80
|
callbacks_for(block).try_respond_with(
|
78
|
-
|
81
|
+
:prepare, initial, decreased
|
82
|
+
)
|
79
83
|
|
80
84
|
ensure_capacity_changed_to(decreased, &block)
|
81
85
|
end
|
@@ -91,27 +95,33 @@ module Rollo
|
|
91
95
|
def wait_for_capacity_change_start(&block)
|
92
96
|
@waiter.until do |attempt|
|
93
97
|
reload
|
94
|
-
|
95
|
-
|
96
|
-
|
98
|
+
if block
|
99
|
+
callbacks_for(block)
|
100
|
+
.try_respond_with(:waiting_for_start, attempt)
|
101
|
+
end
|
102
|
+
started_changing_capacity?
|
97
103
|
end
|
98
104
|
end
|
99
105
|
|
100
106
|
def wait_for_capacity_change_end(&block)
|
101
107
|
@waiter.until do |attempt|
|
102
108
|
reload
|
103
|
-
|
104
|
-
|
105
|
-
|
109
|
+
if block
|
110
|
+
callbacks_for(block)
|
111
|
+
.try_respond_with(:waiting_for_end, attempt)
|
112
|
+
end
|
113
|
+
completed_changing_capacity?
|
106
114
|
end
|
107
115
|
end
|
108
116
|
|
109
117
|
def wait_for_capacity_health(&block)
|
110
118
|
@waiter.until do |attempt|
|
111
119
|
reload
|
112
|
-
|
113
|
-
|
114
|
-
|
120
|
+
if block
|
121
|
+
callbacks_for(block)
|
122
|
+
.try_respond_with(:waiting_for_health, attempt)
|
123
|
+
end
|
124
|
+
desired_capacity?
|
115
125
|
end
|
116
126
|
end
|
117
127
|
|