Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ There are 3 available commands.
```
node-lambda setup
node-lambda run
node-lambda package
node-lambda deploy
```

Expand Down Expand Up @@ -69,6 +70,24 @@ $ node-lambda run --help
-j, --eventFile [event.json] Event JSON File
```

#### package

Bundles your application into a local zip file.

```
$ node-lambda package --help

Usage: package [options]

Options:

-h, --help output usage information
-p, --packageDirectory [build] Local Package Directory
-n, --functionName [node-lambda] Lambda FunctionName
-e, --environment [staging] Choose environment {development, staging, production}
-f, --configFile [] Path to file holding secret environment variables (e.g. "deploy.env")
```

#### deploy

Bundles and deploys your application up to Amazon Lambda.
Expand Down
16 changes: 16 additions & 0 deletions bin/node-lambda
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ 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;

program
.version(lambda.version)
Expand All @@ -48,12 +49,27 @@ program
.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)
.action(function (prg) {
lambda.deploy(prg);
});

program
.version(lambda.version)
.command('package')
.description('Create zipped package for Amazon Lambda deployment')
.option('-p, --packageDirectory [' + PACKAGE_DIRECTORY + ']', 'Local Package Directory', PACKAGE_DIRECTORY)
.option('-n, --functionName [' + AWS_FUNCTION_NAME + ']', 'Lambda FunctionName', AWS_FUNCTION_NAME)
.option('-e, --environment [' + AWS_ENVIRONMENT + ']', 'Choose environment {dev, staging, production}',
AWS_ENVIRONMENT)
.option('-f, --configFile [' + CONFIG_FILE + ']',
'Path to file holding secret environment variables (e.g. "deploy.env")', CONFIG_FILE)
.action(function (prg) {
lambda.package(prg);
});

program
.version(lambda.version)
.command('run')
Expand Down
1 change: 1 addition & 0 deletions lib/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ AWS_DESCRIPTION=
AWS_RUNTIME=nodejs
AWS_VPC_SUBNETS=
AWS_VPC_SECURITY_GROUPS=
PACKAGE_DIRECTORY=build
147 changes: 99 additions & 48 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,12 @@ Lambda.prototype._zipfileTmpPath = function (program) {
};

Lambda.prototype._rsync = function (program, codeDirectory, callback) {
exec('rsync -r --exclude=.git --exclude=*.log --exclude=node_modules . ' + codeDirectory, function (err) {
var excludes = [ ".git*", "*.env", "*.log", "node_modules", "test" ];
if (program.packageDirectory) {
excludes.push(program.packageDirectory);
}
var excludeArgs = excludes.map(function(exclude) { return '--exclude=' + exclude; }).join(" ");
exec('rsync -r ' + excludeArgs + ' . ' + codeDirectory, function (err) {
if (err) {
throw err;
}
Expand Down Expand Up @@ -224,7 +229,7 @@ Lambda.prototype._uploadNew = function(lambda, params, cb) {
});
};

Lambda.prototype.deploy = function (program) {
Lambda.prototype._archive = function (program, archive_callback) {
this._createSampleFile('.env');

// Warn if not building on 64-bit linux
Expand All @@ -236,7 +241,6 @@ Lambda.prototype.deploy = function (program) {
}

var _this = this;
var regions = program.region.split(',');
var codeDirectory = _this._codeDirectory(program);

console.log('=> Moving files to temporary directory');
Expand All @@ -263,54 +267,101 @@ Lambda.prototype.deploy = function (program) {
var archive = process.platform !== 'win32' ? _this._nativeZip : _this._zip;
archive = archive.bind(_this);

archive(program, codeDirectory, function (err, buffer) {
if (err) {
console.error(err);
return;
}
archive(program, codeDirectory, archive_callback);
});
});
};

Lambda.prototype.package = function (program) {
var _this = this;
if (!program.packageDirectory) {
console.error('packageDirectory not specified!');
return;
} else {
try {
var isDir = fs.lstatSync(program.packageDirectory).isDirectory();

if (!isDir) {
console.error(program.packageDirectory + ' is not a directory!');
return;
}
} catch(err) {
if (err.code === 'ENOENT') {
console.log('=> Creating package directory');
fs.mkdirSync(program.packageDirectory);
} else {
console.error(err);
return;
}
}
}

_this._archive(program, function (err, buffer) {
if (err) {
console.error(err);
return;
}
var basename = program.functionName + (program.environment ? '-' + program.environment : '');
var zipfile = path.join(program.packageDirectory, basename + '.zip');
console.log('=> Writing packaged zip');
fs.writeFile(zipfile, buffer, function(err) {
if (err) {
console.error(err);
return;
}
console.log('Packaged zip created: ' + zipfile);
});
});
};

Lambda.prototype.deploy = function (program) {
var _this = this;
var regions = program.region.split(',');
_this._archive(program, function (err, buffer) {
if (err) {
console.error(err);
return;
}

console.log('=> Reading zip file to memory');
var params = _this._params(program, buffer);

async.map(regions, function (region, cb) {
console.log('=> Uploading zip file to AWS Lambda ' + region + ' with parameters:');
console.log(params);

var aws_security = {
accessKeyId: program.accessKey,
secretAccessKey: program.secretKey,
region: region
};

if (program.sessionToken){
aws_security.sessionToken = program.sessionToken;
}

aws.config.update(aws_security);

var lambda = new aws.Lambda({
apiVersion: '2015-03-31'
});

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);
} else {
console.log('=> Zip file(s) done uploading. Results follow: ');
console.log(results);
}
});
console.log('=> Reading zip file to memory');
var params = _this._params(program, buffer);

async.map(regions, function (region, cb) {
console.log('=> Uploading zip file to AWS Lambda ' + region + ' with parameters:');
console.log(params);

var aws_security = {
accessKeyId: program.accessKey,
secretAccessKey: program.secretKey,
region: region
};

if (program.sessionToken){
aws_security.sessionToken = program.sessionToken;
}

aws.config.update(aws_security);

var lambda = new aws.Lambda({
apiVersion: '2015-03-31'
});

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);
} else {
console.log('=> Zip file(s) done uploading. Results follow: ');
console.log(results);
}
});
});
};
Expand Down
18 changes: 18 additions & 0 deletions test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,24 @@ describe('node-lambda', function () {
});
});

describe('_archive', function () {
it('installs and zips with an index.js file and node_modules/async', function (done) {
this.timeout(30000); // give it time to zip

lambda._archive(program, function (err, data) {
var archive = new zip(data);
var contents = _.map(archive.files, function (f) {
return f.name.toString();
});
var result = _.includes(contents, 'index.js');
assert.equal(result, true);
result = _.includes(contents, 'node_modules/async/lib/async.js');
assert.equal(result, true);
done();
})
})
});

describe('environment variable injection', function () {
beforeEach(function () {
// Prep...
Expand Down