Node.js HTTPS call not working in AWS Lambda - node.js

I'm trying to create an AWS Lambda function that will call a DELETE on a schedule.
I'm using Node.js. When running just from Node.js on my local machine the request works fine.
Here is the code:
const https = require('https');
var options = {
host: 'MY_HOST',
path: '/v1/api/orders',
port: 80,
method: 'DELETE'
};
console.info('Do the DELETE call');
var reqDelete = https.request(options, function(res) {
res.on('data', function(d) {
console.info('DELETE result:\n');
console.log(d.toString('utf8'));
console.info('\nCall completed');
});
});
reqDelete.on('error', function(e) {
console.error(e);
});
reqDelete.end();
My output is like this:
Do the DELETE call
DELETE result:
{"message":"Cleanup triggered"}
Call completed
Just as I expect. However when I run from inside an AWS Lambda function I get the result of null and the Log output for the Lambda function is this.
START RequestId: fb2a1969-94e8-4c11-b43e-14ff6a4cc426 Version: $LATEST
2020-06-16T01:42:06.875Z fb2a1969-94e8-4c11-b43e-14ff6a4cc426 INFO Do the DELETE call
END RequestId: fb2a1969-94e8-4c11-b43e-14ff6a4cc426
REPORT RequestId: fb2a1969-94e8-4c11-b43e-14ff6a4cc426 Duration: 483.49 ms Billed Duration: 500 ms
Memory Size: 128 MB Max Memory Used: 67 MB Init Duration: 125.35 ms
Notice that it prints out the "Do the DELETE call" so I know it's getting to my code but nothing else is being printed out.
The body of the Lambda is like this:
exports.handler = async (event) => {
// The exact code from above with the actual host name.
};
Why is my API call not executed from the Lambda function when it is working from my local machine?

You are using an async Lambda function handler, but the HTTP request is not awaited. This causes the function to finish execution before the https.request() call can complete.
If you would like to use callbacks and not promises, then define a non-async handler for the function:
exports.handler = (event) => {
// The exact code from above with the actual host name.
};

Related

How to use request module in node.js lambda

I am trying to use the request module in AWS Lambda for Node.js, but I am not having good results.
This is the function I have so far:
const request = require('request');
url = 'https://www.google.com'
exports.handler = async (event) => {
request(url, (error,res,body) => {
console.log(error); //this is not printing
console.log(res); //this is not printing
console.log(body); //this is not printing
console.log('Come on!!'); //this is not printing
});
console.log(url); //This is the only thing printing, you can see in the Function Logs below, second line.
};
and this is the response I am getting:
Response:
null
Request ID:
"3e46f401-f26d-435d-90be-ac848c6c3a39"
Function Logs:
START RequestId: 3e46f401-f26d-435d-90be-ac848c6c3a39 Version: $LATEST
2019-10-14T08:06:23.755Z 3e46f401-f26d-435d-90be-ac848c6c3a39 INFO https://www.google.com
END RequestId: 3e46f401-f26d-435d-90be-ac848c6c3a39
REPORT RequestId: 3e46f401-f26d-435d-90be-ac848c6c3a39 Duration: 368.93 ms Billed Duration: 400 ms Memory Size: 128 MB Max Memory Used: 92 MB Init Duration: 461.54 ms
I am wondering why I am not getting any response from inside the requests method. I tried on my laptop and the part of the code outside the handler works just fine.
I uploaded the project as a zip file, so the node_modules folder with the request module is indeed there.
I noted a couple of solutions using the http module instead of request, but I would like to understand first why this is not working, before moving to a different solution.
Your function is marked as async, yet you are using a callback to handle the response. So nothing is being awaited on.
This means that your function returns an implicit promise that is already fulfilled. This makes the lambda stop. So, either:
A) Write your code using async/await, which probably requires request-promise or similar:
exports.handler = async event => {
const res = await promisifiedRequest(url);
console.log(res.statusCode);
console.log(url);
};
or
B) Convert the function from async/await to use callbacks, like so:
exports.handler = (event, context, callback) => {
const res = request(url, (error, res, body) => {
console.log('Come on!!');
callback(null, res.statusCode);
});
console.log(url);
};

AWS Lambda function with nodejs to ssh to ec2 and run command, on testing gives 200 success but does nothing

