0

I'm trying to write a Lambda function to upload a file to S3, using AWS SAM... I'm testing it locally but it looks like nothing is happening and the lambda function ends with invalid response. No other errors are logged. What is going on?

This is the execution log:

START RequestId: 71030098-0dd5-1137-0e78-43f1b1671b9c Version: $LATEST
END RequestId: 71030098-0dd5-1137-0e78-43f1b1671b9c
REPORT RequestId: 71030098-0dd5-1137-0e78-43f1b1671b9c  Duration: 2118.53 ms    Billed Duration: 2200 ms    Memory Size: 128 MB Max Memory Used: 46 MB  
2019-03-27 10:59:00 Function returned an invalid response (must include one of: body, headers or statusCode in the response object). Response received: null
2019-03-27 10:59:00 127.0.0.1 - - [27/Mar/2019 10:59:00] "POST /myfunction HTTP/1.1" 502 -

Lambda code. The file that will be uploaded to S3 comes from the request body.

const async = require('async');
const axios = require('axios');
const AWS = require('aws-sdk');
const s3 = new AWS.S3();

const bucketName = process.env.STRINGS_BUCKET_NAME;

exports.lambdaHandler = async (event, context) => {
    let response;

    try {
        const body = JSON.parse(event.body);
        const jsonFilename = new Date().getTime() + '.json';

        async.waterfall([
            function uploadToS3(done) {
                const base64data = new Buffer(body.strings, 'binary');
                s3.putObject({
                        Bucket: bucketName,
                        Key: jsonFilename,
                        Body: base64data,
                    }, (err, success) => {
                        if (err) {
                            console.error(err);
                            throw Error(err);
                        }
                        console.log(success);
                        console.info('File uploaded to S3: ' + jsonFilename);
                        done(null);
                    });
            },
            function doOtherStuff(done) {
                console.log('doOtherStuff');
                done(null);
            }
        ],
            (error) => {
                if (error) {
                    console.error(error);
                    response = {
                        'statusCode': 500,
                        'body': JSON.stringify({
                            statusCode: 500
                        })
                    };
                } else {
                    response = {
                        'statusCode': 200,
                        'body': JSON.stringify({
                            statusCode: 200
                        })
                    };
                }
            });
    } catch (err) {
        console.error(err);
        return err;
    }

    return response
};

Part of my template.yaml where I defined the lambda function:

Resources:
  UserStringsBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: 'mybucket'

  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: my-function/
      Handler: app.lambdaHandler
      Runtime: nodejs8.10
      Events:
        MyFunction:
          Type: Api
          Properties:
            Path: /myfunction
            Method: post
      Environment:
        Variables:
          STRINGS_BUCKET_NAME: 'mybucket'
      Policies:
        - AWSLambdaExecute
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - s3:PutObject
                - s3:PutObjectACL
              Resource: 'arn:aws:s3:::mybucket/*' 
June
  • 592
  • 8
  • 18

2 Answers2

1

It looks like you're getting this error because you're not waiting for the async functions to complete. You call async.waterfall, and then you have your return response line, and because the functions inside the waterfall run async, your return response gets finished first, hence the Function returned an invalid response error.

To confirm this, you can change your let response; line to let response = {'statusCode': 200, 'body': 'not yet completed'};, and see if you get this response back.

Deiv
  • 3,000
  • 2
  • 18
  • 30
0

@Deiv pointed me in the right direction.

I had to change the function's signature to:

exports.lambdaHandler = (event, context, callback) 

And returned response inside waterfall with:

callback(null, response);

I also had to manually create the bucket on AWS... I thought that SAM would do that automatically, but go figure.

June
  • 592
  • 8
  • 18
  • Glad you got this resolved, I do want to ask though, are you sure your bucket isn't getting created? Your AWS::S3::Bucket resource seems correct, and it should be creating the bucket off that automatically...off the top of my head, I think it could be creating it in a different region (it would deploy in the region your cloudformation is deployed in) – Deiv Mar 27 '19 at 17:30