class LambdaWrap::Lambda

Lambda Manager class. Front loads the configuration to the constructor so that the developer can be more declarative with configuration and deployments. @since 1.0

Constants

SUPPORTED_RUNTIMES

Public Class Methods

new(options) click to toggle source

Initializes a Lambda Manager. Frontloaded configuration.

@param [Hash] options The Configuration for the Lambda @option options [String] :lambda_name The name you want to assign to the function you are uploading. The function

names appear in the console and are returned in the ListFunctions API. Function names are used to specify
functions to other AWS Lambda API operations, such as Invoke. Note that the length constraint applies only to
the ARN. If you specify only the function name, it is limited to 64 characters in length.

@option options [String] :handler The function within your code that Lambda calls to begin execution. @option options [String] :role_arn The Amazon Resource Name (ARN) of the IAM role that Lambda assumes when it

executes your function to access any other Amazon Web Services (AWS) resources.

@option options [String] :path_to_zip_file The absolute path to the Deployment Package zip file @option options [String] :runtime The runtime environment for the Lambda function you are uploading. @option options [String] :description ('Deployed with LambdaWrap') A short, user-defined function description.

Lambda does not use this value. Assign a meaningful description as you see fit.

@option options [Integer] :timeout (30) The function execution time at which Lambda should terminate the function. @option options [Integer] :memory_size (128) The amount of memory, in MB, your Lambda function is given. Lambda

uses this memory size to infer the amount of CPU and memory allocated to your function. The value must be a
multiple of 64MB. Minimum: 128, Maximum: 3008.

@option options [Array<String>] :subnet_ids ([]) If your Lambda function accesses resources in a VPC, you provide

this parameter identifying the list of subnet IDs. These must belong to the same VPC. You must provide at least
one security group and one subnet ID to configure VPC access.

@option options [Array<String>] :security_group_ids ([]) If your Lambda function accesses resources in a VPC, you

provide this parameter identifying the list of security group IDs. These must belong to the same VPC. You must
provide at least one security group and one subnet ID.

@option options [Boolean] :delete_unreferenced_versions (true) Option to delete any Lambda Function Versions upon

deployment that do not have an alias pointing to them.

@option options [String] :dead_letter_queue_arn ('') The ARN of the SQS Queue for failed async invocations.

# File lib/lambda_wrap/lambda_manager.rb, line 38
def initialize(options)
  defaults = {
    description: 'Deployed with LambdaWrap', subnet_ids: [], security_group_ids: [], timeout: 30, memory_size: 128,
    delete_unreferenced_versions: true, dead_letter_queue_arn: ''
  }
  options_with_defaults = options.reverse_merge(defaults)
  unless (options_with_defaults[:lambda_name]) && (options_with_defaults[:lambda_name].is_a? String)
    raise ArgumentError, 'lambda_name must be provided (String)!'
  end
  @lambda_name = options_with_defaults[:lambda_name]

  unless (options_with_defaults[:handler]) && (options_with_defaults[:handler].is_a? String)
    raise ArgumentError, 'handler must be provided (String)!'
  end
  @handler = options_with_defaults[:handler]

  unless (options_with_defaults[:role_arn]) && (options_with_defaults[:role_arn].is_a? String)
    raise ArgumentError, 'role_arn must be provided (String)!'
  end
  @role_arn = options_with_defaults[:role_arn]

  unless (options_with_defaults[:path_to_zip_file]) && (options_with_defaults[:path_to_zip_file].is_a? String)
    raise ArgumentError, 'path_to_zip_file must be provided (String)!'
  end
  @path_to_zip_file = options_with_defaults[:path_to_zip_file]

  unless (options_with_defaults[:runtime]) && (options_with_defaults[:runtime].is_a? String)
    raise ArgumentError, 'runtime must be provided (String)!'
  end

  unless SUPPORTED_RUNTIMES.include?(options_with_defaults[:runtime])
    raise ArgumentError, "Invalid Runtime specified: #{options_with_defaults[:runtime]}." \
      "Only accepts: #{SUPPORTED_RUNTIMES}"
  end

  @runtime = options_with_defaults[:runtime]

  unless (options_with_defaults[:memory_size] % 64).zero? && (options_with_defaults[:memory_size] >= 128) &&
         (options_with_defaults[:memory_size] <= 3008)
    raise ArgumentError, 'Invalid Memory Size.'
  end
  @memory_size = options_with_defaults[:memory_size]
  # VPC
  if options_with_defaults[:subnet_ids].empty? ^ options_with_defaults[:security_group_ids].empty?
    raise ArgumentError, 'Must supply values for BOTH Subnet Ids and Security Group ID if VPC is desired.'
  end
  unless options_with_defaults[:subnet_ids].empty?
    @vpc_configuration = {
      subnet_ids: options_with_defaults[:subnet_ids],
      security_group_ids: options_with_defaults[:security_group_ids]
    }
  end
  @description = options_with_defaults[:description]
  @timeout = options_with_defaults[:timeout]
  @delete_unreferenced_versions = options_with_defaults[:delete_unreferenced_versions]
  @dead_letter_queue_arn = options_with_defaults[:dead_letter_queue_arn]
