ecs_compose 0.1.0.pre7 → 0.1.0.pre8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ecs_compose.gemspec +1 -1
- data/exe/ecs-compose +182 -2
- data/lib/ecs_compose/task_definition.rb +40 -1
- metadata +5 -6
- data/lib/ecs_compose/cli.rb +0 -125
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1650cd5ac69bc7a05d43dfb153cecd2a399c3d6
|
4
|
+
data.tar.gz: 2ec1eabb033e0b12c7cb7a4c16b1b945913cf2cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7528f64aac476b999b960c3baeaa93e86f9e17422e84e4a30c2e1d0f6fe8e5395b89948cdbfa8076bc94aed51e2d8d0c15d8d12cb2e3c55cd131688061483a3
|
7
|
+
data.tar.gz: 538236cee418cd6cb01a26dfded1d813a5dab847a63e03776bb34783c23123a9c9535c58f8b2028581f42670bb4d9d8e228e82b1d661a9ee1c707115f56a13c9
|
data/ecs_compose.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
|
-
spec.add_dependency "
|
30
|
+
spec.add_dependency "docopt", "~> 0.5.0"
|
31
31
|
spec.add_dependency "colorize", "~> 0.7.7"
|
32
32
|
|
33
33
|
spec.add_development_dependency "bundler", "~> 1.10"
|
data/exe/ecs-compose
CHANGED
@@ -1,5 +1,185 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "docopt"
|
4
|
+
require "ecs_compose"
|
4
5
|
|
5
|
-
|
6
|
+
USAGE = <<DOCOPT
|
7
|
+
ecs-compose - Like docker-compose, but for AWS EC2 Container Service
|
8
|
+
|
9
|
+
Usage:
|
10
|
+
ecs-compose --help
|
11
|
+
ecs-compose --version
|
12
|
+
ecs-compose [options] register [<task_def>...]
|
13
|
+
ecs-compose [options] up [<service>...]
|
14
|
+
ecs-compose [options] run [-e <name>=<value>]... [--entrypoint <entrypoint>] <task> [--] [<arg>...]
|
15
|
+
ecs-compose [options] json [<task_def>]
|
16
|
+
|
17
|
+
Options:
|
18
|
+
-h, --help Show this help message
|
19
|
+
--version Print the version of this program
|
20
|
+
-m, --manifest <manifest>
|
21
|
+
Path to an ecs-compose manifest (defaults to
|
22
|
+
deploy/DEPLOY-MANIFEST.yml, takes precedence over --file)
|
23
|
+
-f, --file <file> Path to docker-compose.yml file (defaults to
|
24
|
+
docker-compose.yml)
|
25
|
+
-i, --file-info (task:<name> | service:<name>)
|
26
|
+
Specify the task type and task name for a
|
27
|
+
docker-compose.yml file
|
28
|
+
-e <name>=<value> Set an environment variable
|
29
|
+
--entrypoint <entrypoint>
|
30
|
+
Override the container's regular entrypoint
|
31
|
+
|
32
|
+
Commands:
|
33
|
+
register Registers the specified ECS task definitions (defaults to all)
|
34
|
+
up Updates the specified ECS services (defaults to all)
|
35
|
+
run Runs the specified task
|
36
|
+
json Generate JSON for a specific task definition
|
37
|
+
DOCOPT
|
38
|
+
|
39
|
+
# Our command-line application. Parses our rather complicated arguments,
|
40
|
+
# then delegates all the real work to our usual internal classes.
|
41
|
+
class App
|
42
|
+
include EcsCompose
|
43
|
+
|
44
|
+
DEFAULT_FILE = "docker-compose.yml"
|
45
|
+
DEFAULT_MANIFEST = "deploy/DEPLOY-MANIFEST.yml"
|
46
|
+
|
47
|
+
attr_reader :options, :manifest
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@options = Docopt::docopt(USAGE, version: EcsCompose::VERSION)
|
51
|
+
@manifest = load_manifest()
|
52
|
+
|
53
|
+
# Uncomment to dump the docopt parser output, which can be non-obvious.
|
54
|
+
#require "pp"
|
55
|
+
#pp(options)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Figure out which subcommand was chosen, and run it.
|
59
|
+
def run
|
60
|
+
for command in %w{register up run json}
|
61
|
+
if options.fetch(command)
|
62
|
+
send("command_#{command}")
|
63
|
+
return
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# We shouldn't ever get here.
|
67
|
+
raise "Unknown command: #{command}"
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
def command_register
|
73
|
+
available = manifest.task_definitions
|
74
|
+
chosen = all_or_specified(available, options.fetch('<task_def>'))
|
75
|
+
chosen.each {|td| td.register }
|
76
|
+
end
|
77
|
+
|
78
|
+
def command_up
|
79
|
+
available = manifest.task_definitions.select {|td| td.type == :service }
|
80
|
+
chosen = all_or_specified(available, options.fetch('<service>'))
|
81
|
+
chosen.each {|td| td.up }
|
82
|
+
end
|
83
|
+
|
84
|
+
def command_run
|
85
|
+
available = manifest.task_definitions.select {|td| td.type == :task }
|
86
|
+
task_name = options.fetch('<task>')
|
87
|
+
task = available.find {|td| td.name == task_name } or
|
88
|
+
fatal_err("Cannot find task '#{task_name}'")
|
89
|
+
|
90
|
+
env = options.fetch('-e').flatten.inject({}) do |hsh, e_opt|
|
91
|
+
e_opt =~ /\A([^=]+)=(.*)/ or
|
92
|
+
fatal_err "Can't parse '-e #{e_opt}'"
|
93
|
+
hsh[$1] = $2
|
94
|
+
hsh
|
95
|
+
end
|
96
|
+
|
97
|
+
command = options.fetch('<arg>')
|
98
|
+
command = nil if command.empty?
|
99
|
+
|
100
|
+
task.run(environment: env,
|
101
|
+
entrypoint: options.fetch('--entrypoint').flatten[0],
|
102
|
+
command: command)
|
103
|
+
end
|
104
|
+
|
105
|
+
def command_json
|
106
|
+
task_definition = options.fetch('<task_def>')[0]
|
107
|
+
if task_definition.nil?
|
108
|
+
choices = manifest.task_definitions.map {|td| td.name }
|
109
|
+
case choices.length
|
110
|
+
when 0
|
111
|
+
fatal_err("Please supply a manifest with at least one task definition")
|
112
|
+
when 1
|
113
|
+
task_definition = choices.first
|
114
|
+
else
|
115
|
+
fatal_err("Please choose one of: #{choices.join(', ')}")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
found = manifest.task_definitions.find {|td| td.name } or
|
120
|
+
fatal_err("Can't find task definition: #{task_definition}")
|
121
|
+
puts found.to_json
|
122
|
+
end
|
123
|
+
|
124
|
+
# Choose either all items in `available`, or just those with the
|
125
|
+
# specified `names`.
|
126
|
+
def all_or_specified(available, names)
|
127
|
+
if names.empty?
|
128
|
+
available
|
129
|
+
else
|
130
|
+
available.select {|td| names.include?(td.name) }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Figure out whether we have a manifest or a docker-compose.yml. We
|
135
|
+
# check supplied flags first, then defaults, and we prefer manifests
|
136
|
+
# when there's a tie.
|
137
|
+
def mode
|
138
|
+
if options.fetch('--manifest')
|
139
|
+
:manifest
|
140
|
+
elsif options.fetch('--file')
|
141
|
+
:file
|
142
|
+
elsif File.exist?(DEFAULT_MANIFEST)
|
143
|
+
:manifest
|
144
|
+
elsif File.exist?(DEFAULT_FILE)
|
145
|
+
:file
|
146
|
+
else
|
147
|
+
fatal_err("Unable to find either #{DEFAULT_FILE} or #{DEFAULT_MANIFEST}")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Create a manifest, either by reading it in, or synthesizing it from a
|
152
|
+
# `docker-compose.yml` file and some extra arguments.
|
153
|
+
def load_manifest
|
154
|
+
case mode
|
155
|
+
when :manifest
|
156
|
+
Manifest.read_from_manifest(options.fetch('--manifest') || DEFAULT_MANIFEST)
|
157
|
+
when :file
|
158
|
+
info = options.fetch('--file-info')
|
159
|
+
if info.nil?
|
160
|
+
fatal_err("Must pass -i option when using docker-compose.yml")
|
161
|
+
end
|
162
|
+
unless info =~ /\A(service|task):([-_A-Za-z0-9\z]+)/
|
163
|
+
fatal_err("Incorrectly formatted -i option")
|
164
|
+
end
|
165
|
+
type, name = $1, $2
|
166
|
+
Manifest.read_from_file(options.fetch('--file') || DEFAULT_FILE,
|
167
|
+
type.to_sym, name)
|
168
|
+
else raise "Unknown mode: #{mode}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Print an error and quit.
|
173
|
+
def fatal_err(msg)
|
174
|
+
STDERR.puts(msg.red)
|
175
|
+
exit(1)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Run our application.
|
180
|
+
begin
|
181
|
+
App.new.run
|
182
|
+
rescue Docopt::Exit => e
|
183
|
+
puts e.message
|
184
|
+
exit(1)
|
185
|
+
end
|
@@ -1,13 +1,52 @@
|
|
1
1
|
module EcsCompose
|
2
2
|
|
3
|
-
# Information required to create an ECS task definition
|
3
|
+
# Information required to create an ECS task definition, and commands to
|
4
|
+
# act upon it.
|
4
5
|
class TaskDefinition
|
5
6
|
attr_reader :type, :name, :yaml
|
6
7
|
|
8
|
+
# Create a new TaskDefinition. The type should be either `:task` or
|
9
|
+
# `:service`, the name should be both the name of the ECS task
|
10
|
+
# definition and the corresponding ECS service (if any), and the YAML
|
11
|
+
# should be `docker-compose`-format YAML describing the containers
|
12
|
+
# associated with this task.
|
7
13
|
def initialize(type, name, yaml)
|
8
14
|
@name = name
|
9
15
|
@type = type
|
10
16
|
@yaml = yaml
|
11
17
|
end
|
18
|
+
|
19
|
+
# Register this task definition with ECS. Will create the task
|
20
|
+
# definition if it doesn't exist, and add a new version of the task.
|
21
|
+
def register
|
22
|
+
EcsCompose::Ecs.register_task_definition(to_json)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Register this task definition with ECS, and update the corresponding
|
26
|
+
# service.
|
27
|
+
def up
|
28
|
+
EcsCompose::Ecs.update_service_with_json(name, to_json)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Run this task definition as a one-shot ECS task, with the specified
|
32
|
+
# overrides.
|
33
|
+
def run(environment: {}, entrypoint: nil, command: nil)
|
34
|
+
puts "environment: #{environment.inspect}"
|
35
|
+
puts "entrypoint: #{entrypoint.inspect}"
|
36
|
+
puts "command: #{command.inspect}"
|
37
|
+
raise "Not yet implemented"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Generate ECS task definition JSON for this instance.
|
41
|
+
def to_json
|
42
|
+
json_generator.json
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
# Return a JSON generator for this task.
|
48
|
+
def json_generator
|
49
|
+
@json_generator ||= EcsCompose::JsonGenerator.new(name, yaml)
|
50
|
+
end
|
12
51
|
end
|
13
52
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecs_compose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.pre8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Kidd
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: docopt
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.5.0
|
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: 0.
|
26
|
+
version: 0.5.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: colorize
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,7 +104,6 @@ files:
|
|
104
104
|
- exe/ecs-compose
|
105
105
|
- go-publish.sh
|
106
106
|
- lib/ecs_compose.rb
|
107
|
-
- lib/ecs_compose/cli.rb
|
108
107
|
- lib/ecs_compose/ecs.rb
|
109
108
|
- lib/ecs_compose/json_generator.rb
|
110
109
|
- lib/ecs_compose/manifest.rb
|
data/lib/ecs_compose/cli.rb
DELETED
@@ -1,125 +0,0 @@
|
|
1
|
-
require "ecs_compose"
|
2
|
-
require "thor"
|
3
|
-
|
4
|
-
module EcsCompose
|
5
|
-
# Our basic command-line interface.
|
6
|
-
class CLI < Thor
|
7
|
-
DEFAULT_FILE = "docker-compose.yml"
|
8
|
-
DEFAULT_MANIFEST = "deploy/DEPLOY-MANIFEST.yml"
|
9
|
-
|
10
|
-
# Force status 1 on exit. See: https://github.com/erikhuda/thor/issues/244
|
11
|
-
def self.exit_on_failure?
|
12
|
-
true
|
13
|
-
end
|
14
|
-
|
15
|
-
class_option(:manifest, type: :string,
|
16
|
-
aliases: %w(-m),
|
17
|
-
desc: "Manifest describing a set of tasks and services (takes precedence over --file) [default: #{DEFAULT_MANIFEST}]")
|
18
|
-
class_option(:file, type: :string,
|
19
|
-
aliases: %w(-f),
|
20
|
-
desc: "File describing a single task or service [default: #{DEFAULT_FILE}]")
|
21
|
-
class_option(:file_info, type: :string,
|
22
|
-
aliases: %w(-i),
|
23
|
-
desc: "Type and name for use with --file [ex: 'service:hello' or 'task:migrate']")
|
24
|
-
|
25
|
-
desc("up [SERVICES...]", "Register ECS task definitions and update services")
|
26
|
-
def up(*services)
|
27
|
-
available = manifest.task_definitions.select {|td| td.type == :service }
|
28
|
-
chosen = all_or_specified(available, services)
|
29
|
-
|
30
|
-
chosen.each do |service|
|
31
|
-
json = EcsCompose::JsonGenerator.new(service.name, service.yaml).json
|
32
|
-
EcsCompose::Ecs.update_service_with_json(service.name, json)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
desc("register [TASK_DEFINITIONS...]", "Register ECS task definitions")
|
37
|
-
def register(*task_definitions)
|
38
|
-
available = manifest.task_definitions
|
39
|
-
chosen = all_or_specified(available, task_definitions)
|
40
|
-
|
41
|
-
chosen.each do |td|
|
42
|
-
json = EcsCompose::JsonGenerator.new(td.name, td.yaml).json
|
43
|
-
EcsCompose::Ecs.register_task_definition(json)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
desc("json [TASK_DEFINITION]",
|
48
|
-
"Convert a task definition to ECS JSON format")
|
49
|
-
def json(task_definition=nil)
|
50
|
-
if task_definition.nil?
|
51
|
-
choices = manifest.task_definitions.map {|td| td.name }
|
52
|
-
case choices.length
|
53
|
-
when 0
|
54
|
-
fatal_err("Please supply a manifest with at least one task definition")
|
55
|
-
when 1
|
56
|
-
task_definition = choices.first
|
57
|
-
else
|
58
|
-
fatal_err("Please choose one of: #{choices.join(', ')}")
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
found = manifest.task_definitions.find {|td| td.name } or
|
63
|
-
fatal_err("Can't find task definition: #{task_definition}")
|
64
|
-
puts EcsCompose::JsonGenerator.new(found.name, found.yaml).json
|
65
|
-
end
|
66
|
-
|
67
|
-
protected
|
68
|
-
|
69
|
-
# Choose either all items in `available`, or just those with the
|
70
|
-
# specified `names`.
|
71
|
-
def all_or_specified(available, names)
|
72
|
-
if names.empty?
|
73
|
-
available
|
74
|
-
else
|
75
|
-
available.select {|td| names.include?(td.name) }
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Figure out whether we have a manifest or a docker-compose.yml. We
|
80
|
-
# check supplied flags first, then defaults, and we prefer manifests
|
81
|
-
# when there's a tie.
|
82
|
-
def mode
|
83
|
-
@mode ||=
|
84
|
-
if options.manifest
|
85
|
-
:manifest
|
86
|
-
elsif options.file
|
87
|
-
:file
|
88
|
-
elsif File.exist?(DEFAULT_MANIFEST)
|
89
|
-
:manifest
|
90
|
-
elsif File.exist?(DEFAULT_FILE)
|
91
|
-
:file
|
92
|
-
else
|
93
|
-
fatal_err("Unable to find either #{DEFAULT_FILE} or #{DEFAULT_MANIFEST}")
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# Create a manifest, either by reading it in, or synthesizing it from a
|
98
|
-
# `docker-compose.yml` file and some extra arguments.
|
99
|
-
def manifest
|
100
|
-
@manifest ||=
|
101
|
-
case mode
|
102
|
-
when :manifest
|
103
|
-
Manifest.read_from_manifest(options.manifest || DEFAULT_MANIFEST)
|
104
|
-
when :file
|
105
|
-
info = options.file_info
|
106
|
-
if info.nil?
|
107
|
-
fatal_err("Must pass -i option when using docker-compose.yml")
|
108
|
-
end
|
109
|
-
unless info =~ /\A(service|task):([-_A-Za-z0-9\z]+)/
|
110
|
-
fatal_err("Incorrectly formatted -i option")
|
111
|
-
end
|
112
|
-
type, name = $1, $2
|
113
|
-
Manifest.read_from_file(options.file || DEFAULT_FILE,
|
114
|
-
type.to_sym, name)
|
115
|
-
else raise "Unknown mode: #{mode}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Print an error and quit.
|
120
|
-
def fatal_err(msg)
|
121
|
-
STDERR.puts(msg.red)
|
122
|
-
exit(1)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|