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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5027056d7641256dc566be9a2fd16dff8046f42
4
- data.tar.gz: 009c12a42f0fdcd18f3c8a53b35f0e48055112a8
3
+ metadata.gz: a565132ff142d819e1bdc64368264ebc89de1030
4
+ data.tar.gz: d0b5d389d5a9311853d8524e45c56bd54943450a
5
5
  SHA512:
6
- metadata.gz: c53395003a3e8953aef1d63cdf7a59d89b1f45e90b0acef1a00ff4420a4e96139e854427e204650aecfbf02f6a064348a6dea9f5919ef6b161a4e962e9d2b6bf
7
- data.tar.gz: 82ce007f7e3e5ac6f4c30a705a6c16ca16889302d4c316a08990fae786bb403f9679582788584a23032d6894f04bcb6b321c82155c759844283c2400279162e2
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/joker1007/ecs_deploy.
228
+ Bug reports and pull requests are welcome on GitHub at https://github.com/reproio/ecs_deploy.
@@ -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.4"
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
 
@@ -27,3 +27,4 @@ end
27
27
 
28
28
  require "ecs_deploy/task_definition"
29
29
  require "ecs_deploy/service"
30
+ require "ecs_deploy/scheduled_task"
@@ -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
- Thread.current["ecs_auto_scaler_ecs_#{region}"] ||= Aws::ECS::Client.new(
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
- client.update_service(
223
+ cl.update_service(
223
224
  cluster: cluster,
224
225
  service: name,
225
226
  desired_count: next_desired_count,
226
227
  )
227
- client.wait_until(:services_stable, cluster: cluster, services: [name]) do |w|
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 = client.list_container_instances(options)
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 = client.describe_container_instances(cluster: cluster, container_instances: arn_chunk).container_instances
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
- Thread.current["ecs_auto_scaler_cloud_watch_#{region}"] ||= Aws::CloudWatch::Client.new(
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
- Thread.current["ecs_auto_scaler_auto_scaling_#{region}"] ||= Aws::AutoScaling::Client.new(
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
- Thread.current["ecs_auto_scaler_ec2_#{region}"] ||= Aws::EC2::Client.new(
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 = [nil] if regions.empty?
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: r,
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.each do |exec|
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
@@ -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
- service_names = ss.map(&:service_name)
78
- client.wait_until(:services_stable, cluster: cl, services: service_names) do |w|
79
- w.before_attempt do
80
- EcsDeploy.logger.info "wait service stable [#{service_names.join(", ")}]"
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)
@@ -1,3 +1,3 @@
1
1
  module EcsDeploy
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.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: 2016-10-30 00:00:00.000000000 Z
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.4'
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.4'
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.4
126
+ rubygems_version: 2.6.12
126
127
  signing_key:
127
128
  specification_version: 4
128
129
  summary: AWS ECS deploy helper