class AwsRegion::AwsInstance

Class to handle EC2 Instances

Attributes

_instance[RW]
id[RW]
private_ip[RW]
public_ip[RW]
region[RW]
tags[RW]

Public Class Methods

new(region, options = {}) click to toggle source
# File lib/aws_region.rb, line 478
def initialize(region, options = {})
  @tags = {}
  @region = region
  if options.has_key?(:instance)
    @_instance = options[:instance]
    @id = @_instance[:instance_id]
    @public_ip = @_instance[:public_ip_address]
    @private_ip = @_instance[:private_ip_address]
  else
    resp = @region.ec2.run_instances(options[:template])
    raise "Error creating instance using options" if resp.nil? or resp[:instances].length <= 0
    @_instance = resp[:instances][0]
    @id = @_instance[:instance_id]
    @tags = options[:tags]
    self.add_tags(@tags)
    self.wait
    instance = @region.ec2.describe_instances(:instance_ids => [@id])[0][0].instances[0]
    @public_ip = instance[:public_ip_address]
    @private_ip = instance[:private_ip_address]
    raise "could not get ip address" if @public_ip.nil? && @private_ip.nil?
    self.inject_into_environment
  end
  @_instance.tags.each do |t|
    @tags[t[:key].to_sym] = t[:value]
  end
end

Public Instance Methods

add_tags(h_tags) click to toggle source

Add tags to an instance @param h_tags [Hash] - Hash of tags to add to instance

# File lib/aws_region.rb, line 554
def add_tags(h_tags)
  tags = []
  h_tags.each do |k, v|
    tags << {:key => k.to_s, :value => v}
  end
  resp = @region.ec2.create_tags({:resources => [@id],
                                  :tags => tags})
end
add_to_lb(lb_name) click to toggle source

Add an instance to an elastic lb @param lb_name [String] - Name of elastic load balancer

# File lib/aws_region.rb, line 565
def add_to_lb(lb_name)
  @region.elb.register_instances_with_load_balancer({:load_balancer_name => lb_name,
                                                     :instances => [{:instance_id => @id}]})
end
authorize_sg_ingress(groups) click to toggle source

authorize security group ingress for public ip of this instance on port @param groups [Array] - each element is String: “security_group_id:port”. For example: [“sg-0xxxxx:80”, “sg-0xxxxx:8443”, “sg-0yyyyy:3000”]

# File lib/aws_region.rb, line 702
def authorize_sg_ingress(groups)
  raise "no public ip" unless @public_ip.to_s.match /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
  groups.each do |gp|
    options = get_simple_sg_options(gp)
    if has_sg_rule?(gp)
      log "security group rule #{gp} for #{self.public_ip} already exists"
    else
      @region.ec2.authorize_security_group_ingress options
      log "added #{self.public_ip} to security group for :port #{gp}"
    end
  end
end
connect() click to toggle source

Connects using ssh to an ec2 instance

# File lib/aws_region.rb, line 617
def connect
  if self.state(use_cached_state = false) != "running"
    log "Cannot connect, instance: #{@region.region}://#{@id} due to its state: #{self.state}"
    return
  end
  ip = (self.public_ip && self.public_ip.strip() != "") ? self.public_ip : self.private_ip
  log "Connecting: ssh -i ~/.ssh/aws/#{@tags[:key]} #{@tags[:user]}@#{ip}"
  exec "ssh -i ~/.ssh/aws/#{@tags[:key]} #{@tags[:user]}@#{ip}"
end
eject_from_environment() click to toggle source
# File lib/aws_region.rb, line 626
def eject_from_environment
  if @tags.has_key?(:elastic_lb)
    log "Removing instance: #{@id} from '#{@tags[:elastic_lb]}' load balancer"
    self.remove_from_lb(tags[:elastic_lb])
  end
  if @tags.has_key?(:security_groups_foreign)
    self.revoke_sg_ingress(@tags[:security_groups_foreign].split(","))
  end
end
get_simple_sg_options(group_id_port) click to toggle source

construct hash for authorize/revoke ingress and has_sg_rule? @param group_id_port [String] - security_group:port like “sg_xxxxxx:8080” @return [Hash] - Hash for ec2 call for security group management

