class Stack

Class to simplify AWS CloudFormation stack creation

Constants

END_STATES
FAILURE_STATES
SUCESS_STATES

Attributes

deployed[RW]
name[RW]
stack[RW]

Public Class Methods

new(stack_name) click to toggle source

WAITING_STATES = [“CREATE_IN_PROGRESS”,“DELETE_IN_PROGRESS”,“ROLLBACK_IN_PROGRESS”,“UPDATE_COMPLETE_CLEANUP_IN_PROGRESS”,“UPDATE_IN_PROGRESS”,“UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS”,“UPDATE_ROLLBACK_IN_PROGRESS”]

# File lib/cloudformer/stack.rb, line 18
def initialize(stack_name)
  @name = stack_name
  @cf = AWS::CloudFormation.new
  @stack = @cf.stacks[name]
  @ec2 = AWS::EC2.new
end

Public Instance Methods

apply(template_file, parameters, disable_rollback = false, capabilities = [], notify = [], tags = []) click to toggle source
# File lib/cloudformer/stack.rb, line 29
def apply(template_file, parameters, disable_rollback = false,
          capabilities = [], notify = [], tags = [])
  if template_file =~ %r{^https:\/\/s3\S*\.amazonaws\.com\/(.*)}
    template = template_file
  elsif template_file =~ %r{^http.*(.json)$/}
    begin
      response = HTTParty.get(template_file)
      template = response.body
    rescue => e
      puts "Unable to retieve json file for template from #{template_file} - #{e.class}, #{e}"
      return :Failed
    end
  else
    template = File.read(template_file)
  end
  validation = validate(template)
  unless validation["valid"]
    puts "Unable to update - #{validation['response'][:code]} - #{validation['response'][:message]}"
    return :Failed
  end
  pending_operations = false
  begin
    if deployed
      pending_operations = update(template, parameters, capabilities)
    else
      pending_operations = create(template, parameters, disable_rollback, capabilities, notify, tags)
    end
  rescue ::AWS::CloudFormation::Errors::ValidationError => e
    puts e.message
    return e.message == "No updates are to be performed." ? :NoUpdates : :Failed
  end
  wait_until_end if pending_operations
  deploy_succeeded? ? :Succeeded : :Failed
end
delete() click to toggle source
# File lib/cloudformer/stack.rb, line 78
def delete
  with_highlight do
    puts "Attempting to delete stack - #{name}"
    stack.delete
    wait_until_end
    return deploy_succeeded?
  end
end
deploy_succeeded?() click to toggle source
# File lib/cloudformer/stack.rb, line 64
def deploy_succeeded?
  return true unless FAILURE_STATES.include?(stack.status)
  puts "Unable to deploy template. Check log for more information."
  false
end
events(options = {}) click to toggle source
# File lib/cloudformer/stack.rb, line 97
def events(options = {})
  with_highlight do
    if !deployed
      puts "Stack not up."
      return
    end
    stack.events.sort_by { |a| a.timestamp }.each do |event|
      puts "#{event.timestamp} - #{event.physical_resource_id.to_s} - #{event.logical_resource_id} - #{event.resource_type} - #{event.resource_status} - #{event.resource_status_reason.to_s}"
    end
  end
end
outputs() click to toggle source
# File lib/cloudformer/stack.rb, line 109
def outputs
  with_highlight do
    if !deployed
      puts "Stack not up."
      return 1
    end
    stack.outputs.each do |output|
      puts "#{output.key} - #{output.description} - #{output.value}"
    end
  end
  return 0
end
start_instances() click to toggle source
# File lib/cloudformer/stack.rb, line 74
def start_instances
  update_instances("start")
end
status() click to toggle source
# File lib/cloudformer/stack.rb, line 87
def status
  with_highlight do
    if deployed
      puts "#{stack.name} - #{stack.status} - #{stack.status_reason}"
    else
      puts "#{name} - Not Deployed"
    end
  end
end
stop_instances() click to toggle source
# File lib/cloudformer/stack.rb, line 70
def stop_instances
  update_instances("stop")
end
validate(template) click to toggle source
# File lib/cloudformer/stack.rb, line 122
def validate(template)
  response = @cf.validate_template(template)
  return {
    "valid" => response[:code].nil?,
    "response" => response
  }
end

Private Instance Methods

create(template, parameters, disable_rollback, capabilities, notify, tags) click to toggle source
# File lib/cloudformer/stack.rb, line 166
def create(template, parameters, disable_rollback, capabilities, notify, tags)
  puts "Initializing stack creation..."
  @cf.stacks.create(name, template, :parameters => parameters, :disable_rollback => disable_rollback, :capabilities => capabilities, :notify => notify, :tags => tags)
  sleep 10
  return true
end
update(template, parameters, capabilities) click to toggle source
# File lib/cloudformer/stack.rb, line 157
def update(template, parameters, capabilities)
  stack.update({
    :template => template,
    :parameters => parameters,
    :capabilities => capabilities
  })
  return true
end
update_instances(action) click to toggle source
# File lib/cloudformer/stack.rb, line 173
def update_instances(action)
  with_highlight do
    puts "Attempting to #{action} all ec2 instances in the stack #{stack.name}"
    return "Stack not up" if !deployed
    stack.resources.each do |resource|
      begin
        next if resource.resource_type != "AWS::EC2::Instance"
        physical_resource_id = resource.physical_resource_id
        puts "Attempting to #{action} Instance with physical_resource_id: #{physical_resource_id}"
        @ec2.instances[physical_resource_id].send(action)
      rescue
        puts "Some resources are not up."
      end
    end
  end
end
wait_until_end() click to toggle source
# File lib/cloudformer/stack.rb, line 132
def wait_until_end
  printed = []
  current_time = Time.now
  with_highlight do
    if !deployed
      puts "Stack not up."
      return
    end
    loop do
      printable_events = stack.events.reject { |a| (a.timestamp < current_time) }.sort_by { |a| a.timestamp }.reject { |a| a if printed.include?(a.event_id) }
      printable_events.each { |event| puts "#{event.timestamp} - #{event.physical_resource_id.to_s} - #{event.resource_type} - #{event.resource_status} - #{event.resource_status_reason.to_s}" }
      printed.concat(printable_events.map(&:event_id))
      break if END_STATES.include?(stack.status)
      sleep(30)
    end
  end
end
with_highlight() { || ... } click to toggle source
# File lib/cloudformer/stack.rb, line 150
def with_highlight(&block)
  cols = `tput cols`.chomp!.to_i
  puts "=" * cols
  yield
  puts "=" * cols
end