I have an index.js written in node.js 8.10 with the necessary node modules uploaded and the pem file in an aws lambda function. The lambda function needs to ssh to an ec2 instance and run a python script (creates another file inside the directory) inside it.
On running a test, I am getting 200 success but I don't see a new file (intended output of script). I am using simple-ssh to get to run the ec2 script.
'use strict';
console.log('Loading lambda function');
exports.handler = function(event, context, callback) {
let bag_size = event.bag_size === undefined ? 10 : event.bag_size;
var SSH = require('simple-ssh');
var fs = require('fs');
var ssh = new SSH({
host: '##############',
user: 'ubuntu',
key: fs.readFileSync('key.pem'),
passphrase: '##########'
//pass: 'password'
});
var pythonCommand = 'python lambda_test.py ' + bag_size;
ssh.exec('cd /home/ubuntu/***/***/***').exec('ls -al', {
out: function(stdout) {
console.log('ls -al got:');
console.log(stdout);
console.log('now launching command');
console.log(pythonCommand);
}
}).exec('' + pythonCommand, {
out: console.log.bind(console),
exit: function(code, stdout, stderr) {
console.log('operation exited with code: ' + code);
console.log('STDOUT from EC2:\n' + stdout);
console.log('STDERR from EC2:\n' + stderr);
context.succeed('Success!');
}
}).start();
var response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Credentials" : true, // Required for cookies, authorization headers with HTTPS
"Access-Control-Allow-Origin":"*",
"Access-Control-Allow-Methods":"POST,GET,OPTIONS",
"Access-Control-Allow-Headers":"Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
"Content-Type":"application/json"
},
body: JSON.stringify({ "message": "Success" })
};
// Return response to the caller
callback(null, response);
};
Log Output from CloudWatch:
START RequestId: 247cd************************480b Version: $LATEST
END RequestId: 247cd************************480b
REPORT RequestId: 247247cd************************5b480b0b Duration: 10962.61 ms Billed Duration: 11000 ms Memory Size: 128 MB Max Memory Used: 49 MB
I am not sure where I am going wrong. Please help!
Looks like you call the handler's callback before the exec function is finished since it continues to run asynchronously, which causes the lambda to terminate.
Make sure to call the callback only once it's done
(you can do that by passing a callback parameter to the start function).
Check https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html.

how to get the first bytes of a file from a given url node.js

I want to a file from a given url, in a aws lambda function.
I wrote this code:
exports.handler = (event, context, callback) => {
var http = require('http');
var url= "https://mail.google.com/mail/u/0/?ui=2&ik=806f533220&attid=0.1&permmsgid=msg-a:r-8750932957918989452&th=168b03149469bc1f&view=att&disp=safe&realattid=f_jro0gbqh0"
//var client = http.createClient(80, url);
var request = http.request({
port: 80,
host: url
});
request.on('response', function( res ) {
res.on('data', function( data ) {
console.log(data);
});
});
request.end();
const result = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
callback(null, result);
};
but I get an error saying:
"Response: {
"errorMessage": "RequestId: 52baec5e-60bc-47ea-911e-8e6cb1d2f1da Process exited before completing request"
}"
since I only need the first 2 bytes' I thought maybe I should read them, and not the whole file.
any ideas?
thanks a lot!
Did you increase your lambda execution Timeout limit? When you first created, lambda comes setted with only 3 seconds by default. You can change that under Basic settings. Change Timeout to 2 or 3 minutes to allow your lambda to finish execution. Also check if your memory is enough. You may need to increase it a little bit. I have mine with 256 MB.
When you test your lambda, pay attention at the Duration and Memory Size values. Lambda will print that in the last line of the log output. So if you set your lambda execution Timeout to 5 minutes and it only takes 2 minutes or if your lambda Memory Size is to close to your Memory you might want to increase it so your lambda do not fail execution due to a memory problem.
I usually do this with fetch or request. You can make it with request like this:
exports.handler = (event, context, callback) => {
var request = require('request');
var url= "https://mail.google.com/mail/u/0/?ui=2&ik=806f533220&attid=0.1&permmsgid=msg-a:r-8750932957918989452&th=168b03149469bc1f&view=att&disp=safe&realattid=f_jro0gbqh0"
request(url, { json: true, timeout: 1000 }, (err, response, body) => {
if (err) {
console.log(err);
callback(err, null);
} else {
console.log(body);
callback(null, "Hello from Lambda");
}
});
};
Just run npm install request to get module request and you are good to go.
It is always a good idea to start with your local development environment and when everything is ready, you just zip your files and then upload them to lambda. This way you know everything is just fine with your code and you can focus on the lambda configuration details. This way it much easier to test and you do not consume any lambda resource.
This is how to upload your file to lambda:

TypeError: lambda.invoke is not a function

