1
0
Fork 0
mirror of https://github.com/fog/fog-aws.git synced 2022-11-09 13:50:52 -05:00

Add support for AWS Lambda service

This commit is contained in:
Miguel Landaeta 2015-06-22 21:05:47 -03:00
parent 010883c1bf
commit 0c36f98391
24 changed files with 1943 additions and 0 deletions

View file

@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'bundler', '~> 1.6'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'shindo', '~> 0.3'
spec.add_development_dependency 'rubyzip', '~> 0.9.9'
spec.add_dependency 'fog-core', '~> 1.27'
spec.add_dependency 'fog-json', '~> 1.0'

View file

@ -45,6 +45,7 @@ module Fog
autoload :Glacier, File.expand_path('../aws/glacier', __FILE__)
autoload :IAM, File.expand_path('../aws/iam', __FILE__)
autoload :KMS, File.expand_path('../aws/kms', __FILE__)
autoload :Lambda, File.expand_path('../aws/lambda', __FILE__)
autoload :RDS, File.expand_path('../aws/rds', __FILE__)
autoload :Redshift, File.expand_path('../aws/redshift', __FILE__)
autoload :SES, File.expand_path('../aws/ses', __FILE__)
@ -70,6 +71,7 @@ module Fog
service(:glacier, 'Glacier')
service(:iam, 'IAM')
service(:kms, 'KMS')
service(:lambda, 'Lambda')
service(:rds, 'RDS')
service(:redshift, 'Redshift')
service(:ses, 'SES')

195
lib/fog/aws/lambda.rb Normal file
View file

@ -0,0 +1,195 @@
module Fog
module AWS
class Lambda < Fog::Service
extend Fog::AWS::CredentialFetcher::ServiceMethods
requires :aws_access_key_id, :aws_secret_access_key
recognizes :host, :path, :port, :scheme, :persistent, :region, :use_iam_profile, :aws_session_token, :aws_credentials_expire_at, :version, :instrumentor, :instrumentor_name
request_path 'fog/aws/requests/lambda'
request :create_function
request :delete_function
request :get_function
request :get_function_configuration
request :invoke
request :list_functions
request :update_function_code
request :update_function_configuration
request :get_policy
request :add_permission
request :remove_permission
request :create_event_source_mapping
request :delete_event_source_mapping
request :get_event_source_mapping
request :list_event_source_mappings
request :update_event_source_mapping
class Mock
def self.data
@data ||= Hash.new do |hash, region|
hash[region] = Hash.new do |region_hash, key|
region_hash[key] = {
:functions => {},
:permissions => {},
:event_source_mappings => {}
}
end
end
end
attr_reader :region
attr_reader :account_id
attr_reader :aws_access_key_id
def initialize(options={})
@region = options[:region] || 'us-east-1'
@aws_access_key_id = options[:aws_access_key_id]
@account_id = Fog::AWS::Mock.owner_id
@module = "lambda"
unless ['ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2', 'eu-central-1', 'eu-west-1', 'us-east-1', 'us-west-1', 'us-west-2', 'sa-east-1'].include?(@region)
raise ArgumentError, "Unknown region: #{@region.inspect}"
end
end
def data
self.class.data[@region][@aws_access_key_id]
end
def reset_data
self.class.data[@region].delete(@aws_access_key_id)
end
end
class Real
include Fog::AWS::CredentialFetcher::ConnectionMethods
# Initialize connection to Lambda
#
# ==== Notes
# options parameter must include values for :aws_access_key_id and
# :aws_secret_access_key in order to create a connection
#
# ==== Examples
# lambda = Lambda.new(
# :aws_access_key_id => your_aws_access_key_id,
# :aws_secret_access_key => your_aws_secret_access_key
# )
#
# ==== Parameters
# * options<~Hash> - config arguments for connection. Defaults to {}.
#
# ==== Returns
# * Lambda object with connection to AWS.
def initialize(options={})
@use_iam_profile = options[:use_iam_profile]
@connection_options = options[:connection_options] || {}
@instrumentor = options[:instrumentor]
@instrumentor_name = options[:instrumentor_name] || 'fog.aws.lambda'
options[:region] ||= 'us-east-1'
@region = options[:region]
@host = options[:host] || "lambda.#{options[:region]}.amazonaws.com"
@path = options[:path] || '/'
@persistent = options[:persistent] || false
@port = options[:port] || 443
@scheme = options[:scheme] || 'https'
@version = options[:version] || '2015-03-31'
@connection = Fog::Core::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", @persistent, @connection_options)
setup_credentials(options)
end
attr_reader :region
def reload
@connection.reset
end
private
def setup_credentials(options)
@aws_access_key_id = options[:aws_access_key_id]
@aws_secret_access_key = options[:aws_secret_access_key]
@aws_session_token = options[:aws_session_token]
@aws_credentials_expire_at = options[:aws_credentials_expire_at]
@signer = Fog::AWS::SignatureV4.new( @aws_access_key_id, @aws_secret_access_key, @region, 'lambda')
end
def request(params)
refresh_credentials_if_expired
idempotent = params.delete(:idempotent)
parser = params.delete(:parser)
request_path = "/#{@version}#{params.delete(:path)}"
query = params.delete(:query) || {}
method = params.delete(:method) || 'POST'
expects = params.delete(:expects) || 200
headers = { 'Content-Type' => 'application/json' }
headers.merge!(params[:headers] || {})
body, headers = AWS.signed_params_v4(
params,
headers,
{
:method => method,
:aws_session_token => @aws_session_token,
:signer => @signer,
:host => @host,
:path => request_path,
:port => @port,
:query => query,
:body => params[:body]
}
)
if @instrumentor
@instrumentor.instrument("#{@instrumentor_name}.request", params) do
_request(method, request_path, query, body, headers, expects, idempotent, parser)
end
else
_request(method, request_path, query, body, headers, expects, idempotent, parser)
end
end
def _request(method, path, query, body, headers, expects, idempotent, parser=nil)
response = process_response(@connection.request({
:path => path,
:query => query,
:body => body,
:expects => expects,
:idempotent => idempotent,
:headers => headers,
:method => method
}), parser)
rescue Excon::Errors::HTTPStatusError => error
match = Fog::AWS::Errors.match_error(error)
raise if match.empty?
raise Fog::AWS::Lambda::Error.slurp(error,
"#{match[:code]} => #{match[:message]}")
end
def process_response(response, parser)
if response &&
response.body &&
response.body.is_a?(String) &&
!response.body.strip.empty? &&
Fog::AWS.json_response?(response)
begin
response.body = Fog::JSON.decode(response.body)
response.body = parser.process(response.body) if parser
rescue Fog::JSON::DecodeError => e
Fog::Logger.warning("Error parsing response json - #{e}")
response.body = {}
end
end
response
end
end
end
end
end

View file

@ -0,0 +1,39 @@
module Fog
module AWS
module Parsers
module Lambda
class Base
def process(body)
body.inject({}) { |h, (k, v)| h[k] = rules(k, v); h }
end
private
def rules(key, value)
case value
when Hash
process(value)
when Array
value.map { |i| process(i) }
else
case key
when 'LastModified'
Time.parse(value)
when 'Policy', 'Statement'
begin
Fog::JSON.decode(value)
rescue Fog::JSON::DecodeError => e
Fog::Logger.warning("Error parsing response json - #{e}")
{}
end
else
value
end
end
end
end
end
end
end
end

View file

