ecs_deploy 1.0.2 → 1.0.4
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 +23 -0
- data/CHANGELOG.md +37 -0
- data/README.md +11 -5
- data/ecs_deploy.gemspec +1 -0
- data/lib/ecs_deploy/auto_scaler/auto_scaling_group_config.rb +8 -18
- data/lib/ecs_deploy/auto_scaler/cluster_resource_manager.rb +22 -19
- data/lib/ecs_deploy/auto_scaler/instance_drainer.rb +5 -0
- data/lib/ecs_deploy/auto_scaler/service_config.rb +3 -0
- data/lib/ecs_deploy/auto_scaler/spot_fleet_request_config.rb +1 -1
- data/lib/ecs_deploy/capistrano.rb +2 -0
- data/lib/ecs_deploy/instance_fluctuation_manager.rb +3 -0
- data/lib/ecs_deploy/service.rb +59 -20
- data/lib/ecs_deploy/version.rb +1 -1
- metadata +18 -4
- data/.travis.yml +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a731ec39a6928bb4365fbb4784b91dd92737eb5b5796da19e3be8185d6bc10b8
|
|
4
|
+
data.tar.gz: '04758ae740d9ab1bb9108d5dcae1df72102fd161e0f96a38f1e30b461415bc7f'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 429dc1f441f7a67a973293adf8c48a62671ab3f5710cee596186a7fe5773b772b643b9ef879fb0b2143d7f7100754b6521ec6823094122fec85bfd189ca3c306
|
|
7
|
+
data.tar.gz: 03f568218e5e77e2c9b86560ac5dd4f41e19440f98da1ee102ee09057bc7ec8fdf7caa5366b56d06946d5887a0a60d8bae0c1ce04e80e0a50df7a51e40642da0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
ruby-version: ['2.5', '2.6', '2.7', '3.0', '3.1', '3.2']
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v3
|
|
17
|
+
- name: Set up Ruby
|
|
18
|
+
uses: ruby/setup-ruby@v1
|
|
19
|
+
with:
|
|
20
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
21
|
+
bundler-cache: true
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: bundle exec rake
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# v1.0
|
|
2
2
|
|
|
3
|
+
## Release v1.0.4 - 2023/02/10
|
|
4
|
+
|
|
5
|
+
### Bug fixes
|
|
6
|
+
|
|
7
|
+
- Fix Aws::AutoScaling::Errors::ValidationError https://github.com/reproio/ecs_deploy/pull/85
|
|
8
|
+
|
|
9
|
+
- Fix Timeout::Error that occurs in trigger_capacity_update https://github.com/reproio/ecs_deploy/pull/80
|
|
10
|
+
|
|
11
|
+
- use force a new deployment, when switching from launch type to capacity provider strategy on an existing service https://github.com/reproio/ecs_deploy/pull/75
|
|
12
|
+
|
|
13
|
+
### Enhancement
|
|
14
|
+
|
|
15
|
+
- Run test with Ruby 3.2 https://github.com/reproio/ecs_deploy/pull/83
|
|
16
|
+
|
|
17
|
+
- Merge `propagate_tags` to service_options when updating service https://github.com/reproio/ecs_deploy/pull/82
|
|
18
|
+
|
|
19
|
+
- Show service event logs while waiting for services https://github.com/reproio/ecs_deploy/pull/81
|
|
20
|
+
|
|
21
|
+
- Stop supporting ruby 2.4 https://github.com/reproio/ecs_deploy/pull/79
|
|
22
|
+
|
|
23
|
+
- Display warning that desired count has reached max value https://github.com/reproio/ecs_deploy/pull/78
|
|
24
|
+
|
|
25
|
+
- Make draining feature opt-outable https://github.com/reproio/ecs_deploy/pull/77
|
|
26
|
+
|
|
27
|
+
- Add capacity_provider_strategy options to Service https://github.com/reproio/ecs_deploy/pull/74
|
|
28
|
+
|
|
29
|
+
## Release v1.0.3 - 2021/11/17
|
|
30
|
+
|
|
31
|
+
### Bug fixes
|
|
32
|
+
* Fix bug that `InstanceFluctuationManager#decrement` tries to detach instances whose status is 'DEREGISTERING'
|
|
33
|
+
https://github.com/reproio/ecs_deploy/pull/72
|
|
34
|
+
|
|
35
|
+
### Enhancement
|
|
36
|
+
* Add a cluster name to deployment logs
|
|
37
|
+
https://github.com/reproio/ecs_deploy/pull/71
|
|
38
|
+
|
|
39
|
+
|
|
3
40
|
## Release v1.0.2 - 2021/05/26
|
|
4
41
|
|
|
5
42
|
### Enhancement
|
data/README.md
CHANGED
|
@@ -215,6 +215,7 @@ auto_scaling_groups:
|
|
|
215
215
|
# autoscaler will set the capacity to (buffer + desired_tasks * required_capacity).
|
|
216
216
|
# Adjust this value if it takes much time to prepare ECS instances and launch new tasks.
|
|
217
217
|
buffer: 1
|
|
218
|
+
disable_draining: false # cf. spot_instance_intrp_warns_queue_urls
|
|
218
219
|
services:
|
|
219
220
|
- name: repro-api-production
|
|
220
221
|
step: 1
|
|
@@ -242,6 +243,7 @@ spot_fleet_requests:
|
|
|
242
243
|
region: ap-northeast-1
|
|
243
244
|
cluster: ecs-cluster-for-worker
|
|
244
245
|
buffer: 1
|
|
246
|
+
disable_draining: false # cf. spot_instance_intrp_warns_queue_urls
|
|
245
247
|
services:
|
|
246
248
|
- name: repro-worker-production
|
|
247
249
|
step: 1
|
|
@@ -261,11 +263,15 @@ spot_fleet_requests:
|
|
|
261
263
|
state: ALARM
|
|
262
264
|
prioritized_over_upscale_triggers: true
|
|
263
265
|
|
|
264
|
-
#
|
|
265
|
-
#
|
|
266
|
-
# autoscaler
|
|
267
|
-
#
|
|
268
|
-
#
|
|
266
|
+
# When you use spot instances, instances that receive interruption warnings should be drained.
|
|
267
|
+
# If you set URLs of SQS queues for spot instance interruption warnings to `spot_instance_intrp_warns_queue_urls`,
|
|
268
|
+
# autoscaler drains instances to interrupt and detaches the instances from the auto scaling groups with
|
|
269
|
+
# should_decrement_desired_capacity false.
|
|
270
|
+
# If you set ECS_ENABLE_SPOT_INSTANCE_DRAINING to true, we recommend that you opt out of the draining feature
|
|
271
|
+
# by setting disable_draining to true in the configurations of auto scaling groups and spot fleet requests.
|
|
272
|
+
# Otherwise, instances don't seem to be drained on rare occasions.
|
|
273
|
+
# Even if you opt out of the feature, you still have the advantage of setting `spot_instance_intrp_warns_queue_urls`
|
|
274
|
+
# because instances to interrupt are replaced with new instances as soon as possible.
|
|
269
275
|
spot_instance_intrp_warns_queue_urls:
|
|
270
276
|
- https://sqs.ap-northeast-1.amazonaws.com/<account-id>/spot-instance-intrp-warns
|
|
271
277
|
```
|
data/ecs_deploy.gemspec
CHANGED
|
@@ -7,7 +7,7 @@ require "ecs_deploy/auto_scaler/cluster_resource_manager"
|
|
|
7
7
|
|
|
8
8
|
module EcsDeploy
|
|
9
9
|
module AutoScaler
|
|
10
|
-
AutoScalingGroupConfig = Struct.new(:name, :region, :cluster, :buffer, :service_configs) do
|
|
10
|
+
AutoScalingGroupConfig = Struct.new(:name, :region, :cluster, :buffer, :service_configs, :disable_draining) do
|
|
11
11
|
include ConfigBase
|
|
12
12
|
|
|
13
13
|
MAX_DETACHABLE_INSTANCE_COUNT = 20
|
|
@@ -82,7 +82,7 @@ module EcsDeploy
|
|
|
82
82
|
def decrease_desired_capacity(count)
|
|
83
83
|
container_instance_arns_in_service = cluster_resource_manager.fetch_container_instance_arns_in_service
|
|
84
84
|
container_instances_in_cluster = cluster_resource_manager.fetch_container_instances_in_cluster
|
|
85
|
-
auto_scaling_group_instances =
|
|
85
|
+
auto_scaling_group_instances = describe_detachable_instances
|
|
86
86
|
deregisterable_instances = container_instances_in_cluster.select do |i|
|
|
87
87
|
i.pending_tasks_count == 0 &&
|
|
88
88
|
!running_essential_task?(i, container_instance_arns_in_service) &&
|
|
@@ -144,15 +144,8 @@ module EcsDeploy
|
|
|
144
144
|
|
|
145
145
|
def detach_and_terminate_orphan_instances
|
|
146
146
|
container_instance_ids = cluster_resource_manager.fetch_container_instances_in_cluster.map(&:ec2_instance_id)
|
|
147
|
-
orphans =
|
|
147
|
+
orphans = describe_detachable_instances.reject do |i|
|
|
148
148
|
next true if container_instance_ids.include?(i.instance_id)
|
|
149
|
-
|
|
150
|
-
# The lifecycle state of terminated instances becomes "Terminating", "Terminating:Wait", or "Terminating:Proceed",
|
|
151
|
-
# and we can't detach instances in such a state.
|
|
152
|
-
if i.lifecycle_state.start_with?("Terminating")
|
|
153
|
-
AutoScaler.error_logger.warn("#{log_prefix} The lifesycle state of #{i.instance_id} is \"#{i.lifecycle_state}\", so ignore it")
|
|
154
|
-
next true
|
|
155
|
-
end
|
|
156
149
|
end.map(&:instance_id)
|
|
157
150
|
|
|
158
151
|
return if orphans.empty?
|
|
@@ -184,14 +177,11 @@ module EcsDeploy
|
|
|
184
177
|
)
|
|
185
178
|
end
|
|
186
179
|
|
|
187
|
-
def
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
@instances = resp.auto_scaling_groups[0].instances
|
|
193
|
-
else
|
|
194
|
-
@instances
|
|
180
|
+
def describe_detachable_instances
|
|
181
|
+
client.describe_auto_scaling_groups({ auto_scaling_group_names: [name] }).auto_scaling_groups[0].instances.reject do |i|
|
|
182
|
+
# The lifecycle state of terminated instances becomes "Detaching", "Terminating", "Terminating:Wait", or "Terminating:Proceed",
|
|
183
|
+
# and we can't detach instances in such a state.
|
|
184
|
+
i.lifecycle_state.start_with?("Terminating") || i.lifecycle_state == "Detaching"
|
|
195
185
|
end
|
|
196
186
|
end
|
|
197
187
|
|
|
@@ -74,39 +74,42 @@ module EcsDeploy
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def trigger_capacity_update(old_desired_capacity, new_desired_capacity, interval: 5, wait_until_capacity_updated: false)
|
|
77
|
+
return if new_desired_capacity == old_desired_capacity
|
|
78
|
+
|
|
77
79
|
th = Thread.new do
|
|
78
80
|
@logger&.info "#{log_prefix} Start updating capacity: #{old_desired_capacity} -> #{new_desired_capacity}"
|
|
79
81
|
Timeout.timeout(180) do
|
|
80
|
-
until @capacity == new_desired_capacity ||
|
|
82
|
+
until @capacity == new_desired_capacity ||
|
|
83
|
+
(new_desired_capacity > old_desired_capacity && @capacity > new_desired_capacity) ||
|
|
84
|
+
(new_desired_capacity < old_desired_capacity && @capacity < new_desired_capacity)
|
|
81
85
|
@mutex.synchronize do
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
AutoScaler.error_logger.warn("#{log_prefix} `#{__method__}': #{e} (#{e.class})")
|
|
87
|
-
end
|
|
86
|
+
@capacity = calculate_active_instance_capacity
|
|
87
|
+
@resource.broadcast
|
|
88
|
+
rescue => e
|
|
89
|
+
AutoScaler.error_logger.warn("#{log_prefix} `#{__method__}': #{e} (#{e.class})")
|
|
88
90
|
end
|
|
89
91
|
|
|
90
92
|
sleep interval
|
|
91
93
|
end
|
|
92
94
|
@logger&.info "#{log_prefix} capacity is updated to #{@capacity}"
|
|
93
95
|
end
|
|
96
|
+
rescue Timeout::Error => e
|
|
97
|
+
msg = "#{log_prefix} `#{__method__}': #{e} (#{e.class})"
|
|
98
|
+
if @capacity_based_on == "vCPUs"
|
|
99
|
+
# Timeout::Error sometimes occur.
|
|
100
|
+
# For example, the following case never meats the condition of until
|
|
101
|
+
# * old_desired_capaacity is 102
|
|
102
|
+
# * new_desired_capaacity is 101
|
|
103
|
+
# * all instances have 2 vCPUs
|
|
104
|
+
AutoScaler.error_logger.warn(msg)
|
|
105
|
+
else
|
|
106
|
+
AutoScaler.error_logger.error(msg)
|
|
107
|
+
end
|
|
94
108
|
end
|
|
95
109
|
|
|
96
110
|
if wait_until_capacity_updated
|
|
97
111
|
@logger&.info "#{log_prefix} Wait for the capacity of active instances to become #{new_desired_capacity} from #{old_desired_capacity}"
|
|
98
|
-
|
|
99
|
-
th.join
|
|
100
|
-
rescue Timeout::Error => e
|
|
101
|
-
msg = "#{log_prefix} `#{__method__}': #{e} (#{e.class})"
|
|
102
|
-
if @capacity_based_on == "vCPUs"
|
|
103
|
-
# Timeout::Error sometimes occur.
|
|
104
|
-
# For example, @capacity won't be new_desired_capacity if new_desired_capacity is odd and all instances have 2 vCPUs
|
|
105
|
-
AutoScaler.error_logger.warn(msg)
|
|
106
|
-
else
|
|
107
|
-
AutoScaler.error_logger.error(msg)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
112
|
+
th.join
|
|
110
113
|
end
|
|
111
114
|
end
|
|
112
115
|
|
|
@@ -78,6 +78,11 @@ module EcsDeploy
|
|
|
78
78
|
def set_instance_state_to_draining(config_to_instance_ids, region)
|
|
79
79
|
cl = ecs_client(region)
|
|
80
80
|
config_to_instance_ids.each do |config, instance_ids|
|
|
81
|
+
if config.disable_draining == true || config.disable_draining == "true"
|
|
82
|
+
@logger.info "Skip draining instances: region: #{region}, cluster: #{config.cluster}, instance_ids: #{instance_ids.inspect}"
|
|
83
|
+
next
|
|
84
|
+
end
|
|
85
|
+
|
|
81
86
|
arns = cl.list_container_instances(
|
|
82
87
|
cluster: config.cluster,
|
|
83
88
|
filter: "ec2InstanceId in [#{instance_ids.join(",")}]",
|
|
@@ -135,6 +135,9 @@ module EcsDeploy
|
|
|
135
135
|
now = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
|
|
136
136
|
@reach_max_at ||= now
|
|
137
137
|
@logger.info "#{log_prefix} Service waits cooldown elapsed #{(now - @reach_max_at).to_i}sec"
|
|
138
|
+
if next_desired_count > max_task_count[current_level] && current_level == max_task_count.size - 1
|
|
139
|
+
@logger.warn "#{log_prefix} Desired count has reached the maximum value and couldn't be increased"
|
|
140
|
+
end
|
|
138
141
|
elsif current_level == next_level && next_desired_count < max_task_count[current_level]
|
|
139
142
|
level = current_level
|
|
140
143
|
@reach_max_at = nil
|
|
@@ -8,7 +8,7 @@ require "ecs_deploy/auto_scaler/cluster_resource_manager"
|
|
|
8
8
|
|
|
9
9
|
module EcsDeploy
|
|
10
10
|
module AutoScaler
|
|
11
|
-
SpotFleetRequestConfig = Struct.new(:id, :region, :cluster, :buffer, :service_configs) do
|
|
11
|
+
SpotFleetRequestConfig = Struct.new(:id, :region, :cluster, :buffer, :service_configs, :disable_draining) do
|
|
12
12
|
include ConfigBase
|
|
13
13
|
|
|
14
14
|
def initialize(attributes = {}, logger)
|
|
@@ -113,6 +113,7 @@ namespace :ecs do
|
|
|
113
113
|
service_options[:deployment_configuration] = service[:deployment_configuration] if service[:deployment_configuration]
|
|
114
114
|
service_options[:placement_constraints] = service[:placement_constraints] if service[:placement_constraints]
|
|
115
115
|
service_options[:placement_strategy] = service[:placement_strategy] if service[:placement_strategy]
|
|
116
|
+
service_options[:capacity_provider_strategy] = service[:capacity_provider_strategy] if service[:capacity_provider_strategy]
|
|
116
117
|
service_options[:scheduling_strategy] = service[:scheduling_strategy] if service[:scheduling_strategy]
|
|
117
118
|
s = EcsDeploy::Service.new(**service_options)
|
|
118
119
|
s.deploy
|
|
@@ -179,6 +180,7 @@ namespace :ecs do
|
|
|
179
180
|
service_options[:deployment_configuration] = service[:deployment_configuration] if service[:deployment_configuration]
|
|
180
181
|
service_options[:placement_constraints] = service[:placement_constraints] if service[:placement_constraints]
|
|
181
182
|
service_options[:placement_strategy] = service[:placement_strategy] if service[:placement_strategy]
|
|
183
|
+
service_options[:capacity_provider_strategy] = service[:capacity_provider_strategy] if service[:capacity_provider_strategy]
|
|
182
184
|
s = EcsDeploy::Service.new(**service_options)
|
|
183
185
|
s.deploy
|
|
184
186
|
EcsDeploy::TaskDefinition.deregister(current_task_definition_arn, region: r)
|
|
@@ -56,6 +56,9 @@ module EcsDeploy
|
|
|
56
56
|
).container_instances
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# The status of ECS instances sometimes seems to remain 'DEREGISTERING' for a few minutes after they are terminated.
|
|
60
|
+
container_instances.reject! { |ci| ci.status == 'DEREGISTERING' }
|
|
61
|
+
|
|
59
62
|
az_to_container_instances = container_instances.sort_by {|ci| - ci.running_tasks_count }.group_by do |ci|
|
|
60
63
|
ci.attributes.find {|attribute| attribute.name == "ecs.availability-zone" }.value
|
|
61
64
|
end
|
data/lib/ecs_deploy/service.rb
CHANGED
|
@@ -7,7 +7,7 @@ module EcsDeploy
|
|
|
7
7
|
|
|
8
8
|
class TooManyAttemptsError < StandardError; end
|
|
9
9
|
|
|
10
|
-
attr_reader :cluster, :region, :service_name, :delete
|
|
10
|
+
attr_reader :cluster, :region, :service_name, :delete, :deploy_started_at
|
|
11
11
|
|
|
12
12
|
def initialize(
|
|
13
13
|
cluster:, service_name:, task_definition_name: nil, revision: nil,
|
|
@@ -16,6 +16,7 @@ module EcsDeploy
|
|
|
16
16
|
launch_type: nil,
|
|
17
17
|
placement_constraints: [],
|
|
18
18
|
placement_strategy: [],
|
|
19
|
+
capacity_provider_strategy: nil,
|
|
19
20
|
network_configuration: nil,
|
|
20
21
|
health_check_grace_period_seconds: nil,
|
|
21
22
|
scheduling_strategy: 'REPLICA',
|
|
@@ -35,6 +36,7 @@ module EcsDeploy
|
|
|
35
36
|
@launch_type = launch_type
|
|
36
37
|
@placement_constraints = placement_constraints
|
|
37
38
|
@placement_strategy = placement_strategy
|
|
39
|
+
@capacity_provider_strategy = capacity_provider_strategy
|
|
38
40
|
@network_configuration = network_configuration
|
|
39
41
|
@health_check_grace_period_seconds = health_check_grace_period_seconds
|
|
40
42
|
@scheduling_strategy = scheduling_strategy
|
|
@@ -59,6 +61,7 @@ module EcsDeploy
|
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
def deploy
|
|
64
|
+
@deploy_started_at = Time.now
|
|
62
65
|
res = @client.describe_services(cluster: @cluster, services: [@service_name])
|
|
63
66
|
service_options = {
|
|
64
67
|
cluster: @cluster,
|
|
@@ -66,8 +69,25 @@ module EcsDeploy
|
|
|
66
69
|
deployment_configuration: @deployment_configuration,
|
|
67
70
|
network_configuration: @network_configuration,
|
|
68
71
|
health_check_grace_period_seconds: @health_check_grace_period_seconds,
|
|
72
|
+
capacity_provider_strategy: @capacity_provider_strategy,
|
|
69
73
|
enable_execute_command: @enable_execute_command,
|
|
74
|
+
enable_ecs_managed_tags: @enable_ecs_managed_tags,
|
|
75
|
+
placement_constraints: @placement_constraints,
|
|
76
|
+
placement_strategy: @placement_strategy,
|
|
70
77
|
}
|
|
78
|
+
|
|
79
|
+
if @load_balancers && EcsDeploy.config.ecs_service_role
|
|
80
|
+
service_options.merge!({
|
|
81
|
+
role: EcsDeploy.config.ecs_service_role,
|
|
82
|
+
})
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
if @load_balancers
|
|
86
|
+
service_options.merge!({
|
|
87
|
+
load_balancers: @load_balancers,
|
|
88
|
+
})
|
|
89
|
+
end
|
|
90
|
+
|
|
71
91
|
if res.services.select{ |s| s.status == 'ACTIVE' }.empty?
|
|
72
92
|
return if @delete
|
|
73
93
|
|
|
@@ -75,49 +95,56 @@ module EcsDeploy
|
|
|
75
95
|
service_name: @service_name,
|
|
76
96
|
desired_count: @desired_count.to_i,
|
|
77
97
|
launch_type: @launch_type,
|
|
78
|
-
placement_constraints: @placement_constraints,
|
|
79
|
-
placement_strategy: @placement_strategy,
|
|
80
|
-
enable_ecs_managed_tags: @enable_ecs_managed_tags,
|
|
81
98
|
tags: @tags,
|
|
82
99
|
propagate_tags: @propagate_tags,
|
|
83
100
|
})
|
|
84
101
|
|
|
85
|
-
if @load_balancers && EcsDeploy.config.ecs_service_role
|
|
86
|
-
service_options.merge!({
|
|
87
|
-
role: EcsDeploy.config.ecs_service_role,
|
|
88
|
-
})
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
if @load_balancers
|
|
92
|
-
service_options.merge!({
|
|
93
|
-
load_balancers: @load_balancers,
|
|
94
|
-
})
|
|
95
|
-
end
|
|
96
|
-
|
|
97
102
|
if @scheduling_strategy == 'DAEMON'
|
|
98
103
|
service_options[:scheduling_strategy] = @scheduling_strategy
|
|
99
104
|
service_options.delete(:desired_count)
|
|
100
105
|
end
|
|
101
106
|
@response = @client.create_service(service_options)
|
|
102
|
-
EcsDeploy.logger.info "create service [#{@service_name}] [#{@region}] [#{Paint['OK', :green]}]"
|
|
107
|
+
EcsDeploy.logger.info "create service [#{@service_name}] [#{@cluster}] [#{@region}] [#{Paint['OK', :green]}]"
|
|
103
108
|
else
|
|
104
109
|
return delete_service if @delete
|
|
105
110
|
|
|
106
111
|
service_options.merge!({service: @service_name})
|
|
107
112
|
service_options.merge!({desired_count: @desired_count}) if @desired_count
|
|
113
|
+
service_options.merge!({propagate_tags: @propagate_tags}) if @propagate_tags
|
|
114
|
+
|
|
115
|
+
current_service = res.services[0]
|
|
116
|
+
service_options.merge!({force_new_deployment: true}) if need_force_new_deployment?(current_service)
|
|
117
|
+
|
|
108
118
|
update_tags(@service_name, @tags)
|
|
109
119
|
@response = @client.update_service(service_options)
|
|
110
|
-
EcsDeploy.logger.info "update service [#{@service_name}] [#{@region}] [#{Paint['OK', :green]}]"
|
|
120
|
+
EcsDeploy.logger.info "update service [#{@service_name}] [#{@cluster}] [#{@region}] [#{Paint['OK', :green]}]"
|
|
111
121
|
end
|
|
112
122
|
end
|
|
113
123
|
|
|
124
|
+
private def need_force_new_deployment?(service)
|
|
125
|
+
return false unless @capacity_provider_strategy
|
|
126
|
+
return true unless service.capacity_provider_strategy
|
|
127
|
+
|
|
128
|
+
return true if @capacity_provider_strategy.size != service.capacity_provider_strategy.size
|
|
129
|
+
|
|
130
|
+
match_array = @capacity_provider_strategy.all? do |strategy|
|
|
131
|
+
service.capacity_provider_strategy.find do |current_strategy|
|
|
132
|
+
strategy[:capacity_provider] == current_strategy.capacity_provider &&
|
|
133
|
+
strategy[:weight] == current_strategy.weight &&
|
|
134
|
+
strategy[:base] == current_strategy.base
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
!match_array
|
|
139
|
+
end
|
|
140
|
+
|
|
114
141
|
def delete_service
|
|
115
142
|
if @scheduling_strategy != 'DAEMON'
|
|
116
143
|
@client.update_service(cluster: @cluster, service: @service_name, desired_count: 0)
|
|
117
144
|
sleep 1
|
|
118
145
|
end
|
|
119
146
|
@client.delete_service(cluster: @cluster, service: @service_name)
|
|
120
|
-
EcsDeploy.logger.info "delete service [#{@service_name}] [#{@region}] [#{Paint['OK', :green]}]"
|
|
147
|
+
EcsDeploy.logger.info "delete service [#{@service_name}] [#{@cluster}] [#{@region}] [#{Paint['OK', :green]}]"
|
|
121
148
|
end
|
|
122
149
|
|
|
123
150
|
def update_tags(service_name, tags)
|
|
@@ -142,19 +169,31 @@ module EcsDeploy
|
|
|
142
169
|
end
|
|
143
170
|
end
|
|
144
171
|
|
|
172
|
+
def log_events(ecs_service)
|
|
173
|
+
ecs_service.events.sort_by(&:created_at).each do |e|
|
|
174
|
+
next if e.created_at <= deploy_started_at
|
|
175
|
+
next if @last_event && e.created_at <= @last_event.created_at
|
|
176
|
+
|
|
177
|
+
EcsDeploy.logger.info e.message
|
|
178
|
+
@last_event = e
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
145
182
|
def self.wait_all_running(services)
|
|
146
183
|
services.group_by { |s| [s.cluster, s.region] }.flat_map do |(cl, region), ss|
|
|
147
184
|
client = Aws::ECS::Client.new(region: region)
|
|
148
185
|
ss.reject(&:delete).map(&:service_name).each_slice(MAX_DESCRIBE_SERVICES).map do |chunked_service_names|
|
|
149
186
|
Thread.new do
|
|
150
187
|
EcsDeploy.config.ecs_wait_until_services_stable_max_attempts.times do
|
|
151
|
-
EcsDeploy.logger.info "wait service stable [#{chunked_service_names.join(", ")}]"
|
|
188
|
+
EcsDeploy.logger.info "wait service stable [#{chunked_service_names.join(", ")}] [#{cl}]"
|
|
152
189
|
resp = client.describe_services(cluster: cl, services: chunked_service_names)
|
|
153
190
|
resp.services.each do |s|
|
|
154
191
|
# cf. https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-ecs/lib/aws-sdk-ecs/waiters.rb#L91-L96
|
|
155
192
|
if s.deployments.size == 1 && s.running_count == s.desired_count
|
|
156
193
|
chunked_service_names.delete(s.service_name)
|
|
157
194
|
end
|
|
195
|
+
service = ss.detect {|sc| sc.service_name == s.service_name }
|
|
196
|
+
service.log_events(s)
|
|
158
197
|
end
|
|
159
198
|
break if chunked_service_names.empty?
|
|
160
199
|
sleep EcsDeploy.config.ecs_wait_until_services_stable_delay
|
data/lib/ecs_deploy/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ecs_deploy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- joker1007
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-02-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: aws-sdk-autoscaling
|
|
@@ -170,6 +170,20 @@ dependencies:
|
|
|
170
170
|
- - "~>"
|
|
171
171
|
- !ruby/object:Gem::Version
|
|
172
172
|
version: '3.0'
|
|
173
|
+
- !ruby/object:Gem::Dependency
|
|
174
|
+
name: rexml
|
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - ">="
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: '0'
|
|
180
|
+
type: :development
|
|
181
|
+
prerelease: false
|
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
183
|
+
requirements:
|
|
184
|
+
- - ">="
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '0'
|
|
173
187
|
description: AWS ECS deploy helper
|
|
174
188
|
email:
|
|
175
189
|
- kakyoin.hierophant@gmail.com
|
|
@@ -178,8 +192,8 @@ executables:
|
|
|
178
192
|
extensions: []
|
|
179
193
|
extra_rdoc_files: []
|
|
180
194
|
files:
|
|
195
|
+
- ".github/workflows/test.yml"
|
|
181
196
|
- ".gitignore"
|
|
182
|
-
- ".travis.yml"
|
|
183
197
|
- CHANGELOG.md
|
|
184
198
|
- Gemfile
|
|
185
199
|
- README.md
|
|
@@ -222,7 +236,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
222
236
|
- !ruby/object:Gem::Version
|
|
223
237
|
version: '0'
|
|
224
238
|
requirements: []
|
|
225
|
-
rubygems_version: 3.2
|
|
239
|
+
rubygems_version: 3.4.2
|
|
226
240
|
signing_key:
|
|
227
241
|
specification_version: 4
|
|
228
242
|
summary: AWS ECS deploy helper
|