Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to invoke AWS lambda from another lambda within SAM local?

I am using AWS SAM with Python. My goal is to have two Lambdas:

  • Function A: A normal synchronous Lambda which will invoke Function B, then return quickly
  • Function B: A long-running asynchronous Event Lambda

There are a couple other SO questions which deal with this scenario, but as far as I can tell none have touched on how to do it when deploying SAM locally.

Here is my SAM template file:

# template.yaml

Resources:
  FunctionA:
    # PUT /functions/a, should invoke FunctionB asynchronously
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: api/
      Handler: functions.a
      Runtime: python3.7
      Events:
        FunctionA:
          Type: Api
          Properties:
            Path: /functions/a
            Method: put

  FunctionB:
    # Long-running asynchronous function
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: 'FunctionB'
      CodeUri: api/
      Handler: functions.b
      Runtime: python3.7
      EventInvokeConfig:
        MaximumRetryAttempts: 2
        DestinationConfig:
          OnSuccess:
            Type: SQS
          OnFailure:
            Type: SQS

And my Python lambda handler logic:

# functions.py

def a(event, context):
  boto3.client('lambda').invoke(
    FunctionName='FunctionB',
    InvocationType='Event',
    Payload='some_data'.encode('UTF-8')
  )
  return { "statusCode": 200, "body": {} }

def b(data):
  print("SUCCESS!")

I deploy it locally:

# deploy.sh
sam build
sam local start-api

All is well until this point. When I call PUT /functions/a, I get the following error indicating that Function B could not be invoked from Function A:

[ERROR] ResourceNotFoundException: An error occurred (ResourceNotFoundException) when calling the Invoke operation: Function not found: arn:aws:lambda:us-east-2:[iam-user-id]:function:FunctionB

Has anyone found a fix for this? Here's what I've tried:

  1. Verified that Function B can be invoked successfully via the command line:
sam local invoke FunctionB # works great
  1. Attempted to change the InvocationType=Event to InvocationType=RequestResponse and received the same error
  2. Instantiated the lambda client to reference the local URL
boto3.client('lambda', endpoint_url='http://localhost:3000')
# [ERROR] EndpointConnectionError: Could not connect to the endpoint URL: "http://localhost:3000/2015-03-31/functions/ScheduleShowsAsyncFunction/invocations"
like image 730
abuttars Avatar asked Feb 12 '20 04:02

abuttars


People also ask

How do you trigger lambda from another lambda?

Setting up the Policy for ParentFunction. In order to allow the ParentFunction to call the ChildFunction, we need to provide the ParentFunction with specific rights to call another lambda function. This can be done by adding specific policies to a role and then assign that role to the lambda function.

How do you call AWS Lambda locally?

You can invoke your function locally by using the sam local invoke command and providing its function logical ID and an event file. Alternatively, sam local invoke also accepts stdin as an event. For more information about events, see Event in the AWS Lambda Developer Guide.

What is Sam local invoke?

The sam local invoke command is useful for developing serverless functions that handle asynchronous events, such as Amazon Simple Storage Service (Amazon S3) or Amazon Kinesis events. It can also be useful if you want to compose a script of test cases. You can pass in the event body using the --event parameter.


1 Answers

You can use sam local start-lambda to run FunctionA, it will emulates on port 3001, and call it from FunctionB with lambda client:

boto3.client('lambda', endpoint_url='http://docker.for.mac.localhost:3001')

You have to use InvocationType=RequestResponse because Event is not yet supported https://github.com/awslabs/aws-sam-cli/pull/749

If FunctionB is long-running and because you can only use RequestResponse you can change lambda client config to increase timeout:

config_lambda = Config(retries={'total_max_attempts': 1}, read_timeout=1200)

lambda_client = boto3.client('lambda',
                             config=config_lambda,
                             endpoint_url='http://docker.for.mac.localhost:3001')
like image 93
Arless Avatar answered Oct 16 '22 05:10

Arless