class SfnLambda::Control

Constants

DEFAULTS

Attributes

callback[RW]
functions[R]

Public Class Methods

new() click to toggle source

Create a new control instance

@return [self]

# File lib/sfn-lambda/control.rb, line 32
def initialize
  @functions = Smash.new
end

Public Instance Methods

[](key) click to toggle source

Get configuration value for control via sfn configuration and fall back to defined defaults if not set

@return [Object] configuration value

# File lib/sfn-lambda/control.rb, line 197
def [](key)
  callback.config.fetch(:lambda, :config, key.to_s.downcase, DEFAULTS[key.to_s.upcase.to_sym])
end
apply_build!(info) click to toggle source

Build the lambda asset if building is a requirement

@param info [Hash] @return [TrueClass, FalseClass] build was performed

# File lib/sfn-lambda/control.rb, line 112
def apply_build!(info)
  if(build_info = self[:build_required][info[:runtime]])
    Open3.popen3(build_info[:build_command], :chdir => info[:path]) do |stdin, stdout, stderr, wait_thread|
      exit_status = wait_thread.value
      unless(exit_status.success?)
        callback.ui.error "Failed to build lambda assets for storage from path: #{info[:path]}"
        callback.ui.debug "Build command used which generated failure: `#{build_info[:build_command]}`"
        callback.ui.debug "STDOUT: #{stdout.read}"
        callback.ui.debug "STDERR: #{stderr.read}"
        raise "Failed to build lambda asset for storage! (path: `#{info[:path]}`)"
      end
    end
    file = Dir.glob(File.join(info[:path], build_info[:output_directory], "*.#{build_config[:asset_extension]}")).first
    if(file)
      info[:path] = file
      true
    else
      debug "Glob pattern used for build asset detection: `#{File.join(info[:path], build_info[:output_directory], "*.#{build_config[:asset_extension]}")}`"
      raise "Failed to locate generated build asset for storage! (path: `#{info[:path]}`)"
    end
  else
    false
  end
end
bucket() click to toggle source

@return [Miasma::Models::Storage::Bucket]

# File lib/sfn-lambda/control.rb, line 138
def bucket
  storage_bucket = callback.config.fetch(:lambda, :upload, :bucket, callback.config[:nesting_bucket])
  if(storage_bucket)
    s3 = api.connection.api_for(:storage)
    l_bucket = s3.buckets.get(storage_bucket)
  end
  unless(l_bucket)
    raise "Failed to locate configured bucket for lambda storage (#{storage_bucket})"
  else
    l_bucket
  end
end
can_inline?(info) click to toggle source

Determine if function can be defined inline within template

@param info [Hash] @return [TrueClass, FalseClass]

# File lib/sfn-lambda/control.rb, line 189
def can_inline?(info)
  !self[:inline_restricted].include?(info[:runtime]) && File.size(info[:path]) <= self[:inline_max_size]
end
discover_functions!() click to toggle source

Discover all defined lambda functions available in directories provided via configuration

@return [NilClass]

# File lib/sfn-lambda/control.rb, line 205
def discover_functions!
  core_paths = lambda_directories
  core_paths.each do |path|
    Dir.new(path).each do |dir_item|
      next if dir_item.start_with?('.')
      next unless File.directory?(File.join(path, dir_item))
      if(self[:build_required].keys.include?(dir_item))
        Dir.new(File.join(path, dir_item)).each do |item|
          next if item.start_with?('.')
          full_item = File.join(path, dir_item, item)
          next unless File.directory?(full_item)
          functions.set(dir_item, item, full_item)
        end
      else
        items = Dir.glob(File.join(path, dir_item, '**', '**', '*'))
        items.each do |full_item|
          next unless File.file?(full_item)
          item = full_item.sub(File.join(path, dir_item, ''), '').gsub(File::SEPARATOR, '')
          item = item.sub(/#{Regexp.escape(File.extname(item))}$/, '')
          functions.set(dir_item, item, full_item)
        end
      end
    end
  end
  nil
end
format_content(info) click to toggle source

Format lambda function content to use within template. Will provide raw source when function can be inlined within the template. If inline is not available, it will store within S3

@param info [Hash] content information @option info [String] :path path to lambda function @option info [String] :runtime runtime of lambda function @option info [String] :name name of lambda function @return [Smash] content information

# File lib/sfn-lambda/control.rb, line 84
def format_content(info)
  if(can_inline?(info))
    Smash.new(:raw => File.read(info[:path]))
  else
    apply_build!(info)
    key_name = generate_key_name(info)
    io = File.open(info[:path], 'rb')
    file = bucket.files.build
    file.name = key_name
    file.body = io
    file.save
    io.close
    if(versioning_enabled?)
      result = s3.request(
        :path => s3.file_path(file),
        :endpoint => s3.bucket_endpoint(file.bucket),
        :method => :head
      )
      version = result[:headers][:x_amz_version_id]
    end
    Smash(:bucket => storage_bucket, :key => key_name, :version => version)
  end
end
generate_key_name(info) click to toggle source

Generate key name based on state

@param info [Hash] @return [String] key name

# File lib/sfn-lambda/control.rb, line 171
def generate_key_name(info)
  if(versioning_enabled?)
    "sfn.lambda/#{info[:runtime]}/#{File.basename(info[:path])}"
  else
    checksum = Digest::SHA256.new
    File.open(info[:path], 'rb') do |file|
      while(content = file.read(2048))
        checksum << content
      end
    end
    "sfn.lambda/#{info[:runtime]}/#{File.basename(info[:path])}-#{checksum.base64digest}"
  end
end
get(name, runtime=nil) click to toggle source

Get path to lambda function

@param name [String] name of lambda function @param runtime [String] runtime of lambda function @return [Hash] {:path, :runtime}

# File lib/sfn-lambda/control.rb, line 55
def get(name, runtime=nil)
  unless(runtime)
    runtime = functions.keys.find_all do |r_name|
      functions[r_name].keys.include?(name.to_s)
    end
    if(runtime.empty?)
      raise "Failed to locate requested lambda function `#{name}`"
    elsif(runtime.size > 1)
      raise "Multiple lambda function matches for `#{name}`. (In runtimes: `#{runtime.sort.join('`, `')}`)"
    end
    runtime = runtime.first
  end
  result = functions.get(runtime, name)
  if(result.nil?)
    raise "Failed to locate requested lambda function `#{name}`"
  else
    Smash.new(:path => result, :runtime => runtime, :name => name)
  end
end
lambda_directories() click to toggle source

@return [Array<String>] paths to lambda storage directories

# File lib/sfn-lambda/control.rb, line 37
def lambda_directories
  paths = [callback.config.fetch(:lambda, :directory, 'lambda')].flatten.compact.uniq.map do |path|
    File.expand_path(path)
  end
  invalids = paths.find_all do |path|
    !File.directory?(path)
  end
  unless(invalids.empty?)
    raise "Invalid lambda directory paths provided: #{invalids.join(', ')}"
  end
  paths
end
versioning_enabled?() click to toggle source

@return [TrueClass, FalseClass] bucket has versioning enabled

# File lib/sfn-lambda/control.rb, line 152
def versioning_enabled?
  unless(@versioned.nil?)
    s3 = api.connection.api_for(:storage)
    result = s3.request(
      :path => '/',
      :params => {
        :versioning => true
      },
      :endpoint => s3.bucket_endpoint(bucket)
    )
    @versioned = result.get(:body, 'VersioningConfiguration', 'Status') == 'Enabled'
  end
  @versioned
end