diff --git a/bin/jaws b/bin/jaws index 11562c4d0..51d555f16 100755 --- a/bin/jaws +++ b/bin/jaws @@ -7,7 +7,6 @@ var JawsError = require('../lib/jaws-error'), program = require('commander'), utils = require('../lib/utils'), Promise = require('bluebird'), - minimist = require('minimist'), execute = utils.execute; Promise.onPossiblyUnhandledRejection(function(error) { @@ -22,50 +21,69 @@ program /** * New - * - Create a new project|stage|region|action + * - Create a new project|stage|region */ - program - .command('new ') - .allowUnknownOption() - .description('Make a new "project", "stage", "region", or "module "') - .action(function() { + .command('new [params]') + .description('new project, stage and region commands\n\nValid \'s:' + + '\n\nproject: create new JAWS project in CWD.' + + '\n\t Ex: jaws new project' + + '\n\nstage: create new stage in existing region' + + '\n\t Ex: jaws new stage dev' + + '\n\nregion: create new region for given stage' + + '\n\t Ex: jaws new region dev' + ) + .option('-d, --dont-exe-cf', 'Don\'t execute CloudFormation, just generate it.') + .option('-r, --region ', 'name of aws region to use') + .option('-b, --s3-bucket ', 'project & region only: JAWS S3 bucket name. Will be created if DNE in "project" cmd. Must exist for "region" cmd.') + .option('-n, --proj-name ', 'project only: name for new project') + .option('-s, --stage ', 'project only: same of stage for new project') + .option('-e, --email ', 'project only: notification email to use in CloudFormation') + .option('-p, --aws-profile ', 'project only: Admin AWS profile as defined in ~/.aws/credentials to use') + .action(function(cmd, params, options) { - // Parse Args - var args = minimist(process.argv.slice(3)); - var type = args._[0] ? args._[0].toLowerCase() : null; - - if (type == 'project') { - // New Project + if (cmd == 'project') { var CmdNewProject = require('../lib/commands/new_project'); execute(CmdNewProject.run( - args.name, - args.stage ? args.stage.toLowerCase() : null, - args.s3Bucket, - args.region, - args.email, - args.profile, - args.noExeCf + options.projName, + options.stage ? options.stage.toLowerCase() : null, + options.s3Bucket, + options.region, + options.email, + options.awsProfile, + options.dontExeCf )); - } else if (type == 'region' || type == 'stage') { + } else if (cmd == 'region' || cmd == 'stage') { + var theParams = process.argv.slice(3); + + if (!theParams) { + throw new JawsError('Missing params', JawsError.errorCodes.UNKNOWN); + } + + if (theParams[0][0] == '-') { //TODO: how do we get around this commander shortcoming? + throw new JawsError('Specify options after cmd', JawsError.errorCodes.UNKNOWN); + } + + //TODO: iterate thru cmds to see how many are before - + if (theParams.length < 2) { + throw new JawsError('must specify stage name', JawsError.errorCodes.UNKNOWN); + } + + var CmdNewStageRegion = require('../lib/commands/new_stage_region'), + stageName = theParams[1]; - // New Region/Stage - var CmdNewStageRegion = require('../lib/commands/new_stage_region'); execute(CmdNewStageRegion.run( JAWS, - type, - args.stage, - args.region, - args.noExeCf + cmd, + stageName, + options.region, + options.s3Bucket, + options.dontExeCf )); - } else { - - // Unknown Type - console.error('Unsupported type ' + type + '. Must be project|region|stage|module'); + console.error('Unsupported cmd ' + cmd + '. Must be project|stage|region'); process.exit(1); - } }); @@ -77,10 +95,10 @@ program program .command('module [params]') - .description('aws-module commands\n\nValid ' + - '\'s:\n\ninstall: install aws-module.' + + .description('aws-module commands\n\nValid \'s:' + + '\n\ninstall: install aws-module.' + '\n\t Ex: jaws module install ' + - '\n\tupdate: update aws-module' + + '\n\nupdate: update aws-module' + '\n\t Ex: jaws module update ' + '\n\ncreate: create aws-module action. Module will be created if DNE. create ' + '\n\t Ex: jaws module create users list' @@ -89,15 +107,19 @@ program .option('-d, --dont-install-dependencies', 'Install&Update Only: DONT install awsm\'s depdencies automatically') .option('-l, --lambda', 'Create Only: create lambda. Default is create lambda and endpoint.') .option('-e, --endpoint', 'Create Only: create API Gateway endpoint. Default is create lambda and endpoint.') - .option('-r, --runtime', 'Create Only: lambda runtime. Default nodejs') + .option('-r, --runtime ', 'Create Only: lambda runtime. Default nodejs') .action(function(cmd, params, options) { var theParams = process.argv.slice(3); if (!theParams) { throw new JawsError('Missing params', JawsError.errorCodes.UNKNOWN); } + if (theParams[0][0] == '-') { //TODO: how do we get around this commander shortcoming? + throw new JawsError('Specify options after cmd', JawsError.errorCodes.UNKNOWN); + } if (cmd == 'install' || cmd == 'update') { - if (theParams.length !== 2) { + //TODO: iterate thru cmds to see how many are before - + if (theParams.length < 2) { throw new JawsError('Please specify awsm url', JawsError.errorCodes.UNKNOWN); } var url = theParams[1], @@ -105,7 +127,7 @@ program execute(theCmd[cmd](JAWS, url, options.save, options.dontInstallDependencies)); } else if (cmd == 'create') { - if (theParams.length !== 3) { + if (theParams.length < 3) { throw new JawsError('Please specify awsm resource and action name'); } diff --git a/lib/commands/new_project.js b/lib/commands/new_project.js index d1c8d90db..edbff4719 100644 --- a/lib/commands/new_project.js +++ b/lib/commands/new_project.js @@ -71,6 +71,7 @@ function CMD(name, stage, s3Bucket, notificationEmail, region, profile, noCf) { }; this.Prompter = JawsCLI.prompt(); this.Prompter.override = {}; + this._spinner = null; } /** @@ -123,7 +124,7 @@ CMD.prototype._prompt = Promise.method(function() { _this.Prompter.override.name = _this._name; _this._prompts.properties.name = { description: 'Enter a project name: '.yellow, - default: 'jaws-' + shortid.generate().replace(/\W+/g, '').substring(0, 19).replace('_',''), + default: 'jaws-' + shortid.generate().replace(/\W+/g, '').substring(0, 19).replace('_', ''), message: 'Name must be only letters, numbers, underscores or dashes', conform: function(name) { var re = /^[a-zA-Z0-9-_]+$/; @@ -194,17 +195,8 @@ CMD.prototype._prompt = Promise.method(function() { } // Show Prompts - return new Promise(function(resolve, reject) { - _this.Prompter.get(_this._prompts, function(err, answers) { - if (err) { - reject(new JawsError(err)); - } - resolve(answers); - }); - }) + return _this.Prompter.getAsync(_this._prompts) .then(function(answers) { - - // Set Answers _this._name = answers.name; _this._stage = answers.stage.toLowerCase(); _this._s3Bucket = answers.s3Bucket; @@ -213,20 +205,21 @@ CMD.prototype._prompt = Promise.method(function() { _this._awsAdminSecretKey = answers.awsAdminSecretKey; // If region exists, skip select prompt - if (_this._region) return Promise.resolve(); + if (_this._region) return; // Prompt: region select - var choices = [ - {key: '', label: 'us-east-1', value: 'us-east-1'}, - {key: '', label: 'us-west-2', value: 'us-west-2'}, - {key: '', label: 'eu-west-1', value: 'eu-west-1'}, - {key: '', label: 'ap-northeast-1', value: 'ap-northeast-1'}, - ]; + var choices = []; + AWSUtils.validLambdaRegions.forEach(function(r) { + choices.push({ + key: '', + value: r, + label: r, + }); + }); return JawsCLI.select('Select a region for your project: ', choices, false) .then(function(results) { _this._region = results[0].value; - return Promise.resolve(); }); }) .then(function() { @@ -250,7 +243,6 @@ CMD.prototype._prompt = Promise.method(function() { return JawsCLI.select('Select a profile for your project: ', choices, false) .then(function(results) { _this._profile = results[0].value; - return Promise.resolve(); }); }); }); @@ -331,7 +323,6 @@ CMD.prototype._createProjectDirectory = Promise.method(function() { 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([ @@ -339,10 +330,7 @@ CMD.prototype._createProjectDirectory = Promise.method(function() { fs.mkdirAsync(path.join(_this._projectRootPath, 'tests')), fs.mkdirAsync(path.join(_this._projectRootPath, 'back', 'aws_modules')), utils.writeFile(path.join(_this._projectRootPath, 'admin.env'), adminEnv), - utils.writeFile(path.join( - _this._projectRootPath, 'cloudformation', _this._stage, _this._region, 'resources-cf.json'), - JSON.stringify(cfTemplate, null, 2) - ), + utils.generateResourcesCf(_this._projectRootPath, _this._name, _this._stage, _this._region, _this._notificationEmail), ]); }); }); @@ -364,8 +352,7 @@ CMD.prototype._createS3JawsStructure = Promise.method(function() { .then(function() { var envFileContents = 'JAWS_STAGE=' + _this._stage - + '\nJAWS_DATA_MODEL_PREFIX=' + _this._stage - + '\nJAWS_REGION=' + _this._region; + + '\nJAWS_DATA_MODEL_PREFIX=' + _this._stage; return AWSUtils.putEnvFile( _this._profile, @@ -385,9 +372,7 @@ CMD.prototype._createCfStack = Promise.method(function() { var _this = this; - // Start loading icon - JawsCLI.log('Creating CloudFormation Stack for your new project (~5 mins)...'); - _this._spinner = JawsCLI.spinner(''); + _this._spinner = JawsCLI.spinner('Creating CloudFormation Stack for your new project (~5 mins)...'); _this._spinner.start(); // Create CF stack diff --git a/lib/commands/new_stage_region.js b/lib/commands/new_stage_region.js index 3307eaa8c..d2b6b1b40 100644 --- a/lib/commands/new_stage_region.js +++ b/lib/commands/new_stage_region.js @@ -7,7 +7,7 @@ */ var JawsError = require('../jaws-error'), - JawsCli = require('../utils/cli'), + JawsCLI = require('../utils/cli'), Promise = require('bluebird'), fs = require('fs'), path = require('path'), @@ -17,11 +17,17 @@ var JawsError = require('../jaws-error'), Promise.promisifyAll(fs); /** - * Run + * + * @param JAWS + * @param type 'stage'|'region' + * @param stage + * @param region Optional. Will be prompted for if omitted + * @param s3Bucket Optional. only valid for type of region + * @param noCf Optional + * @returns {*} */ - -module.exports.run = function(JAWS, type, stage, region, noCf) { - var command = new CMD(JAWS, type, stage, region, noCf); +module.exports.run = function(JAWS, type, stage, region, s3Bucket, noCf) { + var command = new CMD(JAWS, type, stage, region, s3Bucket, noCf); return command.run(); }; @@ -29,13 +35,14 @@ module.exports.run = function(JAWS, type, stage, region, noCf) { * Command Class * @constructor */ - -function CMD(JAWS, type, stage, region, noCf) { +function CMD(JAWS, type, stage, region, s3Bucket, noCf) { this._JAWS = JAWS; this._type = type; this._stage = stage; this._region = region; + this._s3Bucket = s3Bucket; this._noCf = noCf; + this._spinner = null; } /** @@ -47,15 +54,49 @@ CMD.prototype.run = Promise.method(function() { var _this = this; // Status - if (_this._type === 'stage') JawsCli.log('Creating new stage "' + _this._stage + '"...'); - if (_this._type === 'region') JawsCli.log('Creating new region within stage "' + _this._stage + '"...'); + if (_this._type === 'stage') JawsCLI.log('Creating new stage "' + _this._stage + '"...'); + if (_this._type === 'region') JawsCLI.log('Creating new region within stage "' + _this._stage + '"...'); return _this._JAWS.validateProject() .bind(_this) .then(_this._promptRegion) + .then(function() { + if (_this._type == 'stage') { //if stage we now have region, so we know bucket + _this._s3Bucket = _this._JAWS._meta.projectJson.jawsBuckets[_this._region]; + } else { + return _this._promptJawsS3Bucket(); + } + }) .then(_this._validate) .then(_this._createEnvFile) - .then(_this._createCfStack) + .then(function() { + return utils.generateResourcesCf( + _this._JAWS._meta.projectRootPath, + _this._JAWS._meta.projectJson.name, + _this._stage, + _this._region, + '' + ); + }) + .then(function() { + if (_this._noCf) { + JawsCLI.log('ENV var file created in s3. CloudFormation file can be run manually'); + JawsCLI.log('!!MAKE SURE!! to create stack with name: ' + AWSUtils.cfGetResourcesStackName( + _this._stage, + _this._JAWS._meta.projectJson.name + )); + JawsCLI.log('After creating CF stack, remember to put the IAM role outputs in your project jaws.json'); + return false; + } else { + return _this._createCfStack() + .then(function(cfData) { + if (_this._spinner) { + _this._spinner.stop(true); + } + return cfData; + }); + } + }) .then(_this._updateProjectJson); }); @@ -64,36 +105,70 @@ CMD.prototype.run = Promise.method(function() { */ CMD.prototype._promptRegion = Promise.method(function() { + var _this = this, + msg = 'Choose a region lambda supports: ', + validRegions = AWSUtils.validLambdaRegions; - var _this = this; - - // If region exists, skip - if (_this._region) return; - - var regions = [ - 'us-east-1', - 'us-west-1', - 'us-west-2', - 'eu-west-1', - 'ap-northeast-1', - ]; - - // Create Choices - var choices = []; - for (var i = 0; i < (regions.length + 1); i++) { - choices.push({ - key: (i + 1) + ') ', - value: regions[i], - label: regions[i], - }); + if (_this._type == 'stage') { //must use existing region + validRegions = Object.keys(_this._JAWS._meta.projectJson.jawsBuckets); + if (validRegions.length == 1) { + _this._region = validRegions[0]; + return Promise.resolve(); + } + msg = 'Choose an existing project region: '; } - return JawsCli.select('Choose a region within this stage: ', choices, false) + if (validRegions.indexOf(_this._region) != -1) { //they specified region and its valid + return Promise.resolve(); + } + + var choices = []; + validRegions.forEach(function(r) { + choices.push({ + key: '', + value: r, + label: r, + }); + }); + + return JawsCLI.select(msg, choices, false) .then(function(results) { - _this._region = [results[0].value]; + console.log('results', results[0].value); + _this._region = results[0].value; }); }); +CMD.prototype._promptJawsS3Bucket = function() { + var _this = this; + + //Don't ever auto-create s3 bucket as its against best practice, and this is not the quick start path + var Prompter = JawsCLI.prompt(), + prompts = { + properties: { + s3Bucket: { + description: 'Enter EXISTING s3 bucket for this region. Must be in THIS region: '.yellow, + required: true, + default: 'jaws-' + _this._region + '.yourapp.com', + message: 'Bucket name must only contain lowercase letters, numbers, periods and dashes', + conform: function(bucket) { + var re = /^[a-z0-9-.]+$/; + return re.test(bucket); + } + } + } + }; + + Prompter.override = { + s3Bucket: _this._s3Bucket, + }; + + return Prompter.getAsync(prompts) + .then(function(answers) { + _this._s3Bucket = answers.s3Bucket; + return _this._s3Bucket; + }); +}; + /** * CMD: Validate */ @@ -103,7 +178,7 @@ CMD.prototype._validate = Promise.method(function() { var _this = this; // Check project config is valid - if (!_this._JAWS._meta.projectJson.project || !_this._JAWS._meta.projectJson.stages) { + if (!_this._JAWS._meta.projectJson.stages) { throw new JawsError('Project\'s jaws.json is malformed or has no existing stages object defined'); } @@ -122,7 +197,7 @@ CMD.prototype._validate = Promise.method(function() { // Make sure stage is not already defined in s3 env var - don't want to overwrite it var envCmd = require('./env'); - return envCmd.getEnvFileAsMap(_this._JAWS, _this._stage) + return envCmd.getEnvFileAsMap(_this._JAWS, _this._stage, _this._region) .then(function(envMap) { if (Object.keys(envMap).length > 0) { throw new JawsError('Stage "' + _this._stage + '" can not be created as an env var file already exists'); @@ -151,17 +226,13 @@ CMD.prototype._createEnvFile = Promise.method(function() { var _this = this; - // If type is not stage, skip this - if (_this._type !== 'stage') return; - var envFileContents = 'JAWS_STAGE=' + _this._stage - + '\nJAWS_DATA_MODEL_PREFIX=' + _this._stage - + '\nJAWS_REGION=' + _this._region; + + '\nJAWS_DATA_MODEL_PREFIX=' + _this._stage; return AWSUtils.putEnvFile( _this._JAWS._meta.profile, - _this._JAWS._meta.projectJson.envVarBucket.region, - _this._JAWS._meta.projectJson.envVarBucket.name, + _this._region, + _this._s3Bucket, _this._JAWS._meta.projectJson.name, _this._stage, envFileContents); @@ -175,31 +246,25 @@ CMD.prototype._createCfStack = Promise.method(function() { var _this = this; - // Start loading icon - var spinner = JawsCli.spinner( - 'Creating CloudFormation stack "' + _this._spinner = JawsCLI.spinner( + 'Creating CloudFormation stack for stage: "' + _this._stage - + '" - "' + + '" and region: "' + _this._region - + '"'); - spinner.start(); + + '" (~5 mins)'); + _this._spinner.start(); return AWSUtils.cfCreateResourcesStack( - _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? - ) + _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.monitorCf(cfData, _this._JAWS._meta.profile, _this._region, 'create') - .then(function(cfData) { - _this._cfData = cfData; - spinner.stop(true); - JawsCli.log('CloudFormation Stack "' + cfData.StackName + '" successfully created.'); - }); + return AWSUtils.monitorCf(cfData, _this._JAWS._meta.profile, _this._region, 'create'); }); }); @@ -207,21 +272,25 @@ CMD.prototype._createCfStack = Promise.method(function() { * CMD: Update Project JSON */ -CMD.prototype._updateProjectJson = Promise.method(function() { +CMD.prototype._updateProjectJson = Promise.method(function(cfData) { var _this = this; var regionObj = { region: _this._region, + iamRoleArnLambda: '', + iamRoleArnApiGateway: '', }; - for (var i = 0; i < _this._cfData.Outputs.length; i++) { - if (_this._cfData.Outputs[i].OutputKey === 'IamRoleArnLambda') { - regionObj.IamRoleArnLambda = _this._cfData.Outputs[i].OutputValue; - } + if (cfData) { + for (var i = 0; i < cfData.Outputs.length; i++) { + if (cfData.Outputs[i].OutputKey === 'IamRoleArnLambda') { + regionObj.IamRoleArnLambda = cfData.Outputs[i].OutputValue; + } - if (_this._cfData.Outputs[i].OutputKey === 'IamRoleArnApiGateway') { - regionObj.iamRoleArnApiGateway = _this._cfData.Outputs[i].OutputValue; + if (cfData.Outputs[i].OutputKey === 'IamRoleArnApiGateway') { + regionObj.iamRoleArnApiGateway = cfData.Outputs[i].OutputValue; + } } } diff --git a/lib/utils/aws.js b/lib/utils/aws.js index c56761fa5..709cf5a95 100644 --- a/lib/utils/aws.js +++ b/lib/utils/aws.js @@ -15,6 +15,8 @@ var Promise = require('bluebird'), Promise.promisifyAll(fs); +exports.validLambdaRegions = ['us-east-1', 'us-west-2', 'eu-west-1', 'ap-northeast-1']; + /** * Set AWS SDK Creds and region from a given profile * diff --git a/lib/utils/cli.js b/lib/utils/cli.js index 0b95771c1..8f56c04ae 100644 --- a/lib/utils/cli.js +++ b/lib/utils/cli.js @@ -16,11 +16,12 @@ var Promise = require('bluebird'), keypress = require('keypress'); Promise.promisifyAll(fs); +Promise.promisifyAll(prompt); /** * ASCII */ -module.exports.ascii = function() { +exports.ascii = function() { var art = ''; art = art + ' ____ _____ __ __ _________ ' + os.EOL; @@ -37,7 +38,7 @@ module.exports.ascii = function() { /** * Spinner */ -module.exports.spinner = function(message) { +exports.spinner = function(message) { var spinner = new Spinner('JAWS: ' + chalk.yellow('%s ' + message)); spinner.setSpinnerString('|/-\\'); return spinner; @@ -46,14 +47,14 @@ module.exports.spinner = function(message) { /** * Log */ -module.exports.log = function(message) { +exports.log = function(message) { console.log('JAWS: ' + chalk.yellow(message + ' ')); }; /** * Prompt */ -module.exports.prompt = function() { +exports.prompt = function() { prompt.start(); prompt.delimiter = ''; prompt.message = 'JAWS: '; @@ -136,7 +137,7 @@ Select._clear = function() { // Private: Close Select._close = function(cb) { - + utils.logIfVerbose('Closing select listener'); var _this = this; process.stdin.pause(); @@ -159,15 +160,14 @@ Select._close = function(cb) { * @param doneLabel * @returns {Promise} */ -module.exports.select = function(message, choices, multi, doneLabel) { +exports.select = function(message, choices, multi, doneLabel) { // Set keypress listener, if not set if (!Select.state) { keypress(process.stdin); - process.stdin.on('keypress', function(ch, key) { - + var keypressHandler = function(ch, key) { if (key && key.ctrl && key.name == 'c') { process.stdin.pause(); @@ -208,6 +208,7 @@ module.exports.select = function(message, choices, multi, doneLabel) { // Check if "done" option if (Select.state.choices[Select.state.index - 1].action && Select.state.choices[Select.state.index - 1].action.toLowerCase() === 'done') { + process.stdin.removeListener('keypress', keypressHandler); return Select._close(); } else { @@ -215,14 +216,16 @@ module.exports.select = function(message, choices, multi, doneLabel) { Select.state.choices[Select.state.index - 1].toggled = Select.state.choices[Select.state.index - 1].toggled ? false : true; if (!Select.state.multi) { + process.stdin.removeListener('keypress', keypressHandler); Select._close(); } else { return Select._render(); } } } - }); + }; + process.stdin.on('keypress', keypressHandler); process.stdin.setRawMode(true); } diff --git a/lib/utils/index.js b/lib/utils/index.js index f52cdd8af..ac2fa88e1 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -20,7 +20,7 @@ Promise.promisifyAll(fs); * @param projectRootPath * @param type lambda|endpoint */ -module.exports.findAllAwsmPathsOfType = function(projectRootPath, type) { +exports.findAllAwsmPathsOfType = function(projectRootPath, type) { var _this = this, jawsJsonAttr; switch (type) { @@ -64,7 +64,7 @@ module.exports.findAllAwsmPathsOfType = function(projectRootPath, type) { * @param startDir * @returns {*} */ -module.exports.findProjectRootPath = function(startDir) { +exports.findProjectRootPath = function(startDir) { var _this = this; // Check if startDir is root @@ -99,7 +99,7 @@ module.exports.findProjectRootPath = function(startDir) { * @param promise */ -module.exports.execute = function(promise) { +exports.execute = function(promise) { promise .catch(JawsError, function(e) { console.error(e); @@ -118,7 +118,7 @@ module.exports.execute = function(promise) { * @param filter * @returns {Promise} */ -module.exports.readRecursively = function(path, filter) { +exports.readRecursively = function(path, filter) { return new Promise(function(resolve, reject) { var files = []; @@ -145,7 +145,7 @@ module.exports.readRecursively = function(path, filter) { * @param projectRootPath * @returns {Promise} list of full paths to awsm.json files that are type lambda */ -module.exports.findAllLambdas = function(projectRootPath) { +exports.findAllLambdas = function(projectRootPath) { return this.findAllAwsmPathsOfType(projectRootPath, 'lambda'); }; @@ -155,7 +155,7 @@ module.exports.findAllLambdas = function(projectRootPath) { * @param projectRootPath * @returns {Promise} list of full paths to awsm.json files that are type endpoint */ -module.exports.findAllEndpoints = function(projectRootPath) { +exports.findAllEndpoints = function(projectRootPath) { return this.findAllAwsmPathsOfType(projectRootPath, 'endpoint'); }; @@ -166,7 +166,7 @@ module.exports.findAllEndpoints = function(projectRootPath) { * @returns {Promise} list of full paths to awsm.json files */ -module.exports.findAllAwsmJsons = function(startPath) { +exports.findAllAwsmJsons = function(startPath) { return this.readRecursively(startPath, '*awsm.json') .then(function(jsonPaths) { return jsonPaths.map(function(jsonPath) { @@ -182,7 +182,7 @@ module.exports.findAllAwsmJsons = function(startPath) { * * @param str */ -module.exports.logIfVerbose = function(str) { +exports.logIfVerbose = function(str) { if (process.env.JAWS_VERBOSE) { console.log(str); } @@ -195,7 +195,7 @@ module.exports.logIfVerbose = function(str) { * @param contents node Buffer * @returns {Promise} */ -module.exports.writeFile = function(filePath, contents) { +exports.writeFile = function(filePath, contents) { return mkdirpAsync(path.dirname(filePath)) .then(function() { return fs.writeFileAsync(filePath, contents); @@ -208,7 +208,7 @@ module.exports.writeFile = function(filePath, contents) { * @returns {string} */ -module.exports.generateLambdaName = function(awsmJson) { +exports.generateLambdaName = function(awsmJson) { var handlerName = awsmJson.lambda.cloudFormation.Handler.replace('aws_modules', ''), resourceAction = handlerName.substr(0, handlerName.lastIndexOf('/')); @@ -226,7 +226,7 @@ module.exports.generateLambdaName = function(awsmJson) { * @param projectRootPath * @returns {Promise} list of functionName's */ -module.exports.getAllLambdaNames = function(projectRootPath) { +exports.getAllLambdaNames = function(projectRootPath) { var _this = this, lambdaNames = []; @@ -248,28 +248,28 @@ module.exports.getAllLambdaNames = function(projectRootPath) { * * @param projectJawsJson * @param stage - * @param region + * @param regionName * @returns {*} region object for stage */ -module.exports.getProjRegionConfigForStage = function(projectJawsJson, stage, region) { +exports.getProjRegionConfigForStage = function(projectJawsJson, stage, regionName) { var projectStageObj = projectJawsJson.stages[stage]; var region = projectStageObj.filter(function(regionObj) { - return regionObj.region == region; + return regionObj.region == regionName; }); if (!region || region.length == 0) { - throw new JawsError('Could not find region ' + region, JawsError.errorCodes.UNKNOWN); + throw new JawsError('Could not find region ' + regionName, JawsError.errorCodes.UNKNOWN); } if (region.length > 1) { - throw new JawsError('Multiple regions named ' + region, JawsError.errorCodes.UNKNOWN); + throw new JawsError('Multiple regions named ' + regionName, JawsError.errorCodes.UNKNOWN); } return region[0]; }; -module.exports.dirExistsSync = function(path) { +exports.dirExistsSync = function(path) { try { var stats = fs.lstatSync(path); return stats.isDirectory(); @@ -279,7 +279,7 @@ module.exports.dirExistsSync = function(path) { } }; -module.exports.fileExistsSync = function(path) { +exports.fileExistsSync = function(path) { try { var stats = fs.lstatSync(path); return stats.isFile(); @@ -289,7 +289,7 @@ module.exports.fileExistsSync = function(path) { } }; -module.exports.readAndParseJsonSync = function(path) { +exports.readAndParseJsonSync = function(path) { return JSON.parse(fs.readFileSync(path)); }; @@ -306,4 +306,19 @@ exports.npmInstall = function(dir) { } process.chdir(cwd); -}; \ No newline at end of file +}; + +exports.generateResourcesCf = function(projRootPath, projName, stage, region, notificationEmail) { + var cfTemplate = require('../templates/resources-cf'); + cfTemplate.Parameters.aaProjectName.Default = projName; + cfTemplate.Parameters.aaProjectName.AllowedValues = [projName]; + cfTemplate.Parameters.aaStage.Default = stage; + cfTemplate.Parameters.aaDataModelPrefix.Default = stage; //to simplify bootstrap use same stage + cfTemplate.Parameters.aaNotficationEmail.Default = notificationEmail; + cfTemplate.Description = projName + " resources"; + + return this.writeFile( + path.join(projRootPath, 'cloudformation', stage, region, 'resources-cf.json'), + JSON.stringify(cfTemplate, null, 2) + ); +}; diff --git a/package.json b/package.json index 9ddc20d16..2ecd5892b 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "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", "moment": "^2.10.6", "node-uuid": "^1.4.2", diff --git a/tests/cli/new_stage_region.js b/tests/cli/new_stage_region.js index f83ec7ea6..ab677abdf 100644 --- a/tests/cli/new_stage_region.js +++ b/tests/cli/new_stage_region.js @@ -73,7 +73,7 @@ describe('Test "new stage/region" command', function() { it('Create New region', function(done) { this.timeout(0); - CmdNewStageRegion.run(JAWS, 'region', tempStage, tempRegion2, false) + CmdNewStageRegion.run(JAWS, 'region', tempStage, tempRegion2, config.regionBucket, false) .then(function() { var jawsJson = require(path.join(process.cwd(), '../jaws.json')); var region = false;