From 53fb4e6b3af7fbcfea940b09d466368e20ab3701 Mon Sep 17 00:00:00 2001 From: Austen Collins Date: Tue, 17 Nov 2015 21:23:18 -0800 Subject: [PATCH] CodePackageLambdaNodejs: start work on this --- .../actions/CodePackageLambdaNodejs.js | 396 +++++++++++++++--- .../actions/CodeProvisionLambdaNodejs.js | 2 +- lib/defaults/actions/FunctionDeploy.js | 44 +- lib/defaults/actions/LambdaDeploy.js | 321 +------------- lib/utils/aws/S3.js | 6 +- package.json | 2 - .../bundle/browserify/lambda.awsm.json | 8 +- .../bundle/nonoptimized/lambda.awsm.json | 8 +- .../sessions/create/lambda.awsm.json | 6 +- .../sessions/show/lambda.awsm.json | 8 +- .../aws_modules/users/create/lambda.awsm.json | 8 +- tests/tests/actions/FunctionDeploy.js | 34 +- 12 files changed, 421 insertions(+), 422 deletions(-) diff --git a/lib/defaults/actions/CodePackageLambdaNodejs.js b/lib/defaults/actions/CodePackageLambdaNodejs.js index e1da961f8..e5a72ee90 100644 --- a/lib/defaults/actions/CodePackageLambdaNodejs.js +++ b/lib/defaults/actions/CodePackageLambdaNodejs.js @@ -2,17 +2,23 @@ /** * Action: Code Package: Lambda: Nodejs - * - Collects and optimizes Lambda code in a temp folder + * - Accepts one function + * - Collects and optimizes the function's Lambda code in a temp folder */ const JawsPlugin = require('../../JawsPlugin'), JawsError = require('../../jaws-error'), JawsUtils = require('../../utils/index'), - extend = require('util')._extend, + JawsCli = require('../../utils/cli'), BbPromise = require('bluebird'), path = require('path'), fs = require('fs'), - os = require('os'); + os = require('os'), + babelify = require('babelify'), + browserify = require('browserify'), + UglifyJS = require('uglify-js'), + wrench = require('wrench'), + Zip = require('node-zip'); // Promisify fs module BbPromise.promisifyAll(fs); @@ -41,7 +47,7 @@ class CodePackageLambdaNodejs extends JawsPlugin { registerActions() { - this.Jaws.addAction(this.CodePackageLambdaNodejs.bind(this), { + this.Jaws.addAction(this.codePackageLambdaNodejs.bind(this), { handler: 'codePackageLambdaNodejs', description: 'Deploys the code or endpoint of a function, or both' }); @@ -58,89 +64,355 @@ class CodePackageLambdaNodejs extends JawsPlugin { let _this = this; _this.evt = evt; + // Load AWS Service Instances + let awsConfig = { + region: _this.evt.deployRegion.region, + accessKeyId: _this.Jaws._awsAdminKeyId, + secretAccessKey: _this.Jaws._awsAdminSecretKey, + }; + _this.S3 = require('../../utils/aws/S3')(awsConfig); + // Flow return BbPromise.try(function() {}) .bind(_this) .then(_this._validateAndPrepare) - .then(_this._promptStage) - .then(_this._prepareRegions) - .then(_this._deployRegions); + .then(_this._createDistFolder) + .then(_this._package) } /** * Validate And Prepare - * - If CLI, maps CLI input to event object */ _validateAndPrepare() { + let _this = this, + lambda; + + // Require function config + let functionJson = require(_this.evt.currentFunction); + + // Skip Function if it does not have a lambda + try { + lambda = functionJson.cloudFormation.lambda; + } catch(error) { + return Promise.reject(new JawsError(_this.evt.currentFunction + 'does not have a lambda property')); + } + + // Validate lambda attributes + if (!lambda.Type + || !lambda.Properties + || !lambda.Properties.Runtime + || !lambda.Properties.Handler) { + return Promise.reject(new JawsError('Missing one of many required lambda attributes')); + } + + // Add function path to functionJson + functionJson.path = _this.evt.currentFunction; + + // Change function path to object + _this.evt.currentFunction = functionJson; + + return BbPromise.resolve(); + } + + /** + * Create Distribution Folder + */ + + _createDistFolder() { + let _this = this; - // If cli, process command line input - if (_this.Jaws.cli) { + // Create dist folder + let d = new Date(); + _this.evt.distDir = path.join(os.tmpdir(), _this.evt.currentFunction.name + '@' + d.getTime()); - // Add options to evt - _this.evt = _this.Jaws.cli.options; + // Status + JawsCli.log('Lambda Deployer: Packaging "' + _this.evt.currentFunction.name + '"...'); + JawsCli.log('Lambda Deployer: Saving in dist dir ' + _this.evt.distDir); - // Add type. Should be first in array - _this.evt.type = _this.Jaws.cli.params[0]; + JawsUtils.jawsDebug('copying', _this.Jaws._projectRootPath, 'to', _this.evt.distDir); - // Add function paths. Should be all other array items - _this.Jaws.cli.params.splice(0,1); - _this.evt.functions = _this.Jaws.cli.params; - } - - // Validate type - if (!_this.evt.type || - (_this.evt.type !== 'code' && - _this.evt.type !== 'endpoint' && - _this.evt.type !== 'all') - ) { - throw new JawsError(`Invalid type. Must be "code", "endpoint", or "all" `); - } - - // Validate stage - if (!this.evt.stage) throw new JawsError(`Stage is required`); - - // If region specified, add it to regions array for deployment - if (this.evt.region) { - this.evt.regions = [this.evt.region]; - delete this.evt.region; // Remove original "region" property for cleanliness - } - - // Process noExeCf - this.evt.noExeCf = (this.evt.noExeCf == true || this.evt.noExeCf == 'true'); - - // Get full function paths relative to project root - if (!_this.evt.functions.length) { - - // If no functions, check cwd and resolve that path - return JawsUtils.getFunctions( - _this.Jaws._projectRootPath, - _this.evt.type - ) - .then(function(functions) { - // If no functions, throw error - if (!_this.evt.functions.length) { - throw new JawsError(`No function found. Make sure your current working directory is a function.`); + // Copy entire test project to temp folder + let excludePatterns = _this.evt.currentFunction.package.excludePatterns || []; + wrench.copyDirSyncRecursive( + _this.Jaws._projectRootPath, + _this.evt.distDir, + { + exclude: function(name, prefix) { + if (!excludePatterns.length) { + return false; } - _this.evt.functions = functions; + let relPath = path.join( + prefix.replace(_this.evt.distDir, ''), name); + + return excludePatterns.some(sRegex => { + relPath = (relPath.charAt(0) == path.sep) ? relPath.substr(1) : relPath; + + let re = new RegExp(sRegex), + matches = re.exec(relPath), + willExclude = (matches && matches.length > 0); + + if (willExclude) { + JawsCLI.log(`Lambda Deployer: Excluding ${relPath}`); + } + + return willExclude; + }); + }, + } + ); + + JawsUtils.jawsDebug('Packaging stage & region:', _this.evt.stage, _this.evt.deployRegion); + + // Get ENV file from S3 + return _this.S3.sGetEnvFile( + _this.evt.deployRegion.jawsBucket, + _this.Jaws._projectJson.name, + _this.evt.stage + ) + .then(function(s3ObjData) { + + fs.writeFileSync( + path.join(_this.evt.distDir,'.env'), + s3ObjData.Body); + + }); + } + + /** + * Package + */ + + _package() { + + let _this = this, + lambda = _this.evt.currentFunction.cloudFormation.lambda, + deferred = false, + targetZipPath = path.join(_this.evt.distDir, 'package.zip'), + optimizeSettings = _this.evt.currentFunction.package.optimize; + + if (optimizeSettings.builder) { + + deferred = _this._optimizeNodeJs() + .then(optimizedCodeBuffer => { + let envData = fs.readFileSync(path.join(_this.evt.distDir, '.env')), + handlerFileName = lambda.Function.Properties.Handler.split('.')[0], + compressPaths = [ + // handlerFileName is the full path lambda file including dir rel to back + {fileName: handlerFileName + '.js', data: optimizedCodeBuffer}, + {fileName: '.env', data: envData}, + ]; + + compressPaths = compressPaths.concat(_this._generateIncludePaths()); + return compressPaths; }); } else { - // If functions, resolve their paths - return JawsUtils.getFunctions( - _this.Jaws._projectRootPath, - _this.evt.type, - _this.evt.functions - ) - .then(function(functions) { - _this.evt.functions = functions; - }); + // User chose not to optimize, zip up whatever is in back + optimizeSettings.includePaths = ['.']; + let compressPaths = _this._generateIncludePaths(); + + deferred = Promise.resolve(compressPaths); + + } + + return deferred + .then(compressPaths => { + return _this._compress(compressPaths, targetZipPath); + }) + .then(zipFilePath => { + return Promise.resolve({awsmFilePath: _this._lambdaAwsmPath, zipFilePath: zipFilePath}); + }); + } + + /** + * Optimize + */ + + _optimize() { + + let _this = this, + lambda = _this.evt.currentFunction.cloudFormation.lambda; + + if (!_this.evt.currentFunction.package.optimize + || !_this.evt.currentFunction.package.optimize.builder) { + return Promise.reject(new JawsError('Cant optimize for nodejs. lambda jaws.json does not have optimize.builder set')); + } + + if (_this.evt.currentFunction.package.optimize.builder.toLowerCase() == 'browserify') { + return _this._browserifyBundle(); + } else { + return Promise.reject(new JawsError(`Unsupported builder ${builder}`)); } } + + /** + * Generate Include Paths + */ + + _generateIncludePaths() { + + let _this = this, + compressPaths = [], + ignore = ['.DS_Store'], + stats, + fullPath; + + _this._awsmJson.package.optimize.includePaths.forEach(p => { + try { + fullPath = path.resolve(path.join(_this._distDir, p)); + stats = fs.lstatSync(fullPath); + } catch (e) { + console.error('Cant find includePath ', p, e); + throw e; + } + + if (stats.isFile()) { + JawsUtils.jawsDebug('INCLUDING', fullPath); + compressPaths.push({fileName: p, data: fs.readFileSync(fullPath)}); + } else if (stats.isDirectory()) { + let dirname = path.basename(p); + + wrench + .readdirSyncRecursive(fullPath) + .forEach(file => { + // Ignore certain files + for (let i = 0; i < ignore.length; i++) { + if (file.toLowerCase().indexOf(ignore[i]) > -1) return; + } + + let filePath = [fullPath, file].join('/'); + if (fs.lstatSync(filePath).isFile()) { + let pathInZip = path.join(dirname, file); + JawsUtils.jawsDebug('INCLUDING', pathInZip); + compressPaths.push({fileName: pathInZip, data: fs.readFileSync(filePath)}); + } + }); + } + }); + + return compressPaths; + } + + /** + * Compress + */ + + _compress(compressPaths, targetZipPath) { + let zip = new Zip(); + + compressPaths.forEach(nc => { + zip.file(nc.fileName, nc.data); + }); + + let zipBuffer = zip.generate({ + type: 'nodebuffer', + compression: 'DEFLATE', + }); + + if (zipBuffer.length > 52428800) { + Promise.reject(new JawsError( + 'Zip file is > the 50MB Lambda deploy limit (' + zipBuffer.length + ' bytes)', + JawsError.errorCodes.ZIP_TOO_BIG) + ); + } + + fs.writeFileSync(targetZipPath, zipBuffer); + JawsCLI.log(`Lambda Deployer: Compressed code written to ${targetZipPath}`); + + return Promise.resolve(targetZipPath); + } + + + /** + * Browserify the code and return buffer of bundled code + * + * @returns {Promise.Buffer} + * @private + */ + + _browserifyBundle() { + + let _this = this; + let uglyOptions = { + mangle: true, // @see http://lisperator.net/uglifyjs/compress + compress: {}, + }; + let b = browserify({ + basedir: _this._distDir, + entries: [_this._awsmJson.cloudFormation.lambda.Function.Properties.Handler.split('.')[0] + '.js'], + standalone: 'lambda', + browserField: false, // Setup for node app (copy logic of --node in bin/args.js) + builtins: false, + commondir: false, + ignoreMissing: true, // Do not fail on missing optional dependencies + detectGlobals: true, // Default for bare in cli is true, but we don't care if its slower + insertGlobalVars: { // Handle process https://github.com/substack/node-browserify/issues/1277 + //__filename: insertGlobals.lets.__filename, + //__dirname: insertGlobals.lets.__dirname, + process: function() { + }, + }, + }); + + if (_this._awsmJson.package.optimize.babel) { + b.transform(babelify); + } + + if (_this._awsmJson.package.optimize.transform) { + JawsUtils.jawsDebug('Adding transform', _this._awsmJson.package.optimize.transform); + b.transform(_this._awsmJson.package.optimize.transform); + } + + // optimize.exclude + _this._awsmJson.package.optimize.exclude.forEach(file => { + JawsUtils.jawsDebug('EXCLUDING', file); + b.exclude(file); + }); + + // optimize.ignore + _this._awsmJson.package.optimize.ignore.forEach(file => { + JawsUtils.jawsDebug('IGNORING', file); + b.ignore(file); + }); + + // Perform Bundle + let bundledFilePath = path.join(_this._distDir, 'bundled.js'); // Save for auditing + let minifiedFilePath = path.join(_this._distDir, 'minified.js'); // Save for auditing + + return new Promise(function(resolve, reject) { + b.bundle(function(err, bundledBuf) { + if (err) { + console.error('Error running browserify bundle'); + reject(err); + } else { + fs.writeFileSync(bundledFilePath, bundledBuf); + JawsCLI.log(`Lambda Deployer: Bundled file written to ${bundledFilePath}`); + + if (_this._awsmJson.package.optimize.minify) { + JawsUtils.jawsDebug('Minifying...'); + let result = UglifyJS.minify(bundledFilePath, uglyOptions); + + if (!result || !result.code) { + reject(new JawsError('Problem uglifying code')); + } + + fs.writeFileSync(minifiedFilePath, result.code); + + JawsCLI.log(`Lambda Deployer: Minified file written to ${minifiedFilePath}`); + resolve(result.code); + } else { + resolve(bundledBuf); + } + } + }); + }); + } + } module.exports = CodePackageLambdaNodejs; \ No newline at end of file diff --git a/lib/defaults/actions/CodeProvisionLambdaNodejs.js b/lib/defaults/actions/CodeProvisionLambdaNodejs.js index 3bb130118..36988db27 100644 --- a/lib/defaults/actions/CodeProvisionLambdaNodejs.js +++ b/lib/defaults/actions/CodeProvisionLambdaNodejs.js @@ -41,7 +41,7 @@ class CodeProvisionLambdaNodejs extends JawsPlugin { registerActions() { - this.Jaws.addAction(this.CodeProvisionLambdaNodejs.bind(this), { + this.Jaws.addAction(this.codeProvisionLambdaNodejs.bind(this), { handler: 'codeProvisionLambdaNodejs', description: 'Deploys the code or endpoint of a function, or both' }); diff --git a/lib/defaults/actions/FunctionDeploy.js b/lib/defaults/actions/FunctionDeploy.js index 2e65b4b93..677ccb390 100644 --- a/lib/defaults/actions/FunctionDeploy.js +++ b/lib/defaults/actions/FunctionDeploy.js @@ -11,6 +11,7 @@ const JawsPlugin = require('../../JawsPlugin'), JawsUtils = require('../../utils/index'), extend = require('util')._extend, BbPromise = require('bluebird'), + async = require('async'), path = require('path'), fs = require('fs'), os = require('os'); @@ -138,6 +139,7 @@ class FunctionDeploy extends JawsPlugin { _this.evt.type ) .then(function(functions) { + // If no functions, throw error if (!_this.evt.functions.length) { throw new JawsError(`No function found. Make sure your current working directory is a function.`); @@ -236,14 +238,8 @@ class FunctionDeploy extends JawsPlugin { }) .each(function(region) { - // Clone evt object keeps us safe when we start doing concurrent operations - let evtClone = extend({}, _this.evt); - - // Add Region JSON for the deploy region - evtClone.deployRegion = JawsUtils.getProjRegionConfigForStage(_this.Jaws._projectJson, _this.evt.stage, region); - // Deploy Type - switch(evtClone.type) { + switch(_this.evt.type) { // Deploy Endpoint only case "endpoint": @@ -254,6 +250,40 @@ class FunctionDeploy extends JawsPlugin { // Deploy Code only (lambda) case "code": + + return new BbPromise(function(resolve, reject) { + + // Process Functions concurrently + async.eachLimit(_this.evt.functions, 5, function(func, cb) { + + // Clone evt object for concurrent operations + let evtClone = extend({}, _this.evt); + + // Add Region JSON for the deploy region + evtClone.deployRegion = JawsUtils.getProjRegionConfigForStage( + _this.Jaws._projectJson, + _this.evt.stage, + region + ); + + // Add Function to event clone + evtClone.currentFunction = func; + + return _this.Jaws.actions.codePackageLambdaNodejs(evtClone) + .bind(_this) + .then(_this.Jaws.actions.codeProvisionLambdaNodejs) + .then(function() { + return cb(); + }) + .catch(function(e) { + return reject(new JawsError(func + ' - ' + e.message)); + }); + + }, function() { + return resolve(_this.evt); + }); + }); + break; // Deploy Code then Endpoint diff --git a/lib/defaults/actions/LambdaDeploy.js b/lib/defaults/actions/LambdaDeploy.js index 0caa7147a..e5ee0f09b 100644 --- a/lib/defaults/actions/LambdaDeploy.js +++ b/lib/defaults/actions/LambdaDeploy.js @@ -226,327 +226,7 @@ class Deployer { } } -class Packager { - constructor(JAWS, stage, region, lambdaAwsmPath) { - this._JAWS = JAWS; - this._lambdaAwsmPath = lambdaAwsmPath; - this._stage = stage; - this._region = region; - this._awsmJson = JawsUtils.readAndParseJsonSync(this._lambdaAwsmPath); - this._distDir = ''; - } - /** - * - * @returns {Promise} {awsmFilePath: "", zipFilePath: "/tmp/path/to/blah.zip"} - */ - run() { - let _this = this; - - return this._createDistFolder() - .then(function() { - let runtime = _this._awsmJson.cloudFormation.lambda.Function.Properties.Runtime; - - switch (runtime) { - case 'nodejs': - return _this._packageNodeJs(runtime); - break; - default: - return Promise.reject(new JawsError(`Unsupported lambda runtime ${runtime}`, JawsError.errorCodes.UNKNOWN)); - break; - } - }); - } - - /** - * Create Dist Folder (for an individual lambda) - * - * @returns {Promise} - * @private - */ - _createDistFolder() { - - let _this = this; - - // Create dist folder - let d = new Date(); - _this._distDir = path.join(os.tmpdir(), this._awsmJson.name + '@' + d.getTime()); - - // Status - JawsCLI.log('Lambda Deployer: Packaging "' + this._awsmJson.name + '"...'); - JawsCLI.log('Lambda Deployer: Saving in dist dir ' + _this._distDir); - - JawsUtils.jawsDebug('copying', _this._JAWS._projectRootPath, 'to', _this._distDir); - - // Copy entire test project to temp folder - _this._excludePatterns = _this._awsmJson.package.excludePatterns || []; - wrench.copyDirSyncRecursive( - _this._JAWS._projectRootPath, - _this._distDir, - { - exclude: function(name, prefix) { - if (!_this._excludePatterns.length) { - return false; - } - - let relPath = path.join( - prefix.replace(_this._distDir, ''), name); - - return _this._excludePatterns.some(sRegex => { - relPath = (relPath.charAt(0) == path.sep) ? relPath.substr(1) : relPath; - - let re = new RegExp(sRegex), - matches = re.exec(relPath), - willExclude = (matches && matches.length > 0); - - if (willExclude) { - JawsCLI.log(`Lambda Deployer: Excluding ${relPath}`); - } - - return willExclude; - }); - }, - } - ); - - JawsUtils.jawsDebug('Packaging stage & region:', _this._stage, _this._region); - - // Get ENV file from S3 - return _this._JAWS.getEnvFile(_this._region, _this._stage) - .then(function(s3ObjData) { - fs.writeFileSync(path.join(_this._distDir, '.env'), s3ObjData.Body); - }); - } - - /** - * - * @returns {Promise} {awsmFilePath: "", zipFilePath: "/tmp/blah.zip"} - * @private - */ - _packageNodeJs() { - let _this = this, - deferred = false, - targetZipPath = path.join(this._distDir, 'package.zip'), - optimizeSettings = _this._awsmJson.package.optimize; - - if (optimizeSettings.builder) { - deferred = _this._optimizeNodeJs() - .then(optimizedCodeBuffer => { - let envData = fs.readFileSync(path.join(_this._distDir, '.env')), - handlerFileName = _this._awsmJson.cloudFormation.lambda.Function.Properties.Handler.split('.')[0], - compressPaths = [ - - // handlerFileName is the full path lambda file including dir rel to back - {fileName: handlerFileName + '.js', data: optimizedCodeBuffer}, - {fileName: '.env', data: envData}, - ]; - - compressPaths = compressPaths.concat(_this._generateIncludePaths()); - - return compressPaths; - }); - } else { - // User chose not to optimize, zip up whatever is in back - optimizeSettings.includePaths = ['.']; - let compressPaths = _this._generateIncludePaths(); - - deferred = Promise.resolve(compressPaths); - } - - return deferred - .then(compressPaths => { - return _this._compressCode(compressPaths, targetZipPath); - }) - .then(zipFilePath => { - return Promise.resolve({awsmFilePath: _this._lambdaAwsmPath, zipFilePath: zipFilePath}); - }); - } - - /** - * - * @returns {*} - * @private - */ - _optimizeNodeJs() { - - let _this = this; - - if (!_this._awsmJson.package.optimize - || !_this._awsmJson.package.optimize.builder) { - return Promise.reject(new JawsError('Cant optimize for nodejs. lambda jaws.json does not have optimize.builder set')); - } - - if (_this._awsmJson.package.optimize.builder.toLowerCase() == 'browserify') { - return _this._browserifyBundle(); - } else { - return Promise.reject(new JawsError(`Unsupported builder ${builder}`)); - } - } - - /** - * Browserify the code and return buffer of bundled code - * - * @returns {Promise.Buffer} - * @private - */ - _browserifyBundle() { - - let _this = this; - let uglyOptions = { - mangle: true, // @see http://lisperator.net/uglifyjs/compress - compress: {}, - }; - let b = browserify({ - basedir: _this._distDir, - entries: [_this._awsmJson.cloudFormation.lambda.Function.Properties.Handler.split('.')[0] + '.js'], - standalone: 'lambda', - browserField: false, // Setup for node app (copy logic of --node in bin/args.js) - builtins: false, - commondir: false, - ignoreMissing: true, // Do not fail on missing optional dependencies - detectGlobals: true, // Default for bare in cli is true, but we don't care if its slower - insertGlobalVars: { // Handle process https://github.com/substack/node-browserify/issues/1277 - //__filename: insertGlobals.lets.__filename, - //__dirname: insertGlobals.lets.__dirname, - process: function() { - }, - }, - }); - - if (_this._awsmJson.package.optimize.babel) { - b.transform(babelify); - } - - if (_this._awsmJson.package.optimize.transform) { - JawsUtils.jawsDebug('Adding transform', _this._awsmJson.package.optimize.transform); - b.transform(_this._awsmJson.package.optimize.transform); - } - - // optimize.exclude - _this._awsmJson.package.optimize.exclude.forEach(file => { - JawsUtils.jawsDebug('EXCLUDING', file); - b.exclude(file); - }); - - // optimize.ignore - _this._awsmJson.package.optimize.ignore.forEach(file => { - JawsUtils.jawsDebug('IGNORING', file); - b.ignore(file); - }); - - // Perform Bundle - let bundledFilePath = path.join(_this._distDir, 'bundled.js'); // Save for auditing - let minifiedFilePath = path.join(_this._distDir, 'minified.js'); // Save for auditing - - return new Promise(function(resolve, reject) { - b.bundle(function(err, bundledBuf) { - if (err) { - console.error('Error running browserify bundle'); - reject(err); - } else { - fs.writeFileSync(bundledFilePath, bundledBuf); - JawsCLI.log(`Lambda Deployer: Bundled file written to ${bundledFilePath}`); - - if (_this._awsmJson.package.optimize.minify) { - JawsUtils.jawsDebug('Minifying...'); - let result = UglifyJS.minify(bundledFilePath, uglyOptions); - - if (!result || !result.code) { - reject(new JawsError('Problem uglifying code')); - } - - fs.writeFileSync(minifiedFilePath, result.code); - - JawsCLI.log(`Lambda Deployer: Minified file written to ${minifiedFilePath}`); - resolve(result.code); - } else { - resolve(bundledBuf); - } - } - }); - }); - } - - /** - * - * @returns {Array} - * @private - */ - _generateIncludePaths() { - let _this = this, - compressPaths = [], - ignore = ['.DS_Store'], - stats, - fullPath; - - _this._awsmJson.package.optimize.includePaths.forEach(p => { - try { - fullPath = path.resolve(path.join(_this._distDir, p)); - stats = fs.lstatSync(fullPath); - } catch (e) { - console.error('Cant find includePath ', p, e); - throw e; - } - - if (stats.isFile()) { - JawsUtils.jawsDebug('INCLUDING', fullPath); - compressPaths.push({fileName: p, data: fs.readFileSync(fullPath)}); - } else if (stats.isDirectory()) { - let dirname = path.basename(p); - - wrench - .readdirSyncRecursive(fullPath) - .forEach(file => { - // Ignore certain files - for (let i = 0; i < ignore.length; i++) { - if (file.toLowerCase().indexOf(ignore[i]) > -1) return; - } - - let filePath = [fullPath, file].join('/'); - if (fs.lstatSync(filePath).isFile()) { - let pathInZip = path.join(dirname, file); - JawsUtils.jawsDebug('INCLUDING', pathInZip); - compressPaths.push({fileName: pathInZip, data: fs.readFileSync(filePath)}); - } - }); - } - }); - - return compressPaths; - } - - /** - * - * @param compressPaths - * @param targetZipPath string - * @returns {Promise.string} targetZipPath - * @private - */ - _compressCode(compressPaths, targetZipPath) { - let zip = new Zip(); - - compressPaths.forEach(nc => { - zip.file(nc.fileName, nc.data); - }); - - let zipBuffer = zip.generate({ - type: 'nodebuffer', - compression: 'DEFLATE', - }); - - if (zipBuffer.length > 52428800) { - Promise.reject(new JawsError( - 'Zip file is > the 50MB Lambda deploy limit (' + zipBuffer.length + ' bytes)', - JawsError.errorCodes.ZIP_TOO_BIG) - ); - } - - fs.writeFileSync(targetZipPath, zipBuffer); - JawsCLI.log(`Lambda Deployer: Compressed code written to ${targetZipPath}`); - - return Promise.resolve(targetZipPath); - } -} class DeployLambda extends JawsPlugin { @@ -570,6 +250,7 @@ class DeployLambda extends JawsPlugin { * * @returns {string} */ + static getName() { return 'jaws.core.' + DeployLambda.name; } diff --git a/lib/utils/aws/S3.js b/lib/utils/aws/S3.js index c86612212..76bad89eb 100644 --- a/lib/utils/aws/S3.js +++ b/lib/utils/aws/S3.js @@ -17,8 +17,8 @@ let BbPromise = require('bluebird'), // Promisify fs module. This adds "Async" to the end of every method BbPromise.promisifyAll(fs); - module.exports = function(config) { + // Promisify and configure instance const S3 = BbPromise.promisifyAll(new AWS.S3(config)); @@ -48,6 +48,7 @@ module.exports = function(config) { /** * Get the env file for a given stage */ + S3.sGetEnvFile = function(bucketName, projectName, stage) { let key = ['JAWS', projectName, stage, 'envVars', '.env'].join('/'), params = { @@ -56,7 +57,7 @@ module.exports = function(config) { }; JawsUtils.jawsDebug(`env get s3 bucket: ${bucketName} key: ${key}`); - return S3.getObjectAsync(params); + return S3.getObjectAsync(params) }; /** @@ -94,6 +95,7 @@ module.exports = function(config) { return key; }); }; + // Return configured, customized instance return S3; diff --git a/package.json b/package.json index 68260fbbe..49cdaf6e5 100644 --- a/package.json +++ b/package.json @@ -47,14 +47,12 @@ "browserify": "^12.0.1", "chalk": "^1.1.0", "cli-spinner": "^0.2.1", - "commander": "^2.5.0", "commop": "^1.2.0", "debug": "^2.2.0", "dotenv": "^1.2.0", "download": "^4.2.0", "expand-home-dir": "0.0.2", "insert-module-globals": "^6.5.2", - "jaws-api-gateway-client": "0.11.0", "keypress": "^0.2.1", "minimist": "^1.2.0", "mkdirp-then": "^1.1.0", diff --git a/tests/test-prj/aws_modules/bundle/browserify/lambda.awsm.json b/tests/test-prj/aws_modules/bundle/browserify/lambda.awsm.json index 0c575c31d..2e5ca7768 100644 --- a/tests/test-prj/aws_modules/bundle/browserify/lambda.awsm.json +++ b/tests/test-prj/aws_modules/bundle/browserify/lambda.awsm.json @@ -18,7 +18,8 @@ "lambda": { "Type": "AWS::Lambda::Function", "Properties": { - "Handler": "", + "Runtime": "nodejs", + "Handler": "aws_modules/bundle/browserify/handler.handler" "Role": { "Ref": "aaLambdaRoleArn" }, @@ -26,12 +27,9 @@ "S3Bucket": "", "S3Key": "" }, - "Runtime": "nodejs", "Timeout": 6, "MemorySize": 1024 - }, - "Runtime": "nodejs", - "Handler": "aws_modules/bundle/browserify/handler.handler" + } }, "apiGatewayEndpoint": { "Type": "AWS::ApiGateway::Endpoint", diff --git a/tests/test-prj/aws_modules/bundle/nonoptimized/lambda.awsm.json b/tests/test-prj/aws_modules/bundle/nonoptimized/lambda.awsm.json index aa76f14a4..1cc71d2b6 100644 --- a/tests/test-prj/aws_modules/bundle/nonoptimized/lambda.awsm.json +++ b/tests/test-prj/aws_modules/bundle/nonoptimized/lambda.awsm.json @@ -18,7 +18,8 @@ "lambda": { "Type": "AWS::Lambda::Function", "Properties": { - "Handler": "", + "Runtime": "nodejs", + "Handler": "aws_modules/bundle/nonoptimized/handler.handler", "Role": { "Ref": "aaLambdaRoleArn" }, @@ -26,12 +27,9 @@ "S3Bucket": "", "S3Key": "" }, - "Runtime": "nodejs", "Timeout": 6, "MemorySize": 1024 - }, - "Runtime": "nodejs", - "Handler": "aws_modules/bundle/nonoptimized/handler.handler" + } }, "apiGatewayEndpoint": { "Type": "AWS::ApiGateway::Endpoint", diff --git a/tests/test-prj/aws_modules/sessions/create/lambda.awsm.json b/tests/test-prj/aws_modules/sessions/create/lambda.awsm.json index 67e72087f..4d880b133 100644 --- a/tests/test-prj/aws_modules/sessions/create/lambda.awsm.json +++ b/tests/test-prj/aws_modules/sessions/create/lambda.awsm.json @@ -18,7 +18,8 @@ "lambda": { "Type": "AWS::Lambda::Function", "Properties": { - "Handler": "", + "Runtime": "nodejs", + "Handler": "aws_modules/sessions/create/index.handler", "Role": { "Ref": "aaLambdaRoleArn" }, @@ -26,12 +27,9 @@ "S3Bucket": "", "S3Key": "" }, - "Runtime": "nodejs", "Timeout": 6, "MemorySize": 1024 }, - "Runtime": "nodejs", - "Handler": "aws_modules/sessions/create/index.handler" }, "apiGatewayEndpoint": { "Type": "AWS::ApiGateway::Endpoint", diff --git a/tests/test-prj/aws_modules/sessions/show/lambda.awsm.json b/tests/test-prj/aws_modules/sessions/show/lambda.awsm.json index 0d906cd8c..6460fa31a 100644 --- a/tests/test-prj/aws_modules/sessions/show/lambda.awsm.json +++ b/tests/test-prj/aws_modules/sessions/show/lambda.awsm.json @@ -18,7 +18,8 @@ "lambda": { "Type": "AWS::Lambda::Function", "Properties": { - "Handler": "", + "Runtime": "nodejs", + "Handler": "aws_modules/sessions/show/index.handler", "Role": { "Ref": "aaLambdaRoleArn" }, @@ -26,12 +27,9 @@ "S3Bucket": "", "S3Key": "" }, - "Runtime": "nodejs", "Timeout": 6, "MemorySize": 1024 - }, - "Runtime": "nodejs", - "Handler": "aws_modules/sessions/show/index.handler" + } }, "apiGateway": { "Endpoint": { diff --git a/tests/test-prj/aws_modules/users/create/lambda.awsm.json b/tests/test-prj/aws_modules/users/create/lambda.awsm.json index 074b84dfd..4bc797cdb 100644 --- a/tests/test-prj/aws_modules/users/create/lambda.awsm.json +++ b/tests/test-prj/aws_modules/users/create/lambda.awsm.json @@ -19,7 +19,8 @@ "lambda": { "Type": "AWS::Lambda::Function", "Properties": { - "Handler": "", + "Runtime": "nodejs", + "Handler": "aws_modules/users/create/index.handler", "Role": { "Ref": "aaLambdaRoleArn" }, @@ -27,12 +28,9 @@ "S3Bucket": "", "S3Key": "" }, - "Runtime": "nodejs", "Timeout": 6, "MemorySize": 1024 - }, - "Runtime": "nodejs", - "Handler": "aws_modules/users/create/index.handler" + } }, "apiGateway": { diff --git a/tests/tests/actions/FunctionDeploy.js b/tests/tests/actions/FunctionDeploy.js index 95b0b8275..41bcc2cc4 100644 --- a/tests/tests/actions/FunctionDeploy.js +++ b/tests/tests/actions/FunctionDeploy.js @@ -13,16 +13,19 @@ let JAWS = require('../../../lib/Jaws.js'), let Jaws; -describe('Test action: Function Deploy', function() { +describe('Test Action: Function Deploy', function() { before(function(done) { this.timeout(0); + testUtils.createTestProject(config) .then(projPath => { process.chdir(projPath); Jaws = new JAWS({ interactive: false, + awsAdminKeyId: config.awsAdminKeyId, + awsAdminSecretKey: config.awsAdminSecretKey }); done(); @@ -33,16 +36,16 @@ describe('Test action: Function Deploy', function() { done(); }); - describe('Function Deploy Endpoint positive tests', function() { + describe('Function Deploy Code Lambda Nodejs', function() { + it('Function Deploy Code Lambda Nodejs', function(done) { - it('Function Deploy Endpoint', function(done) { this.timeout(0); let event = { stage: config.stage, region: config.region, noExeCf: config.noExecuteCf, - type: 'endpoint', + type: 'code', functions: ['aws_modules/users/create'], }; @@ -55,4 +58,27 @@ describe('Test action: Function Deploy', function() { }); }); }); + + //describe('Function Deploy: Endpoint: ApiGateway', function() { + // + // it('Function Deploy Endpoint', function(done) { + // this.timeout(0); + // + // let event = { + // stage: config.stage, + // region: config.region, + // noExeCf: config.noExecuteCf, + // type: 'endpoint', + // functions: ['aws_modules/users/create'], + // }; + // + // Jaws.actions.functionDeploy(event) + // .then(function() { + // done(); + // }) + // .catch(e => { + // done(e); + // }); + // }); + //}); });