class EC2Launcher::Launcher

Public Class Methods

new() click to toggle source
# File lib/ec2launcher.rb, line 41
def initialize()
  spec = Gem::Specification.find_by_name("ec2launcher")
  @gem_install_dir = spec.gem_dir
  @startup_scripts_dir = File.join(@gem_install_dir, "startup-scripts")
  @run_url_script_cache = nil
  @setup_script_cache = nil
  @setup_instance_script_cache = nil

  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
end

Public Instance Methods

attach_to_elb(instance, elb_name, subnet = nil) click to toggle source

Attaches an instance to the specified ELB.

@param [AWS::EC2::Instance] instance newly created EC2 instance. @param [String] elb_name name of ELB. @param [String] subnet subnet name or id. Defaults to nil.

# File lib/ec2launcher.rb, line 499
def attach_to_elb(instance, elb_name, subnet = nil)
  begin
    @log.info ""
    @log.info "Adding to ELB: #{elb_name}"
    elb = AWS::ELB.new
    AWS.memoize do
      unless subnet
        # Build list of availability zones for any existing instances
        zones = { }
        zones[instance.availability_zone] = instance.availability_zone
        elb.load_balancers[elb_name].instances.each do |elb_instance|
          zones[elb_instance.availability_zone] = elb_instance.availability_zone
        end
    
        # Build list of existing zones
        existing_zones = { }
        elb.load_balancers[elb_name].availability_zones.each do |zone|
          existing_zones[zone.name] = zone
        end
    
        # Enable zones
        zones.keys.each do |zone_name|
          elb.load_balancers[elb_name].availability_zones.enable(zones[zone_name])
        end
    
        # Disable zones
        existing_zones.keys.each do |zone_name|
          elb.load_balancers[elb_name].availability_zones.disable(existing_zones[zone_name]) unless zones.has_key?(zone_name)
        end
      end
  
      elb.load_balancers[elb_name].instances.register(instance)
    end
  rescue StandardError => bang
    @log.error "Error adding to load balancers: " + bang.to_s
  end
end
build_commands(commands) click to toggle source

Builds a bash script snipp containing a list of commands to execute.

@param [Array<String>] commands List of commands to run. Can be nil.

@return [String] String containing newline separated bash commands to run or an empty string if no commands.

# File lib/ec2launcher.rb, line 910
def build_commands(commands)
  command_str = ""
  unless commands.nil? || commands.empty?
    processed_commands = commands.collect {|cmd| substitute_command_variables(cmd) }
    command_str = "\n" + processed_commands.join("\n") + "\n"
  end
  command_str
end
build_launch_command(launch_options) click to toggle source

Builds the launch scripts that should run on the new instance.

launch_options = {

:ami
:availability_zone
:aws_keyfile
:block_device_mappings
:block_device_tags
:chef_validation_pem_url
:email_notifications
:fqdn
:gems
:iam_profile
:instance_type
:key
:packages
:roles
:security_group_ids
:short_name
:subnet

}

@return [String] Launch commands to pass into new instance as userdata

# File lib/ec2launcher.rb, line 782
    def build_launch_command(launch_options)
      # Build JSON for setup scripts

      # Require ec2launcher gem if cloning and using provisioned IOPS
      setup_json = {
        'hostname' => launch_options[:fqdn],
        'short_hostname' => launch_options[:short_name],
        'block_device_mappings' => launch_options[:block_device_mappings],
        'roles' => launch_options[:roles],
        'chef_server_url' => @environment.chef_server_url,
        'chef_validation_pem_url' => launch_options[:chef_validation_pem_url],
        'aws_keyfile' => launch_options[:aws_keyfile],
        'gems' => launch_options[:gems],
        'packages' => launch_options[:packages],
        'provisioned_iops' => false,
        'dynamic_name' => @options.dynamic_name,
        'domain_name' => @environment.domain_name
      }
      if @options.dynamic_name
        # setup_json['dynamic_name_prefix'] = @hostname_generator.prefix
        setup_json['dynamic_name_suffix'] = "#{@hostname_generator.prefix}.#{@hostname_generator.suffix}"
        setup_json['route53_zone_id'] = @route53_zone_id
      end

      setup_json["gem_path"] = @instance_paths.gem_path
      setup_json["ruby_path"] = @instance_paths.ruby_path
      setup_json["chef_path"] = @instance_paths.chef_path
      setup_json["knife_path"] = @instance_paths.knife_path

      unless @application.block_devices.nil? || @application.block_devices.empty?
        setup_json['block_devices'] = @application.block_devices

        @application.block_devices.each do |bd|
          if bd.provisioned_iops?
            setup_json['provisioned_iops'] = true
            break
          end
        end
      end
      unless launch_options[:email_notifications].nil?
        setup_json['email_notifications'] = launch_options[:email_notifications]
      end

      ##############################
      # Build launch command
      json_text = @options.pretty_print ? JSON.pretty_generate(setup_json) : setup_json.to_json
      user_data = <<EOF
