capistrano-asg-rolling 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile.lock +35 -27
- data/LICENSE.txt +1 -1
- data/README.md +16 -0
- data/capistrano-asg-rolling.gemspec +4 -5
- data/lib/capistrano/asg/rolling/ami.rb +10 -3
- data/lib/capistrano/asg/rolling/autoscale_group.rb +5 -1
- data/lib/capistrano/asg/rolling/aws.rb +2 -0
- data/lib/capistrano/asg/rolling/configuration.rb +16 -0
- data/lib/capistrano/asg/rolling/parallel.rb +19 -3
- data/lib/capistrano/asg/rolling/version.rb +1 -1
- data/lib/capistrano/asg/tasks/rolling.rake +6 -0
- metadata +18 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b336d65bcb11063bb59bd4934158b9cda2f3e03613442800d135d1bed513e208
|
|
4
|
+
data.tar.gz: 1bd33673762478806487a3d10f16662e9909bfbdf9edd21841b4db9de5ded2b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f013f9d109849878be032295ab1c518e7e0f7617d32fadc6c3c71bf3da03d8cf57d0fd28f4260e4047ed91c424573b32341e8d4ecc22c6544aa8147da05c3f89
|
|
7
|
+
data.tar.gz: d0f61692e322b744772d463825044746ac57224b6dbf2c61e1ca85ced9efa09fbae18dc9f26690be9d2f8d82a768d701b9e1bbc024c50a5b4afe5636ee8d51cd
|
data/CODE_OF_CONDUCT.md
CHANGED
|
@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
|
|
|
55
55
|
## Enforcement
|
|
56
56
|
|
|
57
57
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
58
|
-
reported by contacting the project team at
|
|
58
|
+
reported by contacting the project team at tech-arnhem@iraiser.eu. All
|
|
59
59
|
complaints will be reviewed and investigated and will result in a response that
|
|
60
60
|
is deemed necessary and appropriate to the circumstances. The project team is
|
|
61
61
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
capistrano-asg-rolling (0.
|
|
4
|
+
capistrano-asg-rolling (0.8.0)
|
|
5
5
|
aws-sdk-autoscaling (~> 1, >= 1.100.0)
|
|
6
6
|
aws-sdk-ec2 (~> 1)
|
|
7
7
|
capistrano (~> 3)
|
|
@@ -10,17 +10,17 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
addressable (2.
|
|
13
|
+
addressable (2.9.0)
|
|
14
14
|
public_suffix (>= 2.0.2, < 8.0)
|
|
15
|
-
airbrussh (1.6.
|
|
15
|
+
airbrussh (1.6.1)
|
|
16
16
|
sshkit (>= 1.6.1, != 1.7.0)
|
|
17
17
|
ast (2.4.3)
|
|
18
18
|
aws-eventstream (1.4.0)
|
|
19
|
-
aws-partitions (1.
|
|
20
|
-
aws-sdk-autoscaling (1.
|
|
21
|
-
aws-sdk-core (~> 3, >= 3.
|
|
19
|
+
aws-partitions (1.1250.0)
|
|
20
|
+
aws-sdk-autoscaling (1.158.0)
|
|
21
|
+
aws-sdk-core (~> 3, >= 3.247.0)
|
|
22
22
|
aws-sigv4 (~> 1.5)
|
|
23
|
-
aws-sdk-core (3.
|
|
23
|
+
aws-sdk-core (3.247.0)
|
|
24
24
|
aws-eventstream (~> 1, >= 1.3.0)
|
|
25
25
|
aws-partitions (~> 1, >= 1.992.0)
|
|
26
26
|
aws-sigv4 (~> 1.9)
|
|
@@ -28,14 +28,14 @@ GEM
|
|
|
28
28
|
bigdecimal
|
|
29
29
|
jmespath (~> 1, >= 1.6.1)
|
|
30
30
|
logger
|
|
31
|
-
aws-sdk-ec2 (1.
|
|
32
|
-
aws-sdk-core (~> 3, >= 3.
|
|
31
|
+
aws-sdk-ec2 (1.617.0)
|
|
32
|
+
aws-sdk-core (~> 3, >= 3.247.0)
|
|
33
33
|
aws-sigv4 (~> 1.5)
|
|
34
34
|
aws-sigv4 (1.12.1)
|
|
35
35
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
36
36
|
base64 (0.3.0)
|
|
37
|
-
bigdecimal (4.
|
|
38
|
-
capistrano (3.20.
|
|
37
|
+
bigdecimal (4.1.2)
|
|
38
|
+
capistrano (3.20.1)
|
|
39
39
|
airbrussh (>= 1.0.0)
|
|
40
40
|
i18n
|
|
41
41
|
rake (>= 10.0.0)
|
|
@@ -45,11 +45,12 @@ GEM
|
|
|
45
45
|
bigdecimal
|
|
46
46
|
rexml
|
|
47
47
|
diff-lcs (1.6.2)
|
|
48
|
+
docile (1.4.1)
|
|
48
49
|
hashdiff (1.2.1)
|
|
49
50
|
i18n (1.14.8)
|
|
50
51
|
concurrent-ruby (~> 1.0)
|
|
51
52
|
jmespath (1.6.2)
|
|
52
|
-
json (2.
|
|
53
|
+
json (2.19.5)
|
|
53
54
|
language_server-protocol (3.17.0.5)
|
|
54
55
|
lint_roller (1.1.0)
|
|
55
56
|
logger (1.7.0)
|
|
@@ -57,18 +58,18 @@ GEM
|
|
|
57
58
|
net-ssh (>= 2.6.5, < 8.0.0)
|
|
58
59
|
net-sftp (4.0.0)
|
|
59
60
|
net-ssh (>= 5.0.0, < 8.0.0)
|
|
60
|
-
net-ssh (7.3.
|
|
61
|
+
net-ssh (7.3.2)
|
|
61
62
|
ostruct (0.6.3)
|
|
62
|
-
parallel (1.
|
|
63
|
-
parser (3.3.
|
|
63
|
+
parallel (1.28.0)
|
|
64
|
+
parser (3.3.11.1)
|
|
64
65
|
ast (~> 2.4.1)
|
|
65
66
|
racc
|
|
66
|
-
prism (1.
|
|
67
|
+
prism (1.9.0)
|
|
67
68
|
public_suffix (6.0.2)
|
|
68
69
|
racc (1.8.1)
|
|
69
70
|
rainbow (3.1.1)
|
|
70
|
-
rake (13.
|
|
71
|
-
regexp_parser (2.
|
|
71
|
+
rake (13.4.2)
|
|
72
|
+
regexp_parser (2.12.0)
|
|
72
73
|
rexml (3.4.4)
|
|
73
74
|
rspec (3.13.2)
|
|
74
75
|
rspec-core (~> 3.13.0)
|
|
@@ -79,24 +80,24 @@ GEM
|
|
|
79
80
|
rspec-expectations (3.13.5)
|
|
80
81
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
81
82
|
rspec-support (~> 3.13.0)
|
|
82
|
-
rspec-mocks (3.13.
|
|
83
|
+
rspec-mocks (3.13.8)
|
|
83
84
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
84
85
|
rspec-support (~> 3.13.0)
|
|
85
|
-
rspec-support (3.13.
|
|
86
|
-
rubocop (1.
|
|
86
|
+
rspec-support (3.13.7)
|
|
87
|
+
rubocop (1.86.2)
|
|
87
88
|
json (~> 2.3)
|
|
88
89
|
language_server-protocol (~> 3.17.0.2)
|
|
89
90
|
lint_roller (~> 1.1.0)
|
|
90
|
-
parallel (
|
|
91
|
+
parallel (>= 1.10)
|
|
91
92
|
parser (>= 3.3.0.2)
|
|
92
93
|
rainbow (>= 2.2.2, < 4.0)
|
|
93
94
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
94
|
-
rubocop-ast (>= 1.
|
|
95
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
95
96
|
ruby-progressbar (~> 1.7)
|
|
96
97
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
97
|
-
rubocop-ast (1.
|
|
98
|
+
rubocop-ast (1.49.1)
|
|
98
99
|
parser (>= 3.3.7.2)
|
|
99
|
-
prism (~> 1.
|
|
100
|
+
prism (~> 1.7)
|
|
100
101
|
rubocop-performance (1.26.1)
|
|
101
102
|
lint_roller (~> 1.1)
|
|
102
103
|
rubocop (>= 1.75.0, < 2.0)
|
|
@@ -104,10 +105,16 @@ GEM
|
|
|
104
105
|
rubocop-rake (0.7.1)
|
|
105
106
|
lint_roller (~> 1.1)
|
|
106
107
|
rubocop (>= 1.72.1)
|
|
107
|
-
rubocop-rspec (3.
|
|
108
|
+
rubocop-rspec (3.9.0)
|
|
108
109
|
lint_roller (~> 1.1)
|
|
109
110
|
rubocop (~> 1.81)
|
|
110
111
|
ruby-progressbar (1.13.0)
|
|
112
|
+
simplecov (0.22.0)
|
|
113
|
+
docile (~> 1.1)
|
|
114
|
+
simplecov-html (~> 0.11)
|
|
115
|
+
simplecov_json_formatter (~> 0.1)
|
|
116
|
+
simplecov-html (0.13.2)
|
|
117
|
+
simplecov_json_formatter (0.1.4)
|
|
111
118
|
sshkit (1.25.0)
|
|
112
119
|
base64
|
|
113
120
|
logger
|
|
@@ -118,7 +125,7 @@ GEM
|
|
|
118
125
|
unicode-display_width (3.2.0)
|
|
119
126
|
unicode-emoji (~> 4.1)
|
|
120
127
|
unicode-emoji (4.2.0)
|
|
121
|
-
webmock (3.26.
|
|
128
|
+
webmock (3.26.2)
|
|
122
129
|
addressable (>= 2.8.0)
|
|
123
130
|
crack (>= 0.3.2)
|
|
124
131
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -135,6 +142,7 @@ DEPENDENCIES
|
|
|
135
142
|
rubocop-performance (~> 1.20)
|
|
136
143
|
rubocop-rake (~> 0.6)
|
|
137
144
|
rubocop-rspec (~> 3.0)
|
|
145
|
+
simplecov (~> 0.22)
|
|
138
146
|
webmock (~> 3.11)
|
|
139
147
|
|
|
140
148
|
BUNDLED WITH
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
|
@@ -113,6 +113,22 @@ Enable or disable auto-rollback on instance refreshes (default: false):
|
|
|
113
113
|
set :asg_instance_refresh_auto_rollback, true
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
+
The AWS clients are configured with `adaptive` retry mode and a retry limit of 10 by default, so transient throttling (`Aws::AutoScaling::Errors::Throttling: Rate exceeded`) does not abort a deployment. You can tune both:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
# config/deploy.rb
|
|
120
|
+
set :asg_aws_retry_mode, 'adaptive' # default; one of 'legacy', 'standard', 'adaptive'
|
|
121
|
+
set :asg_aws_retry_limit, 10 # default
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
After creating an AMI, the gem waits until it becomes available before triggering an instance refresh. The defaults match the AWS SDK defaults (~10 minutes), but for larger root volumes (e.g. resizing 24 GB → 32 GB) the AMI can take longer to become available. Override the waiter via:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
# config/deploy.rb
|
|
128
|
+
set :asg_ami_wait_delay, 15 # seconds between polls (default: 15)
|
|
129
|
+
set :asg_ami_wait_max_attempts, 40 # max polls (default: 40, ≈ 10 minutes)
|
|
130
|
+
```
|
|
131
|
+
|
|
116
132
|
## Usage
|
|
117
133
|
|
|
118
134
|
Specify the Auto Scaling Groups with the keyword `autoscale` instead of using the `server` keyword in Capistrano's stage configuration. Provide the name of the Auto Scaling Group and any properties you want to pass to the server:
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
-
require 'capistrano/asg/rolling/version'
|
|
3
|
+
require_relative 'lib/capistrano/asg/rolling/version'
|
|
6
4
|
|
|
7
5
|
Gem::Specification.new do |spec|
|
|
8
6
|
spec.name = 'capistrano-asg-rolling'
|
|
9
7
|
spec.version = Capistrano::ASG::Rolling::VERSION
|
|
10
|
-
spec.authors = [
|
|
11
|
-
spec.email = ['
|
|
8
|
+
spec.authors = %w[Kentaa iRaiser]
|
|
9
|
+
spec.email = ['tech-arnhem@iraiser.eu']
|
|
12
10
|
|
|
13
11
|
spec.summary = 'Capistrano plugin for performing rolling updates to AWS Auto Scaling Groups using Instance Refresh'
|
|
14
12
|
spec.homepage = 'https://github.com/KentaaNL/capistrano-asg-rolling'
|
|
@@ -31,6 +29,7 @@ Gem::Specification.new do |spec|
|
|
|
31
29
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
32
30
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
33
31
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
32
|
+
spec.add_development_dependency 'simplecov', '~> 0.22'
|
|
34
33
|
spec.add_development_dependency 'webmock', '~> 3.11'
|
|
35
34
|
|
|
36
35
|
spec.add_dependency 'aws-sdk-autoscaling', '~> 1', '>= 1.100.0'
|
|
@@ -36,10 +36,17 @@ module Capistrano
|
|
|
36
36
|
response = aws_ec2_client.create_image(options)
|
|
37
37
|
|
|
38
38
|
begin
|
|
39
|
-
aws_ec2_client.wait_until(:image_available, image_ids: [response.image_id])
|
|
40
|
-
|
|
39
|
+
aws_ec2_client.wait_until(:image_available, image_ids: [response.image_id]) do |waiter|
|
|
40
|
+
waiter.delay = Configuration.ami_wait_delay
|
|
41
|
+
waiter.max_attempts = Configuration.ami_wait_max_attempts
|
|
42
|
+
end
|
|
43
|
+
rescue Aws::Waiters::Errors::TooManyAttemptsError => e
|
|
41
44
|
# When waiting for the AMI takes longer than the default (10 minutes),
|
|
42
|
-
# then assume it will eventually succeed and just continue.
|
|
45
|
+
# then assume it will eventually succeed and just continue. Surface a
|
|
46
|
+
# warning so operators can see that the wait did not complete in time
|
|
47
|
+
# (for example after increasing the root volume size of the source
|
|
48
|
+
# instance) before the subsequent Instance Refresh is started.
|
|
49
|
+
Kernel.warn("WARNING: timed out waiting for AMI #{response.image_id} to become available: #{e.message}")
|
|
43
50
|
end
|
|
44
51
|
|
|
45
52
|
new(response.image_id, instance)
|
|
@@ -81,7 +81,11 @@ module Capistrano
|
|
|
81
81
|
auto_rollback: auto_rollback
|
|
82
82
|
}.compact
|
|
83
83
|
).instance_refresh_id
|
|
84
|
-
rescue Aws::AutoScaling::Errors::
|
|
84
|
+
rescue Aws::AutoScaling::Errors::ServiceError => e
|
|
85
|
+
# Wrap all AWS Auto Scaling service errors (InstanceRefreshInProgress,
|
|
86
|
+
# ValidationError, throttling, etc.) so the rake task can rescue a
|
|
87
|
+
# single gem-defined error type and continue with the remaining
|
|
88
|
+
# Auto Scaling Group(s).
|
|
85
89
|
raise Capistrano::ASG::Rolling::StartInstanceRefreshError, e
|
|
86
90
|
end
|
|
87
91
|
|
|
@@ -22,6 +22,8 @@ module Capistrano
|
|
|
22
22
|
options = {}
|
|
23
23
|
options[:region] = aws_region if aws_region
|
|
24
24
|
options[:credentials] = aws_credentials if aws_credentials.set?
|
|
25
|
+
options[:retry_mode] = Configuration.aws_retry_mode
|
|
26
|
+
options[:retry_limit] = Configuration.aws_retry_limit
|
|
25
27
|
options[:http_wire_trace] = true if ENV['AWS_HTTP_WIRE_TRACE'] == '1'
|
|
26
28
|
options
|
|
27
29
|
end
|
|
@@ -79,6 +79,22 @@ module Capistrano
|
|
|
79
79
|
def instance_refresh_polling_interval
|
|
80
80
|
fetch(:asg_instance_refresh_polling_interval, 30)
|
|
81
81
|
end
|
|
82
|
+
|
|
83
|
+
def aws_retry_mode
|
|
84
|
+
fetch(:asg_aws_retry_mode, 'adaptive')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def aws_retry_limit
|
|
88
|
+
fetch(:asg_aws_retry_limit, 10)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def ami_wait_delay
|
|
92
|
+
fetch(:asg_ami_wait_delay, 15)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def ami_wait_max_attempts
|
|
96
|
+
fetch(:asg_ami_wait_max_attempts, 40)
|
|
97
|
+
end
|
|
82
98
|
end
|
|
83
99
|
end
|
|
84
100
|
end
|
|
@@ -9,18 +9,34 @@ module Capistrano
|
|
|
9
9
|
module Parallel
|
|
10
10
|
module_function
|
|
11
11
|
|
|
12
|
+
# Runs the given block once per element of `work`, in parallel.
|
|
13
|
+
#
|
|
14
|
+
# All threads are joined before this method returns, so a failure in one
|
|
15
|
+
# block does not abandon the other in-flight threads. If any block
|
|
16
|
+
# raises, the first error is re-raised after all threads have completed;
|
|
17
|
+
# additional errors are surfaced via `Kernel.warn`.
|
|
12
18
|
def run(work)
|
|
13
|
-
|
|
19
|
+
results = Concurrent::Array.new
|
|
20
|
+
errors = Concurrent::Array.new
|
|
14
21
|
|
|
15
22
|
threads = work.map do |w|
|
|
16
23
|
Thread.new do
|
|
17
|
-
|
|
24
|
+
results << yield(w)
|
|
25
|
+
rescue StandardError => e
|
|
26
|
+
errors << e
|
|
18
27
|
end
|
|
19
28
|
end
|
|
20
29
|
|
|
21
30
|
threads.each(&:join)
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
if errors.any?
|
|
33
|
+
errors.drop(1).each do |e|
|
|
34
|
+
Kernel.warn("WARNING: parallel task failed: #{e.class}: #{e.message}")
|
|
35
|
+
end
|
|
36
|
+
raise errors.first
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
results
|
|
24
40
|
end
|
|
25
41
|
end
|
|
26
42
|
end
|
|
@@ -202,6 +202,12 @@ namespace :rolling do
|
|
|
202
202
|
else
|
|
203
203
|
logger.info "Auto Scaling Group: **#{name}**, status '#{refresh.status}'."
|
|
204
204
|
end
|
|
205
|
+
rescue Aws::AutoScaling::Errors::ServiceError => e
|
|
206
|
+
# The instance refresh is still running in AWS even though we hit a
|
|
207
|
+
# transient API error (typically throttling that exceeded the SDK's
|
|
208
|
+
# retry budget). Log and retry on the next polling interval instead
|
|
209
|
+
# of aborting the deployment.
|
|
210
|
+
logger.warning "Auto Scaling Group: **#{name}**, failed to fetch status: #{e.class}: #{e.message} - retrying on next poll."
|
|
205
211
|
end
|
|
206
212
|
next if groups.empty?
|
|
207
213
|
|
metadata
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: capistrano-asg-rolling
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kentaa
|
|
8
|
+
- iRaiser
|
|
8
9
|
autorequire:
|
|
9
10
|
bindir: exe
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
+
date: 2026-05-22 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
15
|
name: bundler
|
|
@@ -52,6 +53,20 @@ dependencies:
|
|
|
52
53
|
- - "~>"
|
|
53
54
|
- !ruby/object:Gem::Version
|
|
54
55
|
version: '3.0'
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: simplecov
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - "~>"
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0.22'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0.22'
|
|
55
70
|
- !ruby/object:Gem::Dependency
|
|
56
71
|
name: webmock
|
|
57
72
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -130,7 +145,7 @@ dependencies:
|
|
|
130
145
|
version: '1'
|
|
131
146
|
description:
|
|
132
147
|
email:
|
|
133
|
-
-
|
|
148
|
+
- tech-arnhem@iraiser.eu
|
|
134
149
|
executables: []
|
|
135
150
|
extensions: []
|
|
136
151
|
extra_rdoc_files: []
|