# File lib/aws_region.rb, line 740
def get_simple_sg_options(group_id_port)
  security_group_id, port = group_id_port.split(':')
  port = port.to_s.to_i
  raise "no security group id" unless security_group_id.to_s.length > 0
  raise "no, or invalid port" unless port.to_s.to_i > 0
  {:group_id => security_group_id,
   :ip_permissions => [ :ip_protocol => "tcp",
                        :from_port => port,
                        :to_port => port,
                        :ip_ranges => [:cidr_ip => "#{self.public_ip}/32"]
                      ]
  }
end
has_sg_rule?(group_port) click to toggle source

Does a security group allow ingress on a port for the public IP of this instance @param group_port [String] - security_group:port like “sg_xxxxxx:8080” @return [Boolean] true if the security group allows ingress for the public IP of this instance on a certain port

# File lib/aws_region.rb, line 682
def has_sg_rule?(group_port)
  options = get_simple_sg_options(group_port)
  options_cidr_ip = options[:ip_permissions][0][:ip_ranges][0][:cidr_ip]
  group_id = options[:group_id]
  raise "missing security group_id" if group_id.nil?
  sg = @region.ec2.describe_security_groups(:group_ids => [group_id]).data.security_groups[0]
  sg.ip_permissions.each do |p|
    if p.ip_protocol == "tcp" &&
        p.from_port == options[:ip_permissions][0][:from_port] &&
        p.to_port == options[:ip_permissions][0][:to_port]
      p[:ip_ranges].each do |ipr|
        return true if ipr.cidr_ip == options_cidr_ip
      end
    end
  end
  false
end
inject_into_environment() click to toggle source
# File lib/aws_region.rb, line 636
def inject_into_environment
  if @tags.has_key?(:elastic_ip)
    @region.ec2.associate_address({:instance_id => @id, :public_ip => @tags[:elastic_ip]})
    log "Associated ip: #{@tags[:elastic_ip]} with instance: #{@id}"
  elsif @tags.has_key?(:elastic_ip_allocation_id)
    @region.ec2.associate_address({:instance_id => @id, :allocation_id => @tags[:elastic_ip_allocation_id]})
    log "Associated allocation id: #{@tags[:elastic_ip_allocation_id]} with instance: #{@id}"
  end
  if @tags.has_key?(:mount_points)
    mounts = @tags[:mount_points].split(";")
    mounts.each do |mnt|
      (volume_id,device) = mnt.split(",")
      log "Mounting volume: #{volume_id} on #{device}"
      self.mount(volume_id, device)
    end
  end
  if @tags.has_key?(:security_group_ids)
    self.set_security_groups(@tags[:security_group_ids].split(","))
  end
  if @tags.has_key?(:security_groups_foreign)
    self.authorize_sg_ingress(@tags[:security_groups_foreign].split(","))
  end

  # if any of the above fails, we probably do not want it in the lb
  if @tags.has_key?(:elastic_lb)
    self.add_to_lb(@tags[:elastic_lb])
    log "Adding instance: #{@id} to '#{@tags[:elastic_lb]}' load balancer"
  end
end
mount(volume_id, device) click to toggle source
# File lib/aws_region.rb, line 731
def mount(volume_id, device)
  @region.ec2.attach_volume({:instance_id => @id,
                             :volume_id => volume_id,
                             :device => device})
end
remove_from_lb(lb_name) click to toggle source

Remove instance from elastic lb @param instance [AwsInstance] Instance to remove from lb @param lb_name [String] Lb name from which the instance is to be removed @return [Aws::PageableResponse]

# File lib/aws_region.rb, line 574
def remove_from_lb(lb_name)
  lb = @region.elb.describe_load_balancers({:load_balancer_names => [lb_name]})
  if lb and lb[:load_balancer_descriptions].length > 0
    lb[:load_balancer_descriptions][0][:instances].each do |lb_i|
      if lb_i[:instance_id] == @id
        @elb.deregister_instances_from_load_balancer({:load_balancer_name => lb_name,
                                                      :instances => [{:instance_id => @id}]})
        sleep 30
      end
    end
  end
end
revoke_sg_ingress(groups) click to toggle source

revoke security group ingress for public ip of this instance on port @param groups [Array] - each element is String: “security_group_id:port”. For example: [“sg-0xxxxx:80”, “sg-0xxxxx:8443”, “sg-0yyyyy:3000”]

