class Hiera::Backend::Sf_hiera_aws_backend

Public Class Methods

new() click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 46
def initialize
    require 'aws-sdk-resources'

    read_link_local_data
    unless @instance_identity.nil?
        Aws.config.update(region: @instance_identity['region'])
    end

    Hiera.debug('Hiera AWS SDK backend started')
end

Public Instance Methods

aws_config() click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 113
def aws_config

    require 'yaml'

    config_file = config_file_name

    if File.exist?(config_file)
        config = YAML.load_file(config_file)
    else
        Hiera.warn("No config file #{config_file} found")
        config = {}
    end

    # Merge configs from the config directory too

    config_directory = config_directory_name

    if File.directory?(config_directory)
        Dir.entries(config_directory).sort.each do |p|
            next if p == '.' or p == '..'
            to_merge = YAML.load_file( File.join( config_directory, p ) )
            config.merge! to_merge
        end
    end

    config
end
config_directory_name() click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 98
def config_directory_name

    default_config_path = '/etc/puppet/sf_hiera_aws.d'

    if !Config[:aws_sdk].nil?
        config_dir = Config[:aws_sdk][:config_directory] || default_config_path
    else
        config_dir = default_config_path
    end

    return config_dir

end
config_file_name() click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 84
def config_file_name

    default_config_path = '/etc/puppet/sf_hiera_aws.yaml'

    if !Config[:aws_sdk].nil?
        config_file = Config[:aws_sdk][:config_file] || default_config_path
    else
        config_file = default_config_path
    end

    return config_file

end
lookup(key, scope, _order_override, _resolution_type) click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 57
def lookup(key, scope, _order_override, _resolution_type)
    config = recursive_interpolate_config(aws_config, scope)

    Hiera.debug("Looking up '#{key} in AWS SDK backend")

    return nil unless config.key? key

    Hiera.debug("Config: #{config[key].inspect}")
    type = config[key]['type']

    if methods.include? "type_#{type}".to_sym

        begin
            answer = send("type_#{type}".to_sym, config[key])
            Hiera.debug(answer)
            return answer
        rescue Aws::Errors::MissingRegionError, Aws::Errors::MissingCredentialsError
            Hiera.warn('No IAM role or ENV based AWS config - skipping')
            return nil
        end

    end

    Hiera.debug("Type of AWS SDK lookup '#{type}' invalid")
    nil
end
recursive_interpolate_config(h, scope) click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 141
def recursive_interpolate_config(h, scope)
    case h
    when Hash
        Hash[
        h.map do |k, v|
            [Backend.parse_answer(k, scope), recursive_interpolate_config(v, scope)]
        end
        ]
    when Enumerable
        h.map { |v| recursive_interpolate_config(v, scope) }
    when String
        Backend.parse_answer(h, scope)
    else
        h
    end