#!/bin/bash
cat > /tmp/setup.json <<End-Of-Message-JSON
#{json_text}
End-Of-Message-JSON
EOF
      if @environment.use_rvm or @application.use_rvm
        user_data += <<-EOF
export HOME=/root
if [[ -s "/etc/profile.d/rvm.sh" ]] ; then
  source "/etc/profile.d/rvm.sh"
fi
EOF
      end

      # pre-commands, if necessary
      user_data += build_commands(@environment.precommand)
      user_data += build_commands(@application.precommand)

      unless @options.skip_setup
        if @run_url_script_cache.nil?
          @run_url_script_cache = load_and_encode_file_with_path(@startup_scripts_dir, "runurl")
        end

        if @setup_script_cache.nil?
          @setup_script_cache = load_and_encode_file_with_path(@startup_scripts_dir, "setup.rb")
        end

        if @setup_instance_script_cache.nil?
          @setup_instance_script_cache = load_and_encode_file_with_path(@startup_scripts_dir, "setup_instance.rb")
        end

        # runurl script
        user_data += "cat > /tmp/runurl.gz.base64 <<End-Of-Message\n"
        user_data += @run_url_script_cache
        user_data += "End-Of-Message"

        # setup scripts
        user_data += "\ncat > /tmp/setup.rb.gz.base64 <<End-Of-Message2\n"
        user_data += @setup_script_cache
        user_data += "End-Of-Message2"

        user_data += "\ncat > /tmp/setup_instance.rb.gz.base64 <<End-Of-Message3\n"
        user_data += @setup_instance_script_cache
        user_data += "End-Of-Message3"

        user_data += "\nbase64 -d /tmp/runurl.gz.base64 | gunzip > /tmp/runurl"
        user_data += "\nchmod +x /tmp/runurl"
        user_data += "\nrm -f /tmp/runurl.gz.base64"

        user_data += "\nbase64 -d /tmp/setup.rb.gz.base64 | gunzip > /tmp/setup.rb"
        user_data += "\nchmod +x /tmp/setup.rb"
        user_data += "\nrm -f /tmp/setup.rb.gz.base64"

        user_data += "\nbase64 -d /tmp/setup_instance.rb.gz.base64 | gunzip > /tmp/setup_instance.rb"
        user_data += "\nchmod +x /tmp/setup_instance.rb"
        user_data += "\nrm -f /tmp/setup_instance.rb.gz.base64"

        user_data += "\ngem install ec2launcher --no-ri --no-rdoc"

        user_data += "\n#{setup_json['ruby_path']} /tmp/setup.rb -e #{@environment.name} -a #{@application.name}" 
        user_data += " -h #{launch_options[:fqdn]}" if launch_options[:fqdn]
        user_data += " /tmp/setup.json"
        user_data += " -c #{@options.clone_host}" unless @options.clone_host.nil?
        user_data += " &> /var/log/cloud-startup.log"
      end

      # Add extra requested commands to the launch sequence
      user_data += build_commands(@options.commands)

      # Post commands
      user_data += build_commands(@environment.postcommand)
      user_data += build_commands(@application.postcommand)

      user_data
    end
find_ami(arch, virtualization, ami_name_match, id = nil) click to toggle source

Searches for the most recent AMI matching the criteria.

