rollo 0.7.0 → 0.8.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/LICENSE.txt
CHANGED
data/Rakefile
CHANGED
@@ -1,24 +1,148 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'yaml'
|
4
|
+
require 'rake_circle_ci'
|
5
|
+
require 'rake_github'
|
6
|
+
require 'rake_ssh'
|
7
|
+
require 'rake_gpg'
|
8
|
+
require 'securerandom'
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
require 'rubocop/rake_task'
|
4
11
|
|
5
|
-
task :
|
12
|
+
task default: %i[
|
13
|
+
library:fix
|
14
|
+
test:unit
|
15
|
+
]
|
16
|
+
|
17
|
+
namespace :encryption do
|
18
|
+
namespace :directory do
|
19
|
+
desc 'Ensure CI secrets directory exists.'
|
20
|
+
task :ensure do
|
21
|
+
FileUtils.mkdir_p('config/secrets/ci')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
namespace :passphrase do
|
26
|
+
desc 'Generate encryption passphrase used by CI.'
|
27
|
+
task generate: ['directory:ensure'] do
|
28
|
+
File.open('config/secrets/ci/encryption.passphrase', 'w') do |f|
|
29
|
+
f.write(SecureRandom.base64(36))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
namespace :keys do
|
36
|
+
namespace :deploy do
|
37
|
+
RakeSSH.define_key_tasks(
|
38
|
+
path: 'config/secrets/ci/',
|
39
|
+
comment: 'maintainers@infrablocks.io'
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
namespace :secrets do
|
44
|
+
namespace :gpg do
|
45
|
+
RakeGPG.define_generate_key_task(
|
46
|
+
output_directory: 'config/secrets/ci',
|
47
|
+
name_prefix: 'gpg',
|
48
|
+
owner_name: 'InfraBlocks Maintainers',
|
49
|
+
owner_email: 'maintainers@infrablocks.io',
|
50
|
+
owner_comment: 'rollo CI Key'
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'Generate key used by CI to access secrets.'
|
55
|
+
task generate: [:'gpg:generate']
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
namespace :secrets do
|
60
|
+
desc 'Regenerate all generatable secrets.'
|
61
|
+
task regenerate: %w[
|
62
|
+
encryption:passphrase:generate
|
63
|
+
keys:deploy:generate
|
64
|
+
keys:secrets:generate
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
RuboCop::RakeTask.new
|
69
|
+
|
70
|
+
namespace :library do
|
71
|
+
desc 'Run all checks of the library'
|
72
|
+
task check: [:rubocop]
|
73
|
+
|
74
|
+
desc 'Attempt to automatically fix issues with the library'
|
75
|
+
task fix: [:'rubocop:auto_correct']
|
76
|
+
end
|
77
|
+
|
78
|
+
namespace :test do
|
79
|
+
RSpec::Core::RakeTask.new(:unit)
|
80
|
+
end
|
81
|
+
|
82
|
+
RakeCircleCI.define_project_tasks(
|
83
|
+
namespace: :circle_ci,
|
84
|
+
project_slug: 'github/infrablocks/rollo'
|
85
|
+
) do |t|
|
86
|
+
circle_ci_config =
|
87
|
+
YAML.load_file('config/secrets/circle_ci/config.yaml')
|
88
|
+
|
89
|
+
t.api_token = circle_ci_config['circle_ci_api_token']
|
90
|
+
t.environment_variables = {
|
91
|
+
ENCRYPTION_PASSPHRASE:
|
92
|
+
File.read('config/secrets/ci/encryption.passphrase')
|
93
|
+
.chomp
|
94
|
+
}
|
95
|
+
t.checkout_keys = []
|
96
|
+
t.ssh_keys = [
|
97
|
+
{
|
98
|
+
hostname: 'github.com',
|
99
|
+
private_key: File.read('config/secrets/ci/ssh.private')
|
100
|
+
}
|
101
|
+
]
|
102
|
+
end
|
103
|
+
|
104
|
+
RakeGithub.define_repository_tasks(
|
105
|
+
namespace: :github,
|
106
|
+
repository: 'infrablocks/rollo'
|
107
|
+
) do |t|
|
108
|
+
github_config =
|
109
|
+
YAML.load_file('config/secrets/github/config.yaml')
|
110
|
+
|
111
|
+
t.access_token = github_config['github_personal_access_token']
|
112
|
+
t.deploy_keys = [
|
113
|
+
{
|
114
|
+
title: 'CircleCI',
|
115
|
+
public_key: File.read('config/secrets/ci/ssh.public')
|
116
|
+
}
|
117
|
+
]
|
118
|
+
end
|
119
|
+
|
120
|
+
namespace :pipeline do
|
121
|
+
desc 'Prepare CircleCI Pipeline'
|
122
|
+
task prepare: %i[
|
123
|
+
circle_ci:project:follow
|
124
|
+
circle_ci:env_vars:ensure
|
125
|
+
circle_ci:checkout_keys:ensure
|
126
|
+
circle_ci:ssh_keys:ensure
|
127
|
+
github:deploy_keys:ensure
|
128
|
+
]
|
129
|
+
end
|
6
130
|
|
7
131
|
namespace :version do
|
8
|
-
desc
|
132
|
+
desc 'Bump version for specified type (pre, major, minor, patch)'
|
9
133
|
task :bump, [:type] do |_, args|
|
10
134
|
bump_version_for(args.type)
|
11
135
|
end
|
12
136
|
end
|
13
137
|
|
14
|
-
desc
|
138
|
+
desc 'Release gem'
|
15
139
|
task :release do
|
16
|
-
sh
|
140
|
+
sh 'gem release --tag --push'
|
17
141
|
end
|
18
142
|
|
19
143
|
def bump_version_for(version_type)
|
20
|
-
sh "gem bump --version #{version_type} "
|
21
|
-
|
22
|
-
|
23
|
-
|
144
|
+
sh "gem bump --version #{version_type} " \
|
145
|
+
'&& bundle install ' \
|
146
|
+
'&& export LAST_MESSAGE="$(git log -1 --pretty=%B)" ' \
|
147
|
+
'&& git commit -a --amend -m "${LAST_MESSAGE} [ci skip]"'
|
24
148
|
end
|
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'rollo'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "rollo"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/lib/rollo.rb
CHANGED
data/lib/rollo/commands.rb
CHANGED
data/lib/rollo/commands/hosts.rb
CHANGED
@@ -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 Hosts < Thor
|
7
10
|
namespace :hosts
|
8
11
|
|
@@ -11,176 +14,219 @@ module Rollo
|
|
11
14
|
end
|
12
15
|
|
13
16
|
desc(
|
14
|
-
|
15
|
-
|
17
|
+
'expand REGION ASG_NAME ECS_CLUSTER_NAME',
|
18
|
+
'Expands the host 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 hosts to add at a time.'
|
26
|
+
)
|
27
|
+
# rubocop:disable Metrics/AbcSize
|
28
|
+
# rubocop:disable Metrics/MethodLength
|
22
29
|
def expand(
|
23
|
-
|
24
|
-
|
30
|
+
region, asg_name, _,
|
31
|
+
host_cluster = nil
|
32
|
+
)
|
25
33
|
batch_size = options[:batch_size]
|
26
34
|
|
27
|
-
host_cluster
|
28
|
-
Rollo::Model::HostCluster.new(asg_name, region)
|
35
|
+
host_cluster ||= Rollo::Model::HostCluster.new(asg_name, region)
|
29
36
|
|
30
37
|
say("Increasing host cluster desired capacity by #{batch_size}...")
|
31
38
|
with_padding do
|
32
39
|
host_cluster.increase_capacity_by(batch_size) do |on|
|
33
40
|
on.prepare do |current, target|
|
34
41
|
say(
|
35
|
-
|
36
|
-
|
42
|
+
"Changing desired capacity from #{current} to " \
|
43
|
+
"#{target}..."
|
44
|
+
)
|
37
45
|
end
|
38
46
|
on.waiting_for_start do |attempt|
|
39
47
|
say(
|
40
|
-
|
41
|
-
|
48
|
+
'Waiting for capacity change to start ' \
|
49
|
+
"(attempt #{attempt})..."
|
50
|
+
)
|
42
51
|
end
|
43
52
|
on.waiting_for_end do |attempt|
|
44
53
|
say(
|
45
|
-
|
46
|
-
|
54
|
+
'Waiting for capacity change to complete ' \
|
55
|
+
"(attempt #{attempt})..."
|
56
|
+
)
|
47
57
|
end
|
48
58
|
on.waiting_for_health do |attempt|
|
49
59
|
say("Waiting for a healthy state (attempt #{attempt})")
|
50
60
|
end
|
51
61
|
end
|
52
62
|
end
|
53
|
-
say
|
63
|
+
say 'Host cluster desired capacity increased, continuing...'
|
54
64
|
end
|
55
65
|
|
66
|
+
# rubocop:enable Metrics/MethodLength
|
67
|
+
# rubocop:enable Metrics/AbcSize
|
68
|
+
|
56
69
|
desc(
|
57
|
-
|
58
|
-
|
70
|
+
'contract REGION ASG_NAME ECS_CLUSTER_NAME',
|
71
|
+
'Contracts the host cluster by one batch'
|
72
|
+
)
|
59
73
|
method_option(
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
74
|
+
:batch_size,
|
75
|
+
aliases: '-b',
|
76
|
+
type: :numeric,
|
77
|
+
default: 3,
|
78
|
+
desc: 'The number of hosts to remove at a time.'
|
79
|
+
)
|
80
|
+
# rubocop:disable Metrics/AbcSize
|
81
|
+
# rubocop:disable Metrics/MethodLength
|
65
82
|
def contract(
|
66
|
-
|
67
|
-
|
83
|
+
region, asg_name, ecs_cluster_name,
|
84
|
+
host_cluster = nil, service_cluster = nil
|
85
|
+
)
|
68
86
|
batch_size = options[:batch_size]
|
69
87
|
|
70
|
-
host_cluster
|
71
|
-
|
72
|
-
|
73
|
-
|
88
|
+
host_cluster ||= Rollo::Model::HostCluster.new(asg_name, region)
|
89
|
+
service_cluster ||= Rollo::Model::ServiceCluster.new(
|
90
|
+
ecs_cluster_name, region
|
91
|
+
)
|
74
92
|
|
75
93
|
say("Decreasing host cluster desired capacity by #{batch_size}...")
|
94
|
+
# rubocop:disable Metrics/BlockLength
|
76
95
|
with_padding do
|
77
96
|
host_cluster.decrease_capacity_by(batch_size) do |on|
|
78
97
|
on.prepare do |current, target|
|
79
98
|
say(
|
80
|
-
|
81
|
-
|
99
|
+
"Changing desired capacity from #{current} to " \
|
100
|
+
"#{target}..."
|
101
|
+
)
|
82
102
|
end
|
83
103
|
on.waiting_for_start do |attempt|
|
84
104
|
say(
|
85
|
-
|
86
|
-
|
105
|
+
'Waiting for capacity change to start ' \
|
106
|
+
"(attempt #{attempt})..."
|
107
|
+
)
|
87
108
|
end
|
88
109
|
on.waiting_for_end do |attempt|
|
89
110
|
say(
|
90
|
-
|
91
|
-
|
111
|
+
'Waiting for capacity change to complete ' \
|
112
|
+
"(attempt #{attempt})..."
|
113
|
+
)
|
92
114
|
end
|
93
115
|
on.waiting_for_health do |attempt|
|
94
116
|
say(
|
95
|
-
|
96
|
-
|
117
|
+
'Waiting for host cluster to reach healthy state ' \
|
118
|
+
"(attempt #{attempt})..."
|
119
|
+
)
|
97
120
|
end
|
98
121
|
end
|
99
|
-
service_cluster.with_replica_services do |
|
100
|
-
|
122
|
+
service_cluster.with_replica_services do |services|
|
123
|
+
services.each_service do |service|
|
101
124
|
service.wait_for_service_health do |on|
|
102
125
|
on.waiting_for_health do |attempt|
|
103
126
|
say(
|
104
|
-
|
105
|
-
|
127
|
+
"Waiting for service #{service.name} to reach a " \
|
128
|
+
"steady state (attempt #{attempt})..."
|
129
|
+
)
|
106
130
|
end
|
107
131
|
end
|
108
132
|
end
|
109
133
|
end
|
110
134
|
end
|
111
|
-
|
135
|
+
# rubocop:enable Metrics/BlockLength
|
136
|
+
say 'Host cluster desired capacity decreased, continuing...'
|
112
137
|
end
|
113
138
|
|
139
|
+
# rubocop:enable Metrics/MethodLength
|
140
|
+
# rubocop:enable Metrics/AbcSize
|
141
|
+
|
114
142
|
desc(
|
115
|
-
|
116
|
-
|
143
|
+
'terminate REGION ASG_NAME ECS_CLUSTER_NAME INSTANCE_IDS*',
|
144
|
+
'Terminates the specified hosts within the cluster.'
|
145
|
+
)
|
117
146
|
method_option(
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
147
|
+
:batch_size,
|
148
|
+
aliases: '-b',
|
149
|
+
type: :numeric,
|
150
|
+
default: 3,
|
151
|
+
desc: 'The number of hosts to add at a time.'
|
152
|
+
)
|
123
153
|
method_option(
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
154
|
+
:startup_time,
|
155
|
+
aliases: '-t',
|
156
|
+
type: :numeric,
|
157
|
+
default: 2,
|
158
|
+
desc: 'The number of minutes to wait for services to start up.'
|
159
|
+
)
|
160
|
+
# rubocop:disable Metrics/MethodLength
|
161
|
+
# rubocop:disable Metrics/AbcSize
|
162
|
+
# rubocop:disable Metrics/ParameterLists
|
129
163
|
def terminate(
|
130
|
-
|
131
|
-
|
164
|
+
region, asg_name, ecs_cluster_name, instance_ids,
|
165
|
+
host_cluster = nil, service_cluster = nil
|
166
|
+
)
|
132
167
|
batch_size = options[:batch_size]
|
133
168
|
|
134
169
|
service_start_wait_minutes = options[:startup_time]
|
135
170
|
service_start_wait_seconds = 60 * service_start_wait_minutes
|
136
171
|
|
137
|
-
host_cluster
|
138
|
-
|
139
|
-
|
140
|
-
Rollo::Model::ServiceCluster.new(ecs_cluster_name, region)
|
172
|
+
host_cluster ||= Rollo::Model::HostCluster.new(asg_name, region)
|
173
|
+
service_cluster ||= Rollo::Model::ServiceCluster.new(ecs_cluster_name,
|
174
|
+
region)
|
141
175
|
|
142
|
-
hosts = host_cluster.hosts.select {|h| instance_ids.include?(h.id) }
|
176
|
+
hosts = host_cluster.hosts.select { |h| instance_ids.include?(h.id) }
|
143
177
|
host_batches = hosts.each_slice(batch_size).to_a
|
144
178
|
|
145
179
|
say(
|
146
|
-
|
147
|
-
|
180
|
+
'Terminating old hosts in host cluster in batches of ' \
|
181
|
+
"#{batch_size}..."
|
182
|
+
)
|
183
|
+
# rubocop:disable Metrics/BlockLength
|
148
184
|
with_padding do
|
149
185
|
host_batches.each_with_index do |host_batch, index|
|
150
186
|
say(
|
151
|
-
|
152
|
-
|
153
|
-
|
187
|
+
"Batch #{index + 1} contains hosts: " \
|
188
|
+
"\n\t\t[#{host_batch.map(&:id).join(",\n\t\t ")}]\n" \
|
189
|
+
'Terminating...'
|
190
|
+
)
|
154
191
|
host_batch.each(&:terminate)
|
155
192
|
host_cluster.wait_for_capacity_health do |on|
|
156
193
|
on.waiting_for_health do |attempt|
|
157
194
|
say(
|
158
|
-
|
159
|
-
|
195
|
+
'Waiting for host cluster to reach healthy state ' \
|
196
|
+
"(attempt #{attempt})"
|
197
|
+
)
|
160
198
|
end
|
161
199
|
end
|
162
|
-
service_cluster.with_replica_services do |
|
163
|
-
|
200
|
+
service_cluster.with_replica_services do |services|
|
201
|
+
services.each_service do |service|
|
164
202
|
service.wait_for_service_health do |on|
|
165
203
|
on.waiting_for_health do |attempt|
|
166
204
|
say(
|
167
|
-
|
168
|
-
|
205
|
+
"Waiting for service #{service.name} to reach a " \
|
206
|
+
"steady state (attempt #{attempt})..."
|
207
|
+
)
|
169
208
|
end
|
170
209
|
end
|
171
210
|
end
|
172
211
|
end
|
173
212
|
say(
|
174
|
-
|
175
|
-
|
213
|
+
"Waiting #{service_start_wait_minutes} minute(s) for " \
|
214
|
+
'services to finish starting...'
|
215
|
+
)
|
176
216
|
sleep(service_start_wait_seconds)
|
177
217
|
say(
|
178
|
-
|
179
|
-
|
218
|
+
"Waited #{service_start_wait_minutes} minute(s). " \
|
219
|
+
'Continuing...'
|
220
|
+
)
|
180
221
|
end
|
181
222
|
end
|
182
|
-
|
223
|
+
# rubocop:enable Metrics/BlockLength
|
183
224
|
end
|
225
|
+
# rubocop:enable Metrics/ParameterLists
|
226
|
+
# rubocop:enable Metrics/MethodLength
|
227
|
+
# rubocop:enable Metrics/AbcSize
|
184
228
|
end
|
229
|
+
|
230
|
+
# rubocop:enable Metrics/ClassLength
|
185
231
|
end
|
186
232
|
end
|