end

Public Instance Methods

delete(client, region = 'AWS_REGION') click to toggle source

Deletes the Lambda Object with associated versions, code, configuration, and aliases.

@param client [Aws::Lambda::Client] Client to use with SDK. Should be passed in by the API class. @param region [String] AWS Region string. Should be passed in by the API class.

Calls superclass method LambdaWrap::AwsService#delete
# File lib/lambda_wrap/lambda_manager.rb, line 147
def delete(client, region = 'AWS_REGION')
  super
  puts "Deleting all versions and aliases for Lambda: #{@lambda_name}"
  lambda_details = retrieve_lambda_details
  if lambda_details.nil?
    puts 'No Lambda to delete.'
  else
    options = { function_name: @lambda_name }
    @client.delete_function(options)
    puts "Lambda #{@lambda_name} and all Versions & Aliases have been deleted."
  end
  true
end
deploy(environment_options, client, region = 'AWS_REGION') click to toggle source

Deploys the Lambda to the specified Environment. Creates a Lambda Function if one didn't exist. Updates the Lambda's configuration, Updates the Lambda's Code, publishes a new version, and creates an alias that points to the newly published version. If the @delete_unreferenced_versions option is enabled, all Lambda Function versions that don't have an alias pointing to them will be deleted.

@param environment_options [LambdaWrap::Environment] The target Environment to deploy @param client [Aws::Lambda::Client] Client to use with SDK. Should be passed in by the API class. @param region [String] AWS Region string. Should be passed in by the API class.

Calls superclass method LambdaWrap::AwsService#deploy
# File lib/lambda_wrap/lambda_manager.rb, line 104
def deploy(environment_options, client, region = 'AWS_REGION')
  super

  puts "Deploying Lambda: #{@lambda_name} to Environment: #{environment_options.name}"

  unless File.exist?(@path_to_zip_file)
    raise ArgumentError, "Deployment Package Zip File does not exist: #{@path_to_zip_file}!"
  end

  lambda_details = retrieve_lambda_details

  if lambda_details.nil?
    function_version = create_lambda
  else
    update_lambda_config
    function_version = update_lambda_code
  end

  create_alias(function_version, environment_options.name, environment_options.description)

  cleanup_unused_versions if @delete_unreferenced_versions

  puts "Lambda: #{@lambda_name} successfully deployed!"
  true
end
teardown(environment_options, client, region = 'AWS_REGION') click to toggle source

Tearsdown an Environment. Deletes an alias with the same name as the environment. Deletes Unreferenced Lambda Function Versions if the option was specified.

@param environment_options [LambdaWrap::Environment] The target Environment to teardown. @param client [Aws::Lambda::Client] Client to use with SDK. Should be passed in by the API class. @param region [String] AWS Region string. Should be passed in by the API class.

Calls superclass method LambdaWrap::AwsService#teardown
# File lib/lambda_wrap/lambda_manager.rb, line 136
def teardown(environment_options, client, region = 'AWS_REGION')
  super
  remove_alias(environment_options.name)
  cleanup_unused_versions if @delete_unreferenced_versions
  true
end
to_s() click to toggle source
Calls superclass method
# File lib/lambda_wrap/lambda_manager.rb, line 161
def to_s
  return @lambda_name if @lambda_name && @lambda_name.is_a?(String)
  super
end

Private Instance Methods

alias_exist?(alias_name) click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 313
def alias_exist?(alias_name)
  retrieve_all_aliases.detect { |a| a.name == alias_name }
end
cleanup_unused_versions() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 258
def cleanup_unused_versions
  puts "Cleaning up unused function versions for #{@lambda_name}."
  function_versions_to_be_deleted = retrieve_all_function_versions -
                                    retrieve_function_versions_used_in_aliases

  return if function_versions_to_be_deleted.empty?

  function_versions_to_be_deleted.each do |version|
    puts "Deleting function version: #{version}."
    options = { function_name: @lambda_name, qualifier: version }
    @client.delete_function(options)
  end

  puts "Cleaned up #{function_versions_to_be_deleted.length} unused versions."
