1

I'm trying to capture the output from an external program launched from an AWS Lambda written in NodeJS. Full example code below. Any test event would do since it's not really used.

exports.handler = async (event) => {
    console.log ("Entering lambda" + JSON.stringify(event))

    var spawn = require('child_process').spawnSync;

    child_process = spawn ('aws', [' --version'], {
        stdio: 'inherit',
        stderr: 'inherit',
        shell: true
    })

    console.log ("done");
    const response = {
        statusCode: 200,
        body: JSON.stringify('done'),
    };
    return response;
};

When I run it, I get the following as output (I removed the test event details for brevity, since it's irrelevant).

What I don't see is the results of the aws --version command that I expected (I'm using it to test the correct invocation of the AWS CLI, but any Linux command would do). The code does execute synchronously because if I replace the invocation with child_process = spawn ('sleep', ['1'], {, lambda's execution time grows to 1117.85 ms, so the one-second sleep happens. But there's nothing captured in the execution logs.

START RequestId: 0c1287e2-d2ee-4436-a577-bc8ec3608120 Version: $LATEST
2019-01-16T19:12:45.130Z    0c1287e2-d2ee-4436-a577-bc8ec3608120    Entering lambda {...}
2019-01-16T19:12:45.143Z    0c1287e2-d2ee-4436-a577-bc8ec3608120    done
END RequestId: 0c1287e2-d2ee-4436-a577-bc8ec3608120
REPORT RequestId: 0c1287e2-d2ee-4436-a577-bc8ec3608120  Duration: 13.29 ms  Billed Duration: 100 ms     Memory Size: 128 MB Max Memory Used: 20 MB  

Am I doing something wrong? Or is there any other way to capture the output (status code, stdio, stderr) for a Lambda written in NodeJS?

wishihadabettername
  • 14,231
  • 21
  • 68
  • 85
  • If you run this outside of Lambda, does it work? – jarmod Jan 16 '19 at 19:48
  • You mention a one second sleep but there's no such thing in the code example you posted. Also in the code, you are using `spawnSync` and not the asynchronous version `spawn`, but you mention "the code /does/ execute synchronously". So which one are you trying to use? – Milan Cermak Jan 16 '19 at 19:55
  • @jarmod: yes, I tried on Windows with the spawned command being `spawn ('date', ['/t']` and I could see the output. – wishihadabettername Jan 16 '19 at 19:58
  • @MilanCermak: About `sleep`, my bad, that line still referenced the original code, I fixed it now. Also, I'm using `spawnSync`, as in the posted code. I was just confirming the code was executed and that it was executed synchronously, as expected, to rule out the case it was not executing at all. – wishihadabettername Jan 16 '19 at 20:01
  • I'm not sure that awscli is available in a Lambda environment. Also probably worth taking a look at the lambdash project (https://github.com/alestic/lambdash) which uses child_process and exec to run arbitrary commands inside Lambda. – jarmod Jan 16 '19 at 21:03
  • AWS CLI SDK is supposed to be available for the Javascript lambdas in particular (see https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html) but that aside, I think the problem I ran into is more general since other commands don't have their output logged either. I also tried with `pipe` instead of `inherit` but no luck. – wishihadabettername Jan 16 '19 at 21:37
  • AWS SDKs are very different things to the awscli. Lambda includes the SDKs, not the awscli. The awscli *uses* an SDK, specifically the Python SDK (boto3). – jarmod Jan 16 '19 at 23:36
  • You're right. For some reason I had missed this. – wishihadabettername Jan 17 '19 at 02:55

2 Answers2

9

This works for me (node.js 8.10 runtime)


exports.handler = async (event) => {
    const spawnSync = require('child_process').spawnSync;
    const process = spawnSync('echo', ['hello', 'world'], {
        stdio: 'pipe',
        stderr: 'pipe'
    });
    console.log(process.status);
    console.log(process.stdout.toString());
};

When trying to run with aws, it throws a ENOENT error. In other words, the command is not available. As mentioned in the question comments by @jarmod, I also believe the awscli is not available in the Lambda container.

What is available is the SDK, so that you can require('aws-sdk'); without bundling it to your Lambda deployment package.

Milan Cermak
  • 7,476
  • 3
  • 44
  • 59
  • While this worked for me with `echo`, another program (`praat_barren`) would complain about it not being run from the CLI as it expects to be. Bottom-line: had better luck with this answer https://stackoverflow.com/a/31104898/1717535 – Fabien Snauwaert Aug 10 '20 at 12:40
3

aws-cli is a python package and is not installed in a Lambda environment.
To verify my statement, you can type shell commands here http://www.lambdashell.com/ and check what is installed by default in your execution environment or check the official documentation.

Your code above returns no output, because trying to execute aws returns ENOENT, meaning the file is not available.

If you want to run aws inside your lambda function, you can follow these instructions : Call aws-cli from AWS Lambda

I would however question why do you want to do this ? The AWS-SDK is included in the local runtime environment and you can call any AWS API directly from your code, without the need to spawn a process and deal with stdin/stdout. I would strongly advise to not spawn aws cli from your code but rather use the SDK instead.

But, if you do want to run a process from Lambda and capture stdout and stderr, here is how I am doing it.

'use strict';

const childProcess = require('child_process');

/*
 * Handle the chile process and returns a Promise
 * that resoved when process finishes executing
 * 
 * The Promise resolves an  exit_code
 */ 
function handleProcess(process) {


    return new Promise((resolve, reject) => {
        process.stdout.on('data', (data) => {
            console.log(`stdout: ${data}`);
            console.log('stdout');
        });

        process.stderr.on('data', (data) => {
            console.log(`stderr: ${data}`);
        });

        process.on('close', (code) => {
            console.log(`child process exited with code ${code}`);
            if (code === 0) {
                resolve(code);
            } else {
                reject(code);
            }
        });
    });
}



exports.handler = (event, context, callback) => {

    // console.log(JSON.stringify(process.env, null, 2));
    // console.log(JSON.stringify(event, null, 2));

    return handleProcess(childProcess.spawn('ls', ['--version']))

        .then((exit_code) => {

            console.log(`exit_code = ${exit_code}`)
            let response = {
                statusCode: (0 == exit_code) ? 200 : 500,
                body: exit_code
            };                
            callback(null, response);

        })

        .catch((error) => {
            console.error(error);
            let response = {
                statusCode: 500,
                body: error
            };
            callback(null, response);
        });

}
Sébastien Stormacq
  • 14,301
  • 5
  • 41
  • 64
  • The reason: some S3 functions (in particular, the one I need to run, `sync`) is not available in the Javascript SDK (it's not listed in the docs at https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html). The only way to invoke it is to call the `aws s3 sync` command in the CLI SDK. – wishihadabettername Jan 16 '19 at 23:18
  • Thanks for the reference to https://stackoverflow.com/questions/33513604/call-aws-cli-from-aws-lambda, I'll go back to Python and package aws-cli with the lambda. – wishihadabettername Jan 17 '19 at 03:01
  • @wishihadabettername Why are you syncing files to an ephemeral environment? – jarmod Jan 17 '19 at 15:00
  • I'm syncing between two S3 buckets, not to an ephemeral environment. That's why I want to use a lambda in fact, I only need the ability to trigger the sync from somewhere in AWS, I don't need an EC2 instance (and yes, I can only schedule the sync from outside, with the CLI, but prefer the entire solution to be contained within AWS). – wishihadabettername Jan 17 '19 at 16:36
  • Indeed, sync is not an S3 API, hence not available in the SDK. It is a convenience built into the CLI – Sébastien Stormacq Jan 17 '19 at 21:36
  • Do you know that there is an alternative way to perform a server side S3 sync, without using the aws cli ? Look at https://aws.amazon.com/blogs/compute/content-replication-using-aws-lambda-and-amazon-s3/ – Sébastien Stormacq Jan 17 '19 at 23:54