module Chef::Knife::Ec2Base

Public Class Methods

included(includer) click to toggle source

@todo Would prefer to do this in a rational way, but can't be done b/c of Mixlib::CLI's design :(

# File lib/chef/knife/helpers/ec2_base.rb, line 29
def self.included(includer)
  includer.class_eval do

    deps do
      require "aws-sdk-ec2"
      require "chef/json_compat"
      require "chef/util/path_helper"
    end

    option :aws_credential_file,
      long: "--aws-credential-file FILE",
      description: "File containing AWS credentials as used by the AWS Command Line Interface."

    option :aws_config_file,
      long: "--aws-config-file FILE",
      description: "File containing AWS configurations as used by the AWS Command Line Interface."

    option :aws_profile,
      long: "--aws-profile PROFILE",
      description: "AWS profile, from AWS credential file and AWS config file, to use",
      default: "default"

    option :aws_access_key_id,
      short: "-A ID",
      long: "--aws-access-key-id KEY",
      description: "Your AWS Access Key ID"

    option :aws_secret_access_key,
      short: "-K SECRET",
      long: "--aws-secret-access-key SECRET",
      description: "Your AWS API Secret Access Key"

    option :aws_session_token,
      long: "--aws-session-token TOKEN",
      description: "Your AWS Session Token, for use with AWS STS Federation or Session Tokens"

    option :region,
      long: "--region REGION",
      description: "Your AWS region"

    option :use_iam_profile,
      long: "--use-iam-profile",
      description: "Use IAM profile assigned to current machine",
      boolean: true,
      default: false
  end
end

Public Instance Methods

ami() click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 180
def ami
  @ami ||= fetch_ami(config[:image])
end
connection_string() click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 77
def connection_string
  conn = {}
  conn[:region] = config[:region] || "us-east-1"
  Chef::Log.debug "Using AWS region #{conn[:region]}"
  conn[:credentials] =
    if config[:use_iam_profile]
      Chef::Log.debug "Using iam profile for authentication as use_iam_profile set"
      Aws::InstanceProfileCredentials.new
    else
      Chef::Log.debug "Setting up AWS connection using aws_access_key_id: #{mask(config[:aws_access_key_id])} aws_secret_access_key: #{mask(config[:aws_secret_access_key])} aws_session_token: #{mask(config[:aws_session_token])}"

      Aws::Credentials.new(config[:aws_access_key_id], config[:aws_secret_access_key], config[:aws_session_token])
    end
  conn
end
ec2_connection() click to toggle source

@return [Aws::EC2::Client]

# File lib/chef/knife/helpers/ec2_base.rb, line 94
def ec2_connection
  @ec2_connection ||= Aws::EC2::Client.new(connection_string)
end
fetch_ami(image_id) click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 102
def fetch_ami(image_id)
  return nil unless image_id

  ec2_connection.describe_images({
    image_ids: [image_id],
  }).images.first
end
fetch_ec2_instance(instance_id) click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 110
def fetch_ec2_instance(instance_id)
  instance = ec2_connection.describe_instances({
    instance_ids: [
      instance_id,
    ],
  }).reservations[0]
  normalize_server_data(server_hashes(instance))
end
fetch_network_interfaces(nic_id) click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 119
def fetch_network_interfaces(nic_id)
  ec2_connection.describe_network_interfaces({
    network_interface_ids: [nic_id],
  }).network_interfaces[0]
end
fetch_password_data(server_id) click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 125
def fetch_password_data(server_id)
  ec2_connection.get_password_data({
    instance_id: server_id,
  })
end
fetch_region() click to toggle source

@return [String]

# File lib/chef/knife/helpers/ec2_base.rb, line 132
def fetch_region
  ec2_connection.instance_variable_get(:@config).region
end
fetch_subnet(subnet_id) click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 136
def fetch_subnet(subnet_id)
  ec2_connection.describe_subnets({
    subnet_ids: [subnet_id],
  }).subnets[0]
end
find_name_tag(tags) click to toggle source

Name tag value return. @return [String]

# File lib/chef/knife/helpers/ec2_base.rb, line 186
def find_name_tag(tags)
  name_tag = tags.find { |tag| tag[:key] == "Name" }
  name_tag ? name_tag[:value] : nil
end
is_image_windows?() click to toggle source

Platform value return for Windows AMIs; otherwise, it is blank. @return [Boolean]

# File lib/chef/knife/helpers/ec2_base.rb, line 193
def is_image_windows?
  ami && ami.platform == "windows"
end
msg_pair(label, value, color = :cyan) click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 174
def msg_pair(label, value, color = :cyan)
  if value && !value.to_s.empty?
    ui.info("#{ui.color(label, color)}: #{value}")
  end
end
normalize_server_data(server_hashes) click to toggle source

@return [Struct]

# File lib/chef/knife/helpers/ec2_base.rb, line 169
def normalize_server_data(server_hashes)
  require "ostruct" unless defined?(OpenStruct)
  OpenStruct.new(server_hashes)
end
server_hashes(server_obj) click to toggle source

@return [Hash]

# File lib/chef/knife/helpers/ec2_base.rb, line 143
def server_hashes(server_obj)
  server_data = {}
  %w{ebs_optimized image_id instance_id instance_type key_name platform public_dns_name public_ip_address private_dns_name private_ip_address root_device_type}.each do |id|
    server_data[id] = server_obj.instances[0].send(id)
  end
  server_data["availability_zone"] = server_obj.instances[0].placement.availability_zone
  server_data["groups"] = server_obj.groups.map(&:group_name) unless vpc_mode?
  server_data["iam_instance_profile"] = ( server_obj.instances[0].iam_instance_profile.nil? ? nil : server_obj.instances[0].iam_instance_profile.arn[%r{instance-profile/(.*)}] )
  server_data["id"] = server_data["instance_id"]

  tags = server_obj.instances[0].tags.map(&:value)
  server_data["name"] = find_name_tag(server_obj.instances[0].tags)
  server_data["placement_group"] = server_obj.instances[0].placement.group_name
  server_data["security_groups"] = server_obj.instances[0].security_groups.map(&:group_name)
  server_data["security_group_ids"] = server_obj.instances[0].security_groups.map(&:group_id)
  server_data["state"] = server_obj.instances[0].state.name
  server_data["subnet_id"] = server_obj.instances[0].network_interfaces[0].subnet_id if vpc_mode?
  server_data["source_dest_check"] = server_obj.instances[0].network_interfaces[0].source_dest_check if vpc_mode?
  server_data["tags"] = tags
  server_data["tenancy"] = server_obj.instances[0].placement.tenancy
  server_data["volume_id"] = server_obj.instances[0].block_device_mappings[0]&.ebs&.volume_id
  server_data["block_device_mappings"] = server_obj.instances[0].block_device_mappings
  server_data
end
validate_aws_config!(keys = %i{aws_access_key_id aws_secret_access_key}) click to toggle source

validate the config options that were passed since some of them cannot be used together also validate the aws configuration file contents if present

# File lib/chef/knife/helpers/ec2_base.rb, line 199
def validate_aws_config!(keys = %i{aws_access_key_id aws_secret_access_key})
  errors = [] # track all errors so we report on all of them

  validate_aws_config_file! if config[:aws_config_file]
  unless config[:use_iam_profile] # skip config file / key validation if we're using iam profile
    # validate the creds file if:
    #   aws keys have not been passed in config / CLI and the default cred file location does exist
    #   OR
    #   the user passed aws_credential_file
    if (config.keys & %i{aws_access_key_id aws_secret_access_key}).empty? && aws_cred_file_location ||
        config[:aws_credential_file]

      unless (config.keys & %i{aws_access_key_id aws_secret_access_key}).empty?
        errors << "Either provide a credentials file or the access key and secret keys but not both."
      end

      validate_aws_credential_file!
    end

    keys.each do |k|
      pretty_key = k.to_s.tr("_", " ").gsub(/\w+/) { |w| (w =~ /(ssh)|(aws)/i) ? w.upcase : w.capitalize }
      if config[k].nil?
        errors << "You did not provide a valid '#{pretty_key}' value."
      end
    end

    if errors.each { |e| ui.error(e) }.any?
      exit 1
    end
  end
end
vpc_mode?() click to toggle source
# File lib/chef/knife/helpers/ec2_base.rb, line 98
def vpc_mode?
  !!config[:subnet_id]
end