ecs_deploy 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ecs_deploy.gemspec +1 -1
- data/lib/ecs_deploy.rb +1 -0
- data/lib/ecs_deploy/auto_scaler.rb +18 -34
- data/lib/ecs_deploy/capistrano.rb +41 -5
- data/lib/ecs_deploy/scheduled_task.rb +85 -0
- data/lib/ecs_deploy/service.rb +8 -5
- data/lib/ecs_deploy/task_definition.rb +7 -2
- data/lib/ecs_deploy/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a565132ff142d819e1bdc64368264ebc89de1030
|
4
|
+
data.tar.gz: d0b5d389d5a9311853d8524e45c56bd54943450a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50c6f3c9aecdacc26de0d61cb487f4196bf31031b8c43b798b4bebae1e2246e35ebf1bb1c6079a62b7d5f54c3f504fefb69ca08f08689d9717c601746db63bdb
|
7
|
+
data.tar.gz: f4fa32bf5a67e71a6265842eaffbff31647d17337fdb23e01007a6082f13b894a45ec127ca098a7c9125935937f9ac04c5d88025223915938da591a3259119e2
|
data/README.md
CHANGED
@@ -225,4 +225,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
225
225
|
|
226
226
|
## Contributing
|
227
227
|
|
228
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
228
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/reproio/ecs_deploy.
|
data/ecs_deploy.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_runtime_dependency "aws-sdk", "~> 2.
|
21
|
+
spec.add_runtime_dependency "aws-sdk", "~> 2.9"
|
22
22
|
spec.add_runtime_dependency "terminal-table"
|
23
23
|
spec.add_runtime_dependency "paint"
|
24
24
|
|
data/lib/ecs_deploy.rb
CHANGED
@@ -23,7 +23,7 @@ module EcsDeploy
|
|
23
23
|
config_groups = service_configs.group_by { |s| [s.region, s.auto_scaling_group_name] }
|
24
24
|
ths = config_groups.map do |(region, auto_scaling_group_name), configs|
|
25
25
|
asg_config = auto_scaling_group_configs.find { |c| c.name == auto_scaling_group_name && c.region == region }
|
26
|
-
Thread.new(asg_config, configs, &method(:main_loop))
|
26
|
+
Thread.new(asg_config, configs, &method(:main_loop)).tap { |th| th.abort_on_exception = true }
|
27
27
|
end
|
28
28
|
|
29
29
|
ths.each(&:join)
|
@@ -33,10 +33,13 @@ module EcsDeploy
|
|
33
33
|
loop_with_polling_interval("loop of #{asg_config.name}") do
|
34
34
|
ths = configs.map do |service_config|
|
35
35
|
Thread.new(service_config) do |s|
|
36
|
-
next if s.idle?
|
37
|
-
|
38
36
|
@logger.debug "Start service scaling of #{s.name}"
|
39
37
|
|
38
|
+
if s.idle?
|
39
|
+
@logger.debug "#{s.name} is idling"
|
40
|
+
next
|
41
|
+
end
|
42
|
+
|
40
43
|
difference = 0
|
41
44
|
s.upscale_triggers.each do |trigger|
|
42
45
|
step = trigger.step || s.step
|
@@ -71,6 +74,7 @@ module EcsDeploy
|
|
71
74
|
end
|
72
75
|
end
|
73
76
|
end
|
77
|
+
ths.each { |th| th.abort_on_exception = true }
|
74
78
|
|
75
79
|
ths.each(&:join)
|
76
80
|
|
@@ -113,6 +117,7 @@ module EcsDeploy
|
|
113
117
|
next if wait_polling_interval?(last_executed_at)
|
114
118
|
yield
|
115
119
|
last_executed_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)
|
120
|
+
@logger.debug "#{name} is last executed at #{last_executed_at}"
|
116
121
|
end
|
117
122
|
|
118
123
|
@logger.debug "Stop #{name}"
|
@@ -148,17 +153,13 @@ module EcsDeploy
|
|
148
153
|
end
|
149
154
|
|
150
155
|
def client
|
151
|
-
|
156
|
+
Aws::ECS::Client.new(
|
152
157
|
access_key_id: EcsDeploy.config.access_key_id,
|
153
158
|
secret_access_key: EcsDeploy.config.secret_access_key,
|
154
159
|
region: region
|
155
160
|
)
|
156
161
|
end
|
157
162
|
|
158
|
-
def clear_client
|
159
|
-
Thread.current["ecs_auto_scaler_ecs_#{region}"] = nil
|
160
|
-
end
|
161
|
-
|
162
163
|
def idle?
|
163
164
|
return false unless @last_updated_at
|
164
165
|
|
@@ -187,7 +188,6 @@ module EcsDeploy
|
|
187
188
|
res.services[0]
|
188
189
|
rescue => e
|
189
190
|
AutoScaler.error_logger.error(e)
|
190
|
-
clear_client
|
191
191
|
end
|
192
192
|
|
193
193
|
def update_service(difference)
|
@@ -218,13 +218,14 @@ module EcsDeploy
|
|
218
218
|
AutoScaler.logger.info "Service \"#{name}\" clears cooldown state"
|
219
219
|
end
|
220
220
|
|
221
|
+
cl = client
|
221
222
|
next_desired_count = [next_desired_count, max_task_count[level]].min
|
222
|
-
|
223
|
+
cl.update_service(
|
223
224
|
cluster: cluster,
|
224
225
|
service: name,
|
225
226
|
desired_count: next_desired_count,
|
226
227
|
)
|
227
|
-
|
228
|
+
cl.wait_until(:services_stable, cluster: cluster, services: [name]) do |w|
|
228
229
|
w.before_wait do
|
229
230
|
AutoScaler.logger.debug "wait service stable [#{name}]"
|
230
231
|
end
|
@@ -234,16 +235,16 @@ module EcsDeploy
|
|
234
235
|
AutoScaler.logger.info "Update service \"#{name}\": desired_count -> #{next_desired_count}"
|
235
236
|
rescue => e
|
236
237
|
AutoScaler.error_logger.error(e)
|
237
|
-
clear_client
|
238
238
|
end
|
239
239
|
|
240
240
|
def fetch_container_instances
|
241
241
|
arns = []
|
242
242
|
resp = nil
|
243
|
+
cl = client
|
243
244
|
loop do
|
244
245
|
options = {cluster: cluster}
|
245
246
|
options.merge(next_token: resp.next_token) if resp && resp.next_token
|
246
|
-
resp =
|
247
|
+
resp = cl.list_container_instances(options)
|
247
248
|
arns.concat(resp.container_instance_arns)
|
248
249
|
break unless resp.next_token
|
249
250
|
end
|
@@ -251,7 +252,7 @@ module EcsDeploy
|
|
251
252
|
chunk_size = 50
|
252
253
|
container_instances = []
|
253
254
|
arns.each_slice(chunk_size) do |arn_chunk|
|
254
|
-
is =
|
255
|
+
is = cl.describe_container_instances(cluster: cluster, container_instances: arn_chunk).container_instances
|
255
256
|
container_instances.concat(is)
|
256
257
|
end
|
257
258
|
|
@@ -269,17 +270,13 @@ module EcsDeploy
|
|
269
270
|
include ConfigBase
|
270
271
|
|
271
272
|
def client
|
272
|
-
|
273
|
+
Aws::CloudWatch::Client.new(
|
273
274
|
access_key_id: EcsDeploy.config.access_key_id,
|
274
275
|
secret_access_key: EcsDeploy.config.secret_access_key,
|
275
276
|
region: region
|
276
277
|
)
|
277
278
|
end
|
278
279
|
|
279
|
-
def clear_client
|
280
|
-
Thread.current["ecs_auto_scaler_cloud_watch_#{region}"] = nil
|
281
|
-
end
|
282
|
-
|
283
280
|
def match?
|
284
281
|
fetch_alarm.state_value == state
|
285
282
|
end
|
@@ -301,29 +298,21 @@ module EcsDeploy
|
|
301
298
|
include ConfigBase
|
302
299
|
|
303
300
|
def client
|
304
|
-
|
301
|
+
Aws::AutoScaling::Client.new(
|
305
302
|
access_key_id: EcsDeploy.config.access_key_id,
|
306
303
|
secret_access_key: EcsDeploy.config.secret_access_key,
|
307
304
|
region: region
|
308
305
|
)
|
309
306
|
end
|
310
307
|
|
311
|
-
def clear_client
|
312
|
-
Thread.current["ecs_auto_scaler_auto_scaling_#{region}"] = nil
|
313
|
-
end
|
314
|
-
|
315
308
|
def ec2_client
|
316
|
-
|
309
|
+
Aws::EC2::Client.new(
|
317
310
|
access_key_id: EcsDeploy.config.access_key_id,
|
318
311
|
secret_access_key: EcsDeploy.config.secret_access_key,
|
319
312
|
region: region
|
320
313
|
)
|
321
314
|
end
|
322
315
|
|
323
|
-
def clear_ec2_client
|
324
|
-
Thread.current["ecs_auto_scaler_ec2_#{region}"] = nil
|
325
|
-
end
|
326
|
-
|
327
316
|
def instances(reload: false)
|
328
317
|
if reload || @instances.nil?
|
329
318
|
resp = client.describe_auto_scaling_groups({
|
@@ -378,7 +367,6 @@ module EcsDeploy
|
|
378
367
|
end
|
379
368
|
rescue => e
|
380
369
|
AutoScaler.error_logger.error(e)
|
381
|
-
clear_client
|
382
370
|
end
|
383
371
|
|
384
372
|
def detach_and_terminate_instances(instance_ids)
|
@@ -398,8 +386,6 @@ module EcsDeploy
|
|
398
386
|
AutoScaler.logger.info "Terminated instances: #{instance_ids.inspect}"
|
399
387
|
rescue => e
|
400
388
|
AutoScaler.error_logger.error(e)
|
401
|
-
clear_client
|
402
|
-
clear_ec2_client
|
403
389
|
end
|
404
390
|
|
405
391
|
def detach_and_terminate_orphan_instances(service_config)
|
@@ -415,8 +401,6 @@ module EcsDeploy
|
|
415
401
|
detach_and_terminate_instances(targets.map(&:instance_id))
|
416
402
|
rescue => e
|
417
403
|
AutoScaler.error_logger.error(e)
|
418
|
-
clear_client
|
419
|
-
clear_ec2_client
|
420
404
|
end
|
421
405
|
end
|
422
406
|
end
|
@@ -20,24 +20,60 @@ namespace :ecs do
|
|
20
20
|
task register_task_definition: [:configure] do
|
21
21
|
if fetch(:ecs_tasks)
|
22
22
|
regions = Array(fetch(:ecs_region))
|
23
|
-
regions = [
|
23
|
+
regions = [EcsDeploy.config.default_region || ENV["AWS_DEFAULT_REGION"]] if regions.empty?
|
24
|
+
ecs_registered_tasks = {}
|
24
25
|
regions.each do |r|
|
26
|
+
ecs_registered_tasks[region] = {}
|
25
27
|
fetch(:ecs_tasks).each do |t|
|
26
28
|
task_definition = EcsDeploy::TaskDefinition.new(
|
27
|
-
region:
|
29
|
+
region: region,
|
28
30
|
task_definition_name: t[:name],
|
29
31
|
container_definitions: t[:container_definitions],
|
30
32
|
task_role_arn: t[:task_role_arn],
|
31
|
-
volumes: t[:volumes]
|
33
|
+
volumes: t[:volumes],
|
34
|
+
network_mode: t[:network_mode],
|
35
|
+
placement_constraints: t[:placement_constraints],
|
32
36
|
)
|
33
|
-
task_definition.register
|
37
|
+
result = task_definition.register
|
38
|
+
ecs_registered_tasks[region][t[:name]] = result
|
34
39
|
|
35
|
-
t[:executions].to_a
|
40
|
+
executions = t[:executions].to_a
|
41
|
+
unless executions.empty?
|
42
|
+
warn "`executions` config is deprecated. I will remove this in near future"
|
43
|
+
end
|
44
|
+
executions.each do |exec|
|
36
45
|
exec[:cluster] ||= fetch(:ecs_default_cluster)
|
37
46
|
task_definition.run(exec)
|
38
47
|
end
|
39
48
|
end
|
40
49
|
end
|
50
|
+
|
51
|
+
set :ecs_registered_tasks, ecs_registered_tasks
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
task deploy_scheduled_task: [:configure, :register_task_definition] do
|
56
|
+
if fetch(:ecs_tasks)
|
57
|
+
regions = Array(fetch(:ecs_region))
|
58
|
+
regions = [nil] if regions.empty?
|
59
|
+
regions.each do |r|
|
60
|
+
fetch(:ecs_scheduled_tasks).each do |t|
|
61
|
+
scheduled_task = EcsDeploy::ScheduledTask.new(
|
62
|
+
region: r,
|
63
|
+
cluster: t[:cluster],
|
64
|
+
rule_name: t[:rule_name],
|
65
|
+
schedule_expression: t[:schedule_expression],
|
66
|
+
enabled: t[:enabled] != false,
|
67
|
+
description: t[:description],
|
68
|
+
target_id: t[:target_id],
|
69
|
+
task_definition_name: t[:task_definition_name],
|
70
|
+
revision: t[:revision],
|
71
|
+
task_count: t[:task_count],
|
72
|
+
role_arn: t[:role_arn],
|
73
|
+
)
|
74
|
+
scheduled_task.deploy
|
75
|
+
end
|
76
|
+
end
|
41
77
|
end
|
42
78
|
end
|
43
79
|
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
module EcsDeploy
|
4
|
+
class ScheduledTask
|
5
|
+
class PutTargetsFailure < StandardError; end
|
6
|
+
|
7
|
+
attr_reader :cluster, :region, :schedule_rule_name
|
8
|
+
|
9
|
+
def initialize(
|
10
|
+
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
|
13
|
+
)
|
14
|
+
@cluster = cluster
|
15
|
+
@rule_name = rule_name
|
16
|
+
@schedule_expression = schedule_expression
|
17
|
+
@enabled = enabled
|
18
|
+
@description = description
|
19
|
+
@target_id = target_id || task_definition_name
|
20
|
+
@task_definition_name = task_definition_name
|
21
|
+
@task_count = task_count || 1
|
22
|
+
@revision = revision
|
23
|
+
@role_arn = role_arn
|
24
|
+
@region = region || EcsDeploy.config.default_region || ENV["AWS_DEFAULT_REGION"]
|
25
|
+
|
26
|
+
@client = Aws::ECS::Client.new(region: @region)
|
27
|
+
@cloud_watch_events = Aws::CloudWatchEvents::Client.new(region: @region)
|
28
|
+
end
|
29
|
+
|
30
|
+
def deploy
|
31
|
+
put_rule
|
32
|
+
put_targets
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def cluster_arn
|
38
|
+
cl = @client.describe_clusters(clusters: [@cluster]).clusters[0]
|
39
|
+
if cl
|
40
|
+
cl.cluster_arn
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def task_definition_arn
|
45
|
+
suffix = @revision ? ":#{@revision}" : ""
|
46
|
+
name = "#{@task_definition_name}#{suffix}"
|
47
|
+
@client.describe_task_definition(task_definition: name).task_definition.task_definition_arn
|
48
|
+
end
|
49
|
+
|
50
|
+
def put_rule
|
51
|
+
res = @cloud_watch_events.put_rule(
|
52
|
+
name: @rule_name,
|
53
|
+
schedule_expression: @schedule_expression,
|
54
|
+
state: @enabled ? "ENABLED" : "DISABLED",
|
55
|
+
description: @description,
|
56
|
+
)
|
57
|
+
EcsDeploy.logger.info "create cloudwatch event rule [#{res.rule_arn}] [#{@region}] [#{Paint['OK', :green]}]"
|
58
|
+
end
|
59
|
+
|
60
|
+
def put_targets
|
61
|
+
res = @cloud_watch_events.put_targets(
|
62
|
+
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
|
+
]
|
74
|
+
)
|
75
|
+
if res.failed_entry_count.zero?
|
76
|
+
EcsDeploy.logger.info "create cloudwatch event target [#{@target_id}] [#{@region}] [#{Paint['OK', :green]}]"
|
77
|
+
else
|
78
|
+
res.failed_entries.each do |entry|
|
79
|
+
EcsDeploy.logger.error "failed to create cloudwatch event target [#{@region}] target_id=#{entry.target_id} error_code=#{entry.error_code} error_message=#{entry.error_message}"
|
80
|
+
end
|
81
|
+
raise PutTargetsFailure
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/ecs_deploy/service.rb
CHANGED
@@ -3,6 +3,8 @@ require 'timeout'
|
|
3
3
|
module EcsDeploy
|
4
4
|
class Service
|
5
5
|
CHECK_INTERVAL = 5
|
6
|
+
MAX_DESCRIBE_SERVICES = 10
|
7
|
+
|
6
8
|
attr_reader :cluster, :region, :service_name
|
7
9
|
|
8
10
|
def initialize(
|
@@ -36,7 +38,7 @@ module EcsDeploy
|
|
36
38
|
task_definition: task_definition_name_with_revision,
|
37
39
|
deployment_configuration: @deployment_configuration,
|
38
40
|
}
|
39
|
-
if res.services.empty?
|
41
|
+
if res.services.select{ |s| s.status == 'ACTIVE' }.empty?
|
40
42
|
service_options.merge!({
|
41
43
|
service_name: @service_name,
|
42
44
|
desired_count: @desired_count.to_i,
|
@@ -74,10 +76,11 @@ module EcsDeploy
|
|
74
76
|
def self.wait_all_running(services)
|
75
77
|
services.group_by { |s| [s.cluster, s.region] }.each do |(cl, region), ss|
|
76
78
|
client = Aws::ECS::Client.new(region: region)
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
ss.map(&:service_name).each_slice(MAX_DESCRIBE_SERVICES) do |chunked_service_names|
|
80
|
+
client.wait_until(:services_stable, cluster: cl, services: chunked_service_names) do |w|
|
81
|
+
w.before_attempt do
|
82
|
+
EcsDeploy.logger.info "wait service stable [#{chunked_service_names.join(", ")}]"
|
83
|
+
end
|
81
84
|
end
|
82
85
|
end
|
83
86
|
end
|
@@ -11,7 +11,7 @@ module EcsDeploy
|
|
11
11
|
|
12
12
|
def initialize(
|
13
13
|
task_definition_name:, region: nil,
|
14
|
-
volumes: [], container_definitions: [],
|
14
|
+
network_mode: "bridge", volumes: [], container_definitions: [], placement_constraints: [],
|
15
15
|
task_role_arn: nil
|
16
16
|
)
|
17
17
|
@task_definition_name = task_definition_name
|
@@ -28,6 +28,8 @@ module EcsDeploy
|
|
28
28
|
cd
|
29
29
|
end
|
30
30
|
@volumes = volumes
|
31
|
+
@network_mode = network_mode
|
32
|
+
@placement_constraints = placement_constraints
|
31
33
|
|
32
34
|
@client = Aws::ECS::Client.new(region: @region)
|
33
35
|
end
|
@@ -43,13 +45,16 @@ module EcsDeploy
|
|
43
45
|
end
|
44
46
|
|
45
47
|
def register
|
46
|
-
@client.register_task_definition({
|
48
|
+
res = @client.register_task_definition({
|
47
49
|
family: @task_definition_name,
|
50
|
+
network_mode: @network_mode,
|
48
51
|
container_definitions: @container_definitions,
|
49
52
|
volumes: @volumes,
|
53
|
+
placement_constraints: @placement_constraints,
|
50
54
|
task_role_arn: @task_role_arn,
|
51
55
|
})
|
52
56
|
EcsDeploy.logger.info "register task definition [#{@task_definition_name}] [#{@region}] [#{Paint['OK', :green]}]"
|
57
|
+
res.task_definition
|
53
58
|
end
|
54
59
|
|
55
60
|
def run(info)
|
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: 0.
|
4
|
+
version: 0.3.0
|
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: 2017-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.9'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
26
|
+
version: '2.9'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: terminal-table
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- lib/ecs_deploy/auto_scaler.rb
|
101
101
|
- lib/ecs_deploy/capistrano.rb
|
102
102
|
- lib/ecs_deploy/configuration.rb
|
103
|
+
- lib/ecs_deploy/scheduled_task.rb
|
103
104
|
- lib/ecs_deploy/service.rb
|
104
105
|
- lib/ecs_deploy/task_definition.rb
|
105
106
|
- lib/ecs_deploy/version.rb
|
@@ -122,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
123
|
version: '0'
|
123
124
|
requirements: []
|
124
125
|
rubyforge_project:
|
125
|
-
rubygems_version: 2.6.
|
126
|
+
rubygems_version: 2.6.12
|
126
127
|
signing_key:
|
127
128
|
specification_version: 4
|
128
129
|
summary: AWS ECS deploy helper
|