class Stacker::Cli

Public Class Methods

new(*args) click to toggle source
Calls superclass method
# File lib/stacker/cli.rb, line 27
def initialize(*args); super(*args) end

Public Instance Methods

diff(stack_name = nil) click to toggle source
# File lib/stacker/cli.rb, line 66
def diff stack_name = nil
  with_one_or_all(stack_name) do |stack|
    begin
      resolve stack
      next unless full_diff stack
    rescue Aws::CloudFormation::Errors::ValidationError,
           Stacker::Stack::Error => err
      Stacker.logger.error err.message
    end
  end
end
dump(stack_name = nil) click to toggle source
# File lib/stacker/cli.rb, line 113
def dump stack_name = nil
  with_one_or_all(stack_name) do |stack|
    if stack.exists?
      diff = stack.template.diff :down, :color
      next Stacker.logger.warn 'Stack up-to-date' if diff.length == 0

      Stacker.logger.debug "\n" + diff.indent
      if yes? "Update local template with these changes (y/n)?"
        stack.template.dump
      else
        Stacker.logger.warn 'Pull skipped'
      end
    else
      Stacker.logger.warn "#{stack.name} does not exist"
    end
  end
end
init(path = nil) click to toggle source
# File lib/stacker/cli.rb, line 30
def init path = nil
  init_project path || options['path']
end
list() click to toggle source
# File lib/stacker/cli.rb, line 35
def list
  Stacker.logger.inspect region.stacks.map(&:name)
end
show(stack_name) click to toggle source
# File lib/stacker/cli.rb, line 40
def show stack_name
  with_one_or_all stack_name do |stack|
    Stacker.logger.inspect(
      'Description'  => stack.description,
      'Status'       => stack.status,
      'Updated'      => stack.last_updated_time || stack.creation_time,
      'Capabilities' => stack.capabilities.remote,
      'Parameters'   => stack.parameters.remote,
      'Outputs'      => stack.outputs
    )
  end
end
status(stack_name = nil) click to toggle source
# File lib/stacker/cli.rb, line 54
def status stack_name = nil
  with_one_or_all(stack_name) do |stack|
    begin
      Stacker.logger.debug stack.status.indent
    rescue Aws::CloudFormation::Errors::ValidationError,
           Stacker::Stack::Error => err
      Stacker.logger.error err.message
    end
  end
end
update(stack_name = nil) click to toggle source
# File lib/stacker/cli.rb, line 79
def update stack_name = nil
  with_one_or_all(stack_name) do |stack|
    begin
      resolve stack

      if stack.exists?
        next unless full_diff stack

        if yes? "Update remote template with these changes (y/n)?"
          time = Benchmark.realtime do
            stack.update allow_destructive: options['allow_destructive']
          end
          Stacker.logger.info formatted_time stack_name, 'updated', time
        else
          Stacker.logger.warn 'Update skipped'
        end
      else
        if yes? "#{stack.name} does not exist. Create it (y/n)?"
          time = Benchmark.realtime do
            stack.create
          end
          Stacker.logger.info formatted_time stack_name, 'created', time
        else
          Stacker.logger.warn 'Create skipped'
        end
      end
    rescue Aws::CloudFormation::Errors::ValidationError,
           Stacker::Stack::Error => err
      Stacker.logger.error err.message
    end
  end
end

Private Instance Methods

environment_config() click to toggle source
# File lib/stacker/cli.rb, line 220
def environment_config
  config_path = File.join environments_path, 'config.yml'
  return {} unless File.exists? config_path
  YAML.load_file(config_path).fetch('environments', {}).fetch(
    options['environment'],
    {}
  )
end
environments?() click to toggle source
# File lib/stacker/cli.rb, line 216
def environments?
  File.exists? environments_path
end
environments_path() click to toggle source
# File lib/stacker/cli.rb, line 212
def environments_path
  File.join working_path, 'environments'
