ecs_deployer 0.1.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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +27 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ecs_deployer.gemspec +37 -0
- data/example/fixtures/task.yml +20 -0
- data/example/sample.rb +8 -0
- data/lib/ecs_deployer/client.rb +128 -0
- data/lib/ecs_deployer/commander.rb +104 -0
- data/lib/ecs_deployer/error.rb +4 -0
- data/lib/ecs_deployer/version.rb +3 -0
- data/lib/ecs_deployer.rb +9 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5923a19be9af0b8b94e8a3a15ba3ef0f47c97321
|
4
|
+
data.tar.gz: 7fad5d15dde84e2320427e7ac17dec3461037be0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 43e7796ee3372669de2c36041ee6a71e0f33a3d9ad24c29adda8f5c601d2b429f0cab8909e0c51c0a8b6a905d2affe13f6ef859c153eacfb2d64b1217474b287
|
7
|
+
data.tar.gz: 99d51da9894390e8db85e2bae8fd107f274c4e3da365b29674afbe3c1990ef8e7e936f45641e91f60a57c153da4eb64f0a50d90db4cd691e747c5f16b3bcb1ae
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# EcsDeployer
|
2
|
+
|
3
|
+
This package provides the service deployment function of ECS.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'ecs_deployer'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install ecs_deployer
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```
|
24
|
+
ecs_deployer = EcsDeployer::Client.new('cluster_name')
|
25
|
+
ecs_deployer.register_task('development.yml')
|
26
|
+
ecs_deployer.update_service('application')
|
27
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ecs_deployer"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ecs_deployer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ecs_deployer"
|
8
|
+
spec.version = EcsDeployer::VERSION
|
9
|
+
spec.authors = ["naomichi-y"]
|
10
|
+
spec.email = ["n.yamakita@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Deploy application to ECS.}
|
13
|
+
spec.description = %q{This package provides the service deployment function of ECS.}
|
14
|
+
spec.homepage = ""
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
# spec.metadata['allowed_push_host'] = ""
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
22
|
+
"public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
26
|
+
f.match(%r{^(test|spec|features)/})
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
spec.add_dependency 'runtime_command'
|
33
|
+
spec.add_dependency 'oj'
|
34
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
35
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
36
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
containerDefinitions:
|
2
|
+
- name: wordpress
|
3
|
+
links:
|
4
|
+
- mysql
|
5
|
+
image: wordpress
|
6
|
+
essential: true
|
7
|
+
portMappings:
|
8
|
+
- containerPort: 80
|
9
|
+
hostPort: 80
|
10
|
+
memory: 500
|
11
|
+
cpu: 10
|
12
|
+
- environment:
|
13
|
+
- name: MYSQL_ROOT_PASSWORD
|
14
|
+
value: password
|
15
|
+
name: mysql
|
16
|
+
image: mysql
|
17
|
+
cpu: 10
|
18
|
+
memory: 500
|
19
|
+
essential: true
|
20
|
+
family: hello_world
|
data/example/sample.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'ecs_deployer'
|
3
|
+
|
4
|
+
task_path = File.expand_path('./fixtures/task.yml', File.dirname(File.realpath(__FILE__)))
|
5
|
+
|
6
|
+
ecs_deployer = EcsDeployer::Client.new('cluster_name')
|
7
|
+
ecs_deployer.register_task(task_path)
|
8
|
+
ecs_deployer.update_service('application')
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'runtime_command'
|
3
|
+
require 'ecs_deployer/commander'
|
4
|
+
|
5
|
+
module EcsDeployer
|
6
|
+
class Client
|
7
|
+
PAULING_INTERVAL = 20
|
8
|
+
|
9
|
+
# @param [String] cluster_name
|
10
|
+
# @param [Hash] options
|
11
|
+
# @option options [String] :profile
|
12
|
+
# @option options [String] :region
|
13
|
+
# @return [EcsDeployer::Client]
|
14
|
+
def initialize(cluster_name, options = {})
|
15
|
+
@cluster_name = cluster_name
|
16
|
+
@ecs_command = Commander.new(cluster_name, options)
|
17
|
+
@family_name = ''
|
18
|
+
@revision = ''
|
19
|
+
@new_task_definition_arn = ''
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [String] task_path
|
23
|
+
# @return [String]
|
24
|
+
def register_task(task_path)
|
25
|
+
raise IOError.new("File does not exist. [#{task_path}]") if !File.exist?(task_path)
|
26
|
+
register_task_process(YAML.load(File.read(task_path)))
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [String] service_name
|
30
|
+
# @return [String]
|
31
|
+
def register_clone_task(service_name)
|
32
|
+
detected_service = false
|
33
|
+
|
34
|
+
result = @ecs_command.describe_services(service_name)
|
35
|
+
result['services'].each do |service|
|
36
|
+
if service['serviceName'] == service_name
|
37
|
+
result = @ecs_command.describe_task_definition(service['taskDefinition'])
|
38
|
+
@new_task_definition_arn = register_task_process(result['taskDefinition'])
|
39
|
+
detected_service = true
|
40
|
+
break
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
raise ServiceNotFoundError.new("'#{service_name}' service is not found.") unless detected_service
|
45
|
+
|
46
|
+
@new_task_definition_arn
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param [String] service_name
|
50
|
+
# @param [Fixnum] timeout
|
51
|
+
def update_service(service_name, wait = true, timeout = 300)
|
52
|
+
register_clone_task(service_name) if @new_task_definition_arn.empty?
|
53
|
+
@ecs_command.update_service(service_name, @family_name, @revision)
|
54
|
+
wait_for_deploy if wait
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def wait_for_deploy(service_name, timeout)
|
59
|
+
detected_service = false
|
60
|
+
|
61
|
+
result = @ecs_command.describe_services(service_name)
|
62
|
+
result['services'].each do |service|
|
63
|
+
next unless service['serviceName'] == service_name
|
64
|
+
detected_service = true
|
65
|
+
|
66
|
+
result = @ecs_command.describe_task_definition(service['taskDefinition'])
|
67
|
+
|
68
|
+
if service['desiredCount'] > 0
|
69
|
+
running_new_task = false
|
70
|
+
wait_time = 0
|
71
|
+
puts 'Start deploing...'
|
72
|
+
|
73
|
+
begin
|
74
|
+
sleep(PAULING_INTERVAL)
|
75
|
+
wait_time += PAULING_INTERVAL
|
76
|
+
|
77
|
+
# Get current tasks
|
78
|
+
result = @ecs_command.list_tasks(service_name)
|
79
|
+
|
80
|
+
if result['taskArns'].size > 0
|
81
|
+
success_count = 0
|
82
|
+
|
83
|
+
result = @ecs_command.describe_tasks(result['taskArns'])
|
84
|
+
result['tasks'].each do |task|
|
85
|
+
success_count += 1 if @new_task_definition_arn == task['taskDefinitionArn']
|
86
|
+
end
|
87
|
+
|
88
|
+
if result['tasks'].size == success_count
|
89
|
+
puts 'Service update succeeded.'
|
90
|
+
puts "New task definition: #{@new_task_definition_arn}"
|
91
|
+
|
92
|
+
running_new_task = true
|
93
|
+
end
|
94
|
+
else
|
95
|
+
raise TaskNotFoundError.new('Desired count is 0.')
|
96
|
+
end
|
97
|
+
|
98
|
+
if wait_time > timeout
|
99
|
+
puts "New task definition: #{@new_task_definition_arn}"
|
100
|
+
raise DeployTimeoutError.new('Service is being updating, but process is timed out.')
|
101
|
+
end
|
102
|
+
|
103
|
+
puts "Deploying... (#{wait_time} seconds elapsed)"
|
104
|
+
|
105
|
+
end while !running_new_task
|
106
|
+
end
|
107
|
+
|
108
|
+
break
|
109
|
+
end
|
110
|
+
|
111
|
+
raise ServiceNotFoundError.new("'#{service_name}' service is not found.") unless detected_service
|
112
|
+
end
|
113
|
+
|
114
|
+
# @param [Hash] task_definition
|
115
|
+
# @return [String]
|
116
|
+
def register_task_process(task_definition)
|
117
|
+
result = @ecs_command.register_task_definition(
|
118
|
+
task_definition['family'],
|
119
|
+
task_definition['containerDefinitions']
|
120
|
+
)
|
121
|
+
|
122
|
+
@family_name = result['taskDefinition']['family']
|
123
|
+
@revision = result['taskDefinition']['revision']
|
124
|
+
@new_task_definition_arn = result['taskDefinition']['taskDefinitionArn']
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'oj'
|
2
|
+
require 'runtime_command'
|
3
|
+
|
4
|
+
module EcsDeployer
|
5
|
+
class Commander
|
6
|
+
# @param [String] cluster_name
|
7
|
+
# @param [Hash] options
|
8
|
+
# @return EcsDeployer::Commander
|
9
|
+
def initialize(cluster_name, options = {})
|
10
|
+
@runtime = RuntimeCommand::Builder.new
|
11
|
+
@options = options
|
12
|
+
@cluster_name = cluster_name
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [String] service_name
|
16
|
+
# @param [String] family_name
|
17
|
+
# @param [Fixnum] revision
|
18
|
+
# @return [Hash]
|
19
|
+
def update_service(service_name, family_name, revision)
|
20
|
+
args = {
|
21
|
+
'cluster': @cluster_name,
|
22
|
+
'service': service_name,
|
23
|
+
'task-definition': family_name + ':' + revision.to_s
|
24
|
+
}
|
25
|
+
|
26
|
+
exec('update-service', args)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [String] service_name
|
30
|
+
# @return [Hash]
|
31
|
+
def list_tasks(service_name)
|
32
|
+
args = {
|
33
|
+
'cluster': @cluster_name,
|
34
|
+
'service-name': service_name,
|
35
|
+
'desired-status': 'RUNNING'
|
36
|
+
}
|
37
|
+
exec('list-tasks', args)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [Array] tasks
|
41
|
+
# @return [Hash]
|
42
|
+
def describe_tasks(tasks)
|
43
|
+
args = {
|
44
|
+
'cluster': @cluster_name,
|
45
|
+
'tasks': tasks.join(' ')
|
46
|
+
}
|
47
|
+
exec('describe-tasks', args)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [String] task_definition
|
51
|
+
# @return [Hash]
|
52
|
+
def describe_task_definition(task_definition)
|
53
|
+
args = {
|
54
|
+
'task-definition': task_definition
|
55
|
+
}
|
56
|
+
exec('describe-task-definition', args)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param [String] service_name
|
60
|
+
# @return [Hash]
|
61
|
+
def describe_services(service_name)
|
62
|
+
args = {
|
63
|
+
'cluster': @cluster_name,
|
64
|
+
'services': service_name
|
65
|
+
}
|
66
|
+
exec('describe-services', args)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param [String] family_name
|
70
|
+
# @param [Hash] container_definitions
|
71
|
+
# @return [Hash]
|
72
|
+
def register_task_definition(family_name, container_definitions)
|
73
|
+
args = {
|
74
|
+
'family': family_name,
|
75
|
+
'container-definitions': '"' + Oj.dump(container_definitions).gsub('"', '\\"') + '"'
|
76
|
+
}
|
77
|
+
exec('register-task-definition', args)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
# @param [String] command
|
82
|
+
# @param [Hash] args
|
83
|
+
# @return [Hash]
|
84
|
+
def exec(command, args)
|
85
|
+
arg = ''
|
86
|
+
args.each do |name, value|
|
87
|
+
arg << "--#{name} #{value} "
|
88
|
+
end
|
89
|
+
|
90
|
+
arg << "--profile #{@options[:profile]} " if @options.has_key?(:profile)
|
91
|
+
arg << "--region #{@options[:region]} " if @options.has_key?(:region)
|
92
|
+
|
93
|
+
command = "aws ecs #{command} #{arg}"
|
94
|
+
result = @runtime.exec(command)
|
95
|
+
|
96
|
+
raise EcsCommandError.new unless result.buffered_stderr.empty?
|
97
|
+
|
98
|
+
result = result.buffered_stdout
|
99
|
+
Oj.load(result)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class EcsCommandError < RuntimeError; end
|
104
|
+
end
|
data/lib/ecs_deployer.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'ecs_deployer/version'
|
2
|
+
require 'ecs_deployer/client'
|
3
|
+
require 'ecs_deployer/error'
|
4
|
+
|
5
|
+
module EcsDeployer
|
6
|
+
class ServiceNotFoundError < EcsDeployer::Error; end
|
7
|
+
class TaskNotFoundError < EcsDeployer::Error; end
|
8
|
+
class DeployTimeoutError < EcsDeployer::Error; end
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ecs_deployer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- naomichi-y
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: runtime_command
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: oj
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.14'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: This package provides the service deployment function of ECS.
|
84
|
+
email:
|
85
|
+
- n.yamakita@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- Gemfile
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- bin/console
|
97
|
+
- bin/setup
|
98
|
+
- ecs_deployer.gemspec
|
99
|
+
- example/fixtures/task.yml
|
100
|
+
- example/sample.rb
|
101
|
+
- lib/ecs_deployer.rb
|
102
|
+
- lib/ecs_deployer/client.rb
|
103
|
+
- lib/ecs_deployer/commander.rb
|
104
|
+
- lib/ecs_deployer/error.rb
|
105
|
+
- lib/ecs_deployer/version.rb
|
106
|
+
homepage: ''
|
107
|
+
licenses: []
|
108
|
+
metadata: {}
|
109
|
+
post_install_message:
|
110
|
+
rdoc_options: []
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.6.10
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Deploy application to ECS.
|
129
|
+
test_files: []
|