ecs_compose 0.1.0.pre7 → 0.1.0.pre8
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 +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
|