ecs_deploy_cli 0.1.0 → 0.2.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 +4 -4
- data/lib/ecs_deploy_cli.rb +4 -1
- data/lib/ecs_deploy_cli/cli.rb +29 -1
- data/lib/ecs_deploy_cli/dsl/auto_options.rb +17 -1
- data/lib/ecs_deploy_cli/dsl/container.rb +4 -10
- data/lib/ecs_deploy_cli/dsl/parser.rb +3 -3
- data/lib/ecs_deploy_cli/dsl/task.rb +2 -0
- data/lib/ecs_deploy_cli/runner.rb +20 -124
- data/lib/ecs_deploy_cli/runners/base.rb +54 -0
- data/lib/ecs_deploy_cli/runners/diff.rb +44 -0
- data/lib/ecs_deploy_cli/runners/run_task.rb +27 -0
- data/lib/ecs_deploy_cli/runners/ssh.rb +45 -0
- data/lib/ecs_deploy_cli/runners/update_crons.rb +41 -0
- data/lib/ecs_deploy_cli/runners/update_services.rb +55 -0
- data/lib/ecs_deploy_cli/runners/validate.rb +47 -0
- data/lib/ecs_deploy_cli/version.rb +3 -1
- data/spec/ecs_deploy_cli/cli_spec.rb +36 -4
- data/spec/ecs_deploy_cli/dsl/container_spec.rb +4 -4
- data/spec/ecs_deploy_cli/dsl/task_spec.rb +2 -0
- data/spec/ecs_deploy_cli/runner_spec.rb +94 -15
- data/spec/support/ECSFile.minimal +12 -0
- metadata +41 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de429c09d039ddd636e85d23ec40bc20d7e91a09af10fefcc4124086494a8127
|
4
|
+
data.tar.gz: 57e24e217b08edf956a8b43894486a6db702756fedd2016b13e635fa9f5c2f3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cecc078f5afcd85d0ca3600fd8d7db360dbf99d3cf27901aca2b15f15500864cc860de616b4bcf49bc29024b0301e3a6af41bb74808e1a94e216d45f1076346c
|
7
|
+
data.tar.gz: 4c66c16a9948dd255ca23f626341a3b052fda205297e71f8ffe185d4e3add1cca7dfe18e7d69800c735db55c606db3275a08a41f63bdafae9eba7a0a88de0abe
|
data/lib/ecs_deploy_cli.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
require 'logger'
|
3
5
|
require 'thor'
|
4
6
|
require 'aws-sdk-ecs'
|
5
7
|
require 'active_support/core_ext/hash/indifferent_access'
|
8
|
+
require 'active_support/concern'
|
6
9
|
|
7
10
|
module EcsDeployCli
|
8
11
|
def self.logger
|
9
12
|
@logger ||= begin
|
10
13
|
logger = Logger.new(STDOUT)
|
11
|
-
logger.formatter = proc { |
|
14
|
+
logger.formatter = proc { |_severity, _datetime, _progname, msg|
|
12
15
|
"#{msg}\n"
|
13
16
|
}
|
14
17
|
logger.level = Logger::INFO
|
data/lib/ecs_deploy_cli/cli.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module EcsDeployCli
|
2
4
|
class CLI < Thor
|
5
|
+
def self.exit_on_failure?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
3
9
|
desc 'validate', 'Validates your ECSFile'
|
4
10
|
option :file, default: 'ECSFile'
|
5
11
|
def validate
|
@@ -8,6 +14,13 @@ module EcsDeployCli
|
|
8
14
|
puts 'Your ECSFile looks fine! 🎉'
|
9
15
|
end
|
10
16
|
|
17
|
+
desc 'diff', 'Check differences between task definitions'
|
18
|
+
option :file, default: 'ECSFile'
|
19
|
+
def diff
|
20
|
+
@parser = load(options[:file])
|
21
|
+
runner.diff
|
22
|
+
end
|
23
|
+
|
11
24
|
desc 'version', 'Updates all services defined in your ECSFile'
|
12
25
|
def version
|
13
26
|
puts "ECS Deploy CLI Version #{EcsDeployCli::VERSION}."
|
@@ -29,7 +42,7 @@ module EcsDeployCli
|
|
29
42
|
runner.update_services! timeout: options[:timeout], service: options[:only]
|
30
43
|
end
|
31
44
|
|
32
|
-
desc 'deploy', 'Updates
|
45
|
+
desc 'deploy', 'Updates all services and scheduled tasks at once'
|
33
46
|
option :file, default: 'ECSFile'
|
34
47
|
option :timeout, type: :numeric, default: 500
|
35
48
|
def deploy
|
@@ -38,6 +51,21 @@ module EcsDeployCli
|
|
38
51
|
runner.update_crons!
|
39
52
|
end
|
40
53
|
|
54
|
+
desc 'run-task NAME', 'Manually runs a task defined in your ECSFile'
|
55
|
+
option :launch_type, default: 'FARGATE'
|
56
|
+
option :security_groups, default: '', type: :string
|
57
|
+
option :subnets, required: true, type: :string
|
58
|
+
option :file, default: 'ECSFile'
|
59
|
+
def run_task(task_name)
|
60
|
+
@parser = load(options[:file])
|
61
|
+
runner.run_task!(
|
62
|
+
task_name,
|
63
|
+
launch_type: options[:launch_type],
|
64
|
+
security_groups: options[:security_groups].split(','),
|
65
|
+
subnets: options[:subnets].split(',')
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
41
69
|
desc 'ssh', 'Connects to ECS instance via SSH'
|
42
70
|
option :file, default: 'ECSFile'
|
43
71
|
def ssh
|
@@ -1,9 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module EcsDeployCli
|
2
4
|
module DSL
|
3
5
|
module AutoOptions
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def allowed_options(*value)
|
10
|
+
@allowed_options = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def _allowed_options
|
14
|
+
@allowed_options ||= []
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
4
18
|
def method_missing(name, *args, &block)
|
5
19
|
if args.count == 1 && !block
|
6
|
-
|
20
|
+
unless self.class._allowed_options.include?(name)
|
21
|
+
EcsDeployCli.logger.info("Used unhandled option #{name.to_sym} = #{args.first} in #{self.class.name}")
|
22
|
+
end
|
7
23
|
_options[name.to_sym] = args.first
|
8
24
|
else
|
9
25
|
super
|
@@ -5,25 +5,23 @@ module EcsDeployCli
|
|
5
5
|
class Container
|
6
6
|
include AutoOptions
|
7
7
|
|
8
|
+
allowed_options :image, :cpu, :working_directory
|
9
|
+
|
8
10
|
def initialize(name, config)
|
9
11
|
@config = config
|
10
12
|
_options[:name] = name.to_s
|
11
13
|
end
|
12
14
|
|
13
|
-
def image(value)
|
14
|
-
_options[:image] = value
|
15
|
-
end
|
16
|
-
|
17
15
|
def command(*command)
|
18
16
|
_options[:command] = command
|
19
17
|
end
|
20
18
|
|
21
19
|
def load_envs(name)
|
22
|
-
_options[:environment] = (_options[:environment] || []) + YAML.safe_load(File.open(name))
|
20
|
+
_options[:environment] = (_options[:environment] || []) + YAML.safe_load(File.open(name), symbolize_names: true)
|
23
21
|
end
|
24
22
|
|
25
23
|
def env(key:, value:)
|
26
|
-
(_options[:environment] ||= []) << {
|
24
|
+
(_options[:environment] ||= []) << { name: key, value: value }
|
27
25
|
end
|
28
26
|
|
29
27
|
def secret(key:, value:)
|
@@ -34,10 +32,6 @@ module EcsDeployCli
|
|
34
32
|
(_options[:port_mappings] ||= []) << options
|
35
33
|
end
|
36
34
|
|
37
|
-
def cpu(value)
|
38
|
-
_options[:cpu] = value
|
39
|
-
end
|
40
|
-
|
41
35
|
def memory(limit:, reservation:)
|
42
36
|
_options[:memory] = limit
|
43
37
|
_options[:memory_reservation] = reservation
|
@@ -55,9 +55,9 @@ module EcsDeployCli
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def resolve
|
58
|
-
resolved_containers = @containers.transform_values(&:as_definition)
|
59
|
-
resolved_tasks = @tasks.transform_values { |t| t.as_definition(resolved_containers) }
|
60
|
-
resolved_crons = @crons.transform_values { |t| t.as_definition(resolved_tasks) }
|
58
|
+
resolved_containers = (@containers || {}).transform_values(&:as_definition)
|
59
|
+
resolved_tasks = (@tasks || {}).transform_values { |t| t.as_definition(resolved_containers) }
|
60
|
+
resolved_crons = (@crons || {}).transform_values { |t| t.as_definition(resolved_tasks) }
|
61
61
|
[@services, resolved_tasks, resolved_crons]
|
62
62
|
end
|
63
63
|
|
@@ -1,3 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ecs_deploy_cli/runners/base'
|
4
|
+
require 'ecs_deploy_cli/runners/ssh'
|
5
|
+
require 'ecs_deploy_cli/runners/validate'
|
6
|
+
require 'ecs_deploy_cli/runners/diff'
|
7
|
+
require 'ecs_deploy_cli/runners/update_crons'
|
8
|
+
require 'ecs_deploy_cli/runners/update_services'
|
9
|
+
require 'ecs_deploy_cli/runners/run_task'
|
10
|
+
|
1
11
|
module EcsDeployCli
|
2
12
|
class Runner
|
3
13
|
def initialize(parser)
|
@@ -5,149 +15,35 @@ module EcsDeployCli
|
|
5
15
|
end
|
6
16
|
|
7
17
|
def validate!
|
8
|
-
@parser.
|
18
|
+
EcsDeployCli::Runners::Validate.new(@parser).run!
|
9
19
|
end
|
10
20
|
|
11
21
|
def update_crons!
|
12
|
-
|
13
|
-
|
14
|
-
crons.each do |cron_name, cron_definition|
|
15
|
-
task_definition = tasks[cron_definition[:task_name]]
|
16
|
-
raise "Undefined task #{cron_definition[:task_name].inspect} in (#{tasks.keys.inspect})" unless task_definition
|
17
|
-
|
18
|
-
updated_task = _update_task(task_definition)
|
19
|
-
|
20
|
-
current_target = cwe_client.list_targets_by_rule(
|
21
|
-
{
|
22
|
-
rule: cron_name,
|
23
|
-
limit: 1
|
24
|
-
}
|
25
|
-
).to_h[:targets].first
|
26
|
-
|
27
|
-
cwe_client.put_rule(
|
28
|
-
cron_definition[:rule]
|
29
|
-
)
|
22
|
+
EcsDeployCli::Runners::UpdateCrons.new(@parser).run!
|
23
|
+
end
|
30
24
|
|
31
|
-
|
32
|
-
|
33
|
-
targets: [
|
34
|
-
id: current_target[:id],
|
35
|
-
arn: current_target[:arn],
|
36
|
-
role_arn: current_target[:role_arn],
|
37
|
-
input: cron_definition[:input].to_json,
|
38
|
-
ecs_parameters: cron_definition[:ecs_parameters].merge(task_definition_arn: updated_task[:task_definition_arn])
|
39
|
-
]
|
40
|
-
)
|
41
|
-
EcsDeployCli.logger.info "Deployed scheduled task \"#{cron_name}\"!"
|
42
|
-
end
|
25
|
+
def run_task!(task_name, launch_type:, security_groups:, subnets:)
|
26
|
+
EcsDeployCli::Runners::RunTask.new(@parser).run!(task_name, launch_type: launch_type, security_groups: security_groups, subnets: subnets)
|
43
27
|
end
|
44
28
|
|
45
29
|
def ssh
|
46
|
-
|
47
|
-
|
48
|
-
).to_h[:container_instance_arns]
|
49
|
-
|
50
|
-
response = ecs_client.describe_container_instances(
|
51
|
-
cluster: config[:cluster],
|
52
|
-
container_instances: instances
|
53
|
-
)
|
54
|
-
|
55
|
-
EcsDeployCli.logger.info "Found instances: #{response.container_instances.map(&:ec2_instance_id).join(', ')}"
|
56
|
-
|
57
|
-
response = ec2_client.describe_instances(
|
58
|
-
instance_ids: response.container_instances.map(&:ec2_instance_id)
|
59
|
-
)
|
60
|
-
|
61
|
-
dns_name = response.reservations[0].instances[0].public_dns_name
|
62
|
-
EcsDeployCli.logger.info "Connecting to ec2-user@#{dns_name}..."
|
30
|
+
EcsDeployCli::Runners::SSH.new(@parser).run!
|
31
|
+
end
|
63
32
|
|
64
|
-
|
65
|
-
|
33
|
+
def diff
|
34
|
+
EcsDeployCli::Runners::Diff.new(@parser).run!
|
66
35
|
end
|
67
36
|
|
68
37
|
def update_services!(service: nil, timeout: 500)
|
69
|
-
|
70
|
-
|
71
|
-
services.each do |service_name, service_definition|
|
72
|
-
next if !service.nil? && service != service_name
|
73
|
-
|
74
|
-
task_definition = _update_task resolved_tasks[service_definition.options[:task]]
|
75
|
-
task_name = "#{task_definition[:family]}:#{task_definition[:revision]}"
|
76
|
-
|
77
|
-
ecs_client.update_service(
|
78
|
-
cluster: config[:cluster],
|
79
|
-
service: service_name,
|
80
|
-
task_definition: "#{task_definition[:family]}:#{task_name}"
|
81
|
-
)
|
82
|
-
wait_for_deploy(service_name, task_name, timeout: timeout)
|
83
|
-
EcsDeployCli.logger.info "Deployed service \"#{service_name}\"!"
|
84
|
-
end
|
38
|
+
EcsDeployCli::Runners::UpdateServices.new(@parser).run!(service: service, timeout: timeout)
|
85
39
|
end
|
86
40
|
|
87
41
|
private
|
88
42
|
|
89
|
-
def wait_for_deploy(service_name, task_name, timeout:)
|
90
|
-
wait_data = { cluster: config[:cluster], services: [service_name] }
|
91
|
-
|
92
|
-
started_at = Time.now
|
93
|
-
ecs_client.wait_until(
|
94
|
-
:services_stable, wait_data,
|
95
|
-
max_attempts: nil,
|
96
|
-
before_wait: lambda { |_, response|
|
97
|
-
deployments = response.services.first.deployments
|
98
|
-
log_deployments task_name, deployments
|
99
|
-
|
100
|
-
throw :success if deployments.count == 1 && deployments[0].task_definition.end_with?(task_name)
|
101
|
-
throw :failure if Time.now - started_at > timeout
|
102
|
-
}
|
103
|
-
)
|
104
|
-
end
|
105
|
-
|
106
43
|
def _update_task(definition)
|
107
44
|
ecs_client.register_task_definition(
|
108
45
|
definition
|
109
46
|
).to_h[:task_definition]
|
110
47
|
end
|
111
|
-
|
112
|
-
def log_deployments(task_name, deployments)
|
113
|
-
EcsDeployCli.logger.info "Waiting for task: #{task_name} to become ok."
|
114
|
-
EcsDeployCli.logger.info 'Deployment status:'
|
115
|
-
deployments.each do |deploy|
|
116
|
-
EcsDeployCli.logger.info "[#{deploy.status}] task=#{deploy.task_definition.split('/').last}, "\
|
117
|
-
"desired_count=#{deploy.desired_count}, pending_count=#{deploy.pending_count}, running_count=#{deploy.running_count}, failed_tasks=#{deploy.failed_tasks}"
|
118
|
-
end
|
119
|
-
EcsDeployCli.logger.info ''
|
120
|
-
end
|
121
|
-
|
122
|
-
def ec2_client
|
123
|
-
@ec2_client ||= begin
|
124
|
-
require 'aws-sdk-ec2'
|
125
|
-
Aws::EC2::Client.new(
|
126
|
-
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
127
|
-
region: config[:aws_region]
|
128
|
-
)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def ecs_client
|
133
|
-
@ecs_client ||= Aws::ECS::Client.new(
|
134
|
-
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
135
|
-
region: config[:aws_region]
|
136
|
-
)
|
137
|
-
end
|
138
|
-
|
139
|
-
def cwe_client
|
140
|
-
@cwe_client ||= begin
|
141
|
-
require 'aws-sdk-cloudwatchevents'
|
142
|
-
Aws::CloudWatchEvents::Client.new(
|
143
|
-
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
144
|
-
region: config[:aws_region]
|
145
|
-
)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def config
|
150
|
-
@parser.config
|
151
|
-
end
|
152
48
|
end
|
153
49
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class Base
|
6
|
+
def initialize(parser)
|
7
|
+
@parser = parser
|
8
|
+
end
|
9
|
+
|
10
|
+
def run!
|
11
|
+
raise NotImplementedError, 'abstract method'
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def _update_task(definition)
|
17
|
+
ecs_client.register_task_definition(
|
18
|
+
definition
|
19
|
+
).to_h[:task_definition]
|
20
|
+
end
|
21
|
+
|
22
|
+
def ec2_client
|
23
|
+
@ec2_client ||= begin
|
24
|
+
require 'aws-sdk-ec2'
|
25
|
+
Aws::EC2::Client.new(
|
26
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
27
|
+
region: config[:aws_region]
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ecs_client
|
33
|
+
@ecs_client ||= Aws::ECS::Client.new(
|
34
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
35
|
+
region: config[:aws_region]
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def cwe_client
|
40
|
+
@cwe_client ||= begin
|
41
|
+
require 'aws-sdk-cloudwatchevents'
|
42
|
+
Aws::CloudWatchEvents::Client.new(
|
43
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
44
|
+
region: config[:aws_region]
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def config
|
50
|
+
@parser.config
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class Diff < Base
|
6
|
+
def run!
|
7
|
+
require 'hashdiff'
|
8
|
+
require 'colorize'
|
9
|
+
|
10
|
+
_, tasks, = @parser.resolve
|
11
|
+
|
12
|
+
tasks.each do |task_name, definition|
|
13
|
+
EcsDeployCli.logger.info '---'
|
14
|
+
EcsDeployCli.logger.info "Task: #{task_name}"
|
15
|
+
|
16
|
+
result = ecs_client.describe_task_definition(task_definition: "#{task_name}").to_h
|
17
|
+
|
18
|
+
current = result[:task_definition].except(:revision, :status, :registered_at, :registered_by, :requires_attributes, :task_definition_arn)
|
19
|
+
|
20
|
+
print_diff Hashdiff.diff(current.except(:container_definitions), definition.except(:container_definitions))
|
21
|
+
|
22
|
+
current[:container_definitions].zip(definition[:container_definitions]).each do |a, b|
|
23
|
+
EcsDeployCli.logger.info "Container #{a&.dig(:name) || 'NONE'} <=> #{b&.dig(:name) || 'NONE'}"
|
24
|
+
|
25
|
+
print_diff Hashdiff.diff(a, b) if a && b
|
26
|
+
end
|
27
|
+
EcsDeployCli.logger.info '---'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def print_diff(diff)
|
32
|
+
diff.each do |(op, path, *values)|
|
33
|
+
if op == '-'
|
34
|
+
EcsDeployCli.logger.info "#{op} #{path} => #{values.join(' ')}".colorize(:red)
|
35
|
+
elsif op == '+'
|
36
|
+
EcsDeployCli.logger.info "#{op} #{path} => #{values.join(' ')}".colorize(:green)
|
37
|
+
else
|
38
|
+
EcsDeployCli.logger.info "#{op} #{path} => #{values.join(' ')}".colorize(:light_blue)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class RunTask < Base
|
6
|
+
def run!(task, launch_type:, security_groups:, subnets:)
|
7
|
+
_, tasks, = @parser.resolve
|
8
|
+
|
9
|
+
task_definition = _update_task tasks[task]
|
10
|
+
task_name = "#{task_definition[:family]}:#{task_definition[:revision]}"
|
11
|
+
|
12
|
+
ecs_client.run_task(
|
13
|
+
cluster: config[:cluster],
|
14
|
+
task_definition: task_name,
|
15
|
+
network_configuration: {
|
16
|
+
awsvpc_configuration: {
|
17
|
+
subnets: subnets,
|
18
|
+
security_groups: security_groups,
|
19
|
+
assign_public_ip: 'ENABLED'
|
20
|
+
}
|
21
|
+
},
|
22
|
+
launch_type: launch_type
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class SSH < Base
|
6
|
+
def run!
|
7
|
+
instance_ids = load_container_instances
|
8
|
+
EcsDeployCli.logger.info "Found instances: #{instance_ids.join(', ')}"
|
9
|
+
|
10
|
+
dns_name = load_dns_name_from_instance_ids(instance_ids)
|
11
|
+
run_ssh(dns_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def load_dns_name_from_instance_ids(instance_ids)
|
17
|
+
response = ec2_client.describe_instances(
|
18
|
+
instance_ids: instance_ids
|
19
|
+
)
|
20
|
+
|
21
|
+
response.reservations[0].instances[0].public_dns_name
|
22
|
+
end
|
23
|
+
|
24
|
+
def load_container_instances
|
25
|
+
instances = ecs_client.list_container_instances(
|
26
|
+
cluster: config[:cluster]
|
27
|
+
).to_h[:container_instance_arns]
|
28
|
+
|
29
|
+
response = ecs_client.describe_container_instances(
|
30
|
+
cluster: config[:cluster],
|
31
|
+
container_instances: instances
|
32
|
+
)
|
33
|
+
|
34
|
+
response.container_instances.map(&:ec2_instance_id)
|
35
|
+
end
|
36
|
+
|
37
|
+
def run_ssh(dns_name)
|
38
|
+
EcsDeployCli.logger.info "Connecting to ec2-user@#{dns_name}..."
|
39
|
+
|
40
|
+
Process.fork { exec("ssh ec2-user@#{dns_name}") }
|
41
|
+
Process.wait
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class UpdateCrons < Base
|
6
|
+
def run!
|
7
|
+
_, tasks, crons = @parser.resolve
|
8
|
+
|
9
|
+
crons.each do |cron_name, cron_definition|
|
10
|
+
task_definition = tasks[cron_definition[:task_name]]
|
11
|
+
raise "Undefined task #{cron_definition[:task_name].inspect} in (#{tasks.keys.inspect})" unless task_definition
|
12
|
+
|
13
|
+
updated_task = _update_task(task_definition)
|
14
|
+
|
15
|
+
current_target = cwe_client.list_targets_by_rule(
|
16
|
+
{
|
17
|
+
rule: cron_name,
|
18
|
+
limit: 1
|
19
|
+
}
|
20
|
+
).to_h[:targets].first
|
21
|
+
|
22
|
+
cwe_client.put_rule(
|
23
|
+
cron_definition[:rule]
|
24
|
+
)
|
25
|
+
|
26
|
+
cwe_client.put_targets(
|
27
|
+
rule: cron_name,
|
28
|
+
targets: [
|
29
|
+
id: current_target[:id],
|
30
|
+
arn: current_target[:arn],
|
31
|
+
role_arn: current_target[:role_arn],
|
32
|
+
input: cron_definition[:input].to_json,
|
33
|
+
ecs_parameters: cron_definition[:ecs_parameters].merge(task_definition_arn: updated_task[:task_definition_arn])
|
34
|
+
]
|
35
|
+
)
|
36
|
+
EcsDeployCli.logger.info "Deployed scheduled task \"#{cron_name}\"!"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class UpdateServices < Base
|
6
|
+
def run!(service: nil, timeout: 500)
|
7
|
+
services, resolved_tasks = @parser.resolve
|
8
|
+
|
9
|
+
services.each do |service_name, service_definition|
|
10
|
+
next if !service.nil? && service != service_name
|
11
|
+
|
12
|
+
task_definition = _update_task resolved_tasks[service_definition.options[:task]]
|
13
|
+
task_name = "#{task_definition[:family]}:#{task_definition[:revision]}"
|
14
|
+
|
15
|
+
ecs_client.update_service(
|
16
|
+
cluster: config[:cluster],
|
17
|
+
service: service_name,
|
18
|
+
task_definition: "#{task_definition[:family]}:#{task_name}"
|
19
|
+
)
|
20
|
+
wait_for_deploy(service_name, task_name, timeout: timeout)
|
21
|
+
EcsDeployCli.logger.info "Deployed service \"#{service_name}\"!"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def wait_for_deploy(service_name, task_name, timeout:)
|
28
|
+
wait_data = { cluster: config[:cluster], services: [service_name] }
|
29
|
+
|
30
|
+
started_at = Time.now
|
31
|
+
ecs_client.wait_until(
|
32
|
+
:services_stable, wait_data,
|
33
|
+
max_attempts: nil,
|
34
|
+
before_wait: lambda { |_, response|
|
35
|
+
deployments = response.services.first.deployments
|
36
|
+
log_deployments task_name, deployments
|
37
|
+
|
38
|
+
throw :success if deployments.count == 1 && deployments[0].task_definition.end_with?(task_name)
|
39
|
+
throw :failure if Time.now - started_at > timeout
|
40
|
+
}
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def log_deployments(task_name, deployments)
|
45
|
+
EcsDeployCli.logger.info "Waiting for task: #{task_name} to become ok."
|
46
|
+
EcsDeployCli.logger.info 'Deployment status:'
|
47
|
+
deployments.each do |deploy|
|
48
|
+
EcsDeployCli.logger.info "[#{deploy.status}] task=#{deploy.task_definition.split('/').last}, "\
|
49
|
+
"desired_count=#{deploy.desired_count}, pending_count=#{deploy.pending_count}, running_count=#{deploy.running_count}, failed_tasks=#{deploy.failed_tasks}"
|
50
|
+
end
|
51
|
+
EcsDeployCli.logger.info ''
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class Validate < Base
|
6
|
+
def run!
|
7
|
+
services, _, crons = @parser.resolve
|
8
|
+
|
9
|
+
validate_cluster!
|
10
|
+
validate_services!(services)
|
11
|
+
validate_crons!(crons)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def validate_cluster!
|
17
|
+
data = ecs_client.describe_clusters(clusters: [config[:cluster]])
|
18
|
+
|
19
|
+
raise "No such cluster #{config[:cluster]}." if data.to_h[:failures]&.any? || data.to_h[:clusters].length == 0
|
20
|
+
rescue Aws::ECS::Errors::ClusterNotFoundException
|
21
|
+
raise "No such cluster #{config[:cluster]}."
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_services!(services)
|
25
|
+
services&.each do |service_name, _|
|
26
|
+
data = ecs_client.describe_services(cluster: config[:cluster], services: [service_name])
|
27
|
+
|
28
|
+
raise "No such service #{service_name}." if data.to_h[:failures]&.any? || data.to_h[:services].length == 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_crons!(crons)
|
33
|
+
crons&.each do |cron_name, _|
|
34
|
+
items = cwe_client.list_targets_by_rule(
|
35
|
+
{
|
36
|
+
rule: cron_name,
|
37
|
+
limit: 1
|
38
|
+
}
|
39
|
+
)
|
40
|
+
raise "No such cron #{cron_name}." if items.targets.empty?
|
41
|
+
rescue Aws::CloudWatchEvents::Errors::ResourceNotFoundException
|
42
|
+
raise "No such cron #{cron_name}."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe EcsDeployCli::CLI do
|
@@ -20,33 +22,63 @@ describe EcsDeployCli::CLI do
|
|
20
22
|
expect { described_class.start(['version']) }.to output(/Version #{EcsDeployCli::VERSION}/).to_stdout
|
21
23
|
end
|
22
24
|
|
25
|
+
it 'runs diff' do
|
26
|
+
expect(runner).to receive(:diff)
|
27
|
+
described_class.no_commands do
|
28
|
+
expect_any_instance_of(described_class).to receive(:runner).at_least(:once).and_return(runner)
|
29
|
+
end
|
30
|
+
|
31
|
+
described_class.start(['diff', '--file', 'spec/support/ECSFile'])
|
32
|
+
end
|
33
|
+
|
23
34
|
it 'runs validate' do
|
35
|
+
expect(runner).to receive(:validate!)
|
36
|
+
described_class.no_commands do
|
37
|
+
expect_any_instance_of(described_class).to receive(:runner).at_least(:once).and_return(runner)
|
38
|
+
end
|
24
39
|
expect { described_class.start(['validate', '--file', 'spec/support/ECSFile']) }.to output(/Your ECSFile looks fine! 🎉/).to_stdout
|
25
40
|
end
|
26
41
|
|
42
|
+
it 'runs run-task' do
|
43
|
+
expect(runner).to receive(:run_task!)
|
44
|
+
described_class.no_commands do
|
45
|
+
expect_any_instance_of(described_class).to receive(:runner).at_least(:once).and_return(runner)
|
46
|
+
end
|
47
|
+
|
48
|
+
described_class.start(['run-task', 'yourproject', '--subnets', 'subnet-123123', '--file', 'spec/support/ECSFile'])
|
49
|
+
end
|
50
|
+
|
27
51
|
it 'runs deploy' do
|
28
52
|
expect(runner).to receive(:update_crons!)
|
29
53
|
expect(runner).to receive(:update_services!).with(timeout: 500)
|
30
|
-
|
54
|
+
described_class.no_commands do
|
55
|
+
expect_any_instance_of(described_class).to receive(:runner).at_least(:once).and_return(runner)
|
56
|
+
end
|
31
57
|
expect { described_class.start(['deploy', '--file', 'spec/support/ECSFile']) }.to output(/[WARNING]/).to_stdout
|
32
58
|
end
|
33
59
|
|
34
60
|
it 'runs deploy-services' do
|
35
61
|
expect(runner).to receive(:update_services!)
|
36
|
-
|
62
|
+
described_class.no_commands do
|
63
|
+
expect_any_instance_of(described_class).to receive(:runner).and_return(runner)
|
64
|
+
end
|
37
65
|
expect { described_class.start(['deploy-services', '--file', 'spec/support/ECSFile']) }.to output(/[WARNING]/).to_stdout
|
38
66
|
end
|
39
67
|
|
40
68
|
it 'runs ssh' do
|
41
69
|
expect(runner).to receive(:ssh)
|
42
|
-
|
70
|
+
described_class.no_commands do
|
71
|
+
expect_any_instance_of(described_class).to receive(:runner).and_return(runner)
|
72
|
+
end
|
43
73
|
|
44
74
|
described_class.start(['ssh', '--file', 'spec/support/ECSFile'])
|
45
75
|
end
|
46
76
|
|
47
77
|
it 'runs deploy-scheduled-tasks' do
|
48
78
|
expect(runner).to receive(:update_crons!)
|
49
|
-
|
79
|
+
described_class.no_commands do
|
80
|
+
expect_any_instance_of(described_class).to receive(:runner).at_least(:once).and_return(runner)
|
81
|
+
end
|
50
82
|
|
51
83
|
described_class.start(['deploy-scheduled-tasks', '--file', 'spec/support/ECSFile'])
|
52
84
|
end
|
@@ -20,10 +20,10 @@ describe EcsDeployCli::DSL::Container do
|
|
20
20
|
expect(subject.as_definition[:environment]).to eq(
|
21
21
|
[
|
22
22
|
{
|
23
|
-
|
23
|
+
name: 'SOME', value: 'env'
|
24
24
|
},
|
25
25
|
{
|
26
|
-
|
26
|
+
name: 'SOME2', value: 'env2'
|
27
27
|
}
|
28
28
|
]
|
29
29
|
)
|
@@ -89,10 +89,10 @@ describe EcsDeployCli::DSL::Container do
|
|
89
89
|
expect(subject.as_definition[:environment]).to eq(
|
90
90
|
[
|
91
91
|
{
|
92
|
-
|
92
|
+
name: 'RAILS_ENV', value: 'production'
|
93
93
|
},
|
94
94
|
{
|
95
|
-
|
95
|
+
name: 'API_KEY', value: '123123123'
|
96
96
|
}
|
97
97
|
]
|
98
98
|
)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'aws-sdk-cloudwatchevents'
|
3
5
|
require 'aws-sdk-ec2'
|
@@ -12,8 +14,65 @@ describe EcsDeployCli::Runner do
|
|
12
14
|
Aws::CloudWatchEvents::Client.new(stub_responses: true)
|
13
15
|
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
context '#validate!' do
|
18
|
+
it 'fails on missing params' do
|
19
|
+
expect { subject.validate! }.to raise_error('Missing required parameter aws_profile_id')
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'with a minimal set of options' do
|
23
|
+
let(:parser) { EcsDeployCli::DSL::Parser.load('spec/support/ECSFile.minimal') }
|
24
|
+
it 'fails on missing params' do
|
25
|
+
mock_ecs_client.stub_responses(:describe_clusters, { clusters: [{ cluster_arn: 'arn:xxx', cluster_name: 'yourproject-cluster' }] })
|
26
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
27
|
+
subject.validate!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'with envs set' do
|
32
|
+
around(:each) do |example|
|
33
|
+
ENV['AWS_PROFILE_ID'] = '123123123'
|
34
|
+
ENV['AWS_REGION'] = 'us-east-1'
|
35
|
+
example.run
|
36
|
+
ENV['AWS_PROFILE_ID'] = nil
|
37
|
+
ENV['AWS_REGION'] = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'fails on missing cluster' do
|
41
|
+
mock_ecs_client.stub_responses(:describe_clusters, { failures: [{ arn: 'arn:xxx', reason: 'MISSING' }] })
|
42
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
43
|
+
expect { subject.validate! }.to raise_error('No such cluster yourproject-cluster.')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'fails on missing service' do
|
47
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
48
|
+
|
49
|
+
mock_ecs_client.stub_responses(:describe_clusters, { clusters: [{ cluster_arn: 'arn:xxx', cluster_name: 'yourproject-cluster' }] })
|
50
|
+
mock_ecs_client.stub_responses(:describe_services, { services: [], failures: [{}] })
|
51
|
+
|
52
|
+
expect { subject.validate! }.to raise_error('No such service yourproject-service.')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'fails on missing crons' do
|
56
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwe_client).at_least(:once).and_return(mock_cwe_client)
|
57
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
58
|
+
|
59
|
+
mock_ecs_client.stub_responses(:describe_clusters, { clusters: [{ cluster_arn: 'arn:xxx', cluster_name: 'yourproject-cluster' }] })
|
60
|
+
mock_ecs_client.stub_responses(:describe_services, { services: [{ service_arn: 'arn:xxx', service_name: 'yourproject-service' }] })
|
61
|
+
|
62
|
+
expect { subject.validate! }.to raise_error('No such cron scheduled_emails.')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'makes API calls to check if everything is there' do
|
66
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
67
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwe_client).at_least(:once).and_return(mock_cwe_client)
|
68
|
+
|
69
|
+
mock_ecs_client.stub_responses(:describe_clusters, { clusters: [{ cluster_arn: 'arn:xxx', cluster_name: 'yourproject-cluster' }] })
|
70
|
+
mock_cwe_client.stub_responses(:list_targets_by_rule, { targets: [{ id: '123', arn: 'arn:123' }] })
|
71
|
+
mock_ecs_client.stub_responses(:describe_services, { services: [{ service_arn: 'arn:xxx', service_name: 'yourproject-service' }] })
|
72
|
+
|
73
|
+
subject.validate!
|
74
|
+
end
|
75
|
+
end
|
17
76
|
end
|
18
77
|
|
19
78
|
context 'with envs set' do
|
@@ -30,33 +89,53 @@ describe EcsDeployCli::Runner do
|
|
30
89
|
expect(mock_ecs_client).to receive(:describe_container_instances).and_return(double(container_instances: [double(ec2_instance_id: 'i-123123')]))
|
31
90
|
|
32
91
|
expect(mock_ec2_client).to receive(:describe_instances)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
)
|
92
|
+
.with(instance_ids: ['i-123123'])
|
93
|
+
.and_return(
|
94
|
+
double(reservations: [
|
95
|
+
double(instances: [double(public_dns_name: 'test.com')])
|
96
|
+
])
|
97
|
+
)
|
40
98
|
|
41
99
|
expect(Process).to receive(:fork) do |&block|
|
42
100
|
block.call
|
43
101
|
end
|
44
102
|
expect(Process).to receive(:wait)
|
45
103
|
|
46
|
-
|
47
|
-
|
48
|
-
|
104
|
+
expect_any_instance_of(EcsDeployCli::Runners::SSH).to receive(:exec).with('ssh ec2-user@test.com')
|
105
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
106
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ec2_client).at_least(:once).and_return(mock_ec2_client)
|
49
107
|
|
50
108
|
subject.ssh
|
51
109
|
end
|
52
110
|
|
111
|
+
it '#diff' do
|
112
|
+
mock_ecs_client.stub_responses(:describe_task_definition)
|
113
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
114
|
+
|
115
|
+
expect(EcsDeployCli.logger).to receive(:info).at_least(:once) do |message|
|
116
|
+
puts message
|
117
|
+
end
|
118
|
+
|
119
|
+
expect { subject.diff }.to output(/Task: yourproject/).to_stdout
|
120
|
+
end
|
121
|
+
|
122
|
+
it '#run_task!' do
|
123
|
+
mock_ecs_client.stub_responses(:register_task_definition, { task_definition: { family: 'some', revision: 1, task_definition_arn: 'arn:task:eu-central-1:xxxx' } })
|
124
|
+
|
125
|
+
mock_cwe_client.stub_responses(:run_task)
|
126
|
+
|
127
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
128
|
+
|
129
|
+
subject.run_task!('yourproject-cron', launch_type: 'FARGATE', security_groups: [], subnets: [])
|
130
|
+
end
|
131
|
+
|
53
132
|
it '#update_crons!' do
|
54
133
|
mock_ecs_client.stub_responses(:register_task_definition, { task_definition: { family: 'some', revision: 1, task_definition_arn: 'arn:task:eu-central-1:xxxx' } })
|
55
134
|
|
56
135
|
mock_cwe_client.stub_responses(:list_targets_by_rule, { targets: [{ id: '123', arn: 'arn:123' }] })
|
57
136
|
|
58
|
-
|
59
|
-
|
137
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
138
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwe_client).at_least(:once).and_return(mock_cwe_client)
|
60
139
|
|
61
140
|
subject.update_crons!
|
62
141
|
end
|
@@ -66,7 +145,7 @@ describe EcsDeployCli::Runner do
|
|
66
145
|
expect(mock_ecs_client).to receive(:update_service)
|
67
146
|
expect(mock_ecs_client).to receive(:wait_until)
|
68
147
|
|
69
|
-
|
148
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
70
149
|
|
71
150
|
subject.update_services!
|
72
151
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
aws_region ENV.fetch('AWS_REGION', 'eu-central-1')
|
2
|
+
|
3
|
+
# Used to create ARNs
|
4
|
+
aws_profile_id '123123'
|
5
|
+
|
6
|
+
# Defining the cluster name
|
7
|
+
cluster 'yourproject-cluster'
|
8
|
+
|
9
|
+
# A container to exec cron jobs
|
10
|
+
container :cron do
|
11
|
+
command 'rails', 'runner'
|
12
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecs_deploy_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mònade
|
@@ -73,6 +73,34 @@ dependencies:
|
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '1'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: colorize
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.8.1
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.8.1
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: hashdiff
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
type: :runtime
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.0'
|
76
104
|
- !ruby/object:Gem::Dependency
|
77
105
|
name: thor
|
78
106
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +162,13 @@ files:
|
|
134
162
|
- lib/ecs_deploy_cli/dsl/service.rb
|
135
163
|
- lib/ecs_deploy_cli/dsl/task.rb
|
136
164
|
- lib/ecs_deploy_cli/runner.rb
|
165
|
+
- lib/ecs_deploy_cli/runners/base.rb
|
166
|
+
- lib/ecs_deploy_cli/runners/diff.rb
|
167
|
+
- lib/ecs_deploy_cli/runners/run_task.rb
|
168
|
+
- lib/ecs_deploy_cli/runners/ssh.rb
|
169
|
+
- lib/ecs_deploy_cli/runners/update_crons.rb
|
170
|
+
- lib/ecs_deploy_cli/runners/update_services.rb
|
171
|
+
- lib/ecs_deploy_cli/runners/validate.rb
|
137
172
|
- lib/ecs_deploy_cli/version.rb
|
138
173
|
- spec/ecs_deploy_cli/cli_spec.rb
|
139
174
|
- spec/ecs_deploy_cli/dsl/container_spec.rb
|
@@ -143,12 +178,14 @@ files:
|
|
143
178
|
- spec/ecs_deploy_cli/runner_spec.rb
|
144
179
|
- spec/spec_helper.rb
|
145
180
|
- spec/support/ECSFile
|
181
|
+
- spec/support/ECSFile.minimal
|
146
182
|
- spec/support/env_file.ext.yml
|
147
183
|
- spec/support/env_file.yml
|
148
184
|
homepage: https://rubygems.org/gems/ecs_deploy_cli
|
149
185
|
licenses:
|
150
186
|
- MIT
|
151
|
-
metadata:
|
187
|
+
metadata:
|
188
|
+
source_code_uri: https://github.com/monade/ecs-deploy-cli
|
152
189
|
post_install_message:
|
153
190
|
rdoc_options: []
|
154
191
|
require_paths:
|
@@ -164,13 +201,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
201
|
- !ruby/object:Gem::Version
|
165
202
|
version: '0'
|
166
203
|
requirements: []
|
167
|
-
rubygems_version: 3.
|
204
|
+
rubygems_version: 3.1.4
|
168
205
|
signing_key:
|
169
206
|
specification_version: 4
|
170
207
|
summary: A command line interface to make ECS deployments easier
|
171
208
|
test_files:
|
172
209
|
- spec/spec_helper.rb
|
173
210
|
- spec/support/ECSFile
|
211
|
+
- spec/support/ECSFile.minimal
|
174
212
|
- spec/support/env_file.yml
|
175
213
|
- spec/support/env_file.ext.yml
|
176
214
|
- spec/ecs_deploy_cli/runner_spec.rb
|