From 14187c212d09ea03cd28df12182b4df2b21f7f2e Mon Sep 17 00:00:00 2001 From: doapp-ryanp Date: Tue, 29 Sep 2015 23:39:57 -0500 Subject: [PATCH] postinstall new features. Module create re-factor --- bin/jaws | 7 ++- lib/commands/dash.js | 7 ++- lib/commands/env.js | 2 +- lib/commands/module_create.js | 109 ++++++++++++++++++++-------------- lib/commands/postinstall.js | 26 +++++--- lib/utils/index.js | 41 +++++++++++-- 6 files changed, 128 insertions(+), 64 deletions(-) diff --git a/bin/jaws b/bin/jaws index ea669ac13..7f12799bd 100755 --- a/bin/jaws +++ b/bin/jaws @@ -88,7 +88,8 @@ program ) .option('-l, --lambda', '[create]: create lambda. Default is create lambda and endpoint.') .option('-e, --endpoint', '[create]: create API Gateway endpoint. Default is create lambda and endpoint.') - .option('-p, --package-manager', '[create]: creates pkg manager scaffolding along with your aws_module. Valid: npm') + .option('-r, --runtime', '[create]: lambda runtime. Valid: nodejs') + .option('-p, --package-manager', '[create]: override default pkg manager used when auto-creating scaffolding. Valid: TODO. Defaults: nodejs->npm, ') .action(function(cmd, params, options) { var theParams = process.argv.slice(3); @@ -109,7 +110,7 @@ program name = theParams[1], action = theParams[2], runtime = options.runtime || 'nodejs', - createPkgMgrScaffold = options.packageManager || false, + pkgMgrOverride = options.packageManager || false, modType = 'both'; if (options.lambda) { @@ -118,7 +119,7 @@ program modType = 'endpoint'; } - execute(theCmd.run(JAWS, name, action, runtime, createPkgMgrScaffold, modType)); + execute(theCmd.run(JAWS, name, action, runtime, pkgMgrOverride, modType)); } else { console.error('Unsupported cmd ' + cmd + '. Must be install|update|create'); process.exit(1); diff --git a/lib/commands/dash.js b/lib/commands/dash.js index a081f146e..b07a36d7d 100644 --- a/lib/commands/dash.js +++ b/lib/commands/dash.js @@ -64,7 +64,8 @@ CMD.prototype.run = Promise.method(function() { // If !allTagged, Show Dashboard if (!_this._allTagged) { - return Promise.try(function() {}) + return Promise.try(function() { + }) .bind(_this) .then(_this._prepareResources) .then(_this._prepareSummary) @@ -140,7 +141,7 @@ CMD.prototype.run = Promise.method(function() { CMD.prototype._prepareResources = Promise.method(function() { var _this = this; - return utils.findAllAwsmJsons(_this._JAWS._meta.projectRootPath) + return utils.findAllAwsmJsons(path.join(_this._JAWS._meta.projectRootPath, 'aws_modules')) .then(function(jsonPaths) { var hybrids = []; @@ -176,7 +177,7 @@ CMD.prototype._prepareResources = Promise.method(function() { value: jsonPaths[i], type: 'endpoint', label: '/' + json.apiGateway.cloudFormation.Path - + ' - ' + json.apiGateway.cloudFormation.Method, + + ' - ' + json.apiGateway.cloudFormation.Method, }; // Create path diff --git a/lib/commands/env.js b/lib/commands/env.js index 8ec31f1c9..3dfdbd35d 100644 --- a/lib/commands/env.js +++ b/lib/commands/env.js @@ -86,7 +86,7 @@ module.exports.listEnv = function(JAWS, stage, region, showWhereUsed) { var _this = this, projRootDir = JAWS._meta.projectRootPath; - return utils.findAllAwsmJsons(projRootDir) + return utils.findAllAwsmJsons(path.join(_this._JAWS._meta.projectRootPath, 'aws_modules')) .then(function(awsmJsonPaths) { return [awsmJsonPaths, _this._getEnvFiles(JAWS, stage, region)]; }) diff --git a/lib/commands/module_create.js b/lib/commands/module_create.js index 569059e8a..2152483cf 100644 --- a/lib/commands/module_create.js +++ b/lib/commands/module_create.js @@ -12,6 +12,13 @@ var JawsError = require('../jaws-error'), path = require('path'), utils = require('../utils'); +var supportedRuntimes = { + nodejs: { + defaultPkgMgr: 'npm', + validPkgMgrs: ['npm'] + } +}; + Promise.promisifyAll(fs); /** @@ -20,22 +27,43 @@ Promise.promisifyAll(fs); * @param name * @param action * @param runtime 'nodejs' default - * @param createPkgMgrScaffold if not false, will create pkg mgr scaffolding. Valid: 'npm' etc + * @param pkgMgrOverride override the default pkg manager for runtime with this one * @param moduleType 'lambda','endpoint','both' * @returns {*} */ -module.exports.run = function(JAWS, name, action, runtime, createPkgMgrScaffold, moduleType) { - var command = new CMD(JAWS, name, action, runtime || 'nodejs', createPkgMgrScaffold || false, moduleType); +module.exports.run = function(JAWS, name, action, runtime, pkgMgrOverride, moduleType) { + var command = new CMD(JAWS, name, action, runtime, pkgMgrOverride, moduleType); return command.run(); }; -function CMD(JAWS, name, action, runtime, createPkgMgrScaffold, modType) { +/** + * + * @param JAWS + * @param name + * @param action + * @param runtime + * @param pkgMgrOverride + * @param modType + * @constructor + */ +function CMD(JAWS, name, action, runtime, pkgMgrOverride, modType) { + if (!runtime) { + runtime = 'nodejs'; + } + + if (!supportedRuntimes[runtime]) { + throw new JawsError('Unsupported runtime "' + runtime + '"', JawsError.errorCodes.UNKNOWN); + } + + var _this = this, + supportedRuntimeObj = supportedRuntimes[runtime]; + this._JAWS = JAWS; - this._module = { + this._moduleName = { name: name, runtime: runtime, action: action, - createPkgMgrScaffold: createPkgMgrScaffold, + pkgMgr: pkgMgrOverride || supportedRuntimeObj.defaultPkgMgr, type: modType, }; this._prompts = { @@ -43,6 +71,10 @@ function CMD(JAWS, name, action, runtime, createPkgMgrScaffold, modType) { }; this.Prompter = JawsCLI.prompt(); this.Prompter.override = {}; + + if (supportedRuntimeObj.validPkgMgrs.indexOf(_this._moduleName.pkgMgr) == -1) { + throw new JawsError('Unsupported package manger "' + _this._moduleName.pkgMgr + '"', JawsError.errorCodes.UNKNOWN); + } } CMD.prototype.constructor = CMD; @@ -52,19 +84,18 @@ CMD.prototype.constructor = CMD; */ CMD.prototype.run = Promise.method(function() { - var _this = this; return _this._JAWS.validateProject() .bind(_this) .then(_this._sanitizeData) .then(_this._createSkeleton) - .then(_this._createPackageMgrSkeleton()) + .then(_this._createPackageMgrSkeleton) .then(function() { JawsCLI.log('Successfully created ' - + _this._module.name + + _this._moduleName.name + '/' - + _this._module.action); + + _this._moduleName.action); }); }); @@ -74,31 +105,28 @@ CMD.prototype.run = Promise.method(function() { * @returns {Promise} */ CMD.prototype._createPackageMgrSkeleton = function() { - if (!this._module.createPkgMgrScaffold) { - return Promise.resolve(); - } - var _this = this, - deferredWrites = []; + deferredWrites = [], + modulePath = path.join( + _this._JAWS._meta.projectRootPath, + 'aws_modules', + _this._moduleName.name); - switch (_this._module.runtime) { + switch (_this._moduleName.runtime) { case 'nodejs': - if (_this._module.createPkgMgrScaffold == 'npm') { - var packageJsonTemplate = utils.readAndParseJsonSync(path.join(__dirname, '../templates/nodejs/package.json')); + if (_this._moduleName.pkgMgr == 'npm') { + var packageJsonTemplate = utils.readAndParseJsonSync(path.join(__dirname, '..', 'templates', 'nodejs', 'package.json')); packageJsonTemplate.name = _this._name; packageJsonTemplate.dependencies = {}; deferredWrites.push( fs.writeFileAsync( - path.join(_this._JAWS._meta.projectRootPath, 'package.json'), + path.join(modulePath, 'package.json'), JSON.stringify(packageJsonTemplate, null, 2) ) ); - } else { - return Promise.reject(new JawsError('Unsupported package manager', JawsError.errorCodes.UNKNOWN)); } break; default: - throw new JawsError('Unsupported runtime "' + _this._module.runtime + '"', JawsError.errorCodes.UNKNOWN); break; } @@ -113,12 +141,12 @@ CMD.prototype._sanitizeData = Promise.method(function() { var _this = this; - _this._module.name = _this._module.name.toLowerCase().trim() + _this._moduleName.name = _this._moduleName.name.toLowerCase().trim() .replace(/\s/g, '-') .replace(/[^a-zA-Z-\d:]/g, '') .substring(0, 19); - _this._module.action = _this._module.action.toLowerCase().trim() + _this._moduleName.action = _this._moduleName.action.toLowerCase().trim() .replace(/\s/g, '-') .replace(/[^a-zA-Z-\d:]/g, '') .substring(0, 19); @@ -140,8 +168,8 @@ CMD.prototype._createSkeleton = Promise.method(function() { var modulePath = path.join( _this._JAWS._meta.projectRootPath, 'aws_modules', - _this._module.name); - var actionPath = path.join(modulePath, _this._module.action); + _this._moduleName.name); + var actionPath = path.join(modulePath, _this._moduleName.action); // If module/action already exists, throw error if (utils.dirExistsSync(actionPath)) { @@ -156,7 +184,7 @@ CMD.prototype._createSkeleton = Promise.method(function() { // If module awsm.json doesn't exist, create it if (!utils.fileExistsSync(path.join(modulePath, 'awsm.json'))) { var moduleTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'module.awsm.json')); - moduleTemplateJson.name = _this._module.name; + moduleTemplateJson.name = _this._moduleName.name; writeFilesDeferred.push( utils.writeFile( path.join(modulePath, 'awsm.json'), @@ -167,25 +195,14 @@ CMD.prototype._createSkeleton = Promise.method(function() { writeFilesDeferred.push(actionPath); // Create action awsm.json - actionTemplateJson.apiGateway.cloudFormation.Path = _this._module.name + '/' + _this._module.action; + actionTemplateJson.apiGateway.cloudFormation.Path = _this._moduleName.name + '/' + _this._moduleName.action; actionTemplateJson.apiGateway.cloudFormation.Method = 'GET'; actionTemplateJson.apiGateway.cloudFormation.Type = 'AWS'; - if (['lambda', 'both'].indexOf(_this._module.type) != -1) { + if (['lambda', 'both'].indexOf(_this._moduleName.type) != -1) { // Create files for lambda actions - switch (_this._module.runtime) { + switch (_this._moduleName.runtime) { case 'nodejs': - // If module package.json doesnt exist, create it - var pkgJsonPath = path.join(modulePath, 'package.json'); - if (!utils.fileExistsSync(pkgJsonPath)) { - var packageTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'nodejs', 'package.json')); - packageTemplateJson.name = _this._module.name; - packageTemplateJson.private = true; - writeFilesDeferred.push( - utils.writeFile(pkgJsonPath, JSON.stringify(packageTemplateJson, null, 2)) - ); - } - var modLibPath = path.join(modulePath, 'lib'); if (!utils.dirExistsSync(modLibPath)) { writeFilesDeferred.push(fs.mkdirAsync(modLibPath)); @@ -195,8 +212,8 @@ CMD.prototype._createSkeleton = Promise.method(function() { actionTemplateJson.lambda.cloudFormation.Runtime = 'nodejs'; actionTemplateJson.lambda.cloudFormation.Handler = path.join( 'aws_modules', - _this._module.name, - _this._module.action, + _this._moduleName.name, + _this._moduleName.action, 'handler.handler'); // Create handler.js, index.js, event.json, package.json @@ -210,17 +227,17 @@ CMD.prototype._createSkeleton = Promise.method(function() { ); break; default: - throw new JawsError('This runtime is not supported "' + _this._module.runtime + '"', JawsError.errorCodes.UNKNOWN); + throw new JawsError('This runtime is not supported "' + _this._moduleName.runtime + '"', JawsError.errorCodes.UNKNOWN); break; } } // Trim unnecessary JSON - if (_this._module.type === 'lambda') { + if (_this._moduleName.type === 'lambda') { delete actionTemplateJson.apiGateway; } - if (_this._module.type === 'endpoint') { + if (_this._moduleName.type === 'endpoint') { delete actionTemplateJson.lambda; } diff --git a/lib/commands/postinstall.js b/lib/commands/postinstall.js index 901924da6..00fc377e4 100644 --- a/lib/commands/postinstall.js +++ b/lib/commands/postinstall.js @@ -30,9 +30,9 @@ function CMD(JAWS, moduleName, packageManager) { return Promise.reject(new JawsError('Unsupported package manager', JawsError.errorCodes.UNKNOWN)); } this._JAWS = JAWS; - this._module = moduleName; + this._moduleName = moduleName; this._packageManager = packageManager; - this._rootAwsmJson; + this._rootAwsmJson = {}; } CMD.prototype.constructor = CMD; @@ -48,8 +48,6 @@ CMD.prototype.run = Promise.method(function() { return Promise.all([module, _this._saveCfTemplate(module.path)]); }) .spread(function(module) { - JawsCLI.log('Successfully installed ' + module.name); - var deferredDepInstalls = []; switch (_this._packageManager) { @@ -68,6 +66,16 @@ CMD.prototype.run = Promise.method(function() { } return Promise.all(deferredDepInstalls); + }) + .then(function() { + return utils.findAllEnvVarsForAwsm(_this._JAWS._meta.projectRootPath, _this._moduleName); + }) + .then(function(envVars) { + JawsCLI.log('Successfully installed ' + _this._moduleName); + + if (envVars && envVars.length > 1) { + JawsCLI.log('This aws module uses env vars MAKE SURE to run jaws env list to see which ones need to be set'); + } }); }); @@ -86,8 +94,8 @@ CMD.prototype._installFiles = Promise.method(function() { pkgMgrDir = 'node_modules'; } - var srcAwsmPath = path.join(_this._JAWS._meta.projectRootPath, pkgMgrDir, _this._module, 'awsm'), - srcAwsmJsonPath = path.join(_this._JAWS._meta.projectRootPath, pkgMgrDir, _this._module, 'awsm.json'), + var srcAwsmPath = path.join(_this._JAWS._meta.projectRootPath, pkgMgrDir, _this._moduleName, 'awsm'), + srcAwsmJsonPath = path.join(_this._JAWS._meta.projectRootPath, pkgMgrDir, _this._moduleName, 'awsm.json'), awsModsPath = path.join(_this._JAWS._meta.projectRootPath, 'aws_modules'); if (!utils.fileExistsSync(srcAwsmJsonPath)) { @@ -124,7 +132,11 @@ CMD.prototype._installFiles = Promise.method(function() { excludeHiddenUnix: false, }); - return {name: awsmJson.name, path: targetModPath}; + //Write mod root awsm.json so we can identify awsm dirs later + return utils.writeFile(path.join(targetModPath, 'awsm.json'), JSON.stringify(awsmJson, null, 2)) + .then(function() { + return {name: awsmJson.name, path: targetModPath}; + }); }); /** diff --git a/lib/utils/index.js b/lib/utils/index.js index e9a0e0977..aa3a76951 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -18,10 +18,10 @@ Promise.promisifyAll(fs); /** * Find all awsm paths of given type * - * @param projectRootPath + * @param startPath * @param type lambda|endpoint */ -exports.findAllAwsmPathsOfType = function(projectRootPath, type) { +exports.findAllAwsmPathsOfType = function(startPath, type) { var _this = this, jawsJsonAttr; switch (type) { @@ -36,7 +36,7 @@ exports.findAllAwsmPathsOfType = function(projectRootPath, type) { break; } - return _this.readRecursively(projectRootPath, '*awsm.json') + return _this.readRecursively(startPath, '*awsm.json') .then(function(jsonPaths) { return new Promise(function(resolve, reject) { @@ -44,7 +44,7 @@ exports.findAllAwsmPathsOfType = function(projectRootPath, type) { // Check each file to ensure it is a lambda async.eachLimit(jsonPaths, 10, function(jsonPath, cb) { - var lambdaJawsPath = path.join(projectRootPath, jsonPath), + var lambdaJawsPath = path.join(startPath, jsonPath), json = require(lambdaJawsPath); if (typeof json[jawsJsonAttr] !== 'undefined') jawsPathsOfType.push(lambdaJawsPath); @@ -160,6 +160,39 @@ exports.findAllEndpoints = function(projectRootPath) { return this.findAllAwsmPathsOfType(projectRootPath, 'endpoint'); }; +/** + * Finds all env var keys that are used by all lambdas in a given aws module + * + * @param projectRootPath + * @param modName + * @returns {Promise} list of ENV var key strings + */ +exports.findAllEnvVarsForAwsm = function(projectRootPath, modName) { + var _this = this; + + console.log(projectRootPath, 'aws_modules', modName); + + return this.findAllAwsmPathsOfType(path.join(projectRootPath, 'aws_modules', modName), 'lambda') + .then(function(awsmJsonPaths) { + var envVarKeys = []; + + awsmJsonPaths.forEach(function(awsmJsonPath) { + var awsmJson = _this.readAndParseJsonSync(awsmJsonPath); + + //TODO: change to es6 set... + if (awsmJson.lambda && awsmJson.lambda.envVars) { + awsmJson.lambda.envVars.forEach(function(envVar) { + if (envVarKeys.indexOf(envVar) == -1) { + envVarKeys.push(envVar); + } + }); + } + }); + + return envVarKeys; + }); +}; + /** * Find all awsm json paths underneath given dir *