ecs_deploy_cli 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|