@ -0,0 +1,100 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Adds a permission to the access policy associated with the specified AWS Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
# ==== Parameters
# * FunctionName <~String> - Name of the Lambda function whose access policy you are updating by adding a new permission.
# * Action <~String> - AWS Lambda action you want to allow in this statement.
# * Principal <~String> - principal who is getting this permission.
# * SourceAccount <~String> - AWS account ID (without a hyphen) of the source owner.
# * SourceArn <~String> - Amazon Resource Name (ARN) of the source resource to assign permissions.
# * StatemendId. <~String> - unique statement identifier.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'Statement' <~Hash> - permission statement you specified in the request.
def add_permission(params={})
function_name = params.delete('FunctionName')
action = params.delete('Action')
principal = params.delete('Principal')
source_account = params.delete('SourceAccount')
source_arn = params.delete('SourceArn')
sid = params.delete('StatementId')
permission = {
'Action' => action,
'Principal' => principal,
'StatementId' => sid
}
permission['SourceAccount'] = source_account if source_account
permission['SourceArn'] = source_arn if source_arn
request({
:method => 'POST',
:path => "/functions/#{function_name}/versions/HEAD/policy",
:expects => 201,
:body => Fog::JSON.encode(permission),
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def add_permission(params={})
function_id = params.delete('FunctionName')
function = self.get_function_configuration(
'FunctionName' => function_id
).body
function_arn = function['FunctionArn']
action = params.delete('Action')
principal = params.delete('Principal')
source_account = params.delete('SourceAccount')
source_arn = params.delete('SourceArn')
sid = params.delete('StatementId')
if action.nil? || action.empty?
message = 'Action cannot be blank'
raise Fog::AWS::Lambda::Error, message
end
if principal.nil? || principal.empty?
message = 'Principal cannot be blank'
raise Fog::AWS::Lambda::Error, message
end
if sid.nil? || sid.empty?
message = 'Sid cannot be blank'
raise Fog::AWS::Lambda::Error, message
end
statement = {
'Action' => [action],
'Principal' => { 'Service' => principal },
'Sid' => sid,
'Resource' => function_arn,
'Effect' => 'Allow'
}
if source_arn
statement['Condition'] = {}
statement['Condition']['ArnLike'] = {
'AWS:SourceArn' => source_arn
}
end
self.data[:permissions][function_arn] ||= []
self.data[:permissions][function_arn] << statement
response = Excon::Response.new
response.status = 201
response.body = { 'Statement' => statement }
response
end
end
end
end
end

View file

@ -0,0 +1,94 @@
module Fog
module AWS
class Lambda
class Real
# Identifies a stream as an event source for a Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_CreateEventSourceMapping.html
# ==== Parameters
# * BatchSize <~Integer> - largest number of records that AWS Lambda will retrieve from your event source at the time of invoking your function.
# * Enabled <~Boolean> - indicates whether AWS Lambda should begin polling the event source.
# * EventSourceArn <~String> - Amazon Resource Name (ARN) of the stream that is the event source
# * FunctionName <~String> - Lambda function to invoke when AWS Lambda detects an event on the stream.
# * StartingPosition <~String> - position in the stream where AWS Lambda should start reading.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'BatchSize' <~Integer> - largest number of records that AWS Lambda will retrieve from your event source at the time of invoking your function.
# * 'EventSourceArn' <~String> - Amazon Resource Name (ARN) of the stream that is the source of events.
# * 'FunctionArn' <~String> - Lambda function to invoke when AWS Lambda detects an event on the stream.
# * 'LastModified' <~Time> - UTC time string indicating the last time the event mapping was updated.
# * 'LastProcessingResult' <~String> - result of the last AWS Lambda invocation of your Lambda function.
# * 'State' <~String> - state of the event source mapping.
# * 'StateTransitionReason' <~String> - reason the event source mapping is in its current state.
# * 'UUID' <~String> - AWS Lambda assigned opaque identifier for the mapping.
def create_event_source_mapping(params={})
enabled = params.delete('Enabled')
batch_size = params.delete('BatchSize')
event_source_arn = params.delete('EventSourceArn')
function_name = params.delete('FunctionName')
starting_pos = params.delete('StartingPosition')
data = {
'EventSourceArn' => event_source_arn,
'FunctionName' => function_name,
'StartingPosition' => starting_pos
}
data.merge!('BatchSize' => batch_size) if batch_size
data.merge!('Enabled' => enabled) if !enabled.nil?
request({
:method => 'POST',
:path => '/event-source-mappings/',
:expects => 202,
:body => Fog::JSON.encode(data)
}.merge(params))
end
end
class Mock
def create_event_source_mapping(params={})
enabled = params.delete('Enabled') || false
batch_size = params.delete('BatchSize') || 100
event_source_arn = params.delete('EventSourceArn')
function_name = params.delete('FunctionName')
starting_pos = params.delete('StartingPosition')
function = self.get_function_configuration('FunctionName' => function_name).body
unless event_source_arn
message = "ValidationException => "
message << "'eventSourceArn' cannot be blank"
raise Fog::AWS::Lambda::Error, message
end
unless starting_pos
message = "ValidationException => "
message << "'startingPosition' cannot be blank"
raise Fog::AWS::Lambda::Error, message
end
event_source_mapping_id = UUID.uuid
event_source_mapping = {
'BatchSize' => batch_size,
'EventSourceArn' => event_source_arn,
'FunctionArn' => function['FunctionArn'],
'LastModified' => Time.now.to_f,
'LastProcessingResult' => 'No records processed',
'State' => 'Creating',
'StateTransitionReason' => 'User action',
'UUID' => event_source_mapping_id
}
self.data[:event_source_mappings].merge!(
event_source_mapping_id => event_source_mapping
)
response = Excon::Response.new
response.body = event_source_mapping
response.status = 202
response
end
end
end
end
end

View file

@ -0,0 +1,146 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Creates a new Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_CreateFunction.html
# ==== Parameters
# * Code <~Hash> - code for the Lambda function.
# * Description <~String> - short, user-defined function description.
# * FunctionName <~String> - name you want to assign to the function you are uploading.
# * Handler <~String> - function within your code that Lambda calls to begin execution.
# * MemorySize <~Integer> - amount of memory, in MB, your Lambda function is given.
# * Role <~String> - ARN of the IAM role that Lambda assumes when it executes your function to access any other AWS resources.
# * Runtime <~String> - runtime environment for the Lambda function you are uploading.
# * Timeout <~Integer> - function execution time at which Lambda should terminate the function.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'CodeSize' <~Integer> - size, in bytes, of the function .zip file you uploaded.
# * 'Description' <~String> - user-provided description.
# * 'FunctionArn' <~String> - Amazon Resource Name (ARN) assigned to the function.
# * 'FunctionName' <~String> - name of the function.
# * 'Handler' <~String> - function Lambda calls to begin executing your function.
# * 'LastModified' <~Time> - timestamp of the last time you updated the function.
# * 'MemorySize' <~Integer> - memory size, in MB, you configured for the function.
# * 'Role' <~String> - ARN of the IAM role that Lambda assumes when it executes your function to access any other AWS resources.
# * 'Runtime' <~String> - runtime environment for the Lambda function.
# * 'Timeout' <~Integer> - function execution time at which Lambda should terminate the function.
def create_function(params={})
runtime = params.delete('Runtime') || 'nodejs'
code = params.delete('Code')
function_name = params.delete('FunctionName')
handler = params.delete('Handler')
role = params.delete('Role')
data = {
'Runtime' => runtime,
'Code' => code,
'FunctionName' => function_name,
'Handler' => handler,
'Role' => role
}
description = params.delete('Description')
data.merge!('Description' => description) if description
memory_size = params.delete('MemorySize')
data.merge!('MemorySize' => memory_size) if memory_size
timeout = params.delete('Timeout')
data.merge!('Timeout' => timeout) if timeout
request({
:method => 'POST',
:path => '/functions',
:expects => 201,
:body => Fog::JSON.encode(data),
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def create_function(params={})
response = Excon::Response.new
runtime = params.delete('Runtime') || 'nodejs'
if !%w(nodejs java8).include?(runtime)
message = 'ValidationException: Runtime must be nodejs or java8.'
raise Fog::AWS::Lambda::Error, message
end
unless code = params.delete('Code')
message = 'ValidationException: Code cannot be blank.'
raise Fog::AWS::Lambda::Error, message
end
unless function_name = params.delete('FunctionName')
message = 'ValidationException: Function name cannot be blank.'
raise Fog::AWS::Lambda::Error, message
end
unless handler = params.delete('Handler')
message = 'ValidationException: Handler cannot be blank.'
raise Fog::AWS::Lambda::Error, message
end
unless role = params.delete('Role')
message = 'ValidationException: Role cannot be blank.'
raise Fog::AWS::Lambda::Error, message
end
code_size = if code.has_key?('ZipFile')
Base64.decode64(code['ZipFile']).length
else
Fog::Mock.random_numbers(5).to_i
end
description = params.delete('Description')
function = {}
begin
opts = { 'FunctionName' => function_name }
function = self.get_function_configuration(opts).body
rescue Fog::AWS::Lambda::Error => e
# ignore: if the function doesn't exist we are OK.
end
if !function.empty?
message = "ResourceConflictException => "
message << "Function already exist: #{function_name}"
raise Fog::AWS::Lambda::Error, message
end
function_path = "function:#{function_name}"
function_arn = Fog::AWS::Mock.arn(
'lambda',
self.account_id,
function_path,
self.region
)
function = {
'CodeSize' => code_size,
'FunctionArn' => function_arn,
'FunctionName' => function_name,
'Handler' => handler,
'LastModified' => Time.now.utc,
'MemorySize' => params.delete('MemorySize') || 128,
'Timeout' => params.delete('Timeout') || 3,
'Role' => role,
'Runtime' => runtime
}
function['Description'] = description if description
self.data[:functions][function_arn] = function
response.body = function
response.status = 200
response
end
end
end
end
end

View file

@ -0,0 +1,46 @@
module Fog
module AWS
class Lambda
class Real
# Removes an event source mapping.
# http://docs.aws.amazon.com/lambda/latest/dg/API_DeleteEventSourceMapping.html
# ==== Parameters
# * UUID <~String> - event source mapping ID.
# ==== Returns
# * response<~Excon::Response>:
# * body<~String>:
def delete_event_source_mapping(params={})
mapping_id = params.delete('UUID')
request({
:method => 'DELETE',
:path => "/event-source-mappings/#{mapping_id}",
:expects => 202
}.merge(params))
end
end
class Mock
def delete_event_source_mapping(params={})
mapping = self.get_event_source_mapping(params).body
unless mapping
message = "ResourceNotFoundException => "
message << "The resource you requested does not exist."
raise Fog::AWS::Lambda::Error, message
end
mapping_id = mapping['UUID']
self.data[:event_source_mappings].delete(mapping_id)
mapping['State'] = 'Deleting'
response = Excon::Response.new
response.status = 202
response.body = mapping
response
end
end
end
end
end

View file

@ -0,0 +1,43 @@
module Fog
module AWS
class Lambda
class Real
# Deletes the specified Lambda function code and configuration.
# http://docs.aws.amazon.com/lambda/latest/dg/API_DeleteFunction.html
# ==== Parameters
# * FunctionName <~String> - Lambda function to delete.
# ==== Returns
# * response<~Excon::Response>:
# * body<~String>:
def delete_function(params={})
function_name = params.delete('FunctionName')
request({
:method => 'DELETE',
:path => "/functions/#{function_name}",
:expects => 204
}.merge(params))
end
end
class Mock
def delete_function(params={})
response = Excon::Response.new
response.status = 204
response.body = ''
function = self.get_function_configuration(params).body
function_id = function['FunctionArn']
self.data[:functions].delete function_id
self.data[:permissions].delete function_id
self.data[:event_source_mappings].delete_if do |m,f|
f['FunctionArn'].eql?(function_id)
end
response
end
end
end
end
end

View file

@ -0,0 +1,54 @@
module Fog
module AWS
class Lambda
class Real
# Returns configuration information for the specified event source mapping.
# http://docs.aws.amazon.com/lambda/latest/dg/API_GetEventSourceMapping.html
# ==== Parameters
# * UUID <~String> - AWS Lambda assigned ID of the event source mapping.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'BatchSize' <~Integer> - largest number of records that AWS Lambda will retrieve from your event source at the time of invoking your function.
# * 'EventSourceArn' <~String> - Amazon Resource Name (ARN) of the stream that is the source of events.
# * 'FunctionArn' <~String> - Lambda function to invoke when AWS Lambda detects an event on the stream.
# * 'LastModified' <~Time> - UTC time string indicating the last time the event mapping was updated.
# * 'LastProcessingResult' <~String> - result of the last AWS Lambda invocation of your Lambda function.
# * 'State' <~String> - state of the event source mapping.
# * 'StateTransitionReason' <~String> - reason the event source mapping is in its current state.
# * 'UUID' <~String> - AWS Lambda assigned opaque identifier for the mapping.
# * 'Code' <~Hash> - object for the Lambda function location.
# * 'Configuration' <~Hash> - function metadata description.
def get_event_source_mapping(params={})
mapping_id = params.delete('UUID')
request({
:method => 'GET',
:path => "/event-source-mappings/#{mapping_id}"
}.merge(params))
end
end
class Mock
def get_event_source_mapping(params={})
mapping_id = params.delete('UUID')
unless mapping = self.data[:event_source_mappings][mapping_id]
message = 'ResourceNotFoundException => '
message << 'The resource you requested does not exist.'
raise Fog::AWS::Lambda::Error, message
end
if mapping['State'].eql?('Creating')
mapping['LastProcessingResult'] = 'OK'
mapping['State'] = 'Enabled'
end
response = Excon::Response.new
response.status = 200
response.body = mapping
response
end
end
end
end
end

View file

@ -0,0 +1,74 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Returns the configuration information of the Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_GetFunction.html
# ==== Parameters
# * FunctionName <~String> - Lambda function name.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'Code' <~Hash> - object for the Lambda function location.
# * 'Configuration' <~Hash> - function metadata description.
def get_function(params={})
function_name = params.delete('FunctionName')
request({
:method => 'GET',
:path => "/functions/#{function_name}/versions/HEAD",
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def get_function(params={})
response = Excon::Response.new
response.status = 200
response.body = ''
unless function_id = params.delete('FunctionName')
raise Fog::AWS::Lambda::Error, 'Function name cannot be blank.'
end
if function_id.match(/^arn:aws:lambda:.+:function:.+/)
function = self.data[:functions][function_id]
else
search_function = Hash[
self.data[:functions].select do |f,v|
v['FunctionName'].eql?(function_id)
end
]
function = search_function.values.first
end
msg = 'The resource you requested does not exist.'
raise Fog::AWS::Lambda::Error, msg if (function.nil? || function.empty?)
location = "https://awslambda-#{self.region}-tasks.s3-#{self.region}"
location << ".amazonaws.com/snapshot/#{self.account_id}/"
location << "#{function['FunctionName']}-#{UUID.uuid}"
location << '?x-amz-security-token='
location << Fog::Mock.random_base64(718)
location << "&AWSAccessKeyId=#{self.aws_access_key_id}"
location << "&Expires=#{Time.now.to_i + 60*10}"
location << '&Signature='
location << Fog::Mock.random_base64(28)
body = {
'Code' => {
'Location' => location,
'RepositoryType' => 'S3'
},
'Configuration' => function
}
response.body = body
response
end
end
end
end
end

View file

@ -0,0 +1,44 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Returns the configuration information of the Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_GetFunction.html
# ==== Parameters
# * FunctionName <~String> - Lambda function name.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'CodeSize' <~Integer> - size, in bytes, of the function .zip file you uploaded.
# * 'Description' <~String> - user-provided description.
# * 'FunctionArn' <~String> - Amazon Resource Name (ARN) assigned to the function.
# * 'FunctionName' <~String> - name of the function.
# * 'Handler' <~String> - function Lambda calls to begin executing your function.
# * 'LastModified' <~Time> - timestamp of the last time you updated the function.
# * 'Memorysize' <~String> - memory size, in MB, you configured for the function.
# * 'Role' <~String> - ARN of the IAM role that Lambda assumes when it executes your function to access any other AWS resources.
# * 'Runtime' <~String> - runtime environment for the Lambda function.
# * 'Timeout' <~Integer> - function execution time at which Lambda should terminate the function.
def get_function_configuration(params={})
function_name = params.delete('FunctionName')
request({
:method => 'GET',
:path => "/functions/#{function_name}/versions/HEAD/configuration",
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def get_function_configuration(params={})
response = self.get_function(params)
function_configuration = response.body['Configuration']
response.body = function_configuration
response
end
end
end
end
end

View file

@ -0,0 +1,52 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Returns the access policy, containing a list of permissions granted via the AddPermission API, associated with the specified bucket.
# http://docs.aws.amazon.com/lambda/latest/dg/API_GetPolicy.html
# ==== Parameters
# * FunctionName <~String> - Function name whose access policy you want to retrieve.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'Policy' <~Hash> - The access policy associated with the specified function.
def get_policy(params={})
function_name = params.delete('FunctionName')
request({
:method => 'GET',
:path => "/functions/#{function_name}/versions/HEAD/policy",
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def get_policy(params={})
response = Excon::Response.new
function = self.get_function_configuration(params).body
function_arn = function['FunctionArn']
statements = self.data[:permissions][function_arn] || []
if statements.empty?
message = "ResourceNotFoundException => "
message << "The resource you requested does not exist."
raise Fog::AWS::Lambda::Error, message
end
policy = {
'Version' => '2012-10-17',
'Statement' => statements,
'Id' => 'default'
}
response.status = 200
response.body = { 'Policy' => policy }
response
end
end
end
end
end

View file

@ -0,0 +1,85 @@
module Fog
module AWS
class Lambda
class Real
# Invokes a specified Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html
# ==== Parameters
# * ClientContext <~Hash> - client-specific information to the Lambda function you are invoking.
# * FunctionName <~String> - Lambda function name.
# * InvocationType <~String> - function invocation type.
# * LogType <~String> - logs format for function calls of "RequestResponse" invocation type.
# * Payload <~Integer> - Lambda function input.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash> - JSON representation of the object returned by the Lambda function.
def invoke(params={})
headers = {}
if client_context = params.delete('ClientContext')
headers['X-Amz-Client-Context'] =
Base64::encode64(Fog::Json.encode(client_context))
end
if client_type = params.delete('InvocationType')
headers['X-Amz-Client-Type'] = client_type
end
if log_type = params.delete('LogType')
headers['X-Amz-Log-Type'] = log_type
end
payload = Fog::JSON.encode(params.delete('Payload'))
function_name = params.delete('FunctionName')
request({
:method => 'POST',
:path => "/functions/#{function_name}/invocations",
:headers => headers,
:body => payload,
:expects => [200, 202, 204]
}.merge(params))
end
end
class Mock
def invoke(params={})
response = Excon::Response.new
response.status = 200
response.body = ''
unless function_id = params.delete('FunctionName')
message = 'AccessDeniedException => '
message << 'Unable to determine service/operation name to be authorized'
raise Fog::AWS::Lambda::Error, message
end
client_context = params.delete('ClientContext')
client_type = params.delete('InvocationType')
log_type = params.delete('LogType')
payload = params.delete('Payload')
if (client_context || client_type || log_type)
message = "invoke parameters handling are not yet mocked [light_black](#{caller.first})[/]"
Fog::Logger.warning message
Fog::Mock.not_implemented
end
if payload
message = "payload parameter is ignored since we are not really "
message << "invoking a function [light_black](#{caller.first})[/]"
Fog::Logger.warning message
end
function = self.get_function_configuration('FunctionName' => function_id).body
if function.is_a?(Hash) && function.has_key?('FunctionArn')
response.body = "\"Imagine #{function['FunctionArn']} was invoked\""
else
message = "ResourceNotFoundException => Function not found: #{function_id}"
raise Fog::AWS::Lambda::Error, message
end
response
end
end
end
end
end

View file

@ -0,0 +1,69 @@
module Fog
module AWS
class Lambda
class Real
# Returns a list of event source mappings where you can identify a stream as an event source.
# http://docs.aws.amazon.com/lambda/latest/dg/API_ListEventSourceMappings.html
# ==== Parameters
# * EventSourceArn <~String> - Amazon Resource Name (ARN) of the stream.
# * FunctionName <~String> - name of the Lambda function.
# * Marker <~String> - opaque pagination token returned from a previous ListEventSourceMappings operation.
# * MaxItems <~Integer> - maximum number of event sources to return in response.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'EventSourceMappings' <~Array> - array of EventSourceMappingConfiguration objects.
# * 'NextMarker' <~String> - present if there are more event source mappings.
def list_event_source_mappings(params={})
event_source_arn = params.delete('EventSourceArn')
function_name = params.delete('FunctionName')
marker = params.delete('Marker')
max_items = params.delete('MaxItems')
query = {}
query.merge!('EventSourceArn' => event_source_arn) if event_source_arn
query.merge!('FunctionName' => function_name) if function_name
query.merge!('Marker' => marker) if marker
query.merge!('MaxItems' => max_items) if max_items
request({
:method => 'GET',
:path => '/event-source-mappings/',
:query => query
}.merge(params))
end
end
class Mock
def list_event_source_mappings(params={})
response = Excon::Response.new
response.status = 200
function_name = params.delete('FunctionName')
begin
function = self.get_function_configuration('FunctionName' => function_name).body
function_arn = function['FunctionArn']
rescue Fog::AWS::Lambda::Error => e
# interestingly enough, if you try to do a list_event_source_mappings
# on a nonexisting function, Lambda API endpoint doesn't return
# error, just an empty array.
end
event_source_mappings = []
if function_arn
event_source_mappings = self.data[:event_source_mappings].values.select do |m|
m['FunctionArn'].eql?(function_arn)
end
end
response.body = {
'EventSourceMappings' => event_source_mappings,
'NextMarker' => nil
}
response
end
end
end
end
end

View file

@ -0,0 +1,39 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Returns a list of your Lambda functions.
# http://docs.aws.amazon.com/lambda/latest/dg/API_ListFunctions.html
# ==== Parameters
# * Marker <~String> - opaque pagination token returned from a previous ListFunctions operation. If present, indicates where to continue the listing.
# * MaxItems <~Integer> - Specifies the maximum number of AWS Lambda functions to return in response.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'Functions' <~Array> - list of Lambda functions.
# * 'NextMarker' <~String> - present if there are more functions.
def list_functions(params={})
request({
:method => 'GET',
:path => '/functions/',
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def list_functions(params={})
response = Excon::Response.new
response.status = 200
response.body = {
'Functions' => self.data[:functions].values,
'NextMarker' => nil
}
response
end
end
end
end
end

View file

@ -0,0 +1,56 @@
module Fog
module AWS
class Lambda
class Real
# Remove individual permissions from an access policy associated with a Lambda function by providing a Statement ID.
# http://docs.aws.amazon.com/lambda/latest/dg/API_RemovePermission.html
# ==== Parameters
# * FunctionName <~String> - Lambda function whose access policy you want to remove a permission from.
# * StatementId <~String> - Statement ID of the permission to remove.
# ==== Returns
# * response<~Excon::Response>:
# * body<~String>:
def remove_permission(params={})
function_name = params.delete('FunctionName')
statement_id = params.delete('StatementId')
request({
:method => 'DELETE',
:path => "/functions/#{function_name}/versions/HEAD/policy/#{statement_id}",
:expects => 204
}.merge(params))
end
end
class Mock
def remove_permission(params={})
function_name = params.delete('FunctionName')
opts = { 'FunctionName' => function_name }
function = self.get_function_configuration(opts).body
function_arn = function['FunctionArn']
statement_id = params.delete('StatementId')
message = 'Statement ID cannot be blank'
raise Fog::AWS::Lambda::Error, message unless statement_id
permissions_qty = self.data[:permissions][function_arn].size
self.data[:permissions][function_arn].delete_if do |s|
s['Sid'].eql?(statement_id)
end
if self.data[:permissions][function_arn].size.eql?(permissions_qty)
message = "ResourceNotFoundException => "
message << "The resource you requested does not exist."
raise Fog::AWS::Lambda::Error, message
end
response = Excon::Response.new
response.status = 204
response.body = ''
response
end
end
end
end
end

View file

@ -0,0 +1,86 @@
module Fog
module AWS
class Lambda
class Real
# Change the parameters of the existing mapping without losing your position in the stream.
# http://docs.aws.amazon.com/lambda/latest/dg/API_UpdateEventSourceMapping.html
# ==== Parameters
# * UUID <~String> - event source mapping identifier.
# * BatchSize <~Integer> - maximum number of stream records that can be sent to your Lambda function for a single invocation.
# * Enabled <~Boolean> - specifies whether AWS Lambda should actively poll the stream or not.
# * FunctionName <~String> - Lambda function to which you want the stream records sent.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'BatchSize' <~Integer> - largest number of records that AWS Lambda will retrieve from your event source at the time of invoking your function.
# * 'EventSourceArn' <~String> - Amazon Resource Name (ARN) of the stream that is the source of events.
# * 'FunctionArn' <~String> - Lambda function to invoke when AWS Lambda detects an event on the stream.
# * 'LastModified' <~Time> - UTC time string indicating the last time the event mapping was updated.
# * 'LastProcessingResult' <~String> - result of the last AWS Lambda invocation of your Lambda function.
# * 'State' <~String> - state of the event source mapping.
# * 'StateTransitionReason' <~String> - reason the event source mapping is in its current state.
# * 'UUID' <~String> - AWS Lambda assigned opaque identifier for the mapping.
def update_event_source_mapping(params={})
function_name = params.delete('FunctionName')
mapping_id = params.delete('UUID')
batch_size = params.delete('BatchSize')
enabled = params.delete('Enabled')
update = {}
update.merge!('BatchSize' => batch_size) if batch_size
update.merge!('Enabled' => enabled) if !enabled.nil?
update.merge!('FunctionName' => function_name) if function_name
request({
:method => 'PUT',
:path => "/event-source-mappings/#{mapping_id}",
:expects => 202,
:body => Fog::JSON.encode(update)
}.merge(params))
end
end
class Mock
def update_event_source_mapping(params={})
mapping_id = params.delete('UUID')
mapping = self.data[:event_source_mappings][mapping_id]
unless mapping
message = 'ResourceNotFoundException => '
message << 'The resource you requested does not exist.'
raise Fog::AWS::Lambda::Error, message
end
function_name = params.delete('FunctionName')
function = {}
if function_name
function_opts = { 'FunctionName' => function_name }
function = self.get_function_configuration(function_opts).body
function_arn = function['FunctionArn']
end
batch_size = params.delete('BatchSize')
enabled = params.delete('Enabled')
update = {}
if function_name && !function.empty? && function_arn
update.merge!('FunctionArn' => function_arn)
end
update.merge!('BatchSize' => batch_size) if batch_size
update.merge!('Enabled' => enabled) if !enabled.nil?
mapping.merge!(update)
mapping['State'] = 'Disabling' if enabled.eql?(false)
mapping['State'] = 'Enabling' if enabled.eql?(true)
response = Excon::Response.new
response.status = 202
response.body = mapping
response
end
end
end
end
end

View file

@ -0,0 +1,75 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Updates the code for the specified Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_UpdateFunctionCode.html
# ==== Parameters
# * FunctionName <~String> - existing Lambda function name whose code you want to replace.
# * S3Bucket <~String> - Amazon S3 bucket name where the .zip file containing your deployment package is stored.
# * S3Key <~String> - Amazon S3 object (the deployment package) key name you want to upload.
# * S3ObjectVersion <~String> - Amazon S3 object (the deployment package) version you want to upload.
# * ZipFile <~String> - Based64-encoded .zip file containing your packaged source code.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'CodeSize' <~Integer> - size, in bytes, of the function .zip file you uploaded.
# * 'Description' <~String> - user-provided description.
# * 'FunctionArn' <~String> - Amazon Resource Name (ARN) assigned to the function.
# * 'FunctionName' <~String> - name of the function.
# * 'Handler' <~String> - function Lambda calls to begin executing your function.
# * 'LastModified' <~Time> - timestamp of the last time you updated the function.
# * 'Memorysize' <~String> - memory size, in MB, you configured for the function.
# * 'Role' <~String> - ARN of the IAM role that Lambda assumes when it executes your function to access any other AWS resources.
# * 'Runtime' <~String> - runtime environment for the Lambda function.
# * 'Timeout' <~Integer> - function execution time at which Lambda should terminate the function.
def update_function_code(params={})
function_name = params.delete('FunctionName')
s3_bucket = params.delete('S3Bucket')
s3_key = params.delete('S3Key')
s3_object_ver = params.delete('S3ObjectVersion')
zip_file = params.delete('ZipFile')
update = {}
update.merge!('S3Bucket' => s3_bucket) if s3_bucket
update.merge!('S3Key' => s3_key) if s3_key
update.merge!('S3ObjectVersion' => s3_object_ver) if s3_object_ver
update.merge!('ZipFile' => zip_file) if zip_file
request({
:method => 'PUT',
:path => "/functions/#{function_name}/versions/HEAD/code",
:body => Fog::JSON.encode(update),
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def update_function_code(params={})
response = self.get_function_configuration(params)
request_data = []
%w(S3Bucket S3Key S3ObjectVersion ZipFile).each do |p|
request_data << params.delete(p) if params.has_key?(p)
end
message = 'Please provide a source for function code.'
raise Fog::AWS::Lambda::Error, message if request_data.empty?
# we ignore any parameters since we are not uploading any code
function_arn = response.body['FunctionArn']
response = Excon::Response.new
response.status = 200
response.body = self.data[:functions][function_arn]
response
end
end
end
end
end

View file

@ -0,0 +1,84 @@
module Fog
module AWS
class Lambda
class Real
require 'fog/aws/parsers/lambda/base'
# Updates the configuration parameters for the specified Lambda function.
# http://docs.aws.amazon.com/lambda/latest/dg/API_UpdateFunctionConfiguration.html
# ==== Parameters
# * FunctionName <~String> - name of the Lambda function.
# * Description <~String> - short user-defined function description.
# * Handler <~String> - function that Lambda calls to begin executing your function.
# * MemorySize <~Integer> - amount of memory, in MB, your Lambda function is given.
# * Role <~String> - ARN of the IAM role that Lambda will assume when it executes your function.
# * Timeout <~Integer> - function execution time at which AWS Lambda should terminate the function.
# ==== Returns
# * response<~Excon::Response>:
# * body<~Hash>:
# * 'CodeSize' <~Integer> - size, in bytes, of the function .zip file you uploaded.
# * 'Description' <~String> - user-provided description.
# * 'FunctionArn' <~String> - Amazon Resource Name (ARN) assigned to the function.
# * 'FunctionName' <~String> - name of the function.
# * 'Handler' <~String> - function Lambda calls to begin executing your function.
# * 'LastModified' <~Time> - timestamp of the last time you updated the function.
# * 'Memorysize' <~String> - memory size, in MB, you configured for the function.
# * 'Role' <~String> - ARN of the IAM role that Lambda assumes when it executes your function to access any other AWS resources.
# * 'Runtime' <~String> - runtime environment for the Lambda function.
# * 'Timeout' <~Integer> - function execution time at which Lambda should terminate the function.
def update_function_configuration(params={})
function_name = params.delete('FunctionName')
description = params.delete('Description')
handler = params.delete('Handler')
memory_size = params.delete('MemorySize')
role = params.delete('Role')
timeout = params.delete('Timeout')
update = {}
update.merge!('Description' => description) if description
update.merge!('Handler' => handler) if handler
update.merge!('MemorySize' => memory_size) if memory_size
update.merge!('Role' => role) if role
update.merge!('Timeout' => timeout) if timeout
request({
:method => 'PUT',
:path => "/functions/#{function_name}/versions/HEAD/configuration",
:body => Fog::JSON.encode(update),
:parser => Fog::AWS::Parsers::Lambda::Base.new
}.merge(params))
end
end
class Mock
def update_function_configuration(params={})
response = self.get_function_configuration(params)
function_arn = response.body['FunctionArn']
description = params.delete('Description')
handler = params.delete('Handler')
memory_size = params.delete('MemorySize')
role = params.delete('Role')
timeout = params.delete('Timeout')
update = {}
update.merge!('Description' => description) if description
update.merge!('Handler' => handler) if handler
update.merge!('MemorySize' => memory_size) if memory_size
update.merge!('Role' => role) if role
update.merge!('Timeout' => timeout) if timeout
self.data[:functions][function_arn].merge!(update)
response = Excon::Response.new
response.status = 200
response.body = self.data[:functions][function_arn]
response
end
end
end
end
end

View file

@ -0,0 +1,9 @@
console.log('Loading function');
exports.handler = function(event, context) {
console.log('value1 =', event.key1);
console.log('value2 =', event.key2);
console.log('value3 =', event.key3);
context.succeed(event.key1); // Echo back the first key value
// context.fail('Something went wrong');
};

View file

@ -0,0 +1,9 @@
console.log('Loading function');
exports.handler = function(event, context) {
console.log('value1 =', event.key1);
console.log('value2 =', event.key2);
console.log('value3 =', event.key3);
context.succeed(event.key2); // Echo back the second key value
// context.fail('Something went wrong');
};

View file

@ -0,0 +1,460 @@
Shindo.tests('AWS::Lambda | function requests', ['aws', 'lambda']) do
_lambda = Fog::AWS[:lambda]
account_id = _lambda.account_id
region = _lambda.region
function1 = IO.read(AWS::Lambda::Samples::FUNCTION_1)
function2 = IO.read(AWS::Lambda::Samples::FUNCTION_2)
zipped_function1 = Base64::encode64(AWS::Lambda::Formats.zip(function1))
zipped_function2 = Base64::encode64(AWS::Lambda::Formats.zip(function2))
function1_arn = nil
function1_name = 'function1'
function2_name = 'function2'
function1_handler = 'index.handler'
function_role = 'arn:aws:iam::647975416665:role/lambda_basic_execution'
sns_principal = 'sns.amazonaws.com'
sns_topic_sid = Fog::Mock.random_letters_and_numbers(32)
sns_allowed_action = 'lambda:invoke'
sns_topic_arn = Fog::AWS::Mock.arn('sns', account_id, 'mock_topic', region)
kinesis_stream_arn = Fog::AWS::Mock.arn('kinesis', account_id, 'mock_stream', region)
event_source_mapping1_id = nil
tests('success') do
tests('#list_functions').formats(AWS::Lambda::Formats::LIST_FUNCTIONS) do
result = _lambda.list_functions.body
functions = result['Functions']
returns(true) { functions.empty? }
result
end
tests('#create_function').formats(AWS::Lambda::Formats::CREATE_FUNCTION) do
description = 'a copy of my first function'
result = _lambda.create_function(
'FunctionName' => function1_name,
'Handler' => function1_handler,
'Role' => function_role,
'Description' => description,
'Code' => { 'ZipFile' => zipped_function1 }
).body
returns(true) { result.has_key?('FunctionArn') }
returns(true) { result['CodeSize'] > 0 }
returns(true) { result['MemorySize'] >= 128 }
returns(true) { result['FunctionName'].eql?(function1_name) }
returns(true) { result['Handler'].eql?(function1_handler) }
function1_arn = result['FunctionArn']
result
end
tests('#invoke') do
payload = { 'value1' => 2, 'value2' => 42 }
result = _lambda.invoke(
'FunctionName' => function1_name,
'Payload' => payload
).body
returns(false) { result.length.zero? }
returns(false) { result.match(/function:#{function1_name} was invoked/).nil? }
result
end
tests('#get_function').formats(AWS::Lambda::Formats::GET_FUNCTION) do
result = _lambda.get_function('FunctionName' => function1_name).body
func_config = result['Configuration']
returns(false) { result['Code']['Location'].match(/^https:\/\/awslambda-/).nil? }
returns(true) { func_config.has_key?('FunctionArn') }
returns(true) { func_config['CodeSize'] > 0 }
returns(true) { func_config['MemorySize'] >= 128 }
returns(true) { func_config['FunctionName'].eql?(function1_name) }
returns(true) { func_config['Handler'].eql?(function1_handler) }
returns(true) { func_config['FunctionArn'].eql?(function1_arn) }
result
end
tests('#get_function_configuration').formats(AWS::Lambda::Formats::GET_FUNCTION_CONFIGURATION) do
result = _lambda.get_function_configuration(
'FunctionName' => function1_name).body
returns(true) { result.has_key?('FunctionArn') }
returns(true) { result['CodeSize'] > 0 }
returns(true) { result['MemorySize'] >= 128 }
returns(true) { result['FunctionName'].eql?(function1_name) }
returns(true) { result['Handler'].eql?(function1_handler) }
returns(true) { result['FunctionArn'].eql?(function1_arn) }
result
end
tests('#update_function_configuration').formats(AWS::Lambda::Formats::UPDATE_FUNCTION_CONFIGURATION) do
new_memory_size = 256
new_description = "this function does nothing, just let's call it foobar"
new_timeout = 10
result = _lambda.update_function_configuration(
'FunctionName' => function1_name,
'MemorySize' => new_memory_size,
'Description' => new_description,
'Timeout' => new_timeout
).body
returns(true) { result['CodeSize'] > 0 }
returns(true) { result['MemorySize'].eql?(new_memory_size) }
returns(true) { result['FunctionArn'].eql?(function1_arn) }
returns(true) { result['Description'].eql?(new_description) }
returns(true) { result['Timeout'].eql?(new_timeout) }
result
end
tests('#update_function_code').formats(AWS::Lambda::Formats::UPDATE_FUNCTION_CODE) do
result = _lambda.update_function_code(
'FunctionName' => function1_name,
'ZipFile' => zipped_function2
).body
returns(true) { result.has_key?('FunctionArn') }
returns(true) { result['CodeSize'] > 0 }
returns(true) { result['MemorySize'] >= 128 }
returns(true) { result['FunctionName'].eql?(function1_name) }
returns(true) { result['Handler'].eql?(function1_handler) }
result
end
tests('#add_permission').formats(AWS::Lambda::Formats::ADD_PERMISSION) do
params = {
'FunctionName' => function1_name,
'Principal' => sns_principal,
'StatementId' => sns_topic_sid,
'Action' => sns_allowed_action,
'SourceArn' => sns_topic_arn
}
result = _lambda.add_permission(params).body
statement = result['Statement']
returns(true) { statement['Action'].include?(sns_allowed_action) }
returns(true) { statement['Principal']['Service'].eql?(sns_principal) }
returns(true) { statement['Sid'].eql?(sns_topic_sid) }
returns(true) { statement['Resource'].eql?(function1_arn) }
returns(true) { statement['Effect'].eql?('Allow') }
returns(false) { statement['Condition'].empty? }
result
end
tests('#get_policy').formats(AWS::Lambda::Formats::GET_POLICY) do
result = _lambda.get_policy('FunctionName' => function1_name).body
policy = result['Policy']
returns(false) { policy['Statement'].empty? }
statement = policy['Statement'].first
returns(true) { statement['Action'].include?(sns_allowed_action) }
returns(true) { statement['Principal']['Service'].eql?(sns_principal) }
returns(true) { statement['Sid'].eql?(sns_topic_sid) }
returns(true) { statement['Resource'].eql?(function1_arn) }
returns(true) { statement['Effect'].eql?('Allow') }
returns(false) { statement['Condition'].empty? }
result
end
tests('#remove_permission') do
params = {
'FunctionName' => function1_name,
'StatementId' => sns_topic_sid
}
result = _lambda.remove_permission(params).body
returns(true) { result.empty? }
raises(Fog::AWS::Lambda::Error) do
_lambda.get_policy('FunctionName' => function1_name)
end
result
end
tests('#create_event_source_mapping').formats(AWS::Lambda::Formats::CREATE_EVENT_SOURCE_MAPPING) do
params = {
'FunctionName' => function1_name,
'EventSourceArn' => kinesis_stream_arn,
'Enabled' => true,
'StartingPosition' => 'TRIM_HORIZON'
}
result = _lambda.create_event_source_mapping(params).body
returns(true) { result['BatchSize'] > 0 }
returns(true) { result['EventSourceArn'].eql?(kinesis_stream_arn) }
returns(true) { result['FunctionArn'].eql?(function1_arn) }
returns(true) { result['LastProcessingResult'].eql?('No records processed') }
returns(true) { result['State'].eql?('Creating') }
returns(true) { result['StateTransitionReason'].eql?('User action') }
event_source_mapping1_id = result['UUID']
result
end
tests('#list_event_source_mappings').formats(AWS::Lambda::Formats::LIST_EVENT_SOURCE_MAPPINGS) do
params = { 'FunctionName' => function1_name }
result = _lambda.list_event_source_mappings(params).body
event_source_mappings = result['EventSourceMappings']
returns(false) { event_source_mappings.empty? }
mapping = event_source_mappings.first
returns(true) { mapping['UUID'].eql?(event_source_mapping1_id) }
result
end
tests('#get_event_source_mapping').formats(AWS::Lambda::Formats::GET_EVENT_SOURCE_MAPPING) do
params = { 'UUID' => event_source_mapping1_id }
result = _lambda.get_event_source_mapping(params).body
returns(true) { result['BatchSize'] > 0 }
returns(true) { result['EventSourceArn'].eql?(kinesis_stream_arn) }
returns(true) { result['FunctionArn'].eql?(function1_arn) }
returns(true) { result['LastProcessingResult'].eql?('OK') }
returns(true) { result['State'].eql?('Enabled') }
returns(true) { result['StateTransitionReason'].eql?('User action') }
returns(true) { result['UUID'].eql?(event_source_mapping1_id) }
result
end
tests('#update_event_source_mapping').formats(AWS::Lambda::Formats::UPDATE_EVENT_SOURCE_MAPPING) do
new_batch_size = 500
enabled_mapping = false
params = {
'UUID' => event_source_mapping1_id,
'BatchSize' => new_batch_size,
'Enabled' => enabled_mapping
}
result = _lambda.update_event_source_mapping(params).body
returns(true) { result['BatchSize'].eql?(new_batch_size) }
returns(true) { result['EventSourceArn'].eql?(kinesis_stream_arn) }
returns(true) { result['FunctionArn'].eql?(function1_arn) }
returns(true) { result['LastProcessingResult'].eql?('OK') }
returns(true) { result['State'].eql?('Disabling') }
returns(true) { result['StateTransitionReason'].eql?('User action') }
returns(true) { result['UUID'].eql?(event_source_mapping1_id) }
result
end
tests('#delete_event_source_mapping').formats(AWS::Lambda::Formats::DELETE_EVENT_SOURCE_MAPPING) do
params = { 'UUID' => event_source_mapping1_id }
result = _lambda.delete_event_source_mapping(params).body
returns(true) { result['BatchSize'] > 0 }
returns(true) { result['EventSourceArn'].eql?(kinesis_stream_arn) }
returns(true) { result['FunctionArn'].eql?(function1_arn) }
returns(false) { result['LastProcessingResult'].empty? }
returns(true) { result['State'].eql?('Deleting') }
returns(true) { result['StateTransitionReason'].eql?('User action') }
returns(true) { result['UUID'].eql?(event_source_mapping1_id) }
result
end
tests('#list_event_source_mappings again').formats(AWS::Lambda::Formats::LIST_EVENT_SOURCE_MAPPINGS) do
params = { 'FunctionName' => function1_name }
result = _lambda.list_event_source_mappings(params).body
event_source_mappings = result['EventSourceMappings']
returns(true) { event_source_mappings.empty? }
result
end
tests('#delete_function') do
result = _lambda.delete_function('FunctionName' => function1_name).body
returns(true) { result.empty? }
raises(Fog::AWS::Lambda::Error) do
_lambda.get_function('FunctionName' => function1_name)
end
result
end
tests('#list_functions again').formats(AWS::Lambda::Formats::LIST_FUNCTIONS) do
result = _lambda.list_functions.body
functions = result['Functions']
returns(true) { functions.empty? }
result
end
tests('#create_function for failures tests').formats(AWS::Lambda::Formats::CREATE_FUNCTION) do
description = 'failure tests function'
result = _lambda.create_function(
'FunctionName' => function2_name,
'Handler' => function1_handler,
'Role' => function_role,
'Description' => description,
'Code' => { 'ZipFile' => zipped_function1 }
).body
returns(true) { result.has_key?('FunctionArn') }
returns(true) { result['CodeSize'] > 0 }
returns(true) { result['MemorySize'] >= 128 }
returns(true) { result['FunctionName'].eql?(function2_name) }
returns(true) { result['Handler'].eql?(function1_handler) }
result
end
end
tests('failures') do
tests("#invoke without function name").raises(Fog::AWS::Lambda::Error) do
response = _lambda.invoke.body
end
tests("#invoke nonexistent function").raises(Fog::AWS::Lambda::Error) do
response = Fog::AWS[:lambda].invoke('FunctionName' => 'nonexistent').body
end
tests("#get_function without function name").raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_function.body
end
tests("#get_function on nonexistent function").raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_function('FunctionName' => 'nonexistent').body
end
tests("#get_function_configuration without function name").raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_function_configuration.body
end
tests("#get_function_configuration on nonexistent function").raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_function_configuration('FunctionName' => 'nonexistent').body
end
tests("update_function_configuration without function name").raises(Fog::AWS::Lambda::Error) do
response = _lambda.update_function_configuration.body
end
tests("#update_function_configuration on nonexistent function").raises(Fog::AWS::Lambda::Error) do
response = _lambda.update_function_configuration('FunctionName' => 'nonexistent').body
end
tests("update_function_code without function name").raises(Fog::AWS::Lambda::Error) do
response = _lambda.update_function_code.body
end
tests("#update_function_code on nonexistent function").raises(Fog::AWS::Lambda::Error) do
response = _lambda.update_function_code(
'FunctionName' => 'nonexistent',
'ZipFile' => zipped_function2
).body
end
tests("#update_function_code on valid function without source").raises(Fog::AWS::Lambda::Error) do
response = _lambda.update_function_code('FunctionName' => 'foobar').body
end
tests("#delete_function without params").raises(Fog::AWS::Lambda::Error) do
response = _lambda.delete_function.body
end
tests("#delete_function on nonexistent function").raises(Fog::AWS::Lambda::Error) do
response = _lambda.delete_function('FunctionName' => 'nonexistent').body
end
tests('#get_policy without params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_policy.body
end
tests('#get_policy on nonexistent function').raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_policy('FunctionName' => 'nonexistent').body
end
tests('#get_policy on function without permissions').raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_policy('FunctionName' => function2_name).body
end
tests('#add_permission without params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.add_permission.body
end
tests('#add_permission on nonexistent function').raises(Fog::AWS::Lambda::Error) do
response = _lambda.add_permission('FunctionName' => 'nonexistent').body
end
tests('#add_permission with missing params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.add_permission('FunctionName' => function2_name).body
end
tests('#remove_permission without params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.remove_permission.body
end
tests('#remove_permission on nonexistent function').raises(Fog::AWS::Lambda::Error) do
response = _lambda.remove_permission('FunctionName' => 'nonexistent').body
end
tests('#remove_permission on function with missing sid param').raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_policy('FunctionName' => function2_name).body
end
tests('#remove_permission on function with missing sid param').raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_policy(
'FunctionName' => function2_name,
'StatementId' => 'nonexistent_statement_id'
).body
end
tests('#create_event_source_mapping without params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.create_event_source_mapping.body
end
tests('#create_event_source_mapping on nonexistent function').raises(Fog::AWS::Lambda::Error) do
response = _lambda.create_event_source_mapping('FunctionName' => 'nonexistent').body
end
tests('#create_event_source_mapping with missing params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.create_event_source_mapping('FunctionName' => function2_name).body
end
tests('#get_event_source_mapping without params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.get_event_source_mapping.body
end
tests('#get_event_source_mapping nonexistent').raises(Fog::AWS::Lambda::Error) do
mapping_id = "deadbeef-caca-cafe-cafa-ffffdeadbeef"
response = _lambda.get_event_source_mapping('UUID' => mapping_id).body
end
tests('#update_event_source_mapping without params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.update_event_source_mapping.body
end
tests('#update_event_source_mapping nonexistent').raises(Fog::AWS::Lambda::Error) do
mapping_id = "deadbeef-caca-cafe-cafa-ffffdeadbeef"
response = _lambda.update_event_source_mapping('UUID' => mapping_id).body
end
tests('#delete_event_source_mapping without params').raises(Fog::AWS::Lambda::Error) do
response = _lambda.delete_event_source_mapping.body
end
tests('#delete_event_source_mapping nonexistent').raises(Fog::AWS::Lambda::Error) do
mapping_id = "deadbeef-caca-cafe-cafa-ffffdeadbeef"
response = _lambda.create_event_source_mapping('UUID' => mapping_id).body
end
end
end

View file

@ -0,0 +1,81 @@
class AWS
class Lambda
module Formats
require 'zip/zip'
GET_FUNCTION_CONFIGURATION = {
'CodeSize' => Integer,
'Description' => Fog::Nullable::String,
'FunctionArn' => String,
'FunctionName' => String,
'Handler' => String,
'LastModified' => Time,
'MemorySize' => Integer,
'Role' => String,
'Runtime' => String,
'Timeout' => Integer
}
LIST_FUNCTIONS = {
'Functions' => [GET_FUNCTION_CONFIGURATION],
'NextMarker' => Fog::Nullable::String
}
GET_FUNCTION = {
'Code' => {
'Location' => String,
'RepositoryType' => String
},
'Configuration' => GET_FUNCTION_CONFIGURATION
}
UPDATE_FUNCTION_CONFIGURATION = GET_FUNCTION_CONFIGURATION
UPDATE_FUNCTION_CODE = GET_FUNCTION_CONFIGURATION
CREATE_FUNCTION = GET_FUNCTION_CONFIGURATION
ADD_PERMISSION = {
'Statement' => {
'Condition' => Fog::Nullable::Hash,
'Action' => Array,
'Resource' => String,
'Effect' => String,
'Principal' => Hash,
'Sid' => String
}
}
GET_POLICY = {
'Policy' => {
'Version' => String,
'Id' => String,
'Statement' => [ADD_PERMISSION['Statement']]
}
}
GET_EVENT_SOURCE_MAPPING = {
'BatchSize' => Integer,
'EventSourceArn' => String,
'FunctionArn' => String,
'LastModified' => Float,
'LastProcessingResult' => String,
'State' => String,
'StateTransitionReason' => String,
'UUID' => String
}
LIST_EVENT_SOURCE_MAPPINGS = {
'EventSourceMappings' => [GET_EVENT_SOURCE_MAPPING],
'NextMarker' => Fog::Nullable::String
}
CREATE_EVENT_SOURCE_MAPPING = GET_EVENT_SOURCE_MAPPING
UPDATE_EVENT_SOURCE_MAPPING = GET_EVENT_SOURCE_MAPPING
DELETE_EVENT_SOURCE_MAPPING = GET_EVENT_SOURCE_MAPPING
def self.zip(data, filename='index.js')
data_io = Zip::ZipOutputStream.write_buffer do |zio|
zio.put_next_entry(filename)
zio.write(data)
end
data_io.rewind
data_io.sysread
end
end
module Samples
FUNCTION_1 = File.dirname(__FILE__) + '/function_sample_1.js'
FUNCTION_2 = File.dirname(__FILE__) + '/function_sample_2.js'
end
end
end