ecs_deploy 0.3.0 → 1.0.2
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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +139 -0
- data/README.md +290 -27
- data/Rakefile +4 -0
- data/ecs_deploy.gemspec +9 -3
- data/lib/ecs_deploy.rb +1 -1
- data/lib/ecs_deploy/auto_scaler.rb +105 -340
- data/lib/ecs_deploy/auto_scaler/auto_scaling_group_config.rb +209 -0
- data/lib/ecs_deploy/auto_scaler/cluster_resource_manager.rb +149 -0
- data/lib/ecs_deploy/auto_scaler/config_base.rb +16 -0
- data/lib/ecs_deploy/auto_scaler/instance_drainer.rb +134 -0
- data/lib/ecs_deploy/auto_scaler/service_config.rb +223 -0
- data/lib/ecs_deploy/auto_scaler/spot_fleet_request_config.rb +102 -0
- data/lib/ecs_deploy/auto_scaler/trigger_config.rb +42 -0
- data/lib/ecs_deploy/capistrano.rb +77 -15
- data/lib/ecs_deploy/configuration.rb +6 -2
- data/lib/ecs_deploy/instance_fluctuation_manager.rb +195 -0
- data/lib/ecs_deploy/scheduled_task.rb +31 -15
- data/lib/ecs_deploy/service.rb +97 -18
- data/lib/ecs_deploy/task_definition.rb +30 -45
- data/lib/ecs_deploy/version.rb +1 -1
- metadata +113 -14
@@ -6,12 +6,16 @@ module EcsDeploy
|
|
6
6
|
:secret_access_key,
|
7
7
|
:default_region,
|
8
8
|
:deploy_wait_timeout,
|
9
|
-
:ecs_service_role
|
9
|
+
:ecs_service_role,
|
10
|
+
:ecs_wait_until_services_stable_max_attempts,
|
11
|
+
:ecs_wait_until_services_stable_delay
|
10
12
|
|
11
13
|
def initialize
|
12
14
|
@log_level = :info
|
13
15
|
@deploy_wait_timeout = 300
|
14
|
-
|
16
|
+
# The following values are the default values of Aws::ECS::Waiters::ServicesStable
|
17
|
+
@ecs_wait_until_services_stable_max_attempts = 40
|
18
|
+
@ecs_wait_until_services_stable_delay = 15
|
15
19
|
end
|
16
20
|
end
|
17
21
|
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require "aws-sdk-autoscaling"
|
2
|
+
require "aws-sdk-ec2"
|
3
|
+
require "aws-sdk-ecs"
|
4
|
+
|
5
|
+
module EcsDeploy
|
6
|
+
class InstanceFluctuationManager
|
7
|
+
attr_reader :logger
|
8
|
+
|
9
|
+
MAX_UPDATABLE_ECS_CONTAINER_COUNT = 10
|
10
|
+
MAX_DETACHEABLE_EC2_INSTACE_COUNT = 20
|
11
|
+
MAX_DESCRIBABLE_ECS_TASK_COUNT = 100
|
12
|
+
|
13
|
+
def initialize(region:, cluster:, auto_scaling_group_name:, desired_capacity:, logger:)
|
14
|
+
@region = region
|
15
|
+
@cluster = cluster
|
16
|
+
@auto_scaling_group_name = auto_scaling_group_name
|
17
|
+
@desired_capacity = desired_capacity
|
18
|
+
@logger = logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def increase
|
22
|
+
asg = fetch_auto_scaling_group
|
23
|
+
|
24
|
+
@logger.info("Increase desired capacity of #{@auto_scaling_group_name}: #{asg.desired_capacity} => #{asg.max_size}")
|
25
|
+
as_client.update_auto_scaling_group(auto_scaling_group_name: @auto_scaling_group_name, desired_capacity: asg.max_size)
|
26
|
+
|
27
|
+
# Run in background because increasing instances may take time
|
28
|
+
Thread.new do
|
29
|
+
loop do
|
30
|
+
cluster = ecs_client.describe_clusters(clusters: [@cluster]).clusters.first
|
31
|
+
instance_count = cluster.registered_container_instances_count
|
32
|
+
if instance_count == asg.max_size
|
33
|
+
@logger.info("Succeeded in increasing instances!")
|
34
|
+
break
|
35
|
+
end
|
36
|
+
@logger.info("Current registered instance count: #{instance_count}")
|
37
|
+
sleep 5
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def decrease
|
43
|
+
asg = fetch_auto_scaling_group
|
44
|
+
|
45
|
+
decrease_count = asg.desired_capacity - @desired_capacity
|
46
|
+
if decrease_count <= 0
|
47
|
+
@logger.info("The capacity is already #{asg.desired_capacity}")
|
48
|
+
return
|
49
|
+
end
|
50
|
+
@logger.info("Decrease desired capacity of #{@auto_scaling_group_name}: #{asg.desired_capacity} => #{@desired_capacity}")
|
51
|
+
|
52
|
+
container_instances = ecs_client.list_container_instances(cluster: @cluster).flat_map do |resp|
|
53
|
+
ecs_client.describe_container_instances(
|
54
|
+
cluster: @cluster,
|
55
|
+
container_instances: resp.container_instance_arns
|
56
|
+
).container_instances
|
57
|
+
end
|
58
|
+
|
59
|
+
az_to_container_instances = container_instances.sort_by {|ci| - ci.running_tasks_count }.group_by do |ci|
|
60
|
+
ci.attributes.find {|attribute| attribute.name == "ecs.availability-zone" }.value
|
61
|
+
end
|
62
|
+
if az_to_container_instances.empty?
|
63
|
+
@logger.info("There are no instances to terminate.")
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
target_container_instances = extract_target_container_instances(decrease_count, az_to_container_instances)
|
68
|
+
|
69
|
+
@logger.info("running tasks: #{ecs_client.list_tasks(cluster: @cluster).task_arns.size}")
|
70
|
+
all_running_task_arns = []
|
71
|
+
target_container_instances.map(&:container_instance_arn).each_slice(MAX_UPDATABLE_ECS_CONTAINER_COUNT) do |arns|
|
72
|
+
@logger.info(arns)
|
73
|
+
ecs_client.update_container_instances_state(
|
74
|
+
cluster: @cluster,
|
75
|
+
container_instances: arns,
|
76
|
+
status: "DRAINING"
|
77
|
+
)
|
78
|
+
arns.each do |arn|
|
79
|
+
all_running_task_arns.concat(list_running_task_arns(arn))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
stop_tasks_not_belonging_service(all_running_task_arns)
|
84
|
+
wait_until_tasks_stopped(all_running_task_arns)
|
85
|
+
|
86
|
+
instance_ids = target_container_instances.map(&:ec2_instance_id)
|
87
|
+
terminate_instances(instance_ids)
|
88
|
+
@logger.info("Succeeded in decreasing instances!")
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def aws_params
|
94
|
+
{
|
95
|
+
access_key_id: EcsDeploy.config.access_key_id,
|
96
|
+
secret_access_key: EcsDeploy.config.secret_access_key,
|
97
|
+
region: @region,
|
98
|
+
logger: @logger
|
99
|
+
}.reject do |_key, value|
|
100
|
+
value.nil?
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def as_client
|
105
|
+
@as_client ||= Aws::AutoScaling::Client.new(aws_params)
|
106
|
+
end
|
107
|
+
|
108
|
+
def ec2_client
|
109
|
+
@ec2_client ||= Aws::EC2::Client.new(aws_params)
|
110
|
+
end
|
111
|
+
|
112
|
+
def ecs_client
|
113
|
+
@ecs_client ||= Aws::ECS::Client.new(aws_params)
|
114
|
+
end
|
115
|
+
|
116
|
+
def fetch_auto_scaling_group
|
117
|
+
as_client.describe_auto_scaling_groups(auto_scaling_group_names: [@auto_scaling_group_name]).auto_scaling_groups.first
|
118
|
+
end
|
119
|
+
|
120
|
+
# Extract container instances to terminate considering AZ balance
|
121
|
+
def extract_target_container_instances(decrease_count, az_to_container_instances)
|
122
|
+
target_container_instances = []
|
123
|
+
decrease_count.times do
|
124
|
+
@logger.debug do
|
125
|
+
"AZ balance: #{az_to_container_instances.sort_by {|az, _| az }.map {|az, instances| [az, instances.size] }.to_h}"
|
126
|
+
end
|
127
|
+
az = az_to_container_instances.max_by {|_az, instances| instances.size }.first
|
128
|
+
target_container_instances << az_to_container_instances[az].pop
|
129
|
+
end
|
130
|
+
@logger.info do
|
131
|
+
"AZ balance: #{az_to_container_instances.sort_by {|az, _| az }.map {|az, instances| [az, instances.size] }.to_h}"
|
132
|
+
end
|
133
|
+
|
134
|
+
target_container_instances
|
135
|
+
end
|
136
|
+
|
137
|
+
# list tasks whose desired_status is "RUNNING" or
|
138
|
+
# whoose desired_status is "STOPPED" but last_status is "RUNNING" on the ECS container
|
139
|
+
def list_running_task_arns(container_instance_arn)
|
140
|
+
running_tasks_arn = ecs_client.list_tasks(cluster: @cluster, container_instance: container_instance_arn).flat_map(&:task_arns)
|
141
|
+
stopped_tasks_arn = ecs_client.list_tasks(cluster: @cluster, container_instance: container_instance_arn, desired_status: "STOPPED").flat_map(&:task_arns)
|
142
|
+
stopped_running_task_arns = stopped_tasks_arn.each_slice(MAX_DESCRIBABLE_ECS_TASK_COUNT).flat_map do |arns|
|
143
|
+
ecs_client.describe_tasks(cluster: @cluster, tasks: arns).tasks.select do |task|
|
144
|
+
task.desired_status == "STOPPED" && task.last_status == "RUNNING"
|
145
|
+
end
|
146
|
+
end.map(&:task_arn)
|
147
|
+
running_tasks_arn + stopped_running_task_arns
|
148
|
+
end
|
149
|
+
|
150
|
+
def wait_until_tasks_stopped(task_arns)
|
151
|
+
@logger.info("All old tasks: #{task_arns.size}")
|
152
|
+
task_arns.each_slice(MAX_DESCRIBABLE_ECS_TASK_COUNT).each do |arns|
|
153
|
+
ecs_client.wait_until(:tasks_stopped, cluster: @cluster, tasks: arns)
|
154
|
+
end
|
155
|
+
@logger.info("All old tasks are stopped")
|
156
|
+
end
|
157
|
+
|
158
|
+
def stop_tasks_not_belonging_service(running_task_arns)
|
159
|
+
@logger.info("Running tasks: #{running_task_arns.size}")
|
160
|
+
unless running_task_arns.empty?
|
161
|
+
running_task_arns.each_slice(MAX_DESCRIBABLE_ECS_TASK_COUNT).each do |arns|
|
162
|
+
ecs_client.describe_tasks(cluster: @cluster, tasks: arns).tasks.each do |task|
|
163
|
+
ecs_client.stop_task(cluster: @cluster, task: task.task_arn) if task.group.start_with?("family:")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def terminate_instances(instance_ids)
|
170
|
+
if instance_ids.empty?
|
171
|
+
@logger.info("There are no instances to terminate.")
|
172
|
+
return
|
173
|
+
end
|
174
|
+
instance_ids.each_slice(MAX_DETACHEABLE_EC2_INSTACE_COUNT) do |ids|
|
175
|
+
as_client.detach_instances(
|
176
|
+
auto_scaling_group_name: @auto_scaling_group_name,
|
177
|
+
instance_ids: ids,
|
178
|
+
should_decrement_desired_capacity: true
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
ec2_client.terminate_instances(instance_ids: instance_ids)
|
183
|
+
|
184
|
+
ec2_client.wait_until(:instance_terminated, instance_ids: instance_ids) do |w|
|
185
|
+
w.before_wait do |attempts, response|
|
186
|
+
@logger.info("Waiting for stopping all instances...#{attempts}")
|
187
|
+
instances = response.reservations.flat_map(&:instances)
|
188
|
+
instances.sort_by(&:instance_id).each do |instance|
|
189
|
+
@logger.info("#{instance.instance_id}\t#{instance.state.name}")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'aws-sdk-cloudwatchevents'
|
1
2
|
require 'timeout'
|
2
3
|
|
3
4
|
module EcsDeploy
|
@@ -8,8 +9,8 @@ module EcsDeploy
|
|
8
9
|
|
9
10
|
def initialize(
|
10
11
|
cluster:, rule_name:, schedule_expression:, enabled: true, description: nil, target_id: nil,
|
11
|
-
task_definition_name:, revision: nil, task_count: nil, role_arn:,
|
12
|
-
region: nil
|
12
|
+
task_definition_name:, revision: nil, task_count: nil, role_arn:, network_configuration: nil, launch_type: nil, platform_version: nil, group: nil,
|
13
|
+
region: nil, container_overrides: nil
|
13
14
|
)
|
14
15
|
@cluster = cluster
|
15
16
|
@rule_name = rule_name
|
@@ -21,9 +22,15 @@ module EcsDeploy
|
|
21
22
|
@task_count = task_count || 1
|
22
23
|
@revision = revision
|
23
24
|
@role_arn = role_arn
|
24
|
-
@
|
25
|
+
@network_configuration = network_configuration
|
26
|
+
@launch_type = launch_type || "EC2"
|
27
|
+
@platform_version = platform_version
|
28
|
+
@group = group
|
29
|
+
region ||= EcsDeploy.config.default_region
|
30
|
+
@container_overrides = container_overrides
|
25
31
|
|
26
|
-
@client = Aws::ECS::Client.new(region:
|
32
|
+
@client = region ? Aws::ECS::Client.new(region: region) : Aws::ECS::Client.new
|
33
|
+
@region = @client.config.region
|
27
34
|
@cloud_watch_events = Aws::CloudWatchEvents::Client.new(region: @region)
|
28
35
|
end
|
29
36
|
|
@@ -58,19 +65,28 @@ module EcsDeploy
|
|
58
65
|
end
|
59
66
|
|
60
67
|
def put_targets
|
68
|
+
target = {
|
69
|
+
id: @target_id,
|
70
|
+
arn: cluster_arn,
|
71
|
+
role_arn: @role_arn,
|
72
|
+
ecs_parameters: {
|
73
|
+
task_definition_arn: task_definition_arn,
|
74
|
+
task_count: @task_count,
|
75
|
+
network_configuration: @network_configuration,
|
76
|
+
launch_type: @launch_type,
|
77
|
+
platform_version: @platform_version,
|
78
|
+
group: @group,
|
79
|
+
},
|
80
|
+
}
|
81
|
+
target[:ecs_parameters].compact!
|
82
|
+
|
83
|
+
if @container_overrides
|
84
|
+
target.merge!(input: { containerOverrides: @container_overrides }.to_json)
|
85
|
+
end
|
86
|
+
|
61
87
|
res = @cloud_watch_events.put_targets(
|
62
88
|
rule: @rule_name,
|
63
|
-
targets: [
|
64
|
-
{
|
65
|
-
id: @target_id,
|
66
|
-
arn: cluster_arn,
|
67
|
-
role_arn: @role_arn,
|
68
|
-
ecs_parameters: {
|
69
|
-
task_definition_arn: task_definition_arn,
|
70
|
-
task_count: @task_count,
|
71
|
-
},
|
72
|
-
}
|
73
|
-
]
|
89
|
+
targets: [target]
|
74
90
|
)
|
75
91
|
if res.failed_entry_count.zero?
|
76
92
|
EcsDeploy.logger.info "create cloudwatch event target [#{@target_id}] [#{@region}] [#{Paint['OK', :green]}]"
|
data/lib/ecs_deploy/service.rb
CHANGED
@@ -5,13 +5,26 @@ module EcsDeploy
|
|
5
5
|
CHECK_INTERVAL = 5
|
6
6
|
MAX_DESCRIBE_SERVICES = 10
|
7
7
|
|
8
|
-
|
8
|
+
class TooManyAttemptsError < StandardError; end
|
9
|
+
|
10
|
+
attr_reader :cluster, :region, :service_name, :delete
|
9
11
|
|
10
12
|
def initialize(
|
11
13
|
cluster:, service_name:, task_definition_name: nil, revision: nil,
|
12
14
|
load_balancers: nil,
|
13
15
|
desired_count: nil, deployment_configuration: {maximum_percent: 200, minimum_healthy_percent: 100},
|
14
|
-
|
16
|
+
launch_type: nil,
|
17
|
+
placement_constraints: [],
|
18
|
+
placement_strategy: [],
|
19
|
+
network_configuration: nil,
|
20
|
+
health_check_grace_period_seconds: nil,
|
21
|
+
scheduling_strategy: 'REPLICA',
|
22
|
+
enable_ecs_managed_tags: nil,
|
23
|
+
tags: nil,
|
24
|
+
propagate_tags: nil,
|
25
|
+
region: nil,
|
26
|
+
delete: false,
|
27
|
+
enable_execute_command: false
|
15
28
|
)
|
16
29
|
@cluster = cluster
|
17
30
|
@service_name = service_name
|
@@ -19,11 +32,25 @@ module EcsDeploy
|
|
19
32
|
@load_balancers = load_balancers
|
20
33
|
@desired_count = desired_count
|
21
34
|
@deployment_configuration = deployment_configuration
|
35
|
+
@launch_type = launch_type
|
36
|
+
@placement_constraints = placement_constraints
|
37
|
+
@placement_strategy = placement_strategy
|
38
|
+
@network_configuration = network_configuration
|
39
|
+
@health_check_grace_period_seconds = health_check_grace_period_seconds
|
40
|
+
@scheduling_strategy = scheduling_strategy
|
22
41
|
@revision = revision
|
23
|
-
@
|
42
|
+
@enable_ecs_managed_tags = enable_ecs_managed_tags
|
43
|
+
@tags = tags
|
44
|
+
@propagate_tags = propagate_tags
|
45
|
+
@enable_execute_command = enable_execute_command
|
46
|
+
|
24
47
|
@response = nil
|
25
48
|
|
26
|
-
|
49
|
+
region ||= EcsDeploy.config.default_region
|
50
|
+
@client = region ? Aws::ECS::Client.new(region: region) : Aws::ECS::Client.new
|
51
|
+
@region = @client.config.region
|
52
|
+
|
53
|
+
@delete = delete
|
27
54
|
end
|
28
55
|
|
29
56
|
def current_task_definition_arn
|
@@ -37,53 +64,105 @@ module EcsDeploy
|
|
37
64
|
cluster: @cluster,
|
38
65
|
task_definition: task_definition_name_with_revision,
|
39
66
|
deployment_configuration: @deployment_configuration,
|
67
|
+
network_configuration: @network_configuration,
|
68
|
+
health_check_grace_period_seconds: @health_check_grace_period_seconds,
|
69
|
+
enable_execute_command: @enable_execute_command,
|
40
70
|
}
|
41
71
|
if res.services.select{ |s| s.status == 'ACTIVE' }.empty?
|
72
|
+
return if @delete
|
73
|
+
|
42
74
|
service_options.merge!({
|
43
75
|
service_name: @service_name,
|
44
76
|
desired_count: @desired_count.to_i,
|
77
|
+
launch_type: @launch_type,
|
78
|
+
placement_constraints: @placement_constraints,
|
79
|
+
placement_strategy: @placement_strategy,
|
80
|
+
enable_ecs_managed_tags: @enable_ecs_managed_tags,
|
81
|
+
tags: @tags,
|
82
|
+
propagate_tags: @propagate_tags,
|
45
83
|
})
|
46
|
-
|
84
|
+
|
85
|
+
if @load_balancers && EcsDeploy.config.ecs_service_role
|
47
86
|
service_options.merge!({
|
48
87
|
role: EcsDeploy.config.ecs_service_role,
|
88
|
+
})
|
89
|
+
end
|
90
|
+
|
91
|
+
if @load_balancers
|
92
|
+
service_options.merge!({
|
49
93
|
load_balancers: @load_balancers,
|
50
94
|
})
|
51
95
|
end
|
96
|
+
|
97
|
+
if @scheduling_strategy == 'DAEMON'
|
98
|
+
service_options[:scheduling_strategy] = @scheduling_strategy
|
99
|
+
service_options.delete(:desired_count)
|
100
|
+
end
|
52
101
|
@response = @client.create_service(service_options)
|
53
102
|
EcsDeploy.logger.info "create service [#{@service_name}] [#{@region}] [#{Paint['OK', :green]}]"
|
54
103
|
else
|
104
|
+
return delete_service if @delete
|
105
|
+
|
55
106
|
service_options.merge!({service: @service_name})
|
56
107
|
service_options.merge!({desired_count: @desired_count}) if @desired_count
|
108
|
+
update_tags(@service_name, @tags)
|
57
109
|
@response = @client.update_service(service_options)
|
58
110
|
EcsDeploy.logger.info "update service [#{@service_name}] [#{@region}] [#{Paint['OK', :green]}]"
|
59
111
|
end
|
60
112
|
end
|
61
113
|
|
62
|
-
def
|
63
|
-
|
114
|
+
def delete_service
|
115
|
+
if @scheduling_strategy != 'DAEMON'
|
116
|
+
@client.update_service(cluster: @cluster, service: @service_name, desired_count: 0)
|
117
|
+
sleep 1
|
118
|
+
end
|
119
|
+
@client.delete_service(cluster: @cluster, service: @service_name)
|
120
|
+
EcsDeploy.logger.info "delete service [#{@service_name}] [#{@region}] [#{Paint['OK', :green]}]"
|
121
|
+
end
|
64
122
|
|
65
|
-
|
123
|
+
def update_tags(service_name, tags)
|
124
|
+
service_arn = @client.describe_services(cluster: @cluster, services: [service_name]).services.first.service_arn
|
125
|
+
if service_arn.split('/').size == 2
|
126
|
+
if tags
|
127
|
+
EcsDeploy.logger.warn "#{service_name} doesn't support tagging operations, so tags are ignored. Long arn format must be used for tagging operations."
|
128
|
+
end
|
129
|
+
return
|
130
|
+
end
|
66
131
|
|
67
|
-
|
68
|
-
|
132
|
+
tags ||= []
|
133
|
+
current_tag_keys = @client.list_tags_for_resource(resource_arn: service_arn).tags.map(&:key)
|
134
|
+
deleted_tag_keys = current_tag_keys - tags.map { |t| t[:key] }
|
69
135
|
|
70
|
-
|
71
|
-
|
72
|
-
|
136
|
+
unless deleted_tag_keys.empty?
|
137
|
+
@client.untag_resource(resource_arn: service_arn, tag_keys: deleted_tag_keys)
|
138
|
+
end
|
139
|
+
|
140
|
+
unless tags.empty?
|
141
|
+
@client.tag_resource(resource_arn: service_arn, tags: tags)
|
73
142
|
end
|
74
143
|
end
|
75
144
|
|
76
145
|
def self.wait_all_running(services)
|
77
|
-
services.group_by { |s| [s.cluster, s.region] }.
|
146
|
+
services.group_by { |s| [s.cluster, s.region] }.flat_map do |(cl, region), ss|
|
78
147
|
client = Aws::ECS::Client.new(region: region)
|
79
|
-
ss.map(&:service_name).each_slice(MAX_DESCRIBE_SERVICES) do |chunked_service_names|
|
80
|
-
|
81
|
-
|
148
|
+
ss.reject(&:delete).map(&:service_name).each_slice(MAX_DESCRIBE_SERVICES).map do |chunked_service_names|
|
149
|
+
Thread.new do
|
150
|
+
EcsDeploy.config.ecs_wait_until_services_stable_max_attempts.times do
|
82
151
|
EcsDeploy.logger.info "wait service stable [#{chunked_service_names.join(", ")}]"
|
152
|
+
resp = client.describe_services(cluster: cl, services: chunked_service_names)
|
153
|
+
resp.services.each do |s|
|
154
|
+
# cf. https://github.com/aws/aws-sdk-ruby/blob/master/gems/aws-sdk-ecs/lib/aws-sdk-ecs/waiters.rb#L91-L96
|
155
|
+
if s.deployments.size == 1 && s.running_count == s.desired_count
|
156
|
+
chunked_service_names.delete(s.service_name)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
break if chunked_service_names.empty?
|
160
|
+
sleep EcsDeploy.config.ecs_wait_until_services_stable_delay
|
83
161
|
end
|
162
|
+
raise TooManyAttemptsError unless chunked_service_names.empty?
|
84
163
|
end
|
85
164
|
end
|
86
|
-
end
|
165
|
+
end.each(&:join)
|
87
166
|
end
|
88
167
|
|
89
168
|
private
|