capistrano-asg-rolling 0.8.0 → 0.9.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/.github/workflows/test.yml +1 -1
- data/.rubocop.yml +4 -1
- data/Gemfile.lock +27 -32
- data/README.md +61 -41
- data/capistrano-asg-rolling.gemspec +2 -2
- data/lib/capistrano/asg/rolling/autoscale_group.rb +4 -6
- data/lib/capistrano/asg/rolling/autoscale_groups.rb +17 -1
- data/lib/capistrano/asg/rolling/aws.rb +5 -3
- data/lib/capistrano/asg/rolling/configuration.rb +27 -13
- data/lib/capistrano/asg/rolling/exception.rb +5 -0
- data/lib/capistrano/asg/rolling/instances.rb +4 -2
- data/lib/capistrano/asg/rolling/launch_templates.rb +10 -8
- data/lib/capistrano/asg/rolling/logger.rb +8 -2
- data/lib/capistrano/asg/rolling/plugin.rb +5 -3
- data/lib/capistrano/asg/rolling/ssh.rb +1 -1
- data/lib/capistrano/asg/rolling/version.rb +1 -1
- data/lib/capistrano/asg/rolling.rb +3 -2
- data/lib/capistrano/asg/tasks/rolling.rake +65 -48
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f2365c9ab16d5af20fe6502ec5ddb678b71d50516a6e9e514b87a94a6a34f60b
|
|
4
|
+
data.tar.gz: ab344dc87be037b41e0e56e18cdc3532339a0e368983cac5ce8c3707d8b82729
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fac5e7d23d81518e9e71924b2596be4e630ab1b38d5f57a481c49bfe71abbb5ea704465e22364957998aa00373420d5f7b8ce3805fd0ba05c5ca97b1f373bb3a
|
|
7
|
+
data.tar.gz: 007da3693ba149357ffe55e600d772c52984295e432eb79b65ecb5696e0861572e0776aa36b40ff2adaaee758209337c4406683bc88cb313e064aea1b82d0c93
|
data/.github/workflows/test.yml
CHANGED
data/.rubocop.yml
CHANGED
|
@@ -5,7 +5,7 @@ plugins:
|
|
|
5
5
|
|
|
6
6
|
AllCops:
|
|
7
7
|
NewCops: enable
|
|
8
|
-
TargetRubyVersion: 3.
|
|
8
|
+
TargetRubyVersion: 3.2
|
|
9
9
|
DisplayCopNames: true
|
|
10
10
|
DisplayStyleGuide: true
|
|
11
11
|
|
|
@@ -39,5 +39,8 @@ RSpec/IndexedLet:
|
|
|
39
39
|
RSpec/MultipleExpectations:
|
|
40
40
|
Max: 5
|
|
41
41
|
|
|
42
|
+
RSpec/MultipleMemoizedHelpers:
|
|
43
|
+
Max: 11
|
|
44
|
+
|
|
42
45
|
Style/GuardClause:
|
|
43
46
|
Enabled: false
|
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.9.0)
|
|
5
5
|
aws-sdk-autoscaling (~> 1, >= 1.100.0)
|
|
6
6
|
aws-sdk-ec2 (~> 1)
|
|
7
7
|
capistrano (~> 3)
|
|
@@ -12,15 +12,15 @@ GEM
|
|
|
12
12
|
specs:
|
|
13
13
|
addressable (2.9.0)
|
|
14
14
|
public_suffix (>= 2.0.2, < 8.0)
|
|
15
|
-
airbrussh (1.6.
|
|
15
|
+
airbrussh (1.6.2)
|
|
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.1282.0)
|
|
20
|
+
aws-sdk-autoscaling (1.166.0)
|
|
21
|
+
aws-sdk-core (~> 3, >= 3.254.0)
|
|
22
22
|
aws-sigv4 (~> 1.5)
|
|
23
|
-
aws-sdk-core (3.
|
|
23
|
+
aws-sdk-core (3.254.1)
|
|
24
24
|
aws-eventstream (~> 1, >= 1.3.0)
|
|
25
25
|
aws-partitions (~> 1, >= 1.992.0)
|
|
26
26
|
aws-sigv4 (~> 1.9)
|
|
@@ -28,8 +28,8 @@ 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.641.0)
|
|
32
|
+
aws-sdk-core (~> 3, >= 3.254.0)
|
|
33
33
|
aws-sigv4 (~> 1.5)
|
|
34
34
|
aws-sigv4 (1.12.1)
|
|
35
35
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
@@ -40,32 +40,31 @@ GEM
|
|
|
40
40
|
i18n
|
|
41
41
|
rake (>= 10.0.0)
|
|
42
42
|
sshkit (>= 1.9.0)
|
|
43
|
-
concurrent-ruby (1.3.
|
|
43
|
+
concurrent-ruby (1.3.8)
|
|
44
44
|
crack (1.0.1)
|
|
45
45
|
bigdecimal
|
|
46
46
|
rexml
|
|
47
47
|
diff-lcs (1.6.2)
|
|
48
|
-
docile (1.4.1)
|
|
49
48
|
hashdiff (1.2.1)
|
|
50
|
-
i18n (1.
|
|
49
|
+
i18n (1.15.2)
|
|
51
50
|
concurrent-ruby (~> 1.0)
|
|
52
51
|
jmespath (1.6.2)
|
|
53
|
-
json (2.
|
|
54
|
-
language_server-protocol (3.17.0.
|
|
52
|
+
json (2.21.2)
|
|
53
|
+
language_server-protocol (3.17.0.6)
|
|
55
54
|
lint_roller (1.1.0)
|
|
56
55
|
logger (1.7.0)
|
|
57
56
|
net-scp (4.1.0)
|
|
58
57
|
net-ssh (>= 2.6.5, < 8.0.0)
|
|
59
58
|
net-sftp (4.0.0)
|
|
60
59
|
net-ssh (>= 5.0.0, < 8.0.0)
|
|
61
|
-
net-ssh (7.3.
|
|
60
|
+
net-ssh (7.3.3)
|
|
62
61
|
ostruct (0.6.3)
|
|
63
62
|
parallel (1.28.0)
|
|
64
|
-
parser (3.3.
|
|
63
|
+
parser (3.3.12.0)
|
|
65
64
|
ast (~> 2.4.1)
|
|
66
65
|
racc
|
|
67
66
|
prism (1.9.0)
|
|
68
|
-
public_suffix (
|
|
67
|
+
public_suffix (7.0.5)
|
|
69
68
|
racc (1.8.1)
|
|
70
69
|
rainbow (3.1.1)
|
|
71
70
|
rake (13.4.2)
|
|
@@ -84,8 +83,8 @@ GEM
|
|
|
84
83
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
85
84
|
rspec-support (~> 3.13.0)
|
|
86
85
|
rspec-support (3.13.7)
|
|
87
|
-
rubocop (1.
|
|
88
|
-
json (
|
|
86
|
+
rubocop (1.90.0)
|
|
87
|
+
json (>= 2.3)
|
|
89
88
|
language_server-protocol (~> 3.17.0.2)
|
|
90
89
|
lint_roller (~> 1.1.0)
|
|
91
90
|
parallel (>= 1.10)
|
|
@@ -95,27 +94,23 @@ GEM
|
|
|
95
94
|
rubocop-ast (>= 1.49.0, < 2.0)
|
|
96
95
|
ruby-progressbar (~> 1.7)
|
|
97
96
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
98
|
-
rubocop-ast (1.
|
|
97
|
+
rubocop-ast (1.50.0)
|
|
99
98
|
parser (>= 3.3.7.2)
|
|
100
99
|
prism (~> 1.7)
|
|
101
|
-
rubocop-performance (1.
|
|
100
|
+
rubocop-performance (1.27.0)
|
|
102
101
|
lint_roller (~> 1.1)
|
|
103
|
-
rubocop (>= 1.
|
|
102
|
+
rubocop (>= 1.89.0, < 2.0)
|
|
104
103
|
rubocop-ast (>= 1.47.1, < 2.0)
|
|
105
104
|
rubocop-rake (0.7.1)
|
|
106
105
|
lint_roller (~> 1.1)
|
|
107
106
|
rubocop (>= 1.72.1)
|
|
108
|
-
rubocop-rspec (3.
|
|
107
|
+
rubocop-rspec (3.10.2)
|
|
109
108
|
lint_roller (~> 1.1)
|
|
110
|
-
|
|
109
|
+
regexp_parser (>= 2.0)
|
|
110
|
+
rubocop (~> 1.86, >= 1.86.2)
|
|
111
111
|
ruby-progressbar (1.13.0)
|
|
112
|
-
simplecov (
|
|
113
|
-
|
|
114
|
-
simplecov-html (~> 0.11)
|
|
115
|
-
simplecov_json_formatter (~> 0.1)
|
|
116
|
-
simplecov-html (0.13.2)
|
|
117
|
-
simplecov_json_formatter (0.1.4)
|
|
118
|
-
sshkit (1.25.0)
|
|
112
|
+
simplecov (1.1.1)
|
|
113
|
+
sshkit (1.25.1)
|
|
119
114
|
base64
|
|
120
115
|
logger
|
|
121
116
|
net-scp (>= 1.1.2)
|
|
@@ -125,7 +120,7 @@ GEM
|
|
|
125
120
|
unicode-display_width (3.2.0)
|
|
126
121
|
unicode-emoji (~> 4.1)
|
|
127
122
|
unicode-emoji (4.2.0)
|
|
128
|
-
webmock (3.26.
|
|
123
|
+
webmock (3.26.3)
|
|
129
124
|
addressable (>= 2.8.0)
|
|
130
125
|
crack (>= 0.3.2)
|
|
131
126
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -142,7 +137,7 @@ DEPENDENCIES
|
|
|
142
137
|
rubocop-performance (~> 1.20)
|
|
143
138
|
rubocop-rake (~> 0.6)
|
|
144
139
|
rubocop-rspec (~> 3.0)
|
|
145
|
-
simplecov (~> 0
|
|
140
|
+
simplecov (~> 1.0)
|
|
146
141
|
webmock (~> 3.11)
|
|
147
142
|
|
|
148
143
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
Capistrano plugin for performing rolling updates to AWS Auto Scaling Groups using the [instance refresh feature](https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-instance-refresh.html).
|
|
8
8
|
|
|
9
|
-
Instead of deploying to live servers, capistrano-asg-rolling will create
|
|
9
|
+
Instead of deploying to live servers, capistrano-asg-rolling will create temporary instances for deployment and then trigger an instance refresh to perform a rolling update of the Auto Scaling Group(s). In more detail, during deployment it will:
|
|
10
10
|
|
|
11
|
-
- Launch an instance from the AMI defined in
|
|
11
|
+
- Launch an instance for each Auto Scaling Group, from the AMI defined in its Launch Template.
|
|
12
12
|
- Deploy your application to the launched instances.
|
|
13
13
|
- After deployment, stop the instances and create an AMI for each instance.
|
|
14
14
|
- Create new Launch Template versions for the new AMIs.
|
|
15
15
|
- Trigger Instance Refresh on the Auto Scaling Group(s) to perform a rolling update.
|
|
16
16
|
- Delete any outdated Launch Template versions, AMIs and snapshots created by previous deployments.
|
|
17
|
-
- Terminate the
|
|
17
|
+
- Terminate the temporary instances.
|
|
18
18
|
|
|
19
19
|
## Important
|
|
20
20
|
|
|
@@ -25,13 +25,13 @@ https://docs.aws.amazon.com/autoscaling/ec2/userguide/instance-refresh-overview.
|
|
|
25
25
|
|
|
26
26
|
### Launch Templates
|
|
27
27
|
|
|
28
|
-
This gem depends on Auto Scaling Groups with Launch Templates. Using an Auto Scaling Group with a Launch Configuration is not supported, and will raise
|
|
28
|
+
This gem depends on Auto Scaling Groups with Launch Templates. Using an Auto Scaling Group with a Launch Configuration is not supported, and will raise a `Capistrano::ASG::Rolling::NoLaunchTemplate` error.
|
|
29
29
|
|
|
30
|
-
Instance refresh uses the desired configuration to update the Launch Template version of the Auto Scaling Group after a
|
|
30
|
+
Instance refresh uses the desired configuration to update the Launch Template version of the Auto Scaling Group after a successful deployment. Setting the Launch Template version to `Latest` on the Auto Scaling Group is not needed.
|
|
31
31
|
|
|
32
32
|
### Experimental
|
|
33
33
|
|
|
34
|
-
Please note that this gem works well for our configuration / use case, but it might not fit yours. Any feedback
|
|
34
|
+
Please note that this gem works well for our configuration / use case, but it might not fit yours. Any feedback via GitHub issues / pull requests is much appreciated.
|
|
35
35
|
|
|
36
36
|
## Installation
|
|
37
37
|
|
|
@@ -59,74 +59,94 @@ install_plugin Capistrano::ASG::Rolling::Plugin
|
|
|
59
59
|
|
|
60
60
|
## Configuration
|
|
61
61
|
|
|
62
|
-
Below
|
|
62
|
+
Below you can find all configurable settings for this gem.
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
### AWS
|
|
65
|
+
|
|
66
|
+
Set up the AWS credentials and region:
|
|
65
67
|
|
|
66
68
|
```ruby
|
|
67
69
|
# config/deploy.rb
|
|
68
70
|
set :aws_access_key_id, ENV['AWS_ACCESS_KEY_ID']
|
|
69
71
|
set :aws_secret_access_key, ENV['AWS_SECRET_ACCESS_KEY']
|
|
72
|
+
set :aws_session_token, ENV['AWS_SESSION_TOKEN']
|
|
70
73
|
set :aws_region, ENV['AWS_REGION']
|
|
71
74
|
```
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
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:
|
|
74
77
|
|
|
75
78
|
```ruby
|
|
76
79
|
# config/deploy.rb
|
|
77
|
-
set :
|
|
80
|
+
set :asg_aws_retry_mode, 'adaptive' # default; one of 'legacy', 'standard', 'adaptive'
|
|
81
|
+
set :asg_aws_retry_limit, 10 # default
|
|
78
82
|
```
|
|
79
83
|
|
|
80
|
-
|
|
84
|
+
### Launched instances
|
|
85
|
+
|
|
86
|
+
Launched instances are added to Capistrano as servers using their private IP address by default. Set this option to false to use the public IP address instead:
|
|
81
87
|
|
|
82
88
|
```ruby
|
|
83
89
|
# config/deploy.rb
|
|
84
90
|
set :asg_rolling_use_private_ip_address, false
|
|
85
91
|
```
|
|
86
92
|
|
|
87
|
-
|
|
93
|
+
When launching an instance, you can override any settings defined in the Launch Template with:
|
|
88
94
|
|
|
89
95
|
```ruby
|
|
90
96
|
# config/deploy.rb
|
|
91
|
-
set :
|
|
97
|
+
set :asg_rolling_instance_overrides, { instance_type: 'c5.large' }
|
|
92
98
|
```
|
|
93
99
|
|
|
94
|
-
|
|
100
|
+
### AMI
|
|
101
|
+
|
|
102
|
+
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:
|
|
95
103
|
|
|
96
104
|
```ruby
|
|
97
105
|
# config/deploy.rb
|
|
98
|
-
set :
|
|
106
|
+
set :asg_ami_wait_delay, 15 # seconds between polls (default: 15)
|
|
107
|
+
set :asg_ami_wait_max_attempts, 40 # max polls (default: 40, ≈ 10 minutes)
|
|
99
108
|
```
|
|
100
109
|
|
|
101
|
-
|
|
110
|
+
### Instance refresh
|
|
111
|
+
|
|
112
|
+
You can make Capistrano wait until the instance refresh on the Auto Scaling Group(s) has completed:
|
|
102
113
|
|
|
103
114
|
```ruby
|
|
104
115
|
# config/deploy.rb
|
|
105
116
|
set :asg_wait_for_instance_refresh, true
|
|
106
|
-
set :asg_instance_refresh_polling_interval, 30 # default
|
|
117
|
+
set :asg_instance_refresh_polling_interval, 30 # seconds (default: 30)
|
|
107
118
|
```
|
|
108
119
|
|
|
109
|
-
Enable
|
|
120
|
+
Enable auto-rollback to have the Auto Scaling Group roll back to its previous configuration when the instance refresh fails (default: false):
|
|
110
121
|
|
|
111
122
|
```ruby
|
|
112
123
|
# config/deploy.rb
|
|
113
124
|
set :asg_instance_refresh_auto_rollback, true
|
|
114
125
|
```
|
|
115
126
|
|
|
116
|
-
|
|
127
|
+
### Cleanup
|
|
128
|
+
|
|
129
|
+
After deployment, any outdated Launch Template versions, AMIs and snapshots will be deleted. By default, the same number of versions as Capistrano's `keep_releases` will be kept. Change this with:
|
|
117
130
|
|
|
118
131
|
```ruby
|
|
119
132
|
# config/deploy.rb
|
|
120
|
-
set :
|
|
121
|
-
set :asg_aws_retry_limit, 10 # default
|
|
133
|
+
set :asg_rolling_keep_versions, 10
|
|
122
134
|
```
|
|
123
135
|
|
|
124
|
-
|
|
136
|
+
### Logging
|
|
137
|
+
|
|
138
|
+
Verbose logging is enabled by default; set this option to false for less verbose logging:
|
|
125
139
|
|
|
126
140
|
```ruby
|
|
127
141
|
# config/deploy.rb
|
|
128
|
-
set :
|
|
129
|
-
|
|
142
|
+
set :asg_rolling_log_verbose, false
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
By default, the log output does not contain any timestamps; set the option below to true to enable timestamps:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
# config/deploy.rb
|
|
149
|
+
set :asg_rolling_log_timestamp, true
|
|
130
150
|
```
|
|
131
151
|
|
|
132
152
|
## Usage
|
|
@@ -145,7 +165,7 @@ Now start a deployment with `cap <stage> deploy` and enjoy.
|
|
|
145
165
|
|
|
146
166
|
There might be cases where you just want to deploy your code to the servers in the Auto Scaling Group(s) without a rolling update.
|
|
147
167
|
|
|
148
|
-
You can configure rolling updates per
|
|
168
|
+
You can configure rolling updates per Auto Scaling Group by using the `rolling` option:
|
|
149
169
|
|
|
150
170
|
```ruby
|
|
151
171
|
# config/deploy/<stage>.rb
|
|
@@ -153,28 +173,28 @@ autoscale 'app-autoscale-group', rolling: true # default: use rolling deploym
|
|
|
153
173
|
autoscale 'web-autoscale-group', rolling: false # override: use normal deployment
|
|
154
174
|
```
|
|
155
175
|
|
|
156
|
-
### Deploy with custom
|
|
176
|
+
### Deploy with custom minimum/maximum healthy percentages
|
|
157
177
|
|
|
158
178
|
The instance refresh is triggered without specifying a value for the minimum / maximum healthy percentages. This means that either the default
|
|
159
179
|
values will be used (minimum: 90%, maximum: 100%) or the percentages set in the instance maintenance policy for the Auto Scaling Group.
|
|
160
|
-
You can tune both the minimum and maximum values to have more control
|
|
180
|
+
You can tune both the minimum and maximum values to have more control over the desired capacity that must be healthy to proceed with replacing instances.
|
|
161
181
|
|
|
162
|
-
For example
|
|
182
|
+
For example, reducing the minimum healthy percentage allows more instances to be terminated and replaced at once during the instance refresh; a value of 0 would terminate and replace all instances in the Auto Scaling Group at once.
|
|
163
183
|
|
|
164
|
-
You can configure the minimum healthy percentage per
|
|
184
|
+
You can configure the minimum healthy percentage per Auto Scaling Group using the `min_healthy_percentage` option, and the maximum healthy percentage using the `max_healthy_percentage` option. Please note that if you specify `max_healthy_percentage`, you must also specify `min_healthy_percentage`.
|
|
165
185
|
|
|
166
186
|
```ruby
|
|
167
187
|
# config/deploy/<stage>.rb
|
|
168
|
-
autoscale 'app-autoscale-group'
|
|
188
|
+
autoscale 'app-autoscale-group' # use default percentages or percentages set in the instance maintenance policy
|
|
169
189
|
autoscale 'web-autoscale-group', min_healthy_percentage: 75 # allow 25% of instances to be terminated and replaced at once
|
|
170
190
|
autoscale 'web-autoscale-group', min_healthy_percentage: 100, max_healthy_percentage: 110 # allow for 10% above desired capacity during instance refresh
|
|
171
191
|
```
|
|
172
192
|
|
|
173
193
|
### Custom stage
|
|
174
194
|
|
|
175
|
-
The rolling configuration of the stage has a side-effect: any Capistrano
|
|
195
|
+
The rolling configuration of the stage has a side-effect: any Capistrano task you run will also launch instances per Auto Scaling Group.
|
|
176
196
|
|
|
177
|
-
For example the command
|
|
197
|
+
For example, the command `cap production rails:console` will launch a new instance and run `rails:console` on that instance. While that can be useful, you often just want to run the task on the primary server. A solution is to create two stages with different rolling configurations, for example:
|
|
178
198
|
|
|
179
199
|
```ruby
|
|
180
200
|
# config/deploy/production.rb
|
|
@@ -199,7 +219,7 @@ With these two stages, you can run any tasks with `cap production <task name>` a
|
|
|
199
219
|
|
|
200
220
|
### Tags
|
|
201
221
|
|
|
202
|
-
During deployment, the following tags will be added to the AMI and snapshot containing information about the current application, stage and deployment:
|
|
222
|
+
During deployment, the following tags will be added to the AMI and snapshot, containing information about the current application, stage and deployment:
|
|
203
223
|
- `capistrano-asg-rolling:application`
|
|
204
224
|
- `capistrano-asg-rolling:stage`
|
|
205
225
|
- `capistrano-asg-rolling:deployment-branch`
|
|
@@ -208,7 +228,7 @@ During deployment, the following tags will be added to the AMI and snapshot cont
|
|
|
208
228
|
- `capistrano-asg-rolling:deployment-user`
|
|
209
229
|
|
|
210
230
|
In addition to that, the tag `capistrano-asg-rolling:gem-version` will be added with the value of the current gem version.
|
|
211
|
-
This tag is also used to determine
|
|
231
|
+
This tag is also used to determine whether the AMI was created by this gem and can therefore be deleted automatically.
|
|
212
232
|
|
|
213
233
|
You can add custom tag(s) to the AMI and snapshot by setting the property `asg_rolling_ami_tags`, for example:
|
|
214
234
|
|
|
@@ -221,7 +241,7 @@ set :asg_rolling_ami_tags, { 'Application' => 'My Application', 'Environment' =>
|
|
|
221
241
|
|
|
222
242
|
### Test deployment
|
|
223
243
|
|
|
224
|
-
Do a test deployment: run the deploy task, but do not trigger
|
|
244
|
+
Do a test deployment: run the deploy task, but do not trigger an instance refresh and do not automatically terminate the launched instances.
|
|
225
245
|
|
|
226
246
|
```bash
|
|
227
247
|
cap <stage> rolling:deploy_test
|
|
@@ -229,8 +249,8 @@ cap <stage> rolling:deploy_test
|
|
|
229
249
|
|
|
230
250
|
### Launch instances
|
|
231
251
|
|
|
232
|
-
|
|
233
|
-
|
|
252
|
+
Launch instance(s) from the Launch Template of the Auto Scaling Group(s).
|
|
253
|
+
These instances are not attached to the Auto Scaling Group and need to be terminated manually.
|
|
234
254
|
|
|
235
255
|
```bash
|
|
236
256
|
cap <stage> rolling:launch_instances
|
|
@@ -238,8 +258,8 @@ cap <stage> rolling:launch_instances
|
|
|
238
258
|
|
|
239
259
|
### Create AMI of instance in ASG
|
|
240
260
|
|
|
241
|
-
Pick an instance in the Auto
|
|
242
|
-
Then create an AMI
|
|
261
|
+
Pick an instance in the Auto Scaling Group(s), put it into standby and stop it.
|
|
262
|
+
Then create an AMI, start the instance and put it back into service, and create a new Launch Template version with the new AMI.
|
|
243
263
|
|
|
244
264
|
```bash
|
|
245
265
|
cap <stage> rolling:create_ami
|
|
@@ -247,7 +267,7 @@ cap <stage> rolling:create_ami
|
|
|
247
267
|
|
|
248
268
|
## Filtering
|
|
249
269
|
|
|
250
|
-
You can filter any command to run on a specific Auto
|
|
270
|
+
You can filter any command to run on a specific Auto Scaling Group by using the parameter `asg_name`.
|
|
251
271
|
|
|
252
272
|
For example, the command:
|
|
253
273
|
|
|
@@ -255,7 +275,7 @@ For example, the command:
|
|
|
255
275
|
cap <stage> deploy asg_name=app-autoscale-group
|
|
256
276
|
```
|
|
257
277
|
|
|
258
|
-
|
|
278
|
+
This will do a deployment only on the Auto Scaling Group named `app-autoscale-group`.
|
|
259
279
|
|
|
260
280
|
## IAM policy
|
|
261
281
|
|
|
@@ -24,12 +24,12 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
25
25
|
spec.require_paths = ['lib']
|
|
26
26
|
|
|
27
|
-
spec.required_ruby_version = '>= 3.
|
|
27
|
+
spec.required_ruby_version = '>= 3.2.0'
|
|
28
28
|
|
|
29
29
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
30
30
|
spec.add_development_dependency 'rake', '~> 13.0'
|
|
31
31
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
32
|
-
spec.add_development_dependency 'simplecov', '~> 0
|
|
32
|
+
spec.add_development_dependency 'simplecov', '~> 1.0'
|
|
33
33
|
spec.add_development_dependency 'webmock', '~> 3.11'
|
|
34
34
|
|
|
35
35
|
spec.add_dependency 'aws-sdk-autoscaling', '~> 1', '>= 1.100.0'
|
|
@@ -21,12 +21,6 @@ module Capistrano
|
|
|
21
21
|
@name = name
|
|
22
22
|
@properties = properties
|
|
23
23
|
|
|
24
|
-
if properties[:healthy_percentage]
|
|
25
|
-
properties[:min_healthy_percentage] = properties.delete(:healthy_percentage)
|
|
26
|
-
|
|
27
|
-
Kernel.warn('WARNING: the property `healthy_percentage` is deprecated and will be removed in a future release. Please update to `min_healthy_percentage`.')
|
|
28
|
-
end
|
|
29
|
-
|
|
30
24
|
validate_properties!
|
|
31
25
|
end
|
|
32
26
|
|
|
@@ -106,6 +100,10 @@ module Capistrano
|
|
|
106
100
|
return nil if status.nil?
|
|
107
101
|
|
|
108
102
|
InstanceRefreshStatus.new(status, percentage_complete)
|
|
103
|
+
rescue Aws::AutoScaling::Errors::ServiceError => e
|
|
104
|
+
# Wrap AWS service errors (typically throttling that exceeded the SDK's
|
|
105
|
+
# retry budget) so callers can rescue a gem-defined error type.
|
|
106
|
+
raise Capistrano::ASG::Rolling::InstanceRefreshStatusError, e
|
|
109
107
|
end
|
|
110
108
|
|
|
111
109
|
# Returns instances with lifecycle state "InService" for this Auto Scaling Group.
|
|
@@ -19,8 +19,24 @@ module Capistrano
|
|
|
19
19
|
@groups.reject { |group| filtered?(group) }.each(&)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def empty?
|
|
23
|
+
none?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def rolling
|
|
27
|
+
self.class.new(select(&:rolling?))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def standard
|
|
31
|
+
self.class.new(reject(&:rolling?))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def with_unique_images
|
|
35
|
+
self.class.new(uniq { |group| group.launch_template.image_id })
|
|
36
|
+
end
|
|
37
|
+
|
|
22
38
|
def launch_templates
|
|
23
|
-
templates =
|
|
39
|
+
templates = map(&:launch_template)
|
|
24
40
|
LaunchTemplates.new(templates)
|
|
25
41
|
end
|
|
26
42
|
|
|
@@ -6,14 +6,16 @@ require 'aws-sdk-ec2'
|
|
|
6
6
|
module Capistrano
|
|
7
7
|
module ASG
|
|
8
8
|
module Rolling
|
|
9
|
-
# AWS SDK.
|
|
9
|
+
# AWS SDK helpers. Clients are memoized per object and shared across objects on the same thread.
|
|
10
10
|
module AWS
|
|
11
11
|
def aws_autoscaling_client
|
|
12
|
-
@aws_autoscaling_client ||=
|
|
12
|
+
@aws_autoscaling_client ||=
|
|
13
|
+
Thread.current[:aws_autoscaling_client] ||= ::Aws::AutoScaling::Client.new(aws_options)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def aws_ec2_client
|
|
16
|
-
@aws_ec2_client ||=
|
|
17
|
+
@aws_ec2_client ||=
|
|
18
|
+
Thread.current[:aws_ec2_client] ||= ::Aws::EC2::Client.new(aws_options)
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
private
|
|
@@ -3,25 +3,39 @@
|
|
|
3
3
|
module Capistrano
|
|
4
4
|
module ASG
|
|
5
5
|
module Rolling
|
|
6
|
-
# Singleton that holds the configuration.
|
|
6
|
+
# Singleton that holds the configuration and global state of the plugin.
|
|
7
|
+
# State is eagerly initialized so accessors are lock-free.
|
|
7
8
|
module Configuration
|
|
8
9
|
extend Capistrano::DSL
|
|
9
10
|
|
|
11
|
+
# Registered Auto Scaling Groups.
|
|
12
|
+
@autoscale_groups = AutoscaleGroups.new
|
|
13
|
+
|
|
14
|
+
# Launched Instances.
|
|
15
|
+
@instances = Instances.new
|
|
16
|
+
|
|
17
|
+
# Updated Launch Templates.
|
|
18
|
+
@launch_templates = LaunchTemplates.new
|
|
19
|
+
|
|
10
20
|
module_function
|
|
11
21
|
|
|
12
|
-
# Registered Auto Scaling Groups.
|
|
13
22
|
def autoscale_groups
|
|
14
|
-
@autoscale_groups
|
|
23
|
+
@autoscale_groups
|
|
15
24
|
end
|
|
16
25
|
|
|
17
|
-
# Launched Instances.
|
|
18
26
|
def instances
|
|
19
|
-
@instances
|
|
27
|
+
@instances
|
|
20
28
|
end
|
|
21
29
|
|
|
22
|
-
# Updated Launch Templates.
|
|
23
30
|
def launch_templates
|
|
24
|
-
@launch_templates
|
|
31
|
+
@launch_templates
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Resets all mutable state.
|
|
35
|
+
def reset!
|
|
36
|
+
@autoscale_groups = AutoscaleGroups.new
|
|
37
|
+
@instances = Instances.new
|
|
38
|
+
@launch_templates = LaunchTemplates.new
|
|
25
39
|
end
|
|
26
40
|
|
|
27
41
|
def aws_access_key_id
|
|
@@ -44,10 +58,6 @@ module Capistrano
|
|
|
44
58
|
fetch(:asg_rolling_group_name)
|
|
45
59
|
end
|
|
46
60
|
|
|
47
|
-
def ssh_options
|
|
48
|
-
fetch(:asg_rolling_ssh_options, fetch(:ssh_options))
|
|
49
|
-
end
|
|
50
|
-
|
|
51
61
|
def instance_overrides
|
|
52
62
|
fetch(:asg_rolling_instance_overrides)
|
|
53
63
|
end
|
|
@@ -60,8 +70,12 @@ module Capistrano
|
|
|
60
70
|
fetch(:asg_rolling_keep_versions, fetch(:keep_releases))
|
|
61
71
|
end
|
|
62
72
|
|
|
63
|
-
def
|
|
64
|
-
fetch(:
|
|
73
|
+
def log_verbose?
|
|
74
|
+
fetch(:asg_rolling_log_verbose)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def log_timestamp?
|
|
78
|
+
fetch(:asg_rolling_log_timestamp)
|
|
65
79
|
end
|
|
66
80
|
|
|
67
81
|
def rolling_update=(value)
|
|
@@ -13,9 +13,14 @@ module Capistrano
|
|
|
13
13
|
class NoLaunchTemplate < Capistrano::ASG::Rolling::Exception
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# Exception when starting an instance refresh failed.
|
|
16
17
|
class StartInstanceRefreshError < Capistrano::ASG::Rolling::Exception
|
|
17
18
|
end
|
|
18
19
|
|
|
20
|
+
# Exception when fetching the instance refresh status failed.
|
|
21
|
+
class InstanceRefreshStatusError < Capistrano::ASG::Rolling::Exception
|
|
22
|
+
end
|
|
23
|
+
|
|
19
24
|
# Exception when the instance refresh failed on one of the ASGs.
|
|
20
25
|
class InstanceRefreshFailed < Capistrano::ASG::Rolling::Exception
|
|
21
26
|
def initialize
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'concurrent/array'
|
|
4
|
+
|
|
3
5
|
module Capistrano
|
|
4
6
|
module ASG
|
|
5
7
|
module Rolling
|
|
@@ -8,7 +10,7 @@ module Capistrano
|
|
|
8
10
|
include Enumerable
|
|
9
11
|
|
|
10
12
|
def initialize(instances = [])
|
|
11
|
-
@instances = instances
|
|
13
|
+
@instances = Concurrent::Array.new(instances)
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def <<(instance)
|
|
@@ -23,7 +25,7 @@ module Capistrano
|
|
|
23
25
|
instances.empty?
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
def
|
|
28
|
+
def auto_terminatable
|
|
27
29
|
self.class.new(select(&:auto_terminate?))
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'set'
|
|
4
|
-
|
|
5
3
|
module Capistrano
|
|
6
4
|
module ASG
|
|
7
5
|
module Rolling
|
|
@@ -29,16 +27,20 @@ module Capistrano
|
|
|
29
27
|
@templates.empty?
|
|
30
28
|
end
|
|
31
29
|
|
|
30
|
+
# For each AMI, find templates still pointing at the old image it was built
|
|
31
|
+
# from, then create new template versions referencing the new AMI in parallel.
|
|
32
|
+
#
|
|
33
|
+
# Returns `LaunchTemplates` containing only the newly created versions.
|
|
32
34
|
def update(amis:, description: nil)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
amis.each do |ami|
|
|
35
|
+
template_image_pairs = amis.flat_map do |ami|
|
|
36
36
|
old_image_id = ami.instance.image_id
|
|
37
37
|
new_image_id = ami.id
|
|
38
38
|
|
|
39
|
-
with_image(old_image_id).
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
with_image(old_image_id).map { |template| [template, new_image_id] }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
updated_templates = Parallel.run(template_image_pairs) do |template, new_image_id|
|
|
43
|
+
template.create_version(image_id: new_image_id, description: description)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
self.class.new(updated_templates)
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
module Capistrano
|
|
4
4
|
module ASG
|
|
5
5
|
module Rolling
|
|
6
|
-
#
|
|
6
|
+
# Colorized stdout/stderr output with optional timestamps and **bold** formatting.
|
|
7
7
|
class Logger
|
|
8
|
-
def initialize(verbose: false)
|
|
8
|
+
def initialize(timestamp: false, verbose: false)
|
|
9
|
+
@timestamp = timestamp
|
|
9
10
|
@verbose = verbose
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -28,6 +29,7 @@ module Capistrano
|
|
|
28
29
|
private
|
|
29
30
|
|
|
30
31
|
def format_text(text, color: nil)
|
|
32
|
+
text = "[#{current_time}] #{text}" if @timestamp
|
|
31
33
|
text = colorize_text(text, color) if color
|
|
32
34
|
text.gsub(/\*\*(.+?)\*\*/, bold_text('\\1'))
|
|
33
35
|
end
|
|
@@ -43,6 +45,10 @@ module Capistrano
|
|
|
43
45
|
def _color
|
|
44
46
|
@_color ||= SSHKit::Color.new($stdout)
|
|
45
47
|
end
|
|
48
|
+
|
|
49
|
+
def current_time
|
|
50
|
+
Time.now.strftime('%H:%M:%S')
|
|
51
|
+
end
|
|
46
52
|
end
|
|
47
53
|
end
|
|
48
54
|
end
|
|
@@ -14,7 +14,8 @@ module Capistrano
|
|
|
14
14
|
def set_defaults
|
|
15
15
|
set_if_empty :asg_rolling_group_name, ENV.fetch('asg_name', nil)
|
|
16
16
|
set_if_empty :asg_rolling_use_private_ip_address, true
|
|
17
|
-
set_if_empty :
|
|
17
|
+
set_if_empty :asg_rolling_log_verbose, true
|
|
18
|
+
set_if_empty :asg_rolling_log_timestamp, false
|
|
18
19
|
set_if_empty :asg_rolling_update, true
|
|
19
20
|
end
|
|
20
21
|
|
|
@@ -28,6 +29,7 @@ module Capistrano
|
|
|
28
29
|
|
|
29
30
|
after 'rolling:update', 'rolling:cleanup'
|
|
30
31
|
after 'rolling:create_ami', 'rolling:cleanup'
|
|
32
|
+
|
|
31
33
|
after 'rolling:update', 'rolling:instance_refresh_status'
|
|
32
34
|
after 'rolling:trigger_instance_refresh', 'rolling:instance_refresh_status'
|
|
33
35
|
|
|
@@ -41,7 +43,7 @@ module Capistrano
|
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
def logger
|
|
44
|
-
@logger ||= Logger.new(verbose: config.
|
|
46
|
+
@logger ||= Logger.new(timestamp: config.log_timestamp?, verbose: config.log_verbose?)
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
def config
|
|
@@ -59,7 +61,7 @@ module Capistrano
|
|
|
59
61
|
|
|
60
62
|
# Make sure any instances that were launched for rolling updates are terminated.
|
|
61
63
|
def cleanup
|
|
62
|
-
instances = config.instances.
|
|
64
|
+
instances = config.instances.auto_terminatable
|
|
63
65
|
return if instances.empty?
|
|
64
66
|
|
|
65
67
|
logger.info 'Terminating instance(s)...'
|
|
@@ -6,7 +6,6 @@ require 'capistrano/asg/rolling/aws'
|
|
|
6
6
|
require 'capistrano/asg/rolling/ami'
|
|
7
7
|
require 'capistrano/asg/rolling/autoscale_group'
|
|
8
8
|
require 'capistrano/asg/rolling/autoscale_groups'
|
|
9
|
-
require 'capistrano/asg/rolling/configuration'
|
|
10
9
|
require 'capistrano/asg/rolling/dsl'
|
|
11
10
|
require 'capistrano/asg/rolling/exception'
|
|
12
11
|
require 'capistrano/asg/rolling/instance'
|
|
@@ -15,8 +14,10 @@ require 'capistrano/asg/rolling/launch_template'
|
|
|
15
14
|
require 'capistrano/asg/rolling/launch_templates'
|
|
16
15
|
require 'capistrano/asg/rolling/logger'
|
|
17
16
|
require 'capistrano/asg/rolling/parallel'
|
|
18
|
-
require 'capistrano/asg/rolling/plugin'
|
|
19
17
|
require 'capistrano/asg/rolling/snapshot'
|
|
20
18
|
require 'capistrano/asg/rolling/ssh'
|
|
21
19
|
require 'capistrano/asg/rolling/tags'
|
|
22
20
|
require 'capistrano/asg/rolling/version'
|
|
21
|
+
|
|
22
|
+
require 'capistrano/asg/rolling/configuration'
|
|
23
|
+
require 'capistrano/asg/rolling/plugin'
|
|
@@ -2,35 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
namespace :rolling do
|
|
4
4
|
desc 'Setup servers to be used for (rolling) deployment'
|
|
5
|
-
task :
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
task setup: %w[rolling:prepare rolling:wait_for_ssh]
|
|
6
|
+
|
|
7
|
+
desc 'Resolve Auto Scaling Groups, launch rolling instances, and register all servers'
|
|
8
|
+
task :prepare do
|
|
9
|
+
rolling_groups = config.autoscale_groups.rolling
|
|
10
|
+
if rolling_groups.any?
|
|
11
|
+
rolling_groups.each do |group|
|
|
8
12
|
logger.info "Auto Scaling Group: **#{group.name}**, rolling deployment strategy."
|
|
13
|
+
end
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
launched_instances = Capistrano::ASG::Rolling::Parallel.run(rolling_groups.with_unique_images) do |group|
|
|
16
|
+
Capistrano::ASG::Rolling::Instance.run(
|
|
17
|
+
autoscaling_group: group,
|
|
18
|
+
overrides: config.instance_overrides
|
|
19
|
+
).tap { |instance| config.instances << instance }
|
|
20
|
+
end
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
launched_instances.each do |instance|
|
|
14
23
|
logger.info "Launched Instance: **#{instance.id}**"
|
|
15
|
-
config.instances << instance
|
|
16
24
|
|
|
25
|
+
group = instance.autoscale_group
|
|
17
26
|
add_instance(instance, group.properties)
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
end
|
|
28
|
+
end
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
standard_groups = config.autoscale_groups.standard
|
|
31
|
+
standard_groups.each do |group|
|
|
32
|
+
logger.info "Auto Scaling Group: **#{group.name}**, standard deployment strategy."
|
|
33
|
+
|
|
34
|
+
group.instances.each_with_index do |instance, index|
|
|
35
|
+
server_properties =
|
|
22
36
|
if index.zero? && group.properties.key?(:primary_roles)
|
|
23
|
-
|
|
24
|
-
server_properties[:roles] = server_properties.delete(:primary_roles)
|
|
37
|
+
group.properties.merge(roles: group.properties[:primary_roles]).except(:primary_roles)
|
|
25
38
|
else
|
|
26
|
-
|
|
39
|
+
group.properties
|
|
27
40
|
end
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
end
|
|
42
|
+
add_instance(instance, server_properties)
|
|
31
43
|
end
|
|
32
44
|
end
|
|
45
|
+
end
|
|
33
46
|
|
|
47
|
+
desc 'Wait for SSH to become available on launched instances'
|
|
48
|
+
task :wait_for_ssh do
|
|
34
49
|
unless config.instances.empty?
|
|
35
50
|
logger.info 'Waiting for SSH to be available...'
|
|
36
51
|
|
|
@@ -54,31 +69,22 @@ namespace :rolling do
|
|
|
54
69
|
updated_templates = launch_templates.update(amis: amis, description: revision_log_message)
|
|
55
70
|
|
|
56
71
|
logger.info 'Triggering Instance Refresh on Auto Scaling Group(s)...'
|
|
57
|
-
updated_templates.
|
|
58
|
-
config.autoscale_groups.with_launch_template(launch_template).
|
|
59
|
-
|
|
72
|
+
groups_with_templates = updated_templates.flat_map do |launch_template|
|
|
73
|
+
config.autoscale_groups.with_launch_template(launch_template).map { |group| [group, launch_template] }
|
|
74
|
+
end
|
|
60
75
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
Capistrano::ASG::Rolling::Parallel.run(groups_with_templates) do |group, launch_template|
|
|
77
|
+
group.start_instance_refresh(launch_template)
|
|
78
|
+
|
|
79
|
+
logger.verbose "Successfully started Instance Refresh on Auto Scaling Group **#{group.name}**."
|
|
80
|
+
rescue Capistrano::ASG::Rolling::StartInstanceRefreshError => e
|
|
81
|
+
logger.warning "Failed to start Instance Refresh on Auto Scaling Group **#{group.name}**: #{e.message}"
|
|
65
82
|
end
|
|
66
83
|
|
|
67
84
|
config.launch_templates.merge(updated_templates)
|
|
68
85
|
end
|
|
69
86
|
end
|
|
70
87
|
|
|
71
|
-
desc 'Trigger instance refresh of deployed autoscaling groups'
|
|
72
|
-
task :trigger_instance_refresh do
|
|
73
|
-
logger.info 'Triggering Instance Refresh on Auto Scaling Group(s)...'
|
|
74
|
-
config.autoscale_groups.each do |group|
|
|
75
|
-
group.start_instance_refresh(group.launch_template)
|
|
76
|
-
logger.info "Successfully started Instance Refresh on Auto Scaling Group **#{group.name}**."
|
|
77
|
-
rescue Capistrano::ASG::Rolling::StartInstanceRefreshError => e
|
|
78
|
-
logger.info "Failed to start Instance Refresh on Auto Scaling Group **#{group.name}**: #{e.message}"
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
88
|
desc 'Clean up old Launch Template versions and AMIs and terminate instances'
|
|
83
89
|
task :cleanup do
|
|
84
90
|
unless config.launch_templates.empty?
|
|
@@ -114,7 +120,7 @@ namespace :rolling do
|
|
|
114
120
|
end
|
|
115
121
|
end
|
|
116
122
|
|
|
117
|
-
instances = config.instances.
|
|
123
|
+
instances = config.instances.auto_terminatable
|
|
118
124
|
if instances.any?
|
|
119
125
|
logger.info 'Terminating instance(s)...'
|
|
120
126
|
begin
|
|
@@ -176,39 +182,50 @@ namespace :rolling do
|
|
|
176
182
|
|
|
177
183
|
logger.info 'Updating Launch Template with the new AMI...'
|
|
178
184
|
launch_template = group.launch_template
|
|
179
|
-
launch_template.create_version(image_id: ami.id, description: revision_log_message)
|
|
185
|
+
new_template = launch_template.create_version(image_id: ami.id, description: revision_log_message)
|
|
180
186
|
|
|
181
|
-
config.launch_templates <<
|
|
187
|
+
config.launch_templates << new_template
|
|
182
188
|
else
|
|
183
189
|
logger.error 'Unable to create AMI. No instance with a valid state was found in the Auto Scaling Group.'
|
|
184
190
|
end
|
|
185
191
|
end
|
|
186
192
|
end
|
|
187
193
|
|
|
194
|
+
desc 'Trigger instance refresh on all Auto Scaling Groups'
|
|
195
|
+
task :trigger_instance_refresh do
|
|
196
|
+
logger.info 'Triggering Instance Refresh on Auto Scaling Group(s)...'
|
|
197
|
+
Capistrano::ASG::Rolling::Parallel.run(config.autoscale_groups) do |group|
|
|
198
|
+
group.start_instance_refresh(group.launch_template)
|
|
199
|
+
|
|
200
|
+
logger.info "Successfully started Instance Refresh on Auto Scaling Group **#{group.name}**."
|
|
201
|
+
rescue Capistrano::ASG::Rolling::StartInstanceRefreshError => e
|
|
202
|
+
logger.warning "Failed to start Instance Refresh on Auto Scaling Group **#{group.name}**: #{e.message}"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
188
206
|
desc 'Get status of instance refresh'
|
|
189
207
|
task :instance_refresh_status do
|
|
190
208
|
if config.wait_for_instance_refresh?
|
|
191
|
-
groups = config.autoscale_groups.to_h { |group| [group.name, group] }
|
|
192
|
-
|
|
209
|
+
groups = config.autoscale_groups.select(&:refresh_id).to_h { |group| [group.name, group] }
|
|
210
|
+
completed_refreshes = []
|
|
193
211
|
|
|
194
212
|
while groups.any?
|
|
195
213
|
groups.each do |name, group|
|
|
196
214
|
refresh = group.latest_instance_refresh
|
|
197
215
|
if refresh.nil? || refresh.completed?
|
|
198
|
-
logger.info "Auto Scaling Group: **#{name}**, completed with status '#{refresh.status}'." if refresh
|
|
199
|
-
|
|
200
|
-
|
|
216
|
+
logger.info "Auto Scaling Group: **#{name}**, completed with status '#{refresh.status}'." if refresh&.completed?
|
|
217
|
+
|
|
218
|
+
groups.delete(name)
|
|
219
|
+
completed_refreshes.push(refresh) if refresh
|
|
220
|
+
elsif refresh.percentage_complete
|
|
201
221
|
logger.info "Auto Scaling Group: **#{name}**, #{refresh.percentage_complete}% completed, status '#{refresh.status}'."
|
|
202
222
|
else
|
|
203
223
|
logger.info "Auto Scaling Group: **#{name}**, status '#{refresh.status}'."
|
|
204
224
|
end
|
|
205
|
-
rescue
|
|
206
|
-
|
|
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."
|
|
225
|
+
rescue Capistrano::ASG::Rolling::InstanceRefreshStatusError => e
|
|
226
|
+
logger.warning "Auto Scaling Group: **#{name}**, failed to fetch status: #{e.message} - retrying on next poll."
|
|
211
227
|
end
|
|
228
|
+
|
|
212
229
|
next if groups.empty?
|
|
213
230
|
|
|
214
231
|
wait_for = config.instance_refresh_polling_interval
|
|
@@ -216,7 +233,7 @@ namespace :rolling do
|
|
|
216
233
|
sleep wait_for
|
|
217
234
|
end
|
|
218
235
|
|
|
219
|
-
failed =
|
|
236
|
+
failed = completed_refreshes.any?(&:failed?)
|
|
220
237
|
raise Capistrano::ASG::Rolling::InstanceRefreshFailed if failed
|
|
221
238
|
end
|
|
222
239
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kentaa
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-08-28 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bundler
|
|
@@ -59,14 +59,14 @@ dependencies:
|
|
|
59
59
|
requirements:
|
|
60
60
|
- - "~>"
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version: '0
|
|
62
|
+
version: '1.0'
|
|
63
63
|
type: :development
|
|
64
64
|
prerelease: false
|
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
|
66
66
|
requirements:
|
|
67
67
|
- - "~>"
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version: '0
|
|
69
|
+
version: '1.0'
|
|
70
70
|
- !ruby/object:Gem::Dependency
|
|
71
71
|
name: webmock
|
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -196,14 +196,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
196
196
|
requirements:
|
|
197
197
|
- - ">="
|
|
198
198
|
- !ruby/object:Gem::Version
|
|
199
|
-
version: 3.
|
|
199
|
+
version: 3.2.0
|
|
200
200
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
201
|
requirements:
|
|
202
202
|
- - ">="
|
|
203
203
|
- !ruby/object:Gem::Version
|
|
204
204
|
version: '0'
|
|
205
205
|
requirements: []
|
|
206
|
-
rubygems_version: 3.
|
|
206
|
+
rubygems_version: 3.4.19
|
|
207
207
|
signing_key:
|
|
208
208
|
specification_version: 4
|
|
209
209
|
summary: Capistrano plugin for performing rolling updates to AWS Auto Scaling Groups
|