class Stax::Stack

Constants

COLORS

Public Instance Methods

_use_previous_value() click to toggle source

return option or method

# File lib/stax/stack/crud.rb, line 70
def _use_previous_value
  @_use_previous_value ||= (options[:use_previous_value] || use_previous_value.map(&:to_s))
end
cancel() click to toggle source
# File lib/stax/stack/crud.rb, line 224
def cancel
  debug("Cancelling update for #{stack_name}")
  Aws::Cfn.cancel(stack_name)
  tail
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  fail_task(e.message)
end
capture_stdout() { || ... } click to toggle source

temporarily grab stdout to a string

# File lib/stax/cfer.rb, line 62
def capture_stdout
  stdout, $stdout = $stdout, StringIO.new
  yield
  $stdout.string
ensure
  $stdout = stdout
end
cfer_client() click to toggle source
# File lib/stax/cfer.rb, line 50
def cfer_client
  @_cfer_client ||= Cfer::Cfn::Client.new({})
end
cfer_generate(filename) click to toggle source

generate JSON for stack without sending to cloudformation

# File lib/stax/cfer.rb, line 55
def cfer_generate(filename)
  Cfer::stack_from_file(filename, client: cfer_client, parameters: stringify_keys(cfn_parameters)).to_json
rescue Cfer::Util::FileDoesNotExistError => e
  fail_task(e.message)
end
cfer_parameters() click to toggle source

backward-compatibility

# File lib/stax/cfer.rb, line 41
def cfer_parameters
  cfn_parameters
end
cfer_s3_path() click to toggle source

override with S3 bucket for upload of large templates as needed

# File lib/stax/cfer.rb, line 46
def cfer_s3_path
  nil
end
cfn_capabilities() click to toggle source

validate template, and return list of require capabilities

# File lib/stax/stack/crud.rb, line 147
def cfn_capabilities
  validate.capabilities
end
cfn_cdk_dir() click to toggle source

by default look for cdk templates in same dir as Staxfile

# File lib/stax/stack/template.rb, line 37
def cfn_cdk_dir
  Stax.root_path
end
cfn_force_s3?() click to toggle source

set this to always do an S3 upload of template

# File lib/stax/stack/crud.rb, line 93
def cfn_force_s3?
  false
end
cfn_notification_arns() click to toggle source

override with SNS ARNs as needed

# File lib/stax/stack/crud.rb, line 117
def cfn_notification_arns
  if self.class.method_defined?(:cfer_notification_arns)
    warn('Method cfer_notification_arns deprecated, please use cfn_notification_arns')
    cfer_notification_arns
  else
    []
  end
end
cfn_parameters() click to toggle source

by default we pass names of imported stacks; you are encouraged to override or extend this method

# File lib/stax/stack/crud.rb, line 8
def cfn_parameters
  stack_imports.each_with_object({}) do |i, h|
    h[i.to_sym] = stack(i).stack_name
  end
end
cfn_parameters_create() click to toggle source

get array of params for stack create

# File lib/stax/stack/crud.rb, line 75
def cfn_parameters_create
  @_cfn_parameters_create ||= cfn_parameters.map { |k,v|
    { parameter_key: k, parameter_value: v }
  }
end
cfn_parameters_update() click to toggle source

get array of params for stack update, use previous where requested

# File lib/stax/stack/crud.rb, line 82
def cfn_parameters_update
  @_cfn_parameters_update ||= cfn_parameters.map { |k,v|
    if _use_previous_value.include?(k.to_s)
      { parameter_key: k, use_previous_value: true }
    else
      { parameter_key: k, parameter_value: v }
    end
  }
end
cfn_s3_path() click to toggle source

set this for template uploads as needed, e.g. s3://bucket-name/stax/#{stack_name}“

# File lib/stax/stack/crud.rb, line 103
def cfn_s3_path
  nil
end
cfn_s3_upload() click to toggle source

upload template to S3 and return public url of new object

