diff --git a/lib/commands/deploy_lambda.js b/lib/commands/deploy_lambda.js index cd3d1f048..57b12757c 100644 --- a/lib/commands/deploy_lambda.js +++ b/lib/commands/deploy_lambda.js @@ -67,13 +67,13 @@ function CMD(JAWS, stage, region, allTagged, noExeCf) { /** * CMD: Run + * + * @returns {Promise} map of lambdaAwsmPaths to {Code:{},lambdaName:""} */ CMD.prototype.run = Promise.method(function() { - var _this = this; - // Flow return _this._JAWS.validateProject() .bind(_this) .then(_this._promptStage) @@ -85,8 +85,9 @@ CMD.prototype.run = Promise.method(function() { .each(function(region) { var deployer = new Deployer(_this._JAWS, _this._lambdaAwsmPaths, _this._stage, region, _this._noExeCf); return deployer.deploy(); - }).then(function() { + }).then(function(lambdaAwsmPkgs) { JawsCLI.log('Lambda Deployer: Successfully deployed lambdas to the requested regions!'); + return lambdaAwsmPkgs; }); }); @@ -99,19 +100,16 @@ CMD.prototype._promptStage = Promise.method(function() { var _this = this; // If stage exists, skip - if (_this._stage) return; + if (_this._stage) { + return Promise.resolve(); + } var stages = Object.keys(_this._JAWS._meta.projectJson.stages); - // Check if project has stages - if (!stages.length) { - throw new JawsError('This project has no stages'); - } - // If project only has 1 stage, skip prompt if (stages.length === 1) { _this._stage = stages[0]; - return; + return Promise.resolve(); } // Create Choices @@ -189,7 +187,7 @@ function Deployer(JAWS, lambdaPaths, stage, region, noExeCf) { /** * Deploy lambdas * - * @returns {Promise} list of full lambda awsm.json paths that were deployed + * @returns {Promise} map of lambdaAwsmPaths to {Code:{},lambdaName:""} */ Deployer.prototype.deploy = Promise.method(function() { @@ -198,7 +196,7 @@ Deployer.prototype.deploy = Promise.method(function() { awsmLambdas = {}; return Promise.try(function() { - }) + }) .bind(_this) .then(function() { return _this._lambdaAwsmPaths; @@ -218,14 +216,14 @@ Deployer.prototype.deploy = Promise.method(function() { JawsCLI.log("Lambda Deployer: uploading " + packagedLambda.lambdaName); return AWSUtils.putLambdaZip( - _this._JAWS._meta._profile, - _this._region, - jawsBucket, - projName, - _this._stage, - packagedLambda.lambdaName, - packagedLambda.zipBuffer - ) + _this._JAWS._meta._profile, + _this._region, + jawsBucket, + projName, + _this._stage, + packagedLambda.lambdaName, + packagedLambda.zipBuffer + ) .then(function(s3Key) { awsmLambdas[lambdaAwsmPath] = { Code: { @@ -248,9 +246,9 @@ Deployer.prototype.deploy = Promise.method(function() { var lambdaRoleArn = utils.getProjRegionConfigForStage(_this._JAWS._meta.projectJson, _this._stage, _this._region); if (existingStack) { - return AWSUtils.cfUpdateLambdasStack(_this._stage, _this._region); + return AWSUtils.cfUpdateLambdasStack(_this._JAWS,_this._stage, _this._region); } else { - return AWSUtils.cfCreateLambdasStack(_this._stage, _this._region); + return AWSUtils.cfCreateLambdasStack(_this._JAWS,_this._stage, _this._region); } } }) @@ -280,8 +278,8 @@ Deployer.prototype._generateLambdaCf = function(taggedLambdaPkgs) { existingStack = true; return AWSUtils.cfGetLambdasStackTemplate( - _this.JAWS._meta.profile, _this._region, _this._stage, _this._JAWS._meta.projectJson.name - ) + _this.JAWS._meta.profile, _this._region, _this._stage, _this._JAWS._meta.projectJson.name + ) .error(function(e) { if (e && e.code !== 'ResourceNotFoundException') { console.error( diff --git a/lib/commands/new_action.js b/lib/commands/new_action.js index 0bd0c35d9..382756d6a 100644 --- a/lib/commands/new_action.js +++ b/lib/commands/new_action.js @@ -98,7 +98,7 @@ CMD.prototype._createSkeleton = Promise.method(function() { var actionPath = path.join( _this._JAWS._meta.projectRootPath, 'back', - 'lambdas', + 'aws_modules', _this._action.resource, _this._action.action); @@ -124,7 +124,7 @@ CMD.prototype._createSkeleton = Promise.method(function() { // Edit jaws.json actionJson.lambda.cloudFormation.Runtime = 'nodejs'; actionJson.lambda.cloudFormation.Handler = path.join( - 'lambdas', + 'aws_modules', _this._action.resource, _this._action.action, 'index.handler'); diff --git a/lib/commands/new_project.js b/lib/commands/new_project.js index 40ce285a1..d99630f1e 100644 --- a/lib/commands/new_project.js +++ b/lib/commands/new_project.js @@ -83,10 +83,10 @@ CMD.prototype.run = Promise.method(function() { return Promise.try(function() { - // ASCII Greeting - JawsCLI.ascii(); + // ASCII Greeting + JawsCLI.ascii(); - }) + }) .bind(_this) .then(_this._prompt) .then(_this._prepareProjectData) @@ -325,11 +325,11 @@ CMD.prototype._createProjectDirectory = Promise.method(function() { // Create Project Scaffolding return utils.writeFile( - path.join(_this._projectRootPath, 'back', '.env'), - 'JAWS_STAGE=' + _this._stage - + '\nJAWS_DATA_MODEL_PREFIX=' + _this._stage - + '\nJAWS_REGION=' + _this._region - ) + path.join(_this._projectRootPath, 'back', '.env'), + 'JAWS_STAGE=' + _this._stage + + '\nJAWS_DATA_MODEL_PREFIX=' + _this._stage + + '\nJAWS_REGION=' + _this._region + ) .then(function() { return Promise.all([ fs.mkdirAsync(path.join(_this._projectRootPath, 'front')), @@ -389,12 +389,13 @@ CMD.prototype._createCfStack = Promise.method(function() { // Create CF stack return AWSUtils.cfCreateResourcesStack( - _this._profile, - _this._region, - _this._projectRootPath, - _this._name, - _this._stage, - _this._notificationEmail) + _this._profile, + _this._region, + _this._projectRootPath, + _this._name, + _this._stage, + _this._s3Bucket, + _this._notificationEmail) .then(function(cfData) { return AWSUtils.monitorCfCreate(cfData, _this._profile, _this._region); }); diff --git a/lib/commands/new_stage_region.js b/lib/commands/new_stage_region.js index d32c162f9..fbb8583a2 100644 --- a/lib/commands/new_stage_region.js +++ b/lib/commands/new_stage_region.js @@ -185,13 +185,14 @@ CMD.prototype._createCfStack = Promise.method(function() { spinner.start(); return AWSUtils.cfCreateResourcesStack( - _this._JAWS._meta.profile, - _this._region, - _this._JAWS._meta.projectRootPath, - _this._JAWS._meta.projectJson.name, - _this._stage, - '' // TODO: read email out of existing jaws-cf.json? - ) + _this._JAWS._meta.profile, + _this._region, + _this._JAWS._meta.projectRootPath, + _this._JAWS._meta.projectJson.name, + _this._stage, + _this._JAWS._meta.projectJson.jawsBuckets[_this._region], + '' // TODO: read email out of existing jaws-cf.json? + ) .then(function(cfData) { return AWSUtils.monitorCfCreate(cfData, _this._JAWS._meta.profile, _this._region) .then(function(cfData) { diff --git a/lib/utils/aws.js b/lib/utils/aws.js index fdd8f7618..c7ef627f1 100644 --- a/lib/utils/aws.js +++ b/lib/utils/aws.js @@ -173,13 +173,107 @@ exports.cfGetLambdasStackTemplate = function(awsProfile, awsRegion, stage, projN })); return CF.getTemplateAsync({ - StackName: _this.cfGetLambdasStackName(stage, projName) - }) + StackName: _this.cfGetLambdasStackName(stage, projName) + }) .then(function(data) { return data.TemplateBody; }); }; +exports.putCfFile = function(awsProfile, projRootPath, awsRegion, bucketName, projName, projStage, type) { + if (['lambdas', 'resources'].indexOf(type) == -1) { + Promise.reject(new JawsError('Type ' + type + ' invalid. Must be lambdas or resources', JawsError.errorCodes.UNKNOWN)); + } + + var d = new Date(), + cfPath = path.join(projRootPath, 'cloudformation', projStage, awsRegion, type + '-cf.json'), + key = ['JAWS', projName, projStage, 'cloudformation/' + type].join('/') + '@' + d.getTime() + '.json', + params = { + Bucket: bucketName, + Key: key, + ACL: 'private', + ContentType: 'application/json', + Body: fs.readfileSync(cfPath), + }; + + return this.putS3Object(awsProfile, awsRegion, params) + .then(function() { + return key; + }) +}; + +exports.cfCreateLambdasStack = function(JAWS, stage, region, lambdaRoleArn) { + var _this = this, + profile = JAWS._meta._profile, + projRootPath = JAWS._meta.projectRootPath, + bucketName = JAWS._meta.projectJson.jawsBuckets[region], + projName = JAWS._meta.projectJson.name; + + _this.configAWS(profile, region); + + var CF = Promise.promisifyAll(new AWS.CloudFormation({ + apiVersion: '2010-05-15', + })), + stackName = _this.cfGetLambdasStackName(stage, projName); + + var params = { + StackName: stackName, + Capabilities: [], + OnFailure: 'ROLLBACK', + Parameters: [{ + ParameterKey: 'aaLambdaRoleArn', + ParameterValue: lambdaRoleArn, + UsePreviousValue: false, + },], + Tags: [{ + Key: 'STAGE', + Value: stage, + },], + }; + + return _this.putCfFile(awsProfile, projRootPath, region, bucketName, projName, stage, 'lambdas') + .then(function(s3CfKey) { + params.TemplateURL = s3CfKey; + return CF.createStackAsync(params); + }); +}; + +exports.cfUpdateLambdasStack = function(JAWS, stage, region) { + var _this = this, + profile = JAWS._meta._profile, + projRootPath = JAWS._meta.projectRootPath, + bucketName = JAWS._meta.projectJson.jawsBuckets[region], + projName = JAWS._meta.projectJson.name; + + _this.configAWS(profile, region); + + var CF = Promise.promisifyAll(new AWS.CloudFormation({ + apiVersion: '2010-05-15', + })), + stackName = _this.cfGetLambdasStackName(stage, projName); + + var params = { + StackName: stackName, + Capabilities: [], + OnFailure: 'ROLLBACK', + Parameters: [{ + ParameterKey: 'aaLambdaRoleArn', + ParameterValue: lambdaRoleArn, + UsePreviousValue: false, + },], + Tags: [{ + Key: 'STAGE', + Value: stage, + },], + }; + + return _this.putCfFile(awsProfile, projRootPath, region, bucketName, projName, stage, 'lambdas') + .then(function(s3CfKey) { + params.TemplateURL = s3CfKey; + return CF.createStackAsync(params); + }); +}; + /** * CloudFormation: Create Stack * @@ -192,69 +286,58 @@ exports.cfGetLambdasStackTemplate = function(awsProfile, awsRegion, stage, projN * @returns {Promise} */ -exports.cfCreateResourcesStack = function(awsProfile, awsRegion, projRootPath, projName, projStage, projNotificationEmail) { +exports.cfCreateResourcesStack = function(awsProfile, awsRegion, projRootPath, projName, projStage, bucketName, projNotificationEmail) { var _this = this; - return new Promise(function(resolve, reject) { + _this.configAWS(awsProfile, awsRegion); - // Config AWS - _this.configAWS(awsProfile, awsRegion); + var CF = Promise.promisifyAll(new AWS.CloudFormation({ + apiVersion: '2010-05-15', + })), + stackName = _this.cfGetResourcesStackName(projStage, projName); - // Instantiate - var CF = new AWS.CloudFormation({ - apiVersion: '2010-05-15', - }); + var params = { + StackName: stackName, + Capabilities: [ + 'CAPABILITY_IAM', + ], + OnFailure: 'ROLLBACK', + Parameters: [{ + ParameterKey: 'aaProjectName', + ParameterValue: projName, + UsePreviousValue: false, + }, { + ParameterKey: 'aaStage', + ParameterValue: projStage, + UsePreviousValue: false, + }, { + ParameterKey: 'aaDataModelPrefix', + ParameterValue: projStage, + UsePreviousValue: false, + }, { + ParameterKey: 'aaHostedZoneName', + ParameterValue: 'mydomain.com', //TODO: should we prompt for this? + UsePreviousValue: false, + }, { + ParameterKey: 'aaNotficationEmail', + ParameterValue: projNotificationEmail, + UsePreviousValue: false, + }, { + ParameterKey: 'aaDefaultDynamoRWThroughput', + ParameterValue: '1', + UsePreviousValue: false, + },], + Tags: [{ + Key: 'STAGE', + Value: projStage, + },], + }; - var params = { - StackName: _this.cfGetResourcesStackName(projStage, projName), - Capabilities: [ - 'CAPABILITY_IAM', - ], - OnFailure: 'ROLLBACK', - Parameters: [{ - ParameterKey: 'aaProjectName', - ParameterValue: projName, - UsePreviousValue: false, - }, { - ParameterKey: 'aaStage', - ParameterValue: projStage, - UsePreviousValue: false, - }, { - ParameterKey: 'aaDataModelPrefix', - ParameterValue: projStage, - UsePreviousValue: false, - }, { - ParameterKey: 'aaHostedZoneName', - ParameterValue: 'mydomain.com', //TODO: should we prompt for this? - UsePreviousValue: false, - }, { - ParameterKey: 'aaNotficationEmail', - ParameterValue: projNotificationEmail, - UsePreviousValue: false, - }, { - ParameterKey: 'aaDefaultDynamoRWThroughput', - ParameterValue: '1', - UsePreviousValue: false, - },], - Tags: [{ - Key: 'STAGE', - Value: projStage, - },], - - // Gotta be careful, TemplateBody has a limit of 51,200 bytes. If we hit limit use TemplateURL - TemplateBody: JSON.stringify(require(path.join(projRootPath, 'jaws-cf.json'))), - }; - - CF.createStack(params, function(error, data) { - - if (error) { - console.error(error); - return reject(new JawsError(error.message, JawsError.errorCodes.UNKNOWN)); - } else { - return resolve(data); - } - }); - }); + return _this.putCfFile(awsProfile, projRootPath, awsRegion, bucketName, projName, projStage, 'resources') + .then(function(s3CfKey) { + params.TemplateURL = s3CfKey; + return CF.createStackAsync(params); + }); }; /** @@ -262,7 +345,7 @@ exports.cfCreateResourcesStack = function(awsProfile, awsRegion, projRootPath, p * @param cfData * @param awsProfile * @param region - * @returns {bluebird|exports|module.exports} + * @returns {Promise} */ exports.monitorCfCreate = function(cfData, awsProfile, region) { diff --git a/lib/utils/index.js b/lib/utils/index.js index 5ae61022f..264f3e536 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -207,7 +207,7 @@ module.exports.writeFile = function(filePath, contents) { */ module.exports.generateLambdaName = function(lambdaAwsmJson) { - var handlerName = lambdaAwsmJson.cloudFormation.Handler.replace('lambdas', ''), + var handlerName = lambdaAwsmJson.cloudFormation.Handler.replace('aws_modules', ''), resourceAction = handlerName.substr(0, handlerName.lastIndexOf('/')); return 'l' + resourceAction.replace(/\/([a-z])/g, function(g) { diff --git a/tests/cli/deploy_endpoint.js b/tests/cli/deploy_endpoint.js index f3b5f3197..b260a44aa 100644 --- a/tests/cli/deploy_endpoint.js +++ b/tests/cli/deploy_endpoint.js @@ -31,9 +31,9 @@ describe('Test deploy endpoint command', function() { JAWS = new Jaws(); // Get Lambda Paths - lambdaPaths.lambda1 = path.join(projPath, 'back', 'lambdas', 'sessions', 'show', 'jaws.json'); - lambdaPaths.lambda2 = path.join(projPath, 'back', 'lambdas', 'sessions', 'create', 'jaws.json'); - lambdaPaths.lambda3 = path.join(projPath, 'back', 'lambdas', 'users', 'create', 'jaws.json'); + lambdaPaths.lambda1 = path.join(projPath, 'back', 'aws_modules', 'sessions', 'show', 'jaws.json'); + lambdaPaths.lambda2 = path.join(projPath, 'back', 'aws_modules', 'sessions', 'create', 'jaws.json'); + lambdaPaths.lambda3 = path.join(projPath, 'back', 'aws_modules', 'users', 'create', 'jaws.json'); }).then(done); }); diff --git a/tests/cli/env.js b/tests/cli/env.js index fb27cff5c..4b7cc464c 100644 --- a/tests/cli/env.js +++ b/tests/cli/env.js @@ -24,7 +24,7 @@ describe('Test "env" command', function() { config.iamRoleArnLambda, config.iamRoleArnApiGateway, config.envBucket); - process.chdir(path.join(projPath, 'back', 'lambdas', 'sessions', 'show')); + process.chdir(path.join(projPath, 'back', 'aws_modules', 'sessions', 'show')); JAWS = new Jaws(); }); diff --git a/tests/cli/install.js b/tests/cli/install.js index 958b66576..458e037af 100644 --- a/tests/cli/install.js +++ b/tests/cli/install.js @@ -25,7 +25,7 @@ describe('Test "install" command', function() { config.iamRoleArnLambda, config.iamRoleArnApiGateway, config.envBucket); - process.chdir(path.join(projPath, 'back', 'lambdas', 'sessions', 'show')); + process.chdir(path.join(projPath, 'back', 'aws_modules', 'sessions', 'show')); JAWS = new Jaws(); }); diff --git a/tests/cli/tag.js b/tests/cli/tag.js index 1843d396b..9d3af912b 100644 --- a/tests/cli/tag.js +++ b/tests/cli/tag.js @@ -32,9 +32,9 @@ describe('Test "tag" command', function() { JAWS = new Jaws(); // Get Lambda Paths - modulePaths.lambda1 = path.join(projPath, 'back', 'lambdas', 'sessions', 'show', 'awsm.json'); - modulePaths.lambda2 = path.join(projPath, 'back', 'lambdas', 'sessions', 'create', 'awsm.json'); - modulePaths.lambda3 = path.join(projPath, 'back', 'lambdas', 'users', 'create', 'awsm.json'); + modulePaths.lambda1 = path.join(projPath, 'back', 'aws_modules', 'sessions', 'show', 'awsm.json'); + modulePaths.lambda2 = path.join(projPath, 'back', 'aws_modules', 'sessions', 'create', 'awsm.json'); + modulePaths.lambda3 = path.join(projPath, 'back', 'aws_modules', 'users', 'create', 'awsm.json'); done(); });