end
create_alias(func_version, alias_name, alias_description) click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 239
def create_alias(func_version, alias_name, alias_description)
  options = {
    function_name: @lambda_name, name: alias_name, function_version: func_version,
    description: alias_description || 'Alias managed by LambdaWrap'
  }
  if alias_exist?(alias_name)
    @client.update_alias(options)
  else
    @client.create_alias(options)
  end
  puts "Created Alias: #{alias_name} for Lambda: #{@lambda_name} v#{func_version}."
end
create_lambda() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 191
def create_lambda
  puts "Creating New Lambda Function: #{@lambda_name}...."
  puts "Runtime Engine: #{@runtime}, Timeout: #{@timeout}, Memory Size: #{@memory_size}."
  options = {
    function_name: @lambda_name, runtime: @runtime, role: @role_arn, handler: @handler,
    code: { zip_file: File.binread(@path_to_zip_file) }, description: @description, timeout: @timeout,
    memory_size: @memory_size, vpc_config: @vpc_configuration, publish: true
  }
  options[:dead_letter_config] = { target_arn: @dead_letter_queue_arn } unless @dead_letter_queue_arn.blank?

  lambda_version = @client.create_function(options).version
  puts "Successfully created Lambda: #{@lambda_name}!"
  lambda_version
end
remove_alias(alias_name) click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 252
def remove_alias(alias_name)
  puts "Deleting Alias: #{alias_name} for #{@lambda_name}"
  options = { function_name: @lambda_name, name: alias_name }
  @client.delete_alias(options)
end
retrieve_all_aliases() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 292
def retrieve_all_aliases
  aliases = []
  response = nil
  loop do
    options = {
      function_name: @lambda_name
    }
    unless !response || response.next_marker.nil? || response.next_marker.empty?
      options[:marker] = response.next_marker
    end
    response = @client.list_aliases(options)
    aliases.concat(response.aliases)
    return aliases if response.aliases.empty? || response.next_marker.nil? || response.next_marker.empty?
  end
end
retrieve_all_function_versions() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 274
def retrieve_all_function_versions
  function_versions = []
  response = nil
  loop do
    options = {
      function_name: @lambda_name
    }
    unless !response || response.next_marker.nil? || response.next_marker.empty?
      options[:marker] = response.next_marker
    end
    response = @client.list_versions_by_function(options)
    function_versions.concat(response.versions.map(&:version))
    if response.next_marker.nil? || response.next_marker.empty?
      return function_versions.reject { |v| v == '$LATEST' }
    end
  end
end
retrieve_function_versions_used_in_aliases() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 308
def retrieve_function_versions_used_in_aliases
  function_versions_with_aliases = Set.new []
  function_versions_with_aliases.merge(retrieve_all_aliases.map(&:function_version)).to_a
end
retrieve_lambda_details() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 180
def retrieve_lambda_details
  lambda_details = nil
  begin
    options = { function_name: @lambda_name }
    lambda_details = @client.get_function(options).configuration
  rescue Aws::Lambda::Errors::ResourceNotFoundException, Aws::Lambda::Errors::NotFound
    puts "Lambda #{@lambda_name} does not exist."
  end
  lambda_details
end
update_lambda_code() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 225
def update_lambda_code
  puts "Updating Lambda Code for #{@lambda_name}...."
  options = {
    function_name: @lambda_name,
    zip_file: File.binread(@path_to_zip_file),
    publish: true
  }

  response = @client.update_function_code(options)

  puts "Successully updated Lambda #{@lambda_name} code to version: #{response.version}"
  response.version
end
update_lambda_config() click to toggle source
# File lib/lambda_wrap/lambda_manager.rb, line 206
    def update_lambda_config
      puts "Updating Lambda Config for #{@lambda_name}..."
      puts "Runtime Engine: #{@runtime}, Timeout: #{@timeout}, Memory Size: #{@memory_size}."
      if @vpc_configuration
        puts "With VPC Configuration: Subnets: #{@vpc_configuration[:subnet_ids]}, Security Groups: \
#{@vpc_configuration[:security_group_ids]}"
      end

      options = {
        function_name: @lambda_name, role: @role_arn, handler: @handler, description: @description, timeout: @timeout,
        memory_size: @memory_size, vpc_config: @vpc_configuration, runtime: @runtime
      }
      options[:dead_letter_config] = { target_arn: @dead_letter_queue_arn } unless @dead_letter_queue_arn.blank?

      @client.update_function_configuration(options)

      puts "Successfully updated Lambda configuration for #{@lambda_name}"
    end