class EC2Launcher::Terminator

Public Class Methods

new(config_directory) click to toggle source
# File lib/ec2launcher/terminator.rb, line 17
def initialize(config_directory)
  begin
    @log = Log4r::Logger['ec2launcher']
    unless @log
      @log = Log4r::Logger.new 'ec2launcher'
      log_output = Log4r::Outputter.stdout
      log_output.formatter = PatternFormatter.new :pattern => "%m"
      @log.outputters = log_output
    end
  rescue
  end

  ##############################
  # Load configuration data
  ##############################
  config_wrapper = ConfigWrapper.new(config_directory)

  @config = config_wrapper.config
  @environments = config_wrapper.environments
end

Public Instance Methods

remove_snapshots(ec2, attachments) click to toggle source
# File lib/ec2launcher/terminator.rb, line 131
def remove_snapshots(ec2, attachments)
  # Iterate over over volumes to find snapshots
  @log.info("Searching for snapshots...")
  snapshots = []
  attachments.each do |attachment|
    volume_snaps = ec2.snapshots.filter("volume-id", attachment.volume.id)
    volume_snaps.each {|volume_snapshot| snapshots << volume_snapshot }
  end

  @log.info("Deleting #{snapshots.size} snapshots...")
  snapshots.each do |snap|
    run_with_backoff(30, 1, "Deleting snapshot #{snap.id}") do
      snap.delete
    end
  end
end
remove_volume(ec2, instance, device, volume) click to toggle source
# File lib/ec2launcher/terminator.rb, line 148
def remove_volume(ec2, instance, device, volume)
  @log.info("  Detaching #{volume.id}...")
  run_with_backoff(30, 1, "detaching #{volume.id}") do
    volume.detach_from(instance, device)
  end

  # Wait for volume to fully detach
  detached = test_with_backoff(120, 1, "waiting for #{volume.id} to detach") do
    volume.status == :available
  end

  # Volume failed to detach - do a force detatch instead
  unless detached
    @log.info("  Failed to detach #{volume.id}")
    run_with_backoff(60, 1, "force detaching #{volume.id}") do
      unless volume.status == :available
        volume.detach_from(instance, device, {:force => true})
      end
    end
    # Wait for volume to fully detach
    detached = test_with_backoff(120, 1, "waiting for #{volume.id} to force detach") do
      volume.status == :available
    end
  end

  @log.info("  Deleting volume #{volume.id}")
  run_with_backoff(30, 1, "delete volume #{volume.id}") do
    volume.delete
  end
end
remove_volumes(ec2, attachments) click to toggle source
# File lib/ec2launcher/terminator.rb, line 179
def remove_volumes(ec2, attachments)
  @log.info("Cleaning up volumes...")

  AWS.memoize do
    removal_threads = []
    attachments.each do |attachment|
      if attachment.exists? && ! attachment.delete_on_termination
        removal_threads << Thread.new {
          remove_volume(ec2, attachment.instance, attachment.device, attachment.volume)
        }
      end
    end

    removal_threads.each {|t| t.join }
  end
end
terminate(server_name, access_key, secret, snapshot_removal = true, force = false) click to toggle source

Terminates a given server instance.

@param server_name Name of the server instance @param access_key Amazon IAM access key @param secret Amazon IAM secret key @param snapshot_removal Remove EBS snapshots for EBS volumes attached to the instance. @param force Force instance termination even if environment is not found.

# File lib/ec2launcher/terminator.rb, line 45
def terminate(server_name, access_key, secret, snapshot_removal = true, force = false)
  ##############################
  # Initialize AWS and create EC2 connection
  ##############################
  initialize_aws(access_key, secret)
  ec2 = AWS::EC2.new

  ##############################
  # Find instance
  ##############################
  instance = nil
  AWS.memoize do
    instances = ec2.instances.filter("tag:Name", server_name)
    instances.each do |i|
      unless i.status == :shutting_down || i.status == :terminated
        instance = i
        break
      end # unless status
    end # instance loop
  end # memoize

  if instance
    environment_name = nil
    AWS.memoize do
      environment_name = instance.tags["environment"].strip if instance.tags["environment"]
    end

    ##############################
    # ENVIRONMENT
    ##############################
    if environment_name.nil? && ! force
      @log.fatal "No environment tag found for host. Use the --force option to override and terminate."
      exit 3
    end

    if (! @environments.has_key?(environment_name)) && (! force)
      @log.fatal "Environment not found: '#{environment_name}'"
      exit 2
    end
    @environment = @environments[environment_name] if environment_name

    ##############################
    # Create Route53 connection
    ##############################
    aws_route53 = nil
    if @environment && @environment.route53_zone_id
      aws_route53 = AWS::Route53.new
      route53 = EC2Launcher::Route53.new(aws_route53, @environment.route53_zone_id, @log)
    end

    ##############################
    # EBS Volumes
    ##############################
    # Find EBS volumes
    attachments = nil
    AWS.memoize do
      attachments = instance.block_device_mappings.values

      # Remove snapshots
      remove_snapshots(ec2, attachments) if snapshot_removal

      # Remove volumes, if necessary
      remove_volumes(ec2, attachments)
    end

    private_ip_address = instance.private_ip_address
    
    run_with_backoff(30, 1, "terminating instance: #{server_name} [#{instance.instance_id}]") do
      instance.terminate
    end

    if route53
      @log.info("Deleting A record from Route53: #{server_name} => #{private_ip_address}")
      route53.delete_record_by_name(server_name, 'A')
    end

    @log.info("Deleting node/client from Chef: #{server_name}")
    node_result = `echo "Y" |knife node delete #{server_name}`
    client_result = `echo "Y" |knife client delete #{server_name}`
    @log.debug("Deleted Chef node: #{node_result}")
    @log.debug("Deleted Chef client: #{client_result}")
  else
    @log.error("Unable to find instance: #{server_name}")
  end
end