@param [String] arch system archicture, ‘i386` or `x86_64` @param [String] virtualization virtualization type, `paravirtual` or `hvm` @param [Regex] ami_name_match regular expression that describes the AMI. @param [String, nil] id id of an AMI. If not nil, ami_name_match is ignored.

@return [AmiDetails] AMI name and id.

# File lib/ec2launcher.rb, line 545
def find_ami(arch, virtualization, ami_name_match, id = nil)
  @log.info "Searching for AMI..."
  ami_name = ""
  ami_id = ""

  # Retrieve list of AMIs
  my_images = @ec2.images.with_owner('self')

  if id.nil?
    # Search for latest AMI with the right architecture and virtualization
    my_images.each do |ami|
      next if arch != ami.architecture.to_s
      next if virtualization != ami.virtualization_type.to_s
      next unless ami.state == :available

      next if ! ami.name.match(ami_name_match)

      if ami.name > ami_name
          ami_name = ami.name
          ami_id = ami.id
      end
    end
  else
    # Look for specified AMI
    ami_arch = nil
    my_images.each do |ami|
      next if ami.id != id
      ami_id = id
      ami_name = ami.name
      ami_arch = ami.architecture
    end

    # Check that AMI exists
    if ami_id.nil?
      abort("AMI id not found: #{ami_id}")
    end

    if arch != ami_arch.to_s
      abort("Invalid AMI selection. Architecture for instance type (#{instance_type} - #{instance_architecture} - #{instance_virtualization}) does not match AMI arch (#{ami_arch.to_s}).")
    end
  end

  AmiDetails.new(ami_name, ami_id)
end
get_instance_dns(ec2_instance, public = true) click to toggle source

Retrieves either the public or private DNS entry for an EC2 Instance. Returns “n/a” if the request DNS entry is undefined.

@param [AWS::EC2::Instance] ec2 instance @param [Boolean] True for public DNS or False for private DNS

@return [String] DNS for the instance or “n/a” if undefined.

# File lib/ec2launcher.rb, line 599
def get_instance_dns(ec2_instance, public = true)
  dns_name = public ? ec2_instance.public_dns_name : ec2_instance.private_dns_name
  dns_name.nil? ? "n/a" : dns_name
end
launch(options) click to toggle source
# File lib/ec2launcher.rb, line 62
def launch(options)
  @options = options

  @log.info "ec2launcher v#{EC2Launcher::VERSION}"

  @log.level = case @options.verbosity 
    when :quiet then WARN
    when :verbose then DEBUG
    else INFO
  end
  
  # Load configuration data
  config_wrapper = ConfigWrapper.new(@options.directory)

  @config = config_wrapper.config
  @environments = config_wrapper.environments
  @applications = config_wrapper.applications

  if @options.list
    puts ""
    env_names = @environments.keys.sort.join(", ")
    puts "Environments: #{env_names}"

    app_names = @applications.keys.sort.join(", ")
    puts "Applications: #{app_names}"
    exit 0
  end

  ##############################
  # ENVIRONMENT
  ##############################
  unless @environments.has_key? options.environ
    @log.fatal "Environment not found: #{options.environ}"
    exit 2
  end
  @environment = @environments[options.environ]

  ##############################
  # APPLICATION
  ##############################
  unless @applications.has_key? options.application
    @log.fatal "Application not found: #{options.application}"
    exit 3
  end
  @application = @applications[options.application]

  ##############################
  # INSTANCE PATHS
  ##############################
  @instance_paths = EC2Launcher::InstancePathsConfig.new(@environment)

  ##############################
  # Initialize AWS and create EC2 connection
  ##############################
  initialize_aws(@options.access_key, @options.secret)
  @ec2 = AWS::EC2.new

  ##############################
  # Create Route53 connection
  ##############################
  @route53 = nil
  @route53_zone_id = nil
  @route53_domain_name = nil
  if @environment.route53_zone_id
    aws_route53 = AWS::Route53.new 
    @route53 = EC2Launcher::Route53.new(aws_route53, @environment.route53_zone_id, @log)
    @route53_zone_id = @environment.route53_zone_id
    route53_zone = aws_route53.client.get_hosted_zone({:id => @environment.route53_zone_id})
    @route53_domain_name = route53_zone[:hosted_zone][:name].chop
  end

  ##############################
  # SUBNET
  ##############################
  subnet = nil
  subnet = @application.subnet unless @application.subnet.nil?
  subnet ||= @environment.subnet unless @environment.subnet.nil?

  ec2_subnet = nil
  unless subnet.nil?
    # Find the matching EC2 subnet
    ec2_subnet = @ec2.subnets[subnet]
  end

  ##############################
  # AVAILABILITY ZONES
  ##############################
  availability_zone = options.zone
  if availability_zone.nil?
    availability_zone = @application.availability_zone
    availability_zone ||= @environment.availability_zone
    availability_zone ||= "us-east-1a"
  end

  ##############################
  # SSH KEY
  ##############################
  key_name = @environment.key_name
  if key_name.nil?
    @log.fatal "Unable to determine SSH key name."
    exit 4
  end

  ##############################
  # SECURITY GROUPS
  ##############################
  security_groups = []
  security_groups += @environment.security_groups_for_environment(@environment.name) unless @environment.security_groups_for_environment(@environment.name).nil?
  security_groups += @application.security_groups_for_environment(@environment.name)

  # Build mapping of existing security group names to security group objects
  sg_map = { }
  AWS.start_memoizing
  @ec2.security_groups.each do |sg|
    next if ec2_subnet.nil? && sg.vpc_id
    next if ec2_subnet && ec2_subnet.vpc_id != sg.vpc_id
    sg_map[sg.name] = sg
  end
  AWS.stop_memoizing

  # Convert security group names to security group ids
  security_group_ids = []
  missing_security_groups = []
  security_groups.each do |sg_name|
    if sg_map.has_key?(sg_name)
      security_group_ids << sg_map[sg_name].security_group_id
    else
      missing_security_groups << sg_name
    end
  end

  if missing_security_groups.length > 0
    @log.fatal "ERROR: Missing security groups: #{missing_security_groups.join(', ')}"
    exit 3
  end

  ##############################
  # IAM PROFILE
  ##############################
  iam_profile = @application.iam_profile_for_environment(@environment.name)
  iam_profile ||= @environment.iam_profile

  ##############################
  # INSTANCE TYPE
  ##############################
  instance_type = options.instance_type
  instance_type ||= @application.instance_type
  instance_type ||= "m1.small"

  ##############################
  # ARCHITECTURE
  ##############################
  instance_architecture = "x86_64"

  instance_virtualization = case instance_type
    when "cc1.4xlarge" then "hvm"
    when "cc2.8xlarge" then "hvm"
    when "cg1.4xlarge" then "hvm"
    else "paravirtual"
  end

  ##############################
  # AMI
  ##############################
  ami_name_match = @application.ami_name
  ami_name_match ||= @environment.ami_name
  ami = nil
  run_with_backoff(60, 1, "searching for ami") do
    ami = find_ami(instance_architecture, instance_virtualization, ami_name_match, @options.ami_id)
  end

  ##############################
  # DOMAIN NAME
  ##############################

  # Note: Route53 domain names override domain names specified in the environments
  @domain_name = @route53_domain_name
  @domain_name ||= @environment.domain_name

  ##############################
  # HOSTNAME
  ##############################
  @hostname_generator = EC2Launcher::HostnameGenerator.new(@ec2, @environment, @application)
  short_hostnames = []
  fqdn_names = []
  unless @options.dynamic_name
    if @options.count > 1
      1.upto(@options.count).each do |i|
        short_hostname = @hostname_generator.generate_hostname()
        long_hostname = @hostname_generator.generate_fqdn(short_hostname, @domain_name)
        short_hostnames << short_hostname
        fqdn_names << long_hostname
      end
    else
      if @options.hostname.nil?
        short_hostname = @hostname_generator.generate_hostname()
        long_hostname = @hostname_generator.generate_fqdn(short_hostname, @domain_name)
      else
        long_hostname = @options.hostname
        short_hostname = @hostname_generator.generate_short_name(long_hostname, @environment.domain_name)
        if long_hostname == short_hostname
          long_hostname = @hostname_generator.generate_fqdn(short_hostname, @environment.domain_name)
        end
      end
      short_hostnames << short_hostname
      fqdn_names << long_hostname
    end
  end

  ##############################
  # Block devices
  ##############################
  @block_device_builder = EC2Launcher::BlockDeviceBuilder.new(@ec2, @options.volume_size)
  block_device_mappings = @block_device_builder.generate_block_devices(instance_type, @environment, @application, @options.clone_host)

  ##############################
  # ELB
  ##############################
  elb_name = nil
  elb_name = @application.elb_for_environment(@environment.name) unless @application.elb.nil?

  ##############################
  # Roles
  ##############################
  roles = []
  roles += @environment.roles unless @environment.roles.nil?
  roles += @application.roles_for_environment(@environment.name)

  ##############################
  # Gems - preinstall
  ##############################
  gems = []
  gems += @environment.gems unless @environment.gems.nil?
  gems += @application.gems unless @application.gems.nil?
  gems << {"name" => "ec2launcher", "version" => VERSION}

  ##############################
  # Packages - preinstall
  ##############################
  packages = []
  packages += @environment.packages unless @environment.packages.nil?
  packages += @application.packages unless @application.packages.nil?

  ##############################
  # Email Notification
  ##############################
  email_notifications = nil
  email_notifications = @application.email_notifications
  email_notifications ||= @environment.email_notifications

  ##############################
  # Chef Validation PEM
  ##############################
  chef_validation_pem_url = nil
  chef_validation_pem_url = @options.chef_validation_url
  chef_validation_pem_url ||= @environment.chef_validation_pem_url

  ##############################
  # File on new instance containing AWS keys
  ##############################
  aws_keyfile = @environment.aws_keyfile

  ##############################
  @log.info
  @log.info "Availability zone   : #{availability_zone}"
  @log.info "Key name            : #{key_name}"
  @log.info "Security groups     : " + security_groups.collect {|name| "#{name} (#{sg_map[name].security_group_id})"}.join(", ")
  @log.info "IAM profile         : #{iam_profile}" if iam_profile
  @log.info "Instance type       : #{instance_type}"
  @log.info "Architecture        : #{instance_architecture}"
  @log.info "AMI name            : #{ami.ami_name}"
  @log.info "AMI id              : #{ami.ami_id}"
  @log.info "ELB                 : #{elb_name}" if elb_name
  @log.info "Route53 Zone        : #{@route53_domain_name}" if @route53_domain_name
  @log.info "Chef PEM            : #{chef_validation_pem_url}"
  @log.info "AWS key file        : #{aws_keyfile}"
  @log.info "Roles               : #{roles.join(', ')}"
  
  gem_list = []
  gems.each do |gem_info|
    if gem_info.kind_of? Hash
      gem_name = gem_info[:name]
      gem_version = gem_info[:version]
      next unless gem_name

      gem_details = gem_name
      gem_details += " (#{gem_version})" if gem_version

      gem_list << gem_details
    else
      gem_list << gem_info
    end
  end
  @log.info "Gems                : #{gem_list.join(', ')}"
  
  @log.info "Packages            : #{packages.join(', ')}"
  if subnet
    cidr_block = @ec2.subnets[subnet].cidr_block
    @log.info "VPC Subnet          : #{subnet} (#{cidr_block})"
  end
  @log.info ""
  if @options.dynamic_name
      @log.info "Name                : **dynamic**"
  else
    fqdn_names.each do |fqdn|
      @log.info "Name                : #{fqdn}"
    end
  end

  unless block_device_mappings.empty?
    @log.info ""
    @log.info "Block devices     :"
    block_device_mappings.keys.sort.each do |key|
      if block_device_mappings[key] =~ /^ephemeral/
          @log.info "  Block device   : #{key}, #{block_device_mappings[key]}"
      else
          block_device_text = "  Block device   : #{key}, #{block_device_mappings[key][:volume_size]}GB, "
          block_device_text += "#{block_device_mappings[key][:snapshot_id]}" if block_device_mappings[key][:snapshot_id]
          block_device_text += ", (#{block_device_mappings[key][:delete_on_termination] ? 'auto-delete' : 'no delete'}), "
          block_device_text += "(#{block_device_mappings[key][:iops].nil? ? 'standard' : block_device_mappings[key][:iops].to_s} IOPS)"
          @log.info block_device_text
      end
    end
  end

  if chef_validation_pem_url.nil?
    @log.fatal "***ERROR*** Missing the URL For the Chef Validation PEM file."
    exit 3
  end

  # Launch options
  launch_options = {
    :ami => ami.ami_id,
    :availability_zone => availability_zone,
    :aws_keyfile => aws_keyfile,
    :block_device_mappings => block_device_mappings,
    :chef_validation_pem_url => chef_validation_pem_url,
    :email_notifications => email_notifications,
    :environment => @environment.name,
    :gems => gems, 
    :iam_profile => iam_profile,
    :instance_type => instance_type,
    :key => key_name,
    :packages => packages,
    :provisioned_iops => @application.has_provisioned_iops?(),
    :roles => roles, 
    :security_group_ids => security_group_ids,
    :subnet => subnet
  }

  # Quit if we're only displaying the defaults
  exit 0 if @options.show_defaults

  if @options.show_user_data
    custom_launch_options = launch_options
    unless @options.dynamic_name
      custom_launch_options = launch_options.merge({
        :fqdn => fqdn_names[0],
        :short_name => short_hostnames[0]
      })
    end

    user_data = build_launch_command(custom_launch_options)

    @log.info ""
    @log.info "---user-data---"
    @log.info user_data
    @log.info "---user-data---"

    exit 0
  end

  ##############################
  # Launch the new intance
  ##############################
  @log.warn ""
  instances = []
  new_instance_names = []
  0.upto(@options.count - 1).each do |i|
    fqdn = nil
    short_hostname = nil

    unless @options.dynamic_name
      fqdn = fqdn_names[i]
      short_hostname = short_hostnames[i]
    end

    block_device_tags = @block_device_builder.generate_device_tags(fqdn, short_hostname, @environment.name, @application.block_devices)
    launch_options.merge!({
      :fqdn => fqdn,
      :short_name => short_hostname,
      :block_device_tags => block_device_tags,
    })
    user_data = build_launch_command(launch_options)

    instance = launch_instance(launch_options, user_data)
    instances << instance

    if @options.dynamic_name
      short_hostname = @hostname_generator.generate_dynamic_hostname(instance.id)
      fqdn = @hostname_generator.generate_fqdn(short_hostname, @environment.domain_name)
    end

    public_dns_name = get_instance_dns(instance, true)
    private_dns_name = get_instance_dns(instance, false)
    @log.info "Launched #{fqdn} (#{instance.id}) [#{public_dns_name} / #{private_dns_name} / #{instance.private_ip_address} ]"

    new_instance_names << fqdn
  end

  @log.info "********************"    
  new_instance_names.each_index do |i|
    public_dns_name = get_instance_dns(instances[i], true)
    private_dns_name = get_instance_dns(instances[i], false)
    @log.warn "** New instance: #{new_instance_names[i]} | #{instances[i].id} | #{public_dns_name} | #{private_dns_name} | #{instances[i].private_ip_address}"
  end

  ##############################
  # ELB
  ##############################
  unless elb_name.nil?
    instances.each {|instance| attach_to_elb(instance, elb_name, ec2_subnet) }
  end

  ##############################
  # COMPLETED
  ##############################
  @log.info "********************"
  instances
end
launch_instance(launch_options, user_data) click to toggle source

Launches an EC2 instance.

launch_options = {

:ami
:availability_zone
:aws_keyfile
:block_device_mappings
:block_device_tags
:chef_validation_pem_url
:email_notifications
:fqdn
:gems
:iam_profile
:instance_type
:key
:packages
:roles
:security_group_ids
:short_name
:subnet

}

@return [AWS::EC2::Instance] newly created EC2 instance or nil if the launch failed.

# File lib/ec2launcher.rb, line 628
def launch_instance(launch_options, user_data)
  @log.warn "Launching instance... #{launch_options[:fqdn]}"
  new_instance = nil
  run_with_backoff(30, 1, "launching instance") do
    launch_mapping = {
        :image_id => launch_options[:ami],
        :availability_zone => launch_options[:availability_zone],
        :key_name => launch_options[:key],
        :security_group_ids => launch_options[:security_group_ids],
        :user_data => user_data,
        :instance_type => launch_options[:instance_type]
    }
    unless launch_options[:block_device_mappings].nil? || launch_options[:block_device_mappings].keys.empty?
      if launch_options[:provisioned_iops]
        # Only include ephemeral devices if we're using provisioned IOPS for the EBS volumes
        launch_mapping[:block_device_mappings] = {}
        launch_options[:block_device_mappings].keys.sort.each do |block_device_name|
          if launch_options[:block_device_mappings][block_device_name] =~ /^ephemeral/
            launch_mapping[:block_device_mappings][block_device_name] = launch_options[:block_device_mappings][block_device_name]
          end
        end
      else
        launch_mapping[:block_device_mappings] = launch_options[:block_device_mappings]
      end

      # Remove the block_device_mappings entry if it's empty. Otherwise the AWS API will throw an error.
      launch_mapping.delete(:block_device_mappings) if launch_mapping[:block_device_mappings].keys.empty?
    end

    launch_mapping[:iam_instance_profile] = launch_options[:iam_profile] if launch_options[:iam_profile]
    launch_mapping[:subnet] = launch_options[:subnet] if launch_options[:subnet]

    new_instance = @ec2.instances.create(launch_mapping)
  end
  sleep 5

  @log.info "  Waiting for instance to start up..."
  sleep 2
  instance_ready = false
  until instance_ready
    sleep 1
    begin
      instance_ready = new_instance.status != :pending
    rescue
    end
  end

  unless new_instance.status == :running
    @log.fatal "Instance launch failed. Aborting."
    exit 5
  end

  if @options.dynamic_name
    launch_options[:short_name] = @hostname_generator.generate_dynamic_hostname(new_instance.id)
    launch_options[:fqdn] = @hostname_generator.generate_fqdn(launch_options[:short_name], @environment.domain_name)
    launch_options[:block_device_tags] = @block_device_builder.generate_device_tags(launch_options[:fqdn], launch_options[:short_name], @environment.name, @application.block_devices)
  end

  ##############################
  # Tag instance
  @log.info "Tagging instance..."
  run_with_backoff(30, 1, "tag #{new_instance.id}, tag: name, value: #{launch_options[:fqdn]}") { new_instance.add_tag("Name", :value => launch_options[:fqdn]) }
  run_with_backoff(30, 1, "tag #{new_instance.id}, tag: short_name, value: #{launch_options[:short_name]}") { new_instance.add_tag("short_name", :value => launch_options[:short_name]) }
  run_with_backoff(30, 1, "tag #{new_instance.id}, tag: environment, value: #{@environment.name}") { new_instance.add_tag("environment", :value => @environment.name) }
  run_with_backoff(30, 1, "tag #{new_instance.id}, tag: application, value: #{@application.name}") { new_instance.add_tag("application", :value => @application.name) }
  if @options.clone_host
    run_with_backoff(30, 1, "tag #{new_instance.id}, tag: cloned_from, value: #{@options.clone_host}") { new_instance.add_tag("cloned_from", :value => @options.clone_host) }
  end

  ##############################
  # Tag volumes
  unless launch_options[:provisioned_iops] || launch_options[:block_device_tags].empty?
    @log.info "Tagging volumes..."
    AWS.start_memoizing
    launch_options[:block_device_tags].keys.each do |device|
      v = new_instance.block_device_mappings[device].volume
      launch_options[:block_device_tags][device].keys.each do |tag_name|
        run_with_backoff(30, 1, "tag #{v.id}, tag: #{tag_name}, value: #{launch_options[:block_device_tags][device][tag_name]}") do
          v.add_tag(tag_name, :value => launch_options[:block_device_tags][device][tag_name])
        end
      end
    end
    AWS.stop_memoizing
  end

  ##############################
  # Add to Route53
  if @route53
    @log.info "Adding A record to Route53: #{launch_options[:fqdn]} => #{new_instance.private_ip_address}"
    @route53.create_record(launch_options[:fqdn], new_instance.private_ip_address, 'A')
  end

  new_instance
end
load_and_encode_file(pathname) click to toggle source
# File lib/ec2launcher.rb, line 750
def load_and_encode_file(pathname)
  `cat #{pathname} |sed '/^[ \t]*$/d'|sed '/^[ \t]*#/d'|gzip -f |base64`