I am trying to call an aws lambda function within another lambda function. I tried a very simple code snippet. However, every time I am receiving "TypeError: lambda.invoke is not a function".
Here is my code:
exports.handler = function(event, context) {
var AWS = require('aws-sdk');
AWS.config.apiVersions = {
lambda: '2015-03-31',
// other service API versions
};
var lambda = new AWS.Lambda();
var params = {
FunctionName: "node-sendsms",
InvocationType: "Event",
LogType: "Tail",
Payload: null
};
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
};
I referred to the documentation here for invoke function: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#invoke-property
and followed same code. I tried changing api version but that did not help. I also tried looking out for any solution online as well as on SO; at least information I got is that it can be an issue of variable scoping for AWS variable. But, as it can be seen in code - it does not seems like that.
Any experts on this error? I am working on node.js 6.10. Here is error detail:
Response:
{
"errorMessage": "RequestId: 849581c1-d669-11e7-ad15-6f5e52f0e184 Process exited before completing request"
}
Request ID:
"849581c1-d669-11e7-ad15-6f5e52f0e184"
Function Logs:
START RequestId: 849581c1-d669-11e7-ad15-6f5e52f0e184 Version: $LATEST
2017-12-01T07:30:35.785Z 849581c1-d669-11e7-ad15-6f5e52f0e184 TypeError: lambda.invoke is not a function
at exports.handler (/var/task/index.js:22:9)
END RequestId: 849581c1-d669-11e7-ad15-6f5e52f0e184
REPORT RequestId: 849581c1-d669-11e7-ad15-6f5e52f0e184 Duration: 1385.62 ms Billed Duration: 1400 ms Memory Size: 128 MB Max Memory Used: 24 MB
RequestId: 849581c1-d669-11e7-ad15-6f5e52f0e184 Process exited before completing request
install the latest version of aws sdk or mention like
exports.handler = (event, context, callback) => {
var AWS = require('aws-sdk');
AWS.config.apiVersions = {
lambda: '2015-03-31' ///ignore if latest version
};
var lambda = new AWS.Lambda();
refer https://github.com/aws/aws-sdk-js/issues/1823 for more details

Can AWS Lambda reach/interact with S/FTP?

I wrote some basic js to just list the files of a FTP but I get:
"Process exited before completing request"
Is that because Lambda can't interact with FTP?
I'm using jsftp btw.
Here's my setup:
I use Serverless to create the project
For my lambda, I used nodejs and I'm using JSFTP to deal with the ftp stuff.
My code:
// Require Serverless ENV vars
var ServerlessHelpers = require('serverless-helpers-js').loadEnv();
// Require Logic
var lib = require('../lib');
// Lambda Handler
module.exports.handler = function (event, context) {
lib.respond(event, function (error, response) {
return context.done(error, response);
});
};
My ftp lambda code:
var JSFtp = require("jsftp");
module.exports.respond = function (event, cb) {
var ftp = new JSFtp({
host: "host",
user: "user",
password: "password"
});
ftp.auth(ftp.user, ftp.password, function(err, res) {
if (err) console.log(err);
else console.log(res);
ftp.ls(".", function (err, res) {
var results = [];
res.forEach(function (file) {
results.push(file.name);
});
ftp.raw.quit();
return cb(null, results.length);
})
});
};
I added some console.log() all over the place and it seems like it choked once it tried to ftp.auth.
The output I see in cloud watch:
START RequestId: __ID__ Version: $LATEST
END RequestId: __ID__
REPORT RequestId: __ID__ Duration: 526.46 ms Billed Duration: 600 ms Memory Size: 1024 MB Max Memory Used: 33 MB
Process exited before completing request
So it looks like it just choked somewhere...
in short, ftp will not work with lambda since they use ephemeral ports.
sftp will work nicely with lambda. i tested using java code via jsch with no issues; tho i cant see how it wouldnt work with any js sftp lib.
It is possible tested just now.
Make sure ur timeout is set to be long enough and you are calling context.succeed() on process termination
function __main__(event, context) {
var JSFtp = require("jsftp");
var ftp = new JSFtp({
host: "speedtest.tele2.net",
port: 21, // defaults to 21
});
ftp.ls(".", function(err, res) {
var results = []; res.forEach(function(file) {
results.push(file.name);
});
context.succeed(results);
});
};
By default, Lambda functions only have 3 seconds to complete. If it takes longer than that, you'll get the error you are seeing.
You can adjust the timeout to anything up to 5 minutes. To change it using the aws CLI, run:
aws lambda update-function-configuration --function-name my-lambda-function --timeout 300

Resources