end
formatted_time(stack, action, benchmark) click to toggle source
# File lib/stacker/cli.rb, line 157
def formatted_time stack, action, benchmark
  "Stack #{stack} #{action} in: #{(benchmark / 60).floor} min and #{(benchmark % 60).round} seconds."
end
full_diff(stack) click to toggle source
# File lib/stacker/cli.rb, line 161
def full_diff stack
  templ_diff = stack.template.diff :color
  param_diff = stack.parameters.diff :color

  if (templ_diff + param_diff).length == 0
    Stacker.logger.warn 'Stack up-to-date'
    return false
  end

  Stacker.logger.info "\n#{templ_diff.indent}\n" if templ_diff.length > 0
  Stacker.logger.info "\n#{param_diff.indent}\n" if param_diff.length > 0

  Stacker.logger.info stack.pretty_change_set
  true
end
init_project(path) click to toggle source
# File lib/stacker/cli.rb, line 133
    def init_project path
      project_path = File.expand_path path

      %w[ regions templates ].each do |dir|
        directory_path = File.join project_path, dir
        unless Dir.exists? directory_path
          Stacker.logger.debug "Creating directory at #{directory_path}"
          FileUtils.mkdir_p directory_path
        end
      end

      region_path = File.join project_path, 'regions', 'us-east-1.yml'
      unless File.exists? region_path
        Stacker.logger.debug "Creating region file at #{region_path}"
        File.open(region_path, 'w+') { |f| f.print <<-YAML }
defaults:
  parameters:
    CidrBlock: '10.0'
stacks:
  - name: VPC
YAML
      end
    end
region() click to toggle source
# File lib/stacker/cli.rb, line 177
def region
  @region ||= begin
    config_path =  region_config_path
    if File.exists? config_path
      begin
        config = YAML.load_file(config_path)
      rescue Psych::SyntaxError => err
        Stacker.logger.fatal err.message
        exit 1
      end

      defaults = config.fetch 'defaults', {}
      stacks = config.fetch 'stacks', {}
      region_options = {
        stack_prefix: environment_config.fetch('prefix', '')
      }

      Region.new options['region'], defaults, stacks, templates_path,
                 region_options
    else
      Stacker.logger.fatal "#{options['region']}.yml does not exist. Please configure or use stacker init"
      exit 1
    end
  end
end
region_config_path() click to toggle source
# File lib/stacker/cli.rb, line 203
def region_config_path
  region_path = if environments?
    File.join working_path, 'environments', options['environment']
  else
    File.join working_path, 'regions'
  end
  File.join region_path, "#{options['region']}.yml"
end
resolve(stack) click to toggle source
# File lib/stacker/cli.rb, line 229
def resolve stack
  return {} if stack.parameters.dependencies.none?

  Stacker.logger.debug 'Resolving dependencies...'
  stack.parameters.resolved
end
templates_path() click to toggle source
# File lib/stacker/cli.rb, line 262
def templates_path
  File.join working_path, 'templates'
end
with_one_or_all(stack_name = nil) { |stack| ... } click to toggle source
# File lib/stacker/cli.rb, line 236
def with_one_or_all stack_name = nil, &block
  yield_with_stack = proc do |stack|
    Stacker.logger.info "#{stack.name}:"
    yield stack
    Stacker.logger.info ''
  end

  if stack_name
    yield_with_stack.call region.stack(stack_name)
  else
    region.stacks.each(&yield_with_stack)
  end

rescue Stacker::Stack::StackPolicyError => err
  if options['allow_destructive']
    Stacker.logger.fatal err.message
  else
    Stacker.logger.fatal 'Stack update policy prevents replacing or destroying resources.'
    Stacker.logger.warn 'Try running again with \'--allow-destructive\''
  end
  exit 1
rescue Stacker::Stack::Error => err
  Stacker.logger.fatal err.message
  exit 1
end
working_path() click to toggle source
# File lib/stacker/cli.rb, line 266
def working_path
  File.expand_path options['path']
end