# File lib/stax/stack/crud.rb, line 108
def cfn_s3_upload
  fail_task('No S3 bucket set for template upload: please set cfn_s3_path') unless cfn_s3_path
  uri = URI(cfn_s3_path)
  obj = ::Aws::S3::Object.new(bucket_name: uri.host, key: uri.path.sub(/^\//, ''))
  obj.put(body: cfn_template)
  obj.public_url + ((v = obj.version_id) ? "?versionId=#{v}" : '')
end
cfn_tags() click to toggle source

stack tags applied to stack and propagated to all supported resources; you should override this with a hash of tag keys and values

# File lib/stax/stack/crud.rb, line 16
def cfn_tags
  nil
end
cfn_tags_array() click to toggle source

convert hash to form needed by cfn sdk

# File lib/stax/stack/crud.rb, line 21
def cfn_tags_array
  @_cfn_tags_array ||= cfn_tags&.map { |k, v|
    { key: k, value: v }
  }
end
cfn_template() click to toggle source

get cfn template based on stack type

# File lib/stax/stack/template.rb, line 55
def cfn_template
  @_cfn_template ||= \
  begin
    if stack_type
      send("cfn_template_#{stack_type}")
    else
      cfn_template_guess || fail_task('cannot find template')
    end
  end
end
cfn_template_body() click to toggle source

template body, or nil if uploading to S3

# File lib/stax/stack/crud.rb, line 137
def cfn_template_body
  @_cfn_template_body ||= cfn_use_s3? ? nil : cfn_template
end
cfn_template_cdk() click to toggle source

transcompile and load a cdk template

# File lib/stax/stack/template.rb, line 42
def cfn_template_cdk
  Dir.chdir(cfn_cdk_dir) do
    %x[npm run build]
    %x[cdk synth]
  end
end
cfn_template_cfer() click to toggle source

load a ruby cfer template

# File lib/stax/stack/template.rb, line 30
def cfn_template_cfer
  if File.exists?(f = "#{cfn_template_stub}.rb")
    cfer_generate(f)
  end
end
cfn_template_dir() click to toggle source

location of templates relative to Staxfile

# File lib/stax/stack/template.rb, line 6
def cfn_template_dir
  'cf'
end
cfn_template_guess() click to toggle source

try to guess template by filename

# File lib/stax/stack/template.rb, line 50
def cfn_template_guess
  cfn_template_cfer || cfn_template_yaml || cfn_template_json
end
cfn_template_json() click to toggle source

load a json template

# File lib/stax/stack/template.rb, line 23
def cfn_template_json
  if File.exists?(f = "#{cfn_template_stub}.json")
    File.read(f)
  end
end
cfn_template_stub() click to toggle source

template filename without extension

# File lib/stax/stack/template.rb, line 11
def cfn_template_stub
  @_cfn_template_stub ||= File.join(cfn_template_dir, "#{class_name}")
end
cfn_template_url() click to toggle source

template S3 URL, or nil if not uploading to S3

# File lib/stax/stack/crud.rb, line 142
def cfn_template_url
  @_cfn_template_url ||= cfn_use_s3? ? cfn_s3_upload : nil
end
cfn_template_yaml() click to toggle source

load a yaml template

# File lib/stax/stack/template.rb, line 16
def cfn_template_yaml
  if File.exists?(f = "#{cfn_template_stub}.yaml")
    File.read(f)
  end
end
cfn_termination_protection() click to toggle source

set true to protect stack

# File lib/stax/stack/crud.rb, line 127
def cfn_termination_protection
  if self.class.method_defined?(:cfer_termination_protection)
    warn('Method cfer_termination_protection deprecated, please use cfn_termination_protection')
    cfer_termination_protection
  else
    false
  end
end
cfn_use_s3?() click to toggle source

decide if we are uploading template to S3

# File lib/stax/stack/crud.rb, line 98
def cfn_use_s3?
  cfn_force_s3? || (cfn_template.bytesize > 51200)
end
change() click to toggle source
# File lib/stax/stack/changeset.rb, line 87
def change
  id = change_set_update
  change_set_complete?(id) || fail_task(change_set_reason(id))
  change_set_changes(id)
  change_set_unlock
  change_set_execute(id) && tail && update_warn_imports
ensure
  change_set_lock
end
change_set_changes(id) click to toggle source

display planned changes

# File lib/stax/stack/changeset.rb, line 52
def change_set_changes(id)
  debug("Changes to #{stack_name}")
  print_table Aws::Cfn.changes(stack_name: stack_name, change_set_name: id).map { |c|
    r = c.resource_change
    replacement = set_color(change_set_replacement(r.replacement), :red)
    [color(r.action, Aws::Cfn::COLORS), r.logical_resource_id, r.physical_resource_id, r.resource_type, replacement]
  }
end
change_set_complete?(id) click to toggle source

wait and return true if changeset ready for execute

# File lib/stax/stack/changeset.rb, line 34
def change_set_complete?(id)
  begin
    Aws::Cfn.client.wait_until(:change_set_create_complete, stack_name: stack_name, change_set_name: id) { |w| w.delay = 1 }
  rescue ::Aws::Waiters::Errors::FailureStateError => e
    false                 # no changes to apply
  end
end
change_set_execute(id) click to toggle source

confirm and execute the change set

# File lib/stax/stack/changeset.rb, line 67
def change_set_execute(id)
  if yes?("Apply these changes to stack #{stack_name}?", :yellow)
    Aws::Cfn.execute(stack_name: stack_name, change_set_name: id)
  end
end
change_set_lock() click to toggle source
# File lib/stax/stack/changeset.rb, line 79
def change_set_lock
  unless stack_policy.nil?
    Aws::Cfn.set_policy(stack_name: stack_name, stack_policy_body: stack_policy)
  end
end
change_set_name() click to toggle source

can be anything unique

# File lib/stax/stack/changeset.rb, line 12
def change_set_name
  stack_name + '-' + Time.now.strftime('%Y%m%d%H%M%S')
end
change_set_reason(id) click to toggle source

get status reason, used for a failure message

# File lib/stax/stack/changeset.rb, line 62
def change_set_reason(id)
  Aws::Cfn.client.describe_change_set(stack_name: stack_name, change_set_name: id).status_reason
end
change_set_replacement(string) click to toggle source

string to print for replacement flag

# File lib/stax/stack/changeset.rb, line 43
def change_set_replacement(string)
  case string
    when 'True' then 'Replace'
    when 'Conditional' then 'May replace'
    else ''
  end
end
change_set_unlock() click to toggle source
# File lib/stax/stack/changeset.rb, line 73
def change_set_unlock
  unless stack_policy_during_update.nil?
    Aws::Cfn.set_policy(stack_name: stack_name, stack_policy_body: stack_policy_during_update)
  end
end
change_set_update() click to toggle source

create a change set to update existing stack

# File lib/stax/stack/changeset.rb, line 17
def change_set_update
  Aws::Cfn.changeset(
    stack_name: stack_name,
    template_body: cfn_template_body,
    template_url: cfn_template_url,
    parameters: cfn_parameters_update,
    capabilities: cfn_capabilities,
    notification_arns: cfn_notification_arns,
    change_set_name: change_set_name,
    change_set_type: :UPDATE,
    tags: cfn_tags_array,
  ).id
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  fail_task(e.message)
end
class_name() click to toggle source

get name of stack in Staxfile, or infer it from class

# File lib/stax/stack.rb, line 7
def class_name
  @_class_name ||= self.class.instance_variable_get(:@name).to_s || self.class.to_s.split('::').last.underscore
end
continue() click to toggle source
# File lib/stax/stack/crud.rb, line 239
def continue
  Aws::Cfn.client.continue_update_rollback(
    stack_name: stack_name,
    resources_to_skip: options[:skip],
  )
  tail
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  fail_task(e.message)
end
create() click to toggle source
# File lib/stax/stack/crud.rb, line 164
def create
  debug("Creating stack #{stack_name}")

  ## ensure stacks we import exist
  ensure_stack(*stack_imports)

  ## create the stack
  Aws::Cfn.create(
    stack_name: stack_name,
    template_body: cfn_template_body,
    template_url: cfn_template_url,
    parameters: cfn_parameters_create,
    capabilities: cfn_capabilities,
    stack_policy_body: stack_policy,
    notification_arns: cfn_notification_arns,
    enable_termination_protection: cfn_termination_protection,
    tags: cfn_tags_array,
  )

  ## show stack events
  tail
rescue ::Aws::CloudFormation::Errors::AlreadyExistsException => e
  fail_task(e.message)
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  warn(e.message)
end
delete() click to toggle source
# File lib/stax/stack/crud.rb, line 213
def delete
  delete_warn_imports
  if yes? "Really delete stack #{stack_name}?", :yellow
    Aws::Cfn.delete(stack_name)
    tail unless options[:notail]
  end
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  fail_task(e.message)
end
delete_warn_imports() click to toggle source
# File lib/stax/stack/imports.rb, line 17
def delete_warn_imports
  unless import_stacks.empty?
    warn("The following stacks import from this one: #{import_stacks.join(',')}")
  end
end
drifts() click to toggle source
# File lib/stax/stack/drift.rb, line 49
def drifts
  run_drift_detection
  drifts = show_drifts
  show_drifts_details(drifts)
end
event_fields(e) click to toggle source
# File lib/stax/stack/cfn.rb, line 6
def event_fields(e)
  [e.timestamp, color(e.resource_status, Aws::Cfn::COLORS), e.resource_type, e.logical_resource_id, e.resource_status_reason]
end
events() click to toggle source
# File lib/stax/stack/cfn.rb, line 34
def events
  print_events(Aws::Cfn.events(stack_name)[0..options[:number]-1])
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  puts e.message
end
exists() click to toggle source
# File lib/stax/stack.rb, line 47
def exists
  puts exists?
end
exists?() click to toggle source
# File lib/stax/stack.rb, line 29
def exists?
  Aws::Cfn.exists?(stack_name)
end
generate() click to toggle source
# File lib/stax/stack/crud.rb, line 233
def generate
  puts cfn_template
end
id(resource) click to toggle source
# File lib/stax/stack/resources.rb, line 30
def id(resource)
  puts Aws::Cfn.id(stack_name, resource)
end
import_stacks() click to toggle source
# File lib/stax/stack/imports.rb, line 5
def import_stacks
  @_import_stacks ||= Aws::Cfn.exports(stack_name).map do |e|
    Aws::Cfn.imports(e.export_name)
  end.flatten.uniq
end
imports() click to toggle source
# File lib/stax/stack/imports.rb, line 25
def imports
  debug("Stacks that import from #{stack_name}")
  print_table Aws::Cfn.exports(stack_name).map { |e|
    imports = (i = Aws::Cfn.imports(e.export_name)).empty? ? '-' : i.join('  ')
    [e.output_key, imports]
  }.sort
end
info() click to toggle source
# File lib/stax/cli/info.rb, line 11
def info
  ## get mixins in the order we declared them
  self.class.subcommands.reverse.each do |cmd|
    begin
      invoke cmd, [:info]
    rescue Thor::UndefinedCommandError => e
      # no info no problem
    end
  end
end
lint() click to toggle source
# File lib/stax/stack/crud.rb, line 291
def lint
  require 'open3'
  Open3.popen3('cfn-lint -') do |stdin, stdout|
    stdin.write(cfn_template)
    stdin.close
    puts stdout.read
  end
rescue Errno::ENOENT => e
  fail_task(e.message)
end
outputs(key = nil) click to toggle source
# File lib/stax/stack/outputs.rb, line 15
def outputs(key = nil)
  if key
    puts stack_output(key)
  else
    print_table Aws::Cfn.describe(stack_name).outputs.map { |o|
      [o.output_key, o.output_value, o.description, o.export_name]
    }.sort
  end
end
parameters() click to toggle source
# File lib/stax/stack/parameters.rb, line 17
def parameters
  print_table stack_parameters.each_with_object({}) { |p, h|
    h[p.parameter_key] = p.parameter_value
  }.sort
end
policy(json = nil) click to toggle source
# File lib/stax/stack/crud.rb, line 263
def policy(json = nil)
  if json
    Aws::Cfn.set_policy(stack_name: stack_name, stack_policy_body: json)
  else
    puts Aws::Cfn.get_policy(stack_name: stack_name)
  end
end
print_events(events) click to toggle source
protection() click to toggle source
# File lib/stax/stack/crud.rb, line 252
def protection
  if options[:enable]
    Aws::Cfn.protection(stack_name, true)
  elsif options[:disable]
    Aws::Cfn.protection(stack_name, false)
  end
  debug("Termination protection for #{stack_name}")
  puts Aws::Cfn.describe(stack_name)&.enable_termination_protection
end
resource(id) click to toggle source
# File lib/stax/stack.rb, line 41
def resource(id)
  Aws::Cfn.id(stack_name, id)
end
resources() click to toggle source
# File lib/stax/stack/resources.rb, line 18
def resources
  print_table stack_resources.tap { |resources|
    if options[:match]
      m = Regexp.new(options[:match], Regexp::IGNORECASE)
      resources.select! { |r| m.match(r.resource_type) }
    end
  }.map { |r|
    [r.logical_resource_id, r.resource_type, color(r.resource_status, Aws::Cfn::COLORS), r.physical_resource_id]
  }
end
run_drift_detection() click to toggle source

start a drift detection job and wait for it to complete

# File lib/stax/stack/drift.rb, line 13
def run_drift_detection
  debug("Running drift detection for #{stack_name}")
  id = Aws::Cfn.detect_drift(stack_name: stack_name)
  puts "waiting for #{id}"
  loop do
    sleep(1)
    break unless Aws::Cfn.drift_status(id).detection_status == 'DETECTION_IN_PROGRESS'
  end
end
show_drifts() click to toggle source

show the latest drift status for each resource

# File lib/stax/stack/drift.rb, line 24
def show_drifts
  debug("Resource drift status for #{stack_name}")
  Aws::Cfn.drifts(stack_name: stack_name).tap do |drifts|
    print_table drifts.map { |d|
      [d.logical_resource_id, d.resource_type, color(d.stack_resource_drift_status, COLORS), d.timestamp]
    }
  end
end
show_drifts_details(drifts) click to toggle source

show drift diffs for out of sync resources

# File lib/stax/stack/drift.rb, line 34
def show_drifts_details(drifts)
  drifts.select{ |d| d.stack_resource_drift_status == 'MODIFIED' }.each do |r|
    debug("Property differences for #{r.logical_resource_id}")
    r.property_differences.each do |p|
      puts(
        p.property_path + ' ' + color(p.difference_type, COLORS),
        '  ' + set_color('-' + p.expected_value, :red),
        '  ' + set_color('+' + p.actual_value, :green)
      )
    end
  end
end
skeleton() click to toggle source
# File lib/stax/stack/crud.rb, line 273
def skeleton
  skel = {
    StackName: stack_name,
    TemplateBody: cfn_template_body,
    TemplateURL: cfn_template_url,
    Parameters: cfn_parameters_create,
    Capabilities: cfn_capabilities,
    StackPolicyBody: stack_policy,
    NotificationARNs: cfn_notification_arns,
    EnableTerminationProtection: cfn_termination_protection,
    Tags: cfn_tags_array,
  }.compact
  method = options[:pretty] ? :pretty_generate : :generate
  puts JSON.send(method, skel)
end
stack_force_changeset() click to toggle source

set this in stack to force changesets on update

# File lib/stax/stack/changeset.rb, line 7
def stack_force_changeset
  false
end
stack_groups() click to toggle source
# File lib/stax/stack.rb, line 25
def stack_groups
  self.class.instance_variable_get(:@groups) || [:default]
end
stack_imports() click to toggle source

list of other stacks we need to reference

# File lib/stax/stack.rb, line 17
def stack_imports
  self.class.instance_variable_get(:@imports)
end
stack_name() click to toggle source

build valid name for the stack

# File lib/stax/stack.rb, line 12
def stack_name
  @_stack_name ||= stack_prefix + cfn_safe(class_name)
end
stack_notification_arns() click to toggle source
# File lib/stax/stack.rb, line 37
def stack_notification_arns
  Aws::Cfn.describe(stack_name).notification_arns
end
stack_output(key) click to toggle source
# File lib/stax/stack/outputs.rb, line 9
def stack_output(key)
  stack_outputs.fetch(key.to_s, nil)
end
stack_outputs() click to toggle source
# File lib/stax/stack/outputs.rb, line 5
def stack_outputs
  @_stack_outputs ||= Aws::Cfn.outputs(stack_name)
end
stack_parameter(key) click to toggle source
# File lib/stax/stack/parameters.rb, line 9
def stack_parameter(key)
  stack_parameters.find do |p|
    p.parameter_key == key.to_s
  end&.parameter_value
end
stack_parameters() click to toggle source
# File lib/stax/stack/parameters.rb, line 5
def stack_parameters
  @_stack_parameters ||= Aws::Cfn.parameters(stack_name)
end
stack_policy() click to toggle source

policy to lock the stack to all updates, for example: {

Statement: [
  Effect:    'Deny',
  Action:    'Update:*',
  Principal: '*',
  Resource:  '*'
]

}.to_json

# File lib/stax/stack/crud.rb, line 36
def stack_policy
  nil
end
stack_policy_during_update() click to toggle source

tmp policy during updates, in case a deny was set in stack_policy() for example: {

Statement: [
  Effect:    'Allow',
  Action:    'Update:*',
  Principal: '*',
  Resource:  '*'
]

}.to_json

# File lib/stax/stack/crud.rb, line 50
def stack_policy_during_update
  nil
end
stack_resources() click to toggle source
# File lib/stax/stack/resources.rb, line 5
def stack_resources
  @_stack_resources ||= Aws::Cfn.resources(stack_name)
end
stack_resources_by_type(type) click to toggle source
# File lib/stax/stack/resources.rb, line 9
def stack_resources_by_type(type)
  stack_resources.select do |r|
    r.resource_type == type
  end
end
stack_status() click to toggle source
# File lib/stax/stack.rb, line 33
def stack_status
  Aws::Cfn.describe(stack_name).stack_status
end
stack_type() click to toggle source
# File lib/stax/stack.rb, line 21
def stack_type
  self.class.instance_variable_get(:@type)
end
tags() click to toggle source
# File lib/stax/stack/cfn.rb, line 73
def tags
  print_table Aws::Cfn.describe(stack_name).tags.map { |t|
    [ t.key, t.value ]
  }
end
tail() click to toggle source
# File lib/stax/stack/cfn.rb, line 42
def tail
  trap('SIGINT', 'EXIT')    # clean exit with ctrl-c

  ## print some historical events
  events = Aws::Cfn.events(stack_name).first(options[:number] || 1)
  return unless events
  print_events(events)
  last_seen = events&.first&.event_id

  loop do
    sleep(1)
    events = []

    Aws::Cfn.events(stack_name).each do |e|
      (last_seen == e.event_id) ? break : events << e
    end

    unless events.empty?
      print_events(events)
      last_seen = events.first.event_id
    end

    ## get stack status and break if stack gone, or delete complete/failed
    s = Aws::Cfn.describe(stack_name)
    break if s.nil? || s.stack_status.end_with?('COMPLETE', 'FAILED')
  end
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  puts e.message
end
template() click to toggle source
# File lib/stax/stack/cfn.rb, line 20
def template
  body = Aws::Cfn.template(stack_name)
  if options[:pretty]
    begin
      body = JSON.pretty_generate(JSON.parse(body))
    rescue JSON::ParserError
      ## not valid json, may be yaml
    end
  end
  puts body
end
update() click to toggle source
# File lib/stax/stack/crud.rb, line 192
def update
  return change if stack_force_changeset
  debug("Updating stack #{stack_name}")
  Aws::Cfn.update(
    stack_name: stack_name,
    template_body: cfn_template_body,
    template_url: cfn_template_url,
    parameters: cfn_parameters_update,
    capabilities: cfn_capabilities,
    stack_policy_during_update_body: stack_policy_during_update,
    notification_arns: cfn_notification_arns,
    tags: cfn_tags_array,
  )
  tail
  update_warn_imports
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  warn(e.message)
end
update_warn_imports() click to toggle source
# File lib/stax/stack/imports.rb, line 11
def update_warn_imports
  unless import_stacks.empty?
    warn("You may also need to update stacks that import from this one: #{import_stacks.join(',')}")
  end
end
use_previous_value() click to toggle source

stack should monkey-patch with list of params to keep on update

# File lib/stax/stack/crud.rb, line 65
def use_previous_value
  []
end
validate() click to toggle source
# File lib/stax/stack/crud.rb, line 154
def validate
  Aws::Cfn.validate(
    template_body: cfn_template_body,
    template_url:  cfn_template_url,
  )
rescue ::Aws::CloudFormation::Errors::ValidationError => e
  fail_task(e.message)
end
wait_for_delete(seconds = 5) click to toggle source

cleanup sometimes needs to wait

# File lib/stax/stack/crud.rb, line 55
def wait_for_delete(seconds = 5)
  return unless exists?
  debug("Waiting for #{stack_name} to delete")
  loop do
    sleep(seconds)
    break unless exists?
  end
end