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
         
     |