module AwsPocketknife::Ec2
Constants
- DELAY_SECONDS
- Logging
include
AwsPocketknife::Common::Logging
- MAX_ATTEMPTS
- STATE_AVAILABLE
- STATE_DEREGISTERED
- STATE_ERROR
- STATE_FAILED
- STATE_INVALID
- STATE_PENDING
Public Class Methods
clean_ami(options)
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 74 def clean_ami(options) Logging.info "options: #{options}" dry_run = options.fetch(:dry_run, true) Logging.info "Finding AMIs by creation time" image_ids = find_ami_by_creation_time(options) Logging.info "Done. Finding unusued AMIs now..." images_to_delete = find_unused_ami(image_ids: image_ids) Logging.info "images (#{image_ids.length}): #{image_ids}" Logging.info "images to delete (#{images_to_delete.length}): #{images_to_delete}" unless dry_run images_to_delete.each do |image_id| delete_ami_by_id(id: image_id) end end end
create_image(instance_id: "", name: "", description: "Created at
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 148 def create_image(instance_id: "", name: "", description: "Created at #{Time.now}", timeout: 1800, publish_to_account: "", volume_type: "gp2", iops: 3, encrypted: false, volume_size: 60 ) begin Logging.info "creating image" instance = find_by_id(instance_id: instance_id) instance = ec2.instances[instance_id] image = instance.create_image(name, :description => description) sleep 2 until image.exists? Logging.info "image #{image.id} state: #{image.state}" sleep 10 until image.state != :pending if image.state == :failed raise "Create image failed" end Logging.info "image created" rescue => e Logging.error "Creating AMI failed #{e.message}" Logging.error e.backtrace.join("\n") raise e end if publish_to_account.length != 0 Logging.info "add permissions for #{publish_to_account}" image.permissions.add(publish_to_account.gsub(/-/, '')) end image.id.tap do |image_id| Logging.info "Image #{@name}[#{image_id}] created" return image_id end end
delete_ami_by_id(id: '')
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 41 def delete_ami_by_id(id: '') Logging.info "deleting image #{id}" image = find_ami_by_id(id: id) snapshot_ids = snapshot_ids(image) ec2_client.deregister_image(image_id: id) Retryable.retryable(:tries => 20, :sleep => lambda { |n| 2**n }, :on => StandardError) do |retries, exception| image = find_ami_by_id(id: id) message = "retry #{retries} - Deleting image #{id}" message << " State: #{image.state}" if image Logging.info message raise StandardError unless image.nil? end delete_snapshots(snapshot_ids: snapshot_ids) end
delete_snapshots(snapshot_ids: [])
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 58 def delete_snapshots(snapshot_ids: []) snapshot_ids.each do |snapshot_id| Logging.info "Deleting Snapshot: #{snapshot_id}" ec2_client.delete_snapshot(snapshot_id: snapshot_id) end end
describe_instances_by_image_id(image_id_list: [])
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 215 def describe_instances_by_image_id(image_id_list: []) instances = [] resp = ec2_client.describe_instances({dry_run: false, filters: [ { name: "image-id", values: image_id_list } ]}) resp.reservations.each do |reservation| reservation.instances.each do |instance| instances << instance end end instances end
find_ami_by_creation_time(options)
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 110 def find_ami_by_creation_time(options) days = options.fetch(:days, '30').to_i * 24 * 3600 creation_time = Time.now-days Logging.info "Cleaning up images older than #{days} days, i.e, with creation_time < #{creation_time})" image_ids = [] images = find_ami_by_name(name: options.fetch(:ami_name_pattern, '')) images.each do |image| image_creation_time = Time.parse(image.creation_date) msg = "image #{image.name} (#{image.image_id}) (image_creation_time: #{image_creation_time}) < (#{creation_time}) ? " if image_creation_time <= creation_time image_ids << image.image_id msg << "YES, marking to be deleted" else msg << "NO" end Logging.info msg end Logging.info "Done reading AMIs" return image_ids end
find_ami_by_id(id: '')
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 36 def find_ami_by_id(id: '') ec2_client.describe_images({dry_run: false, image_ids: [id]}).images.first end
find_ami_by_name(name: '')
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 26 def find_ami_by_name(name: '') ec2_client.describe_images({dry_run: false, filters: [ { name: "tag:Name", values: [name] } ]}).images end
find_by_id(instance_id: "")
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 232 def find_by_id(instance_id: "") resp = ec2_client.describe_instances({dry_run: false, instance_ids: [instance_id.to_s]}) if resp.nil? or resp.reservations.length == 0 or resp.reservations[0].instances.length == 0 return nil else return resp.reservations.first.instances.first end end
find_by_name(name: "")
click to toggle source
serverfault.com/questions/560337/search-ec2-instance-by-its-name-from-aws-command-line-tool
# File lib/aws_pocketknife/ec2.rb, line 198 def find_by_name(name: "") instances = [] resp = ec2_client.describe_instances({dry_run: false, filters: [ { name: "tag:Name", values: [name] } ]}) resp.reservations.each do |reservation| reservation.instances.each do |instance| instances << instance end end instances end
find_unused_ami(image_ids: [])
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 94 def find_unused_ami(image_ids: []) images_to_delete = [] image_ids.each do |image_id| # check if there is any instance using the image id Logging.info "Checking if #{image_id} can be deleted..." instances = describe_instances_by_image_id(image_id_list: [image_id]) if instances.empty? images_to_delete << image_id else Logging.info "#{image_id} is used by instance #{instances.map { |instance| instance.instance_id }}" end Kernel.sleep 2 end return images_to_delete end
get_windows_password(instance_id: "")
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 241 def get_windows_password(instance_id: "") private_keyfile_dir = ENV["AWS_POCKETKNIFE_KEYFILE_DIR"] || "" raise "Environment variable AWS_POCKETKNIFE_KEYFILE_DIR is not defined" if private_keyfile_dir.length == 0 instance = find_by_id(instance_id: instance_id) key_name = instance.key_name private_keyfile = File.join(private_keyfile_dir, "#{key_name}.pem") raise "File #{private_keyfile} not found" unless File.exist?(private_keyfile) resp = ec2_client.get_password_data({dry_run: false, instance_id: instance_id}) encrypted_password = resp.password_data decrypted_password = decrypt_windows_password(encrypted_password, private_keyfile) RecursiveOpenStruct.new({password: decrypted_password, instance_id: instance.instance_id, private_ip_address: instance.private_ip_address, public_ip_address: instance.public_ip_address}, recurse_over_arrays: true) end
snapshot_ids(image)
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 65 def snapshot_ids(image) snapshot_ids = [] image.block_device_mappings.each do |device_mapping| ebs = device_mapping.ebs snapshot_ids << ebs.snapshot_id if ebs && !ebs.snapshot_id.to_s.empty? end snapshot_ids end
start_instance_by_id(instance_ids)
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 191 def start_instance_by_id(instance_ids) instance_id_list = get_instance_id_list(instance_ids: instance_ids) Logging.info "Start instance id: #{instance_id_list}" ec2_client.start_instances({ instance_ids: instance_id_list }) end
stop_instance_by_id(instance_ids)
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 183 def stop_instance_by_id(instance_ids) instance_id_list = get_instance_id_list(instance_ids: instance_ids) Logging.info "Stoping instance id: #{instance_id_list}" resp = ec2_client.stop_instances({ instance_ids: instance_id_list }) wait_till_instance_is_stopped(instance_id_list, max_attempts: MAX_ATTEMPTS, delay_seconds: DELAY_SECONDS) Logging.info "Stopped ec2 instance #{instance_id_list}" end
Private Class Methods
create_launch_permission(user_id)
click to toggle source
def ec2
@ec2 ||= Aws::EC2.new(:ec2_endpoint => "ec2.#{AwsPocketknife::AWS_REGION}.amazonaws.com")
end
# File lib/aws_pocketknife/ec2.rb, line 268 def create_launch_permission(user_id) { add: [ { user_id: user_id }, ] } end
decrypt_windows_password(encrypted_password, private_keyfile)
click to toggle source
Decrypts an encrypted password using a provided RSA private key file (PEM-format).
# File lib/aws_pocketknife/ec2.rb, line 280 def decrypt_windows_password(encrypted_password, private_keyfile) encrypted_password_bytes = Base64.decode64(encrypted_password) private_keydata = File.open(private_keyfile, "r").read private_key = OpenSSL::PKey::RSA.new(private_keydata) private_key.private_decrypt(encrypted_password_bytes) end
get_instance_id_list(instance_ids: "")
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 287 def get_instance_id_list(instance_ids: "") instance_ids.strip.split(";") end
wait_till_instance_is_stopped(instance_ids, max_attempts: 12, delay_seconds: 10)
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 291 def wait_till_instance_is_stopped(instance_ids, max_attempts: 12, delay_seconds: 10) total_wait_seconds = max_attempts * delay_seconds; Logging.info "Waiting up to #{total_wait_seconds} seconds with #{delay_seconds} seconds delay for ec2 instance #{instance_ids} to be stopped" ec2_client.wait_until(:instance_stopped, { instance_ids: instance_ids }) do |w| w.max_attempts = max_attempts w.delay = delay_seconds end end
wait_till_instance_is_terminated(instance_ids, max_attempts: 12, delay_seconds: 10)
click to toggle source
# File lib/aws_pocketknife/ec2.rb, line 300 def wait_till_instance_is_terminated(instance_ids, max_attempts: 12, delay_seconds: 10) total_wait_seconds = max_attempts * delay_seconds; Logging.info "Waiting up to #{total_wait_seconds} seconds with #{delay_seconds} seconds delay for ec2 instance #{instance_ids} to be terminated" ec2_client.wait_until(:instance_terminated, { instance_ids: instance_ids }) do |w| w.max_attempts = max_attempts w.delay = delay_seconds end end