From fc1a3535c36f732b7164cfa25dfdafc06f66da6a Mon Sep 17 00:00:00 2001 From: Justin Chase Date: Thu, 25 Feb 2016 14:38:21 -0600 Subject: [PATCH 1/3] Add -e to not escape \n when echo'ing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4d45c22..cd069ef9 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ $ node-lambda setup --help After running setup, it's a good idea to gitignore the generated `event.json` and `.env` files. ``` -echo ".env\ndeploy.env\nevent.json" >> .gitignore +echo -e ".env\ndeploy.env\nevent.json" >> .gitignore ``` #### run From 5ea4782316ddde93d9c94dc96bd9972f1e044dce Mon Sep 17 00:00:00 2001 From: Chase Sillevis Date: Fri, 25 Mar 2016 12:05:10 +0100 Subject: [PATCH 2/3] VpcConfig support: - http://docs.aws.amazon.com/lambda/latest/dg/API_VpcConfig.html - http://docs.aws.amazon.com/lambda/latest/dg/vpc.html This allows users to place their Lambda functions in specific subnets with specific security groups. --- README.md | 2 ++ bin/node-lambda | 5 +++++ lib/main.js | 8 +++++++- test/main.js | 13 +++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b4d45c22..a1fdaf81 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,8 @@ $ node-lambda deploy --help -u, --runtime [nodejs] Lambda Runtime -v, --version [custom-version] Lambda Version -f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")` + -b, --vpcSubnets [] VPC Subnet(s) you want your Lambda Function to deploy in to (comma separated list), when using this, the below param is also required + -g, --vpcSecurityGroups [] VPC Security Group(s) you want your Lambda Function to deploy in to (comma separated list), when using this, the above param is also required ``` ## Custom Environment Variables diff --git a/bin/node-lambda b/bin/node-lambda index ecbee08c..093ef97c 100755 --- a/bin/node-lambda +++ b/bin/node-lambda @@ -22,6 +22,8 @@ var AWS_TIMEOUT = process.env.AWS_TIMEOUT || 60; var AWS_DESCRIPTION = process.env.AWS_DESCRIPTION || ''; var AWS_RUNTIME = process.env.AWS_RUNTIME || 'nodejs'; var AWS_FUNCTION_VERSION = process.env.AWS_FUNCTION_VERSION || ''; +var AWS_VPC_SUBNETS = process.env.AWS_VPC_SUBNETS || ''; +var AWS_VPC_SECURITY_GROUPS = process.env.AWS_VPC_SECURITY_GROUPS || ''; var EVENT_FILE = process.env.EVENT_FILE || 'event.json'; program @@ -43,6 +45,9 @@ program .option('-d, --description [' + AWS_DESCRIPTION + ']', 'Lambda Description', AWS_DESCRIPTION) .option('-u, --runtime [' + AWS_RUNTIME + ']', 'Lambda Runtime', AWS_RUNTIME) .option('-v, --version [' + AWS_FUNCTION_VERSION + ']', 'Lambda Function Version', AWS_FUNCTION_VERSION) + .option('-b, --vpcSubnets [' + AWS_VPC_SUBNETS + ']', 'Lambda Function VPC Subnets', AWS_VPC_SUBNETS) + .option('-g, --vpcSecurityGroups [' + AWS_VPC_SECURITY_GROUPS + ']', 'Lambda VPC Security Group', + AWS_VPC_SECURITY_GROUPS) .option('-f, --configFile [' + CONFIG_FILE + ']', 'Path to file holding secret environment variables (e.g. "deploy.env")', CONFIG_FILE) .action(function (prg) { diff --git a/lib/main.js b/lib/main.js index 8e147765..7a495696 100644 --- a/lib/main.js +++ b/lib/main.js @@ -81,6 +81,12 @@ Lambda.prototype._params = function (program, buffer) { if (program.version) { params.FunctionName += ('-' + program.version); } + if (program.vpcSubnets && program.vpcSecurityGroups) { + params.VpcConfig = { + 'SubnetIds': program.vpcSubnets.split(','), + 'SecurityGroupIds': program.vpcSecurityGroups.split(',') + }; + } return params; }; @@ -245,7 +251,7 @@ Lambda.prototype.deploy = function (program) { if (program.sessionToken){ aws_security.sessionToken = program.sessionToken; - }; + } aws.config.update(aws_security); diff --git a/test/main.js b/test/main.js index c0db0185..db47d3f5 100644 --- a/test/main.js +++ b/test/main.js @@ -53,6 +53,19 @@ describe('node-lambda', function () { var params = lambda._params(program); assert.equal(params.FunctionName, 'node-lambda-development-2015-02-01'); }); + + it('appends VpcConfig to params when vpc params set', function() { + program.vpcSubnets = 'subnet-00000000,subnet-00000001,subnet-00000002'; + program.vpcSecurityGroups = 'sg-00000000,sg-00000001,sg-00000002'; + var params = lambda._params(program); + assert.equal(params.VpcConfig.SubnetIds[0], program.vpcSubnets.split(',')[0]); + assert.equal(params.VpcConfig.SecurityGroupIds[0], program.vpcSecurityGroups.split(',')[0]); + }); + + it('does not append VpcConfig when params are not set', function() { + var params = lambda._params(program); + assert.equal('VpcConfig' in params, false); + }); }); describe('_zipfileTmpPath', function () { From a8e27ca19f9e1ee0180a7e7581ff447994827a59 Mon Sep 17 00:00:00 2001 From: Chase Sillevis Date: Fri, 25 Mar 2016 12:58:59 +0100 Subject: [PATCH 3/3] Use AWS API 2015-03-31: - Check if the function exists, if it does update else create new - This API provides compatibility with VpcConfig --- README.md | 5 +++-- bin/node-lambda | 4 ++-- lib/.env.example | 2 ++ lib/main.js | 52 +++++++++++++++++++++++++++++++++++++++++------- test/main.js | 7 +++++-- 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a1fdaf81..6fe51f60 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,11 @@ $ node-lambda deploy --help -t, --timeout [3] Lambda Timeout -d, --description [missing] Lambda Description -u, --runtime [nodejs] Lambda Runtime + -p, --publish [false] This boolean parameter can be used to request AWS Lambda to create the Lambda function and publish a version as an atomic operation -v, --version [custom-version] Lambda Version -f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")` - -b, --vpcSubnets [] VPC Subnet(s) you want your Lambda Function to deploy in to (comma separated list), when using this, the below param is also required - -g, --vpcSecurityGroups [] VPC Security Group(s) you want your Lambda Function to deploy in to (comma separated list), when using this, the above param is also required + -b, --vpcSubnets [] VPC Subnet ID(s, comma separated list) for your Lambda Function, when using this, the below param is also required + -g, --vpcSecurityGroups [] VPC Security Group ID(s, comma separated list) for your Lambda Function, when using this, the above param is also required ``` ## Custom Environment Variables diff --git a/bin/node-lambda b/bin/node-lambda index 093ef97c..f927601a 100755 --- a/bin/node-lambda +++ b/bin/node-lambda @@ -15,12 +15,12 @@ var AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN || ''; var AWS_REGION = process.env.AWS_REGION || 'us-east-1,us-west-2,eu-west-1'; var AWS_FUNCTION_NAME = process.env.AWS_FUNCTION_NAME || packageJson.name; var AWS_HANDLER = process.env.AWS_HANDLER || 'index.handler'; -var AWS_MODE = 'event'; var AWS_ROLE = process.env.AWS_ROLE_ARN || process.env.AWS_ROLE || 'missing'; var AWS_MEMORY_SIZE = process.env.AWS_MEMORY_SIZE || 128; var AWS_TIMEOUT = process.env.AWS_TIMEOUT || 60; var AWS_DESCRIPTION = process.env.AWS_DESCRIPTION || ''; var AWS_RUNTIME = process.env.AWS_RUNTIME || 'nodejs'; +var AWS_PUBLISH = process.env.AWS_PUBLIS || false; var AWS_FUNCTION_VERSION = process.env.AWS_FUNCTION_VERSION || ''; var AWS_VPC_SUBNETS = process.env.AWS_VPC_SUBNETS || ''; var AWS_VPC_SECURITY_GROUPS = process.env.AWS_VPC_SECURITY_GROUPS || ''; @@ -38,12 +38,12 @@ program .option('-r, --region [' + AWS_REGION + ']', 'AWS Region', AWS_REGION) .option('-n, --functionName [' + AWS_FUNCTION_NAME + ']', 'Lambda FunctionName', AWS_FUNCTION_NAME) .option('-h, --handler [' + AWS_HANDLER + ']', 'Lambda Handler {index.handler}', AWS_HANDLER) - .option('-c, --mode [' + AWS_MODE + ']', 'Lambda Mode', AWS_MODE) .option('-o, --role [' + AWS_ROLE + ']', 'Amazon Role ARN', AWS_ROLE) .option('-m, --memorySize [' + AWS_MEMORY_SIZE + ']', 'Lambda Memory Size', AWS_MEMORY_SIZE) .option('-t, --timeout [' + AWS_TIMEOUT + ']', 'Lambda Timeout', AWS_TIMEOUT) .option('-d, --description [' + AWS_DESCRIPTION + ']', 'Lambda Description', AWS_DESCRIPTION) .option('-u, --runtime [' + AWS_RUNTIME + ']', 'Lambda Runtime', AWS_RUNTIME) + .option('-p, --publish [' + AWS_PUBLISH + ']', 'Lambda Publish', AWS_PUBLISH) .option('-v, --version [' + AWS_FUNCTION_VERSION + ']', 'Lambda Function Version', AWS_FUNCTION_VERSION) .option('-b, --vpcSubnets [' + AWS_VPC_SUBNETS + ']', 'Lambda Function VPC Subnets', AWS_VPC_SUBNETS) .option('-g, --vpcSecurityGroups [' + AWS_VPC_SECURITY_GROUPS + ']', 'Lambda VPC Security Group', diff --git a/lib/.env.example b/lib/.env.example index 7280b4d1..7b89ba52 100644 --- a/lib/.env.example +++ b/lib/.env.example @@ -11,3 +11,5 @@ AWS_MEMORY_SIZE=128 AWS_TIMEOUT=3 AWS_DESCRIPTION= AWS_RUNTIME=nodejs +AWS_VPC_SUBNETS= +AWS_VPC_SECURITY_GROUPS= diff --git a/lib/main.js b/lib/main.js index 7a495696..1928853a 100644 --- a/lib/main.js +++ b/lib/main.js @@ -69,14 +69,17 @@ Lambda.prototype._runHandler = function (handler, event) { Lambda.prototype._params = function (program, buffer) { var params = { FunctionName: program.functionName + (program.environment ? '-' + program.environment : ''), - FunctionZip: buffer, + Code: { + ZipFile: buffer + }, Handler: program.handler, - Mode: program.mode, Role: program.role, Runtime: program.runtime, Description: program.description, MemorySize: program.memorySize, - Timeout: program.timeout + Timeout: program.timeout, + Publish: program.publish, + VpcConfig: {} }; if (program.version) { params.FunctionName += ('-' + program.version); @@ -191,6 +194,36 @@ Lambda.prototype._setEnvironmentVars = function (program, codeDirectory) { fs.writeFileSync(handlerFileName, prefix + contents.toString()); }; +Lambda.prototype._uploadExisting = function(lambda, params, cb) { + return lambda.updateFunctionCode({ + 'FunctionName': params.FunctionName, + 'ZipFile': params.Code.ZipFile, + 'Publish': params.publish + }, function(err, data) { + if(err) { + return cb(err, data); + } + + return lambda.updateFunctionConfiguration({ + 'FunctionName': params.FunctionName, + 'Description': params.Description, + 'Handler': params.Handler, + 'MemorySize': params.MemorySize, + 'Role': params.Role, + 'Timeout': params.Timeout, + 'VpcConfig': params.VpcConfig + }, function(err, data) { + return cb(err, data); + }); + }); +}; + +Lambda.prototype._uploadNew = function(lambda, params, cb) { + return lambda.createFunction(params, function(err, data) { + return cb(err, data); + }); +}; + Lambda.prototype.deploy = function (program) { this._createSampleFile('.env'); @@ -256,13 +289,18 @@ Lambda.prototype.deploy = function (program) { aws.config.update(aws_security); var lambda = new aws.Lambda({ - apiVersion: '2014-11-11' + apiVersion: '2015-03-31' }); - lambda.uploadFunction(params, function (err, data) { - cb(err, data); - }); + return lambda.getFunction({ + 'FunctionName': params.FunctionName + }, function(err) { + if(err) { + return _this._uploadNew(lambda, params, cb); + } + return _this._uploadExisting(lambda, params, cb); + }); }, function (err, results) { if (err) { console.error(err); diff --git a/test/main.js b/test/main.js index db47d3f5..e905fe8d 100644 --- a/test/main.js +++ b/test/main.js @@ -16,7 +16,6 @@ var originalProgram = { sessionToken: 'token', functionName: 'node-lambda', handler: 'index.handler', - mode: 'event', role: 'some:arn:aws:iam::role', memorySize: 128, timeout: 3, @@ -59,12 +58,16 @@ describe('node-lambda', function () { program.vpcSecurityGroups = 'sg-00000000,sg-00000001,sg-00000002'; var params = lambda._params(program); assert.equal(params.VpcConfig.SubnetIds[0], program.vpcSubnets.split(',')[0]); + assert.equal(params.VpcConfig.SubnetIds[1], program.vpcSubnets.split(',')[1]); + assert.equal(params.VpcConfig.SubnetIds[2], program.vpcSubnets.split(',')[2]); assert.equal(params.VpcConfig.SecurityGroupIds[0], program.vpcSecurityGroups.split(',')[0]); + assert.equal(params.VpcConfig.SecurityGroupIds[1], program.vpcSecurityGroups.split(',')[1]); + assert.equal(params.VpcConfig.SecurityGroupIds[2], program.vpcSecurityGroups.split(',')[2]); }); it('does not append VpcConfig when params are not set', function() { var params = lambda._params(program); - assert.equal('VpcConfig' in params, false); + assert.equal(Object.keys(params.VpcConfig).length, 0); }); });