# File lib/aws_region.rb, line 717
def revoke_sg_ingress(groups)
  # revoke the public ip of this instance for ingress on port for security group
  # groups is array of strings: security_group_id:port
  raise "no public ip" unless @public_ip.to_s.match /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
  groups.each do |gp|
    options = get_simple_sg_options(gp)
    if has_sg_rule?(gp)
      @region.ec2.revoke_security_group_ingress options
      log "removed #{self.public_ip} from security group for :port #{gp}"
    else
      log "not removing #{self.public_ip} rule #{gp} because it does not exist"
    end
  end
end
set_security_groups(groups) click to toggle source
# File lib/aws_region.rb, line 674
def set_security_groups(groups)
  # only works on instances in a vpc
  @region.ec2.modify_instance_attribute({:instance_id => @id, :groups => groups})
end
start(wait=false) click to toggle source

Start an EC2 instance @param wait [Boolean] - When true, will wait for instance to move into “running” state before returning

# File lib/aws_region.rb, line 526
def start(wait=false)
  if self.state(use_cached_state = false) != "stopped"
    log "Instance cannot be started - #{@region.region}://#{@id} is in the state: #{self.state}"
    return
  end
  log "Starting instance: #{@region.region}://#{@id}"
  @region.ec2.start_instances({:instance_ids => [@id]})
  if wait
    begin
      sleep 10
      log "Starting instance: #{@region.region}://#{@id} - state: #{self.state}"
    end while self.state(use_cached_state = false) != "running"
  end
  if @tags.has_key?("elastic_ip")
    @region.ec2.associate_address({:instance_id => @id, :public_ip => @tags['elastic_ip']})
    log "Associated ip: #{@tags['elastic_ip']} with instance: #{@id}"
  elsif @tags.has_key?("elastic_ip_allocation_id")
    @region.ec2.associate_address({:instance_id => @id, :allocation_id => @tags['elastic_ip_allocation_id']})
    log "Associated allocation id: #{@tags['elastic_ip_allocation_id']} with instance: #{@id}"
  end
  if @tags.has_key?("elastic_lb")
    self.add_to_lb(@tags["elastic_lb"])
    log "Adding instance: #{@id} to '#{@tags['elastic_lb']}' load balancer"
  end
end
state(use_cached_state=true) click to toggle source

Determine the state of an ec2 instance @param use_cached_state [Boolean] - When true will use a cached version of the state rather than querying EC2 directly

# File lib/aws_region.rb, line 508
def state(use_cached_state=true)
  if !use_cached_state
    response = @region.ec2.describe_instances({instance_ids: [@id]})
    response[:reservations].each do |res|
      res[:instances].each do |inst|
        if inst[:instance_id] == @id
          return inst[:state][:name].strip()
        end
      end
    end
    return ""
  else
    @_instance.state[:name].strip()
  end
end
stop(wait=false) click to toggle source

Stops an ec2 instance @param wait [Boolean] - When true, will wait for the instance to be completely stopped before returning

# File lib/aws_region.rb, line 595
def stop(wait=false)
  if self.state(use_cached_state = false) != "running"
    log "Instance cannot be stopped - #{@region.region}://#{@id} is in the state: #{self.state}"
    return
  end
  self.eject_from_environment
  if @tags.has_key?("elastic_lb")
    log "Removing instance: #{@id} from '#{@tags['elastic_lb']}' load balancer"
    remove_from_lb(tags["elastic_lb"])
  end
  log "Stopping instance: #{@region.region}://#{@id}"
  @region.ec2.stop_instances({:instance_ids => [@id]})
  while self.state(use_cached_state = false) != "stopped"
    sleep 10
    log "Stopping instance: #{@region.region}://#{@id} - state: #{self.state}"
  end if wait
  if self.state(use_cached_state = false) == "stopped"
    log "Instance stopped: #{@region.region}://#{@id}"
  end
end
terminate() click to toggle source

Terminates ec2 instance

# File lib/aws_region.rb, line 588
def terminate()
  eject_from_environment
  @region.ec2.terminate_instances({:instance_ids => [@id]})
end
wait(options = {:timeout => 300, :desired_status => "running"}) click to toggle source
# File lib/aws_region.rb, line 665
def wait(options = {:timeout => 300, :desired_status => "running"})
  t0 = Time.now.to_i
  begin
    sleep 10
    log "Waiting on instance: #{@region.region}://#{@id} - current state: #{self.state}"
    return if Time.now.to_i - t0 > options[:timeout]
  end while self.state(use_cached_state = false) != options[:desired_status]
end