end
type_autoscaling_group(options) click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 394
def type_autoscaling_group(options)
    autoscaling = get_autoscaling_client

    if options.key? 'auto_scaling_group_names'
        asgs = autoscaling.describe_auto_scaling_groups(
            auto_scaling_group_names: options['auto_scaling_group_names'],
        ).auto_scaling_groups
    else
        asgs = autoscaling.describe_auto_scaling_groups.auto_scaling_groups
    end

    if !options.key? 'return'
        return asgs.collect do |asg|
            {
                'auto_scaling_group_name'   => asg.auto_scaling_group_name,
                'launch_configuration_name' => asg.launch_configuration_name,
                'load_balancer_names'       => asg.load_balancer_names,
                'instances'                 => asg.instances.collect do | instance |
                    {
                        'instance_id' => instance.instance_id,
                        'availability_zone' => instance.availability_zone,
                        'lifecycle_state' => instance.lifecycle_state,
                        'health_status' => instance.health_status,
                        'launch_configuration_name' => instance.launch_configuration_name,
                        'protected_from_scale_in' => instance.protected_from_scale_in
                    }
                end
            }
        end
    else
        unless options.key? 'auto_scaling_group_names'
            Hiera.warn('Requested to return hash of instances for auto scaling group, but no auto_scaling_group_names specified')
            return nil
        end
    end

    if options['return'] == :instance_details_inservice_ip
        instances = []
        asgs = asgs.collect do |asg|
            {
                'instances' => asg.instances.select{|i| i.lifecycle_state == 'InService'}.map { |instance|
                    { 'instance_id' => instance.instance_id } 
                }
            }
        end

        asgs.each do |asg|
            instances += asg['instances']
        end

        ec2_options = {
            'filters' => [{ 
                'name' => 'instance-id', 
                'values' => instances.map { |i| i['instance_id'] }
            }],
            'return'  => [ 'private_ip_address']
        }
        return type_ec2_instance(ec2_options)
    else
        asgs.collect do |a|
            if options['return'].is_a?(Array)

                # If the 'return' option is a list, we treat these
                # as a list of desired hash keys, and return a hash
                # containing only those keys from the API call
                
                Hash[options['return'].map do |f|
                    [f.to_s, a.key?(f) ? a[f] : nil]
                end]

            elsif options['return'].is_a?(Symbol)

                # If the 'return' option is a symbol, we treat that
                # as the one hash key we care about, and return a list
                # of that.

                a.key?(options['return']) ? a[options['return']] : nil

            end
        end
    end
end
type_ec2_instance(options) click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 158
def type_ec2_instance(options)
    options = {
        'return'  => [
            :instance_id,
            :private_ip_address,
            :private_dns_name,
        ]
    }.merge(options)

    ec2 = get_ec2_client

    instances = []

    if options.key? 'filters'

        ec2.describe_instances(filters: options['filters']).reservations.each do |r|
            r.instances.each do |i|
                instances << i
            end
        end

    else

        ec2.describe_instances().reservations.each do |r|
            r.instances.each do |i|
                instances << i
            end
        end

    end

    instances.collect do |i|
        if options['return'].is_a?(Array)

            # If the 'return' option is a list, we treat these
            # as a list of desired hash keys, and return a hash
            # containing only those keys from the API call
            
            Hash[options['return'].map do |f|
                [f.to_s, i.key?(f) ? i[f] : nil]
            end]

        elsif options['return'].is_a?(Symbol)

            # If the 'return' option is a symbol, we treat that
            # as the one hash key we care about, and return a list
            # of that.

            i.key?(options['return']) ? i[options['return']] : nil

        end
    end
end
type_elasticache_cache_cluster(options) click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 257
def type_elasticache_cache_cluster(options)
    elasticache = get_elasticache_client

    if options.key? 'cache_cluster_id'
        clusters = elasticache.describe_cache_clusters(
            cache_cluster_id: options['cache_cluster_id'],
            show_cache_node_info: true,
        ).cache_clusters
    else
        clusters = elasticache.describe_cache_clusters(
            show_cache_node_info: true
        ).cache_clusters
    end

    if !options.key? 'return'

        return clusters.collect do |i|
            {
                'cache_cluster_id' => i.cache_cluster_id,
                'cache_nodes'      => i.cache_nodes.collect do |n|
                    {
                        'cache_node_id'    => n.cache_node_id,
                        'endpoint_address' => n.endpoint.address,
                        'endpoint_port'    => n.endpoint.port,
                    }
                end
            }
        end

    end

    if options['return'] == :hostname

        nodes = []

        clusters.each do |c|
            c.cache_nodes.each do |n|
                nodes.push(n.endpoint.address)
            end
        end

        return nodes

    elsif options['return'] == :hostname_and_port

        nodes = []

        clusters.each do |c|
            c.cache_nodes.each do |n|
                nodes.push( [ n.endpoint.address, n.endpoint.port ].join(':') )
            end
        end

        return nodes

    end

