I am using nodeJS 12.x lambda function to invoke certain commands on one of the EC2 instance. I have made sure that
SSM agent is installed on the EC2 instance
Proper roles are assigned to the lambda function, Policies are - AmazonEC2ReadOnlyAccess, AmazonSSMFullAccess, AWSLambdaExecute.
Below is the lambda code:
var AWS = require('aws-sdk');
const ssm = new AWS.SSM();
AWS.config.update({region:'ap-south-1'});
exports.handler = function(event, context) {
var ec2 = new AWS.EC2();
ec2.describeInstances(function(err, data) {
if(err) {
console.log(err, err.stack);
}
else {
let instance = data.Reservations[0].Instances[0].InstanceId;
console.log("\nInstance Id: ", instance);
ssm.sendCommand({
DocumentName: "AWS-RunShellScript",
InstanceIds: [ instance ],
TimeoutSeconds: 3600,
Parameters: {
commands: ['ifconfig']
}
}, function(err, data) {
if (err) {
console.log("\nError:", err);
} else {
console.log("\nSuccess: ", data);
}
context.done(null, 'Function Finished!');
})
}
});
};
When I invoke this function manually I am getting the status as pending. Below is the output log.
Response:
"Function Finished!"
Request ID:
"748b280a-4277-42a1-a0c3-************"
Function logs:
START RequestId: 748b280a-4277-42a1-a0c3-************ Version: $LATEST
2020-11-05T08:52:26.895Z 748b280a-4277-42a1-a0c3-************ INFO
Inside describe instances:
2020-11-05T08:52:26.952Z 748b280a-4277-42a1-a0c3-************ INFO
Instance Id: i-016f4673e082a829e
2020-11-05T08:52:27.237Z 748b280a-4277-42a1-a0c3-************ INFO
Success: {
Command: {
CommandId: '8b7a3b6d-4a7a-4259-9c82-************',
DocumentName: 'AWS-RunShellScript',
DocumentVersion: '',
Comment: '',
ExpiresAfter: 2020-11-05T10:52:27.220Z,
Parameters: { commands: [Array] },
InstanceIds: [ 'i-****************' ],
Targets: [],
RequestedDateTime: 2020-11-05T08:52:27.220Z,
Status: 'Pending',
StatusDetails: 'Pending',
OutputS3BucketName: '',
OutputS3KeyPrefix: '',
MaxConcurrency: '50',
MaxErrors: '0',
TargetCount: 1,
CompletedCount: 0,
ErrorCount: 0,
DeliveryTimedOutCount: 0,
ServiceRole: '',
NotificationConfig: {
NotificationArn: '',
NotificationEvents: [],
NotificationType: ''
},
CloudWatchOutputConfig: { CloudWatchLogGroupName: '', CloudWatchOutputEnabled: false },
TimeoutSeconds: 3600
}
}
END RequestId: 748b280a-4277-42a1-a0c3-************
REPORT RequestId: 748b280a-4277-42a1-a0c3-************ Duration: 677.90 ms Billed Duration: 700 ms Memory Size: 128 MB Max Memory Used: 96 MB
Why is the status not success? When I manually use 'RunCommand' it works properly.
What am I doing wrong?
The command status is showing as pending because, it is currently in pending. Once you execute the command it goes to from pending ----> Complete.
if you take the command ID (CommandId: '8b7a3b6d-4a7a-4259-9c82-************' in above case) and look into System Manager Run Command, by the time you try to search for it, it will show successful or failed
Related
I am developing a REST API with Node.Js. my technology stack is AWS Lambda, API Gateway and RDS (MySQL). Below is my code
roles.js
const mysql = require('mysql');
const con = mysql.createConnection({
host : "*****.rds.amazonaws.com",
user : "*****",
password : "*****",
port : 3306,
database : "******"
});
exports.lambdaHandler = (event, context, callback) => {
// allows for using callbacks as finish/error-handlers
context.callbackWaitsForEmptyEventLoop = false;
const sql = "select * from role";
con.query(sql, function (err, result) {
if (err) throw err;
var response = {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify(result),
"isBase64Encoded": false
};
callback(null, response)
});
};
exports.selectRoleByIDHandler = (event, context, callback) => {
const { id } = event.queryStringParameters;
console.log("id", id);
// allows for using callbacks as finish/error-handlers
context.callbackWaitsForEmptyEventLoop = false;
const sql = "select * from role where idRole = "+id;
con.query(sql, function (err, result) {
if (err) throw err;
var response = {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": JSON.stringify(result),
"isBase64Encoded": false
};
callback(null, response)
});
};
Below is my template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
node2
Sample SAM Template for node2
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 100
VpcConfig:
SecurityGroupIds:
- sg-sdsdsdsd
SubnetIds:
- subnet-ssdsds
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs14.x
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: get
Role: !GetAtt LambdaRole.Arn
RoleFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: roles.lambdaHandler
Runtime: nodejs14.x
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /roles
Method: get
Role: !GetAtt LambdaRole.Arn
SelectRolesByIDFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: roles.selectRoleByIDHandler
Runtime: nodejs14.x
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /selectRoleByIDHandler
Method: get
Role: !GetAtt LambdaRole.Arn
LambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: root
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:DescribeNetworkInterfaces
- ec2:CreateNetworkInterface
- ec2:DeleteNetworkInterface
- ec2:DescribeInstances
- ec2:AttachNetworkInterface
Resource: '*'
When I try to execute the sam local invoke, i get the following error
Error: You must provide a function logical ID when there are more than one functions in your template. Possible options in your template: ['HelloWorldFunction', 'RoleFunction', 'SelectRolesByIDFunction']
Now, I have 2 questions.
How to solve this issue?
Is it a bad practice to have more than one function in a single file in AWS Lambda?
Looks like you are trying to build Lambda functions via the AWS SDK for Javascipt. Have you looked at the AWS examples in the AWS SDK for JavaScript V3 DEV Guide. There are end to end instructions on how to build Lambda function using the JS API that can help you.
See this topic and the child topics in the TOC:
Cross-service examples for the AWS SDK for JavaScript
I found the issue, I had to invoke this as sam local invoke HelloWorldFunction . The HelloWorldFunction is the function name I need to deploy.
I get the following error when I try to run the command: artillery run realtime_transcribing_test.yaml:
TypeError: Cannot read property 'capture' of null.
realtime_transcribing_test.yaml:
config:
target: "ws://localhost:8001/calls/live-calls"
processor: "./binary-payload.js"
phases:
- duration: 60
arrivalRate: 5
scenarios:
- engine: "ws"
flow:
- send:
rate: 48000
format: 1
language: "en-IN"
user_id: "Test client"
- think: 1
- loop:
- function: "sendBinaryData"
- send: "{{payload}}"
- think: 1
count: 100
binary-payload.js:
module.exports = {
sendBinaryData
};
function sendBinaryData(userContext, events, done) {
navigator.mediaDevices
.getUserMedia({ audio: true, video: false })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream, {
mimeType: 'audio/webm',
});
mediaRecorder.addEventListener('dataavailable', event => {
if (event.data.size > 0) {
userContext.vars.payload = event.data;
}
});
mediaRecorder.start(100);
setTimeout(event => {
mediaRecorder.stop();
}, 100);
});
return done();
}
Both the files are placed within the same directory. With my current findings this is a very generic error statement thrown by artillery. I have also verified the validity of the YAML file. Please help me understand the issue with my configuration.
Question
I set up the following Jest test to ensure an exception is thrown:
it('can not rollback a step', async () => {
await expect(app.service('rides').patch(ride._id, {
currentStep: 1,
})).rejects.toThrow(BadRequest);
});
How can I assert the exception content?
Example
Considering this BadRequest error:
BadRequest {
type: 'FeathersError',
name: 'BadRequest',
message: 'invalid data',
code: 400,
className: 'bad-request',
data: {},
errors: [
{
message: 'currentStep must be greater than 1',
path: [Array],
type: 'array.min',
context: [Object]
}
],
I would like to check errors[0].message content, regardless the rest of the error object.
you catch and then analyze. Don't forget jest.assertions() to ensure code really throws.
jest.assertions(1);
try {
await app.service('rides').patch(ride._id, {
currentStep: 1,
});
} catch(e) {
expect(e.errors[0].message).toEqual('abc');
}
With the Javascript API, I'm doing this:
client.search({
index:'530d8aa855df0c2d269a5a58',
type:'532a2b28495c533e5eaeb020',
q:'slide'
},function (error,response){
if(error){
console.log(error);
}
if(response){
console.log(response);
}
});
I want to search any document containing the word 'slide'.
The query returns no hits.
However, if I type this:
http://server:9200/530d8aa855df0c2d269a5a58/532a2b28495c533e5eaeb020/_search?pretty=true
Into a browser, I get hits.
What am I doing wrong?
More information:
The javascript query returns this:
Elasticsearch DEBUG: 2014-05-20T00:19:04Z
starting request { method: 'POST',
path: '/530d8aa855df0c2d269a5a58/532a2b28495c533e5eaeb020/_search',
query: { q: 'slide' } }
Elasticsearch TRACE: 2014-05-20T00:19:04Z
-> POST http:/server:9200/530d8aa855df0c2d269a5a58/532a2b28495c533e5eae
b020/_search?q=slide
<- 200
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}
Elasticsearch INFO: 2014-05-20T00:19:04Z
Request complete
{ took: 3,
timed_out: false,
_shards: { total: 5, successful: 5, failed: 0 },
hits: { total: 0, max_score: null, hits: [] } }
I'm indexing my information like this:
client.index({
index:data2.name,
type:data2.files[i].author,
id:data2.files[i]._id.toString(),
body:data2.files[i]
},function(err,resp){
if(err){console.log(err)};
if(resp){console.log(resp)};
});
Edit:
The entirety of my node.js app is this:
var elasticsearch = require('elasticsearch');
var client = new elasticsearch.Client({
host: 'server:9200',
log:'trace'
});
client.search({
q:'slide'
},function (error,response){
if(error){
console.log(error);
}
if(response){
console.log(response);
}
});
With the exception that server is replaced with a real ip address.
My node app is running locally, and it's connecting to an EC2 instance which has ports 80, 443, 9200, and 9300 open.
EDIT2:
I changed my app to this:
client.search({
},function (error,response){
if(error){
console.log(error);
}
if(response){
console.log(response);
}
});
And I got everything. However, how do I specify a query, and yet have it work?
EDIT3:
The node.js elasticsearch module I'm using is v2.1.6
The API I'm looking at is for something called v1.1
I was able to run queries by replacing
q:'slide'
With
query:'slide'
So either the version of elasticsearch that I have doesn't match the documentation, or the documentation is wrong.
For some reason, I can't seem to get vows.js sub-topics working in my real test-suite, but they work fine in a example file... can you spot my problem?
This works:
vows.describe('An Education in Vows').addBatch({
'when I write an asynchronous topic': {
topic: function() {
var that = this;
setTimeout(function() {
that.callback(true);
}, 100);
},
'it passes': function (topic) {
assert.equal(topic, true);
},
'and it has an asynchronous sub-topic': {
topic: function() {
var that = this;
setTimeout(function() {
that.callback(true);
}, 100);
},
'it also passes': function (topic) {
assert.equal(topic, true);
}
}
}
}).run();
When I run this via:
node tests/learning-vows.js
I get:
·
·
✓ OK » 2 honored (0.207s)
This Doesn't work:
I have a file ./tests/smoke.js
vows.describe('Registration & Authentication').addBatch({
'when a user registers with a valid username and password': {
topic: function () {
client.register({
username: validusername,
password: validpassword
}, this.callback);
},
'we return status 200 OK': function (data, response) {
assert.equal(200, response.statusCode);
},
'simple sub-topic': {
topic: true,
'should work': function(topic) {
assert.equal(true, topic);
}
},
}
}).run()
When I execute this via:
node tests/smoke.js
I get:
·
✗ Errored » 1 honored ∙ 1 errored
Note that in the second example, without the sub topic I get:
·
✓ OK » 1 honored (0.100s)
Vows is using node's convention for callbacks (see : http://nodemanual.org/latest/nodejs_dev_guide/working_with_callbacks.html), it assumes that first parameter on callback is an error object.
So when you send data as first parameter your telling vows that an error happened in client.register. It prevents vows from evaluating sub-topic. Sub-topic is marked errored but assertion succeeded and current topic is marked as honored.
It is really not trivial to guess that from output. Moreover vows behavior is not consistent, try replacing true to 0 and then '0' as callback parameter in your first test and you will see two other results.
Here is a working exemple :
var vows = require('vows'), assert = require('assert');
var client = {
register: function(obj,callback){
callback(null, obj, {statusCode:200});
}
};
vows.describe('Registration & Authentication').addBatch({
'when a user registers with a valid username and password': {
topic: function () {
client.register({
username: 'validusername',
password: 'validpassword'
}, this.callback);
},
'we return status 200 OK': function (err, data, response) {
assert.equal(response.statusCode, 200);
},
'simple sub-topic': {
topic: true,
'should work': function(topic) {
assert.equal(true, topic);
}
}
}
}).export(module)