Skip to content

Commit cec1b33

Browse files
Sean ShiraziDeviaVir
authored andcommitted
Added --prebuiltDirectory (#116)
* added --prebuiltDirectory * Use rsync to copy necessary files before archiving * Read package.json's name only if it exists When using webpack, we may have one npm package and multiple lambda functions sharing code. Lambda functions don't necessarily have a package.json in them. * Bug fix. All tests pass now.
1 parent 7cf9b3a commit cec1b33

File tree

4 files changed

+92
-17
lines changed

4 files changed

+92
-17
lines changed

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ $ node-lambda run --help
6868
-h, --help Output usage information
6969
--handler [index.handler] Lambda Handler {index.handler}
7070
-j, --eventFile [event.json] Event JSON File
71-
-u, --runtime [nodejs4.3] Lambda Runtime {nodejs4.3, nodejs} - "nodejs4.3" is the current standard, "nodejs" is v0.10.36
71+
-u, --runtime [nodejs4.3] Lambda Runtime {nodejs4.3, nodejs} - "nodejs4.3" is the current standard, "nodejs" is v0.10.36
7272
-x, --contextFile [context.json] Context JSON file
7373
```
7474

@@ -89,6 +89,8 @@ $ node-lambda package --help
8989
-e, --environment [staging] Choose environment {development, staging, production}
9090
-f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")
9191
-x, --excludeGlobs [] Add a space separated list of file(type)s to ignore (e.g. "*.json .env")
92+
-P, --prebuiltDirectory [] Prebuilt directory
93+
9294
```
9395

9496
#### deploy
@@ -114,22 +116,23 @@ $ node-lambda deploy --help
114116
-m, --memorySize [128] Lambda Memory Size
115117
-t, --timeout [3] Lambda Timeout
116118
-d, --description [missing] Lambda Description
117-
-u, --runtime [nodejs4.3] Lambda Runtime {nodejs4.3, nodejs} - "nodejs4.3" is the current standard, "nodejs" is v0.10.36
119+
-u, --runtime [nodejs4.3] Lambda Runtime {nodejs4.3, nodejs} - "nodejs4.3" is the current standard, "nodejs" is v0.10.36
118120
-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
119121
-v, --version [custom-version] Lambda Version
120122
-f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")
121123
-b, --vpcSubnets [] VPC Subnet ID(s, comma separated list) for your Lambda Function, when using this, the below param is also required
122124
-g, --vpcSecurityGroups [] VPC Security Group ID(s, comma separated list) for your Lambda Function, when using this, the above param is also required
123125
-x, --excludeGlobs [] Add a space separated list of file(type)s to ignore (e.g. "*.json .env")
126+
-P, --prebuiltDirectory [] Prebuilt directory
124127
```
125128

126129
## Custom Environment Variables
127130

128-
AWS Lambda doesn't let you set environment variables for your function, but in many cases you will need to configure your function with secure values that you don't want to check into version control, for example a DB connection string or encryption key. Use the sample `deploy.env` file in combination with the `--configFile` flag to set values which will be prepended to your compiled Lambda function as `process.env` environment variables before it gets uploaded to S3.
131+
AWS Lambda doesn't let you set environment variables for your function, but in many cases you will need to configure your function with secure values that you don't want to check into version control, for example a DB connection string or encryption key. Use the sample `deploy.env` file in combination with the `--configFile` flag to set values which will be prepended to your compiled Lambda function as `process.env` environment variables before it gets uploaded to S3.
129132

130133
## Node.js Runtime Configuration
131134

132-
AWS Lambda now supports Node.js v4.3.2, and there have been some [API changes](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-using-old-runtime.html) for the new version. Most notably,
135+
AWS Lambda now supports Node.js v4.3.2, and there have been some [API changes](http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-using-old-runtime.html) for the new version. Most notably,
133136
`context.done()`, `context.succeed()`, and `context.fail()` are deprecated in favor of the Node convention of passing in
134137
a callback function. These will still work for now for backward compatibility, but are no longer recommended.
135138

@@ -138,6 +141,12 @@ v0.10.36 is still supported, and can be targeted by changing the `AWS_RUNTIME` v
138141
## Post install script
139142
When running `node-lambda deploy` if you need to do some action after `npm install --production` and before deploying to AWS Lambda (i.e. replace some modules with precompiled ones or download some libraries) you can create `post_install.sh` script. If the file exists the script will be executed (and output shown after execution) if not it is skipped. Make sure that the script is executable.
140143

144+
## Prebuilt packages
145+
The `--prebuiltDirectory` flag is useful for working with Webpack for example. It skips `npm install --production` and `post_install.sh` and simply packages the specified directory.
146+
147+
## Handling `npm link` and Dependencies With Local Paths
148+
Perhaps the easiest way to handle these cases is to bundle the code using Webpack and use the `--prebuiltDirectory` flag to package the output for deployment.
149+
141150
## Other AWS Lambda Tools Projects
142151

143152
+ [lambdaws](https://github.com/mentum/lambdaws)

bin/node-lambda

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
var dotenv = require('dotenv');
44
var lambda = require('../lib/main.js');
55
var program = require('commander');
6-
var packageJson = require(process.cwd() + '/package.json');
6+
var fs = require('fs');
7+
var packageJsonName = fs.existsSync(process.cwd() + '/package.json')
8+
? require(process.cwd() + '/package.json').name : 'UnnamedFunction';
79

810
dotenv.load();
911

@@ -14,7 +16,7 @@ var AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID;
1416
var AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY;
1517
var AWS_SESSION_TOKEN = process.env.AWS_SESSION_TOKEN || '';
1618
var AWS_REGION = process.env.AWS_REGION || 'us-east-1,us-west-2,eu-west-1';
17-
var AWS_FUNCTION_NAME = process.env.AWS_FUNCTION_NAME || packageJson.name;
19+
var AWS_FUNCTION_NAME = process.env.AWS_FUNCTION_NAME || packageJsonName;
1820
var AWS_HANDLER = process.env.AWS_HANDLER || 'index.handler';
1921
var AWS_ROLE = process.env.AWS_ROLE_ARN || process.env.AWS_ROLE || 'missing';
2022
var AWS_MEMORY_SIZE = process.env.AWS_MEMORY_SIZE || 128;
@@ -28,6 +30,9 @@ var AWS_VPC_SECURITY_GROUPS = process.env.AWS_VPC_SECURITY_GROUPS || '';
2830
var EVENT_FILE = process.env.EVENT_FILE || 'event.json';
2931
var PACKAGE_DIRECTORY = process.env.PACKAGE_DIRECTORY;
3032
var CONTEXT_FILE = process.env.CONTEXT_FILE || 'context.json';
33+
var PREBUILT_DIRECTORY = process.env.PREBUILT_DIRECTORY || '';
34+
35+
3136

3237
program
3338
.version(lambda.version)
@@ -56,6 +61,7 @@ program
5661
'Path to file holding secret environment variables (e.g. "deploy.env")', CONFIG_FILE)
5762
.option('-x, --excludeGlobs [' + EXCLUDE_GLOBS + ']',
5863
'Space-separated glob pattern(s) for additional exclude files (e.g. "event.json dotenv.sample")', EXCLUDE_GLOBS)
64+
.option('-P, --prebuiltDirectory [' + PREBUILT_DIRECTORY + ']', 'Prebuilt directory', PREBUILT_DIRECTORY)
5965
.action(function (prg) {
6066
lambda.deploy(prg);
6167
});
@@ -73,6 +79,7 @@ program
7379
'Space-separated glob pattern(s) for additional exclude files (e.g. "event.json dotenv.sample")', EXCLUDE_GLOBS)
7480
.option('-f, --configFile [' + CONFIG_FILE + ']',
7581
'Path to file holding secret environment variables (e.g. "deploy.env")', CONFIG_FILE)
82+
.option('-P, --prebuiltDirectory [' + PREBUILT_DIRECTORY + ']', 'Prebuilt directory', PREBUILT_DIRECTORY)
7683
.action(function (prg) {
7784
lambda.package(prg);
7885
});

lib/main.js

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ Lambda.prototype._zipfileTmpPath = function (program) {
137137
return zipfile;
138138
};
139139

140-
Lambda.prototype._rsync = function (program, codeDirectory, callback) {
140+
Lambda.prototype._rsync = function (program, src, dest, callback) {
141141
var excludes = ['.git*', '*.swp', '.editorconfig', 'deploy.env', '*.log', 'build/', 'node_modules'],
142142
excludeGlobs = [];
143143
if (program.excludeGlobs) {
@@ -147,12 +147,14 @@ Lambda.prototype._rsync = function (program, codeDirectory, callback) {
147147
return '--exclude=' + exclude;
148148
}).join(' ');
149149

150-
exec('mkdir -p ' + codeDirectory, function(err) {
150+
exec('mkdir -p ' + dest, function(err) {
151151
if (err) {
152152
return callback(err);
153153
}
154154

155-
exec('rsync -r ' + excludeArgs + ' . ' + codeDirectory, function (err) {
155+
// we need the extra / after src to make sure we are copying the content
156+
// of the directory, not the directory itself.
157+
exec('rsync -r ' + excludeArgs + ' ' + src.trim() + '/ ' + dest, function (err) {
156158
if (err) {
157159
return callback(err);
158160
}
@@ -326,6 +328,33 @@ Lambda.prototype._uploadNew = function(lambda, params, cb) {
326328
};
327329

328330
Lambda.prototype._archive = function (program, archive_callback) {
331+
return program.prebuiltDirectory
332+
? this._archivePrebuilt(program, archive_callback)
333+
: this._buildAndArchive(program, archive_callback);
334+
}
335+
336+
Lambda.prototype._archivePrebuilt = function (program, archive_callback) {
337+
338+
var codeDirectory = this._codeDirectory(program)
339+
var _this = this;
340+
this._rsync(program, program.prebuiltDirectory, codeDirectory, function (err) {
341+
if (err) {
342+
return archive_callback(err);
343+
}
344+
345+
// Add custom environment variables if program.configFile is defined
346+
if (program.configFile) {
347+
_this._setEnvironmentVars(program, codeDirectory);
348+
}
349+
console.log('=> Zipping deployment package');
350+
var archive = process.platform !== 'win32' ? _this._nativeZip : _this._zip;
351+
archive = archive.bind(_this);
352+
353+
archive(program, codeDirectory, archive_callback);
354+
});
355+
}
356+
357+
Lambda.prototype._buildAndArchive = function (program, archive_callback) {
329358
this._createSampleFile('.env', '.env');
330359

331360
// Warn if not building on 64-bit linux
@@ -345,7 +374,7 @@ Lambda.prototype._archive = function (program, archive_callback) {
345374
}
346375
console.log('=> Moving files to temporary directory');
347376
// Move files to tmp folder
348-
_this._rsync(program, codeDirectory, function (err) {
377+
_this._rsync(program, '.', codeDirectory, function (err) {
349378
if (err) {
350379
return archive_callback(err);
351380
}
@@ -401,6 +430,7 @@ Lambda.prototype.package = function (program) {
401430
if (err) {
402431
throw err;
403432
}
433+
404434
var basename = program.functionName + (program.environment ? '-' + program.environment : '');
405435
var zipfile = path.join(program.packageDirectory, basename + '.zip');
406436
console.log('=> Writing packaged zip');
@@ -464,4 +494,5 @@ Lambda.prototype.deploy = function (program) {
464494
});
465495
};
466496

497+
467498
module.exports = new Lambda();

test/main.js

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var lambda = require('../lib/main');
66
var fs = require('fs');
77
var _ = require('lodash');
88
var zip = require('node-zip');
9+
var rimraf = require('rimraf');
910

1011
var assert = chai.assert;
1112

@@ -23,7 +24,8 @@ var originalProgram = {
2324
runtime: 'nodejs',
2425
region: 'us-east-1,us-west-2,eu-west-1',
2526
eventFile: 'event.json',
26-
contextFile: 'context.json'
27+
contextFile: 'context.json',
28+
prebuiltDirectory: '',
2729
};
2830

2931
var codeDirectory = lambda._codeDirectory(program);
@@ -87,7 +89,7 @@ describe('node-lambda', function () {
8789
});
8890

8991
it('rsync an index.js as well as other files', function (done) {
90-
lambda._rsync(program, codeDirectory, function (err, result) {
92+
lambda._rsync(program, '.', codeDirectory, function (err, result) {
9193
var contents = fs.readdirSync(codeDirectory);
9294

9395
result = _.includes(contents, 'index.js') ||
@@ -105,7 +107,7 @@ describe('node-lambda', function () {
105107
});
106108

107109
it('rsync an index.js as well as other files', function (done) {
108-
lambda._rsync(program, codeDirectory, function (err, result) {
110+
lambda._rsync(program, '.', codeDirectory, function (err, result) {
109111
var contents = fs.readdirSync(codeDirectory);
110112

111113
result = _.includes(contents, 'index.js') ||
@@ -117,7 +119,7 @@ describe('node-lambda', function () {
117119
});
118120

119121
it('rsync excludes files matching excludeGlobs', function (done) {
120-
lambda._rsync(program, codeDirectory, function (err, result) {
122+
lambda._rsync(program, '.', codeDirectory, function (err, result) {
121123
var contents = fs.readdirSync(codeDirectory);
122124

123125
result = _.includes(contents, 'node-lambda.png') ||
@@ -137,7 +139,7 @@ describe('node-lambda', function () {
137139
return done(err);
138140
}
139141

140-
lambda._rsync(program, codeDirectory, function (err) {
142+
lambda._rsync(program, '.', codeDirectory, function (err) {
141143
if (err) {
142144
return done(err);
143145
}
@@ -168,7 +170,7 @@ describe('node-lambda', function () {
168170
return done(err);
169171
}
170172

171-
lambda._rsync(program, codeDirectory, function (err) {
173+
lambda._rsync(program, '.', codeDirectory, function (err) {
172174
if (err) {
173175
return done(err);
174176
}
@@ -213,7 +215,33 @@ describe('node-lambda', function () {
213215
assert.equal(result, true);
214216
done();
215217
})
216-
})
218+
});
219+
220+
it('packages a prebuilt module without installing', function (done) {
221+
var path = '.build_' + Date.now();
222+
after(function() {
223+
rimraf.sync(path, fs);
224+
});
225+
226+
fs.mkdirSync(path);
227+
fs.mkdirSync(path + '/d');
228+
fs.writeFileSync(path + '/testa', 'a');
229+
fs.writeFileSync(path + '/d/testb', 'b');
230+
231+
program.prebuiltDirectory = path;
232+
lambda._archive(program, function (err, data) {
233+
var archive = new zip(data);
234+
var contents = _.map(archive.files, function (f) {
235+
return f.name.toString();
236+
});
237+
var result = _.includes(contents, 'testa');
238+
assert.equal(result, true);
239+
result = _.includes(contents, 'd/testb');
240+
assert.equal(result, true);
241+
done();
242+
243+
})
244+
});
217245
});
218246

219247
describe('environment variable injection', function () {

0 commit comments

Comments
 (0)