end
load_and_encode_file_with_path(base_path, filename) click to toggle source
# File lib/ec2launcher.rb, line 754
def load_and_encode_file_with_path(base_path, filename)
  pathname = File.join(base_path, filename)
  load_and_encode_file(pathname)
end
substitute_command_variables(cmd) click to toggle source

Given a string containing a command to run, replaces any inline variables. Supported variables include:

* @APPLICATION@ - name of the application
* @APP@ - name of the application
* @ENVIRONMENT@ - name of the environment
* @ENV@ - name of the environment
* @RUBY@ - Full pathname to the ruby executable
* @GEM@ - Full pathname to the gem executable
* @CHEF@ - Full pathname to the chef-client executable
* @KNIFE@ - Full pathname to the knife executable

@return [String] command with variables replaced

# File lib/ec2launcher.rb, line 735
def substitute_command_variables(cmd)
  substitutions = {
    /@APPLICATION@/ => @application.name,
    /@APP@/ => @application.name,
    /@ENVIRONMENT@/ => @environment.name,
    /@ENV@/ => @environment.name,
    /@RUBY@/ => @instance_paths.ruby_path,
    /@GEM@/ => @instance_paths.gem_path,
    /@CHEF@/ => @instance_paths.chef_path,
    /@KNIFE@/ => @instance_paths.knife_path
  }
  substitutions.keys.each {|key| cmd.gsub!(key, substitutions[key]) }
  cmd
end