class Lono::Cfn::Base

Public Instance Methods

capabilities() click to toggle source
# File lib/lono/cfn/base.rb, line 158
def capabilities
  return @options[:capabilities] if @options[:capabilities]
  if @options[:sure] || @options[:iam]
    ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]
  end
end
command_with_iam(capabilities) click to toggle source
# File lib/lono/cfn/base.rb, line 103
def command_with_iam(capabilities)
  "#{File.basename($0)} #{ARGV.join(' ')} --capabilities #{capabilities}"
end
continue_update_rollback() click to toggle source
# File lib/lono/cfn/base.rb, line 58
def continue_update_rollback
  continue_update_rollback_sure?
  options = {stack_name: @stack}
  show_options(options, "cfn.continue_update_rollback")
  begin
    cfn.continue_update_rollback(options)
  rescue Aws::CloudFormation::Errors::ValidationError => e
    puts "ERROR5: #{e.message}".color(:red)
    exit 1
  end
end
continue_update_rollback_sure?() click to toggle source
# File lib/lono/cfn/base.rb, line 45
    def continue_update_rollback_sure?
      puts <<~EOL
        The stack is in the UPDATE_ROLLBACK_FAILED state. More info here: https://amzn.to/2IiEjc5
        Would you like to try to continue the update rollback? (y/N)
      EOL

      sure = @options[:sure] ? "y" : $stdin.gets
      unless sure =~ /^y/
        puts "Exiting without continuing the update rollback."
        exit 0
      end
    end
delete_rollback_stack() click to toggle source
# File lib/lono/cfn/base.rb, line 70
def delete_rollback_stack
  rollback = Rollback.new(@stack)
  rollback.delete_stack
end
exit_unless_updatable!() click to toggle source
# File lib/lono/cfn/base.rb, line 133
def exit_unless_updatable!
  return true if testing_update?
  return false if @options[:noop]

  status = stack_status
  unless status =~ /_COMPLETE$/ || status == "UPDATE_ROLLBACK_FAILED"
    puts "Cannot create a change set for the stack because the #{@stack} is not in an updatable state.  Stack status: #{status}".color(:red)
    quit(1)
  end
end
generate_all() click to toggle source
# File lib/lono/cfn/base.rb, line 107
def generate_all
  Lono::Generate.new(@options).all
end
notification_arns() click to toggle source
# File lib/lono/cfn/base.rb, line 165
def notification_arns
  @setting ||= Lono::Setting.new
  settings = @setting.data
  arns = @options["notification_arns"] || settings["notification_arns"]
  arns == [''] ? [] : arns # allow removing the notification_arns setting
end
pretty_path(path) click to toggle source
# File lib/lono/cfn/base.rb, line 197
def pretty_path(path)
  path.sub("#{Lono.root}/",'')
end
prompt_for_iam(capabilities) click to toggle source
# File lib/lono/cfn/base.rb, line 95
def prompt_for_iam(capabilities)
  puts "This stack will create IAM resources.  Please approve to run the command again with #{capabilities} capabilities."
  puts "  #{command_with_iam(capabilities)}"

  puts "Please confirm (y/N)"
  $stdin.gets
end
quit(signal) click to toggle source

To allow mocking in specs

# File lib/lono/cfn/base.rb, line 154
def quit(signal)
  exit signal
end
rerun_with_iam?(e) click to toggle source
# File lib/lono/cfn/base.rb, line 80
def rerun_with_iam?(e)
  # e.message is "Requires capabilities : [CAPABILITY_IAM]"
  # grab CAPABILITY_IAM with regexp
  capabilities = e.message.match(/\[(.*)\]/)[1]
  confirm = prompt_for_iam(capabilities)
  if confirm =~ /^y/
    @options.merge!(capabilities: [capabilities])
    puts "Re-running: #{command_with_iam(capabilities).color(:green)}"
    true
  else
    puts "Exited"
    exit 1
  end
end
run() click to toggle source
# File lib/lono/cfn/base.rb, line 14
def run
  starting_message
  parameters = generate_all
  begin
    save(parameters) # defined in the sub class
  rescue Aws::CloudFormation::Errors::InsufficientCapabilitiesException => e
    yes = rerun_with_iam?(e)
    retry if yes
  rescue Aws::CloudFormation::Errors::ValidationError => e
    if e.message.include?("No updates") # No updates are to be performed.
      puts "WARN: #{e.message}".color(:yellow)
    elsif e.message.include?("UPDATE_ROLLBACK_FAILED") # https://amzn.to/2IiEjc5
      continue_update_rollback
    else
      puts "ERROR: #{e.message}".color(:red)
      exit 1
    end
  end

  return unless @options[:wait]

  success = false
  if !@options[:noop]
    success = status.wait
  end

  # exit code for cfn.rb cli, so there's less duplication
  exit 1 unless success
  success
end
set_template_url!(options) click to toggle source

Lono always uploads the template to s3 so we can use much larger templates.

template_body: 51,200 bytes - filesystem limit
template_url: 460,800 bytes - s3 limit

Reference: docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html

# File lib/lono/cfn/base.rb, line 189
def set_template_url!(options)
  url_path = template_path.sub("#{Lono.root}/",'')
  url = Lono::S3::Uploader.new(url_path).presigned_url
  url.gsub!(/\.yml.*/, ".yml") # Interesting dont need presign query string. For stack sets it actually breaks it. So removing.
  options[:template_url] = url
  options
end
show_options(options, meth=nil) click to toggle source
# File lib/lono/cfn/base.rb, line 172
def show_options(options, meth=nil)
  options = options.clone.compact
  if options[:template_body] # continue_update_rollback
    options[:template_body] = "Hidden due to size... View at: #{pretty_path(template_path)}"
    options[:template_url] = options[:template_url].sub(/\?.*/,'')
  end
  to = meth || "AWS API"
  puts "Parameters passed to #{to}:"
  puts YAML.dump(options.deep_stringify_keys)
end
stack_status() click to toggle source

All CloudFormation states listed here: docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html

# File lib/lono/cfn/base.rb, line 146
def stack_status
  return true if testing_update?
  return false if @options[:noop]
  resp = cfn.describe_stacks(stack_name: @stack)
  resp.stacks[0].stack_status
end
starting_message() click to toggle source
# File lib/lono/cfn/base.rb, line 9
def starting_message
  action = self.class.to_s.split('::').last
  puts "#{action} #{@stack.color(:green)} stack..."
end
status() click to toggle source
# File lib/lono/cfn/base.rb, line 75
def status
  Status.new(@stack)
end
tags() click to toggle source

Maps to CloudFormation format. Example:

{"a"=>"1", "b"=>"2"}

To

[{key: "a", value: "1"}, {key: "b", value: "2"}]
# File lib/lono/cfn/base.rb, line 117
def tags
  tags = @options[:tags] || []
  tags = tags.map do |k,v|
    { key: k, value: v }
  end

  update_operation = %w[Preview Update].include?(self.class.to_s)
  if tags.empty? && update_operation
    resp = cfn.describe_stacks(stack_name: @stack)
    tags = resp.stacks.first.tags
    tags = tags.map(&:to_h)
  end

  tags
end