diff --git a/README.md b/README.md index 546a9a54..a4bc09f1 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,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 @@ -113,8 +113,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 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 4a97903e..b846586f 100755 --- a/bin/node-lambda +++ b/bin/node-lambda @@ -15,13 +15,15 @@ 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 || ''; var EVENT_FILE = process.env.EVENT_FILE || 'event.json'; var PACKAGE_DIRECTORY = process.env.PACKAGE_DIRECTORY; @@ -37,13 +39,16 @@ 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', + AWS_VPC_SECURITY_GROUPS) .option('-p, --packageDirectory [' + PACKAGE_DIRECTORY + ']', 'Local Package Directory', PACKAGE_DIRECTORY) .option('-f, --configFile [' + CONFIG_FILE + ']', 'Path to file holding secret environment variables (e.g. "deploy.env")', CONFIG_FILE) diff --git a/lib/.env.example b/lib/.env.example index 70982a9f..a0da97af 100644 --- a/lib/.env.example +++ b/lib/.env.example @@ -11,4 +11,6 @@ AWS_MEMORY_SIZE=128 AWS_TIMEOUT=3 AWS_DESCRIPTION= AWS_RUNTIME=nodejs +AWS_VPC_SUBNETS= +AWS_VPC_SECURITY_GROUPS= PACKAGE_DIRECTORY=build diff --git a/lib/main.js b/lib/main.js index c4906e23..82738b58 100644 --- a/lib/main.js +++ b/lib/main.js @@ -69,18 +69,27 @@ 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); } + if (program.vpcSubnets && program.vpcSecurityGroups) { + params.VpcConfig = { + 'SubnetIds': program.vpcSubnets.split(','), + 'SecurityGroupIds': program.vpcSecurityGroups.split(',') + }; + } return params; }; @@ -190,6 +199,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._archive = function (program, archive_callback) { this._createSampleFile('.env'); @@ -299,18 +338,23 @@ Lambda.prototype.deploy = function (program) { if (program.sessionToken){ aws_security.sessionToken = program.sessionToken; - }; + } 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 04ac5d3b..d954e86d 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, @@ -53,6 +52,23 @@ 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.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(Object.keys(params.VpcConfig).length, 0); + }); }); describe('_zipfileTmpPath', function () {