rollo 0.5.0 → 0.8.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Toby Clemson
3
+ Copyright (c) 2021 InfraBlocks Maintainers
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/Rakefile CHANGED
@@ -1,24 +1,148 @@
1
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
2
 
3
- RSpec::Core::RakeTask.new(:spec)
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 :default => :spec
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 "Bump version for specified type (pre, major, minor patch)"
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 "Release gem"
138
+ desc 'Release gem'
15
139
  task :release do
16
- sh "gem release --tag --push"
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
- "&& bundle install " +
22
- "&& export LAST_MESSAGE=\"$(git log -1 --pretty=%B)\" " +
23
- "&& git commit -a --amend -m \"${LAST_MESSAGE} [ci skip]\""
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 "bundler/setup"
4
- require "rollo"
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 "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -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
- 'expand REGION ASG_NAME ECS_CLUSTER_NAME',
15
- 'Expands the host cluster by one batch.')
17
+ 'expand REGION ASG_NAME ECS_CLUSTER_NAME',
18
+ 'Expands the host cluster by one batch.'
19
+ )
16
20
  method_option(
17
- :batch_size,
18
- aliases: '-b',
19
- type: :numeric,
20
- default: 3,
21
- desc: 'The number of hosts to add at a time.')
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
- region, asg_name, _,
24
- host_cluster = nil)
30
+ region, asg_name, _,
31
+ host_cluster = nil
32
+ )
25
33
  batch_size = options[:batch_size]
26
34
 
27
- host_cluster = 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
- "Changing desired capacity from #{current} to " +
36
- "#{target}...")
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
- 'Waiting for capacity change to start ' +
41
- "(attempt #{attempt})...")
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
- 'Waiting for capacity change to complete ' +
46
- "(attempt #{attempt})...")
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 "Host cluster desired capacity increased, continuing..."
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
- 'contract REGION ASG_NAME ECS_CLUSTER_NAME',
58
- 'Contracts the host cluster by one batch')
70
+ 'contract REGION ASG_NAME ECS_CLUSTER_NAME',
71
+ 'Contracts the host cluster by one batch'
72
+ )
59
73
  method_option(
60
- :batch_size,
61
- aliases: '-b',
62
- type: :numeric,
63
- default: 3,
64
- desc: 'The number of hosts to remove at a time.')
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
- region, asg_name, ecs_cluster_name,
67
- host_cluster = nil, service_cluster = nil)
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 = host_cluster ||
71
- Rollo::Model::HostCluster.new(asg_name, region)
72
- service_cluster = service_cluster ||
73
- Rollo::Model::ServiceCluster.new(ecs_cluster_name, region)
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
- "Changing desired capacity from #{current} to " +
81
- "#{target}...")
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
- "Waiting for capacity change to start " +
86
- "(attempt #{attempt})...")
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
- "Waiting for capacity change to complete " +
91
- "(attempt #{attempt})...")
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
- "Waiting for host cluster to reach healthy state " +
96
- "(attempt #{attempt})...")
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 |on|
100
- on.each_service do |service|
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
- "Waiting for service #{service.name} to reach a " +
105
- "steady state (attempt #{attempt})...")
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
- say "Host cluster desired capacity decreased, continuing..."
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
- 'terminate REGION ASG_NAME ECS_CLUSTER_NAME INSTANCE_IDS*',
116
- 'Terminates the specified hosts within the cluster.')
143
+ 'terminate REGION ASG_NAME ECS_CLUSTER_NAME INSTANCE_IDS*',
144
+ 'Terminates the specified hosts within the cluster.'
145
+ )
117
146
  method_option(
118
- :batch_size,
119
- aliases: '-b',
120
- type: :numeric,
121
- default: 3,
122
- desc: 'The number of hosts to add at a time.')
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
- :startup_time,
125
- aliases: '-t',
126
- type: :numeric,
127
- default: 2,
128
- desc: 'The number of minutes to wait for services to start up.')
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
- region, asg_name, ecs_cluster_name, instance_ids,
131
- host_cluster = nil, service_cluster = nil)
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 = host_cluster ||
138
- Rollo::Model::HostCluster.new(asg_name, region)
139
- service_cluster = service_cluster ||
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
- 'Terminating old hosts in host cluster in batches of ' +
147
- "#{batch_size}...")
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
- "Batch #{index + 1} contains hosts: " +
152
- "\n\t\t[#{host_batch.map(&:id).join(",\n\t\t ")}]\n" +
153
- 'Terminating...')
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
- 'Waiting for host cluster to reach healthy state ' +
159
- "(attempt #{attempt})")
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 |on|
163
- on.each_service do |service|
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
- "Waiting for service #{service.name} to reach a " +
168
- "steady state (attempt #{attempt})...")
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
- "Waiting #{service_start_wait_minutes} minute(s) for " +
175
- 'services to finish starting...')
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
- "Waited #{service_start_wait_minutes} minute(s). " +
179
- 'Continuing...')
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
@@ -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,27 +26,44 @@ module Rollo
24
26
  end
25
27
 
26
28
  desc('roll REGION ASG_NAME ECS_CLUSTER_NAME',
27
- 'Rolls all hosts in the cluster')
29
+ 'Rolls all hosts in the cluster')
30
+ method_option(
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
+ )
28
39
  method_option(
29
- :batch_size,
30
- aliases: '-b',
31
- type: :numeric,
32
- default: 3,
33
- desc:
34
- 'The number of hosts / service instances to add / remove at ' +
35
- 'a time.')
40
+ :maximum_service_instances,
41
+ aliases: '-mx',
42
+ type: :numeric,
43
+ desc: 'The maximum number of service instances to expand to.'
44
+ )
45
+ method_option(
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
36
53
  def roll(region, asg_name, ecs_cluster_name)
37
54
  host_cluster = Rollo::Model::HostCluster.new(asg_name, region)
38
55
  service_cluster = Rollo::Model::ServiceCluster
39
- .new(ecs_cluster_name, region)
56
+ .new(ecs_cluster_name, region)
40
57
 
41
58
  initial_hosts = host_cluster.hosts
42
59
 
43
60
  say(
44
- "Rolling instances in host cluster #{host_cluster.name} for " +
45
- "service cluster #{service_cluster.name}...")
61
+ "Rolling instances in host cluster #{host_cluster.name} for " \
62
+ "service cluster #{service_cluster.name}..."
63
+ )
64
+ # rubocop:disable Metrics/BlockLength
46
65
  with_padding do
47
- unless host_cluster.has_desired_capacity?
66
+ unless host_cluster.desired_capacity?
48
67
  say('ERROR: Host cluster is not in stable state.')
49
68
  say('This may be due to scaling above or below the desired')
50
69
  say('capacity or because hosts are not in service or are ')
@@ -53,44 +72,53 @@ module Rollo
53
72
  end
54
73
 
55
74
  invoke(
56
- "hosts:expand",
57
- [
58
- region, asg_name, ecs_cluster_name,
59
- host_cluster
60
- ])
75
+ 'hosts:expand',
76
+ [
77
+ region, asg_name, ecs_cluster_name,
78
+ host_cluster
79
+ ]
80
+ )
61
81
 
62
82
  invoke(
63
- "services:expand",
64
- [
65
- region, asg_name, ecs_cluster_name,
66
- service_cluster
67
- ])
83
+ 'services:expand',
84
+ [
85
+ region, asg_name, ecs_cluster_name,
86
+ service_cluster
87
+ ],
88
+ maximum_instances: options[:maximum_service_instances]
89
+ )
68
90
 
69
91
  invoke(
70
- "hosts:terminate",
71
- [
72
- region, asg_name, ecs_cluster_name, initial_hosts.map(&:id),
73
- host_cluster, service_cluster
74
- ])
92
+ 'hosts:terminate',
93
+ [
94
+ region, asg_name, ecs_cluster_name, initial_hosts.map(&:id),
95
+ host_cluster, service_cluster
96
+ ]
97
+ )
75
98
 
76
99
  invoke(
77
- "hosts:contract",
78
- [
79
- region, asg_name, ecs_cluster_name,
80
- host_cluster, service_cluster
81
- ])
100
+ 'hosts:contract',
101
+ [
102
+ region, asg_name, ecs_cluster_name,
103
+ host_cluster, service_cluster
104
+ ]
105
+ )
82
106
 
83
107
  invoke(
84
- "services:contract",
85
- [
86
- region, asg_name, ecs_cluster_name,
87
- service_cluster
88
- ])
108
+ 'services:contract',
109
+ [
110
+ region, asg_name, ecs_cluster_name,
111
+ service_cluster
112
+ ],
113
+ minimum_instances: options[:minimum_service_instances]
114
+ )
89
115
  end
90
-
91
- say("Instances in host cluster #{host_cluster.name} rolled " +
92
- "successfully.")
116
+ # rubocop:enable Metrics/BlockLength
117
+ say("Instances in host cluster #{host_cluster.name} rolled " \
118
+ 'successfully.')
93
119
  end
120
+ # rubocop:enable Metrics/MethodLength
121
+ # rubocop:enable Metrics/AbcSize
94
122
  end
95
123
  end
96
- end
124
+ end