end
type_elasticache_replication_group(options) click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 316
def type_elasticache_replication_group(options)
    elasticache = get_elasticache_client

    if options.key? 'replication_group_id'
        replgroups = elasticache.describe_replication_groups(
            replication_group_id: options['replication_group_id'],
        ).replication_groups
    else
        replgroups = elasticache.describe_replication_groups.replication_groups
    end

    if !options.key? 'return'

        return replgroups.collect do |rg|
            {
                'replication_group_id' => rg.replication_group_id,
                'primary_endpoint_address' => rg.node_groups[0].primary_endpoint.address,
                'primary_endpoint_port' => rg.node_groups[0].primary_endpoint.port,
                'node_group_members' => rg.node_groups[0].node_group_members.collect do |ngm| 
                    {
                        'cache_node_id' => ngm.cache_node_id,
                        'cache_cluster_id' => ngm.cache_cluster_id,
                        'read_endpoint_address' => ngm.read_endpoint.address,
                        'read_endpoint_port' => ngm.read_endpoint.port,
                        'current_role' => ngm.current_role,
                    }
                end
            }
        end

    end

    if options['return'] == :read_endpoints

        if options.key? 'replication_group_id'
            return replgroups[0].node_groups[0].node_group_members.map { |ngm| ngm.read_endpoint.address }
        else
            Hiera.warn('Requested to return array of read endpoints for replication group, but no replication_group_id specified')
            return nil
        end

    end

    if options['return'] == :read_endpoints_and_ports

        if options.key? 'replication_group_id'
            return replgroups[0].node_groups[0].node_group_members.map { |ngm| "#{ngm.read_endpoint.address}:#{ngm.read_endpoint.port}" }
        else
            Hiera.warn('Requested to return array of read endpoints and ports for replication group, but no replication_group_id specified')
            return nil
        end

    end

    if options['return'] == :primary_endpoint

        primary_endpoints = []

        replgroups.each do |rg|
            primary_endpoints.push(rg.node_groups[0].primary_endpoint.address)
        end

        return primary_endpoints

    elsif options['return'] == :primary_endpoint_and_port

        primary_endpoints = []

        replgroups.each do |rg|
            primary_endpoints.push( [ rg.node_groups[0].primary_endpoint.address, rg.node_groups[0].primary_endpoint.port ].join(':') )
        end

        return primary_endpoints

   end

end
type_rds_db_instance(options) click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 212
def type_rds_db_instance(options)
    rds = get_rds_client

    if options.key? 'db_instance_identifier'
        instances = rds.describe_db_instances(
            db_instance_identifier: options['db_instance_identifier']
        ).db_instances
    else
        instances = rds.describe_db_instances.db_instances
    end

    if !options.key? 'return'

        return instances.collect do |i|
            {
                'db_instance_identifier' => i.db_instance_identifier,
                'endpoint_address'       => i.endpoint.address,
                'endpoint_port'          => i.endpoint.port,
            }
        end

    end

    if options['return'] == :hostname

        return instances.first.endpoint.address

    elsif options['return'] == :hostname_and_port

        return [
            instances.first.endpoint.address,
            instances.first.endpoint.port,
        ].join(':')

    else

        Hiera.warn("No return handler for #{options['return']} in rds_db_instance")
        return nil

    end



end

Private Instance Methods

get_autoscaling_client() click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 40
def get_autoscaling_client
    Aws::AutoScaling::Client.new
end
get_ec2_client() click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 32
def get_ec2_client
    Aws::EC2::Client.new
end
get_elasticache_client() click to toggle source
# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 36
def get_elasticache_client
    Aws::ElastiCache::Client.new
end
get_rds_client() click to toggle source

Wrapped these in getters for dependency injection purposes

# File lib/hiera/backend/sf_hiera_aws_backend.rb, line 28
def get_rds_client
    Aws::RDS::Client.new
end