This commit is contained in:
doapp-ryanp 2015-10-21 15:49:30 -05:00
parent eedfd4421c
commit 69c5e53472
23 changed files with 1351 additions and 1597 deletions

View File

@ -40,8 +40,8 @@ class Jaws {
silent: silent,
path: path.join(this._projectRootPath, 'admin.env'),
});
this._profile = process.env.JAWS_ADMIN_AWS_PROFILE;
this._credentials = AWSUtils.profilesGet(this._profile)[this._profile];
this._awsProfile = process.env.JAWS_ADMIN_AWS_PROFILE;
this._credentials = AWSUtils.profilesGet(this._awsProfile)[this._awsProfile];
}
// TODO: Remove these eventually, keeping them for reference...
@ -148,14 +148,14 @@ class Jaws {
*/
_executeQueue(queue) {
return Promise.try(() => {
return queue;
})
.each(function(p) {
return p();
})
.catch(function(error) {
throw new JawsError(error);
});
return queue;
})
.each(function(p) {
return p();
})
.catch(function(error) {
throw new JawsError(error);
});
}
@ -192,6 +192,17 @@ class Jaws {
* @returns {Promise}
*/
command(argv) {
JawsUtils.jawsDebug('command argv', argv);
if (argv.version) {
console.log(this._version);
return Promise.resolve();
}
if (argv.help) {
//TODO: spit out cli help
return Promise.resolve();
}
// Check if command context and action are defined
if (!this.commands[argv._[0]] || !this.commands[argv._[0]][argv._[1]]) {

View File

@ -75,7 +75,7 @@ class JawsPlugin {
//TODO: implement
return Promise.reject(new JawsError('Not implemented', JawsError.errorCodes.UNKNOWN));
} else {
return Promise.reject(new JawsError('You must specify all necessary options when in a non-interactive mode', JawsError.errorCodes.UNKNOWN));
return Promise.resolve(); //in non interactive mode. All options must be set programatically
}
}

View File

@ -114,7 +114,7 @@ class ApiDeployer {
let _this = this;
return AWSUtils.cfGetLambdaResourceSummaries(
_this._JAWS._profile,
_this._JAWS._awsProfile,
_this._regionJson.region,
AWSUtils.cfGetLambdasStackName(_this._stage, _this._JAWS._projectJson.name)
)
@ -701,7 +701,7 @@ class ApiDeployer {
let _this = this;
return AWSUtils.lambdaGetPolicy(
_this._JAWS._meta.profile,
_this._JAWS._awsProfile,
_this._regionJson.region,
endpoint.apiGateway.apig.lambda.PhysicalResourceId)
.then(function(data) {
@ -737,7 +737,7 @@ class ApiDeployer {
if (!statement) return Promise.resolve(endpoint);
return AWSUtils.lambdaRemovePermission(
_this._JAWS._meta.profile,
_this._JAWS._awsProfile,
_this._regionJson.region,
endpoint.apiGateway.apig.lambda.PhysicalResourceId,
'jaws-apigateway-access')
@ -792,7 +792,7 @@ class ApiDeployer {
+ endpoint.apiGateway.cloudFormation.Path;
return AWSUtils.lambdaAddPermission(
_this._JAWS._meta.profile,
_this._JAWS._awsProfile,
_this._regionJson.region,
statement)
.then(function(data) {

View File

@ -185,7 +185,7 @@ const ResourceDeployer = class ResourceDeployer extends ProjectCmd {
return _this._updateStack()
.bind(_this)
.then(function(cfData) {
return AWSUtils.monitorCf(cfData, _this._JAWS._meta.profile, _this._regionJson.region, 'update');
return AWSUtils.monitorCf(cfData, _this._JAWS._awsProfile, _this._regionJson.region, 'update');
})
.then(function(data) {
spinner.stop(true);

View File

@ -32,7 +32,7 @@ let CMD = class JawsEnv extends ProjectCmd {
let deferred;
if (_this._stage == 'local') {
deferred = Promise.resolve(fs.readFileSync(path.join(_this._JAWS._meta.projectRootPath, '.env')));
deferred = Promise.resolve(fs.readFileSync(path.join(_this._JAWS._projectRootPath, '.env')));
} else {
deferred = _this._JAWS.getEnvFile(_this._region, _this._stage)
.then(function(s3ObjData) {
@ -94,7 +94,7 @@ let CMD = class JawsEnv extends ProjectCmd {
*/
listEnv(showWhereUsed) {
let _this = this,
projRootDir = _this._JAWS._meta.projectRootPath,
projRootDir = _this._JAWS._projectRootPath,
stage = _this._stage;
return utils.findAllAwsmJsons(path.join(projRootDir, 'aws_modules'))
@ -208,7 +208,7 @@ let CMD = class JawsEnv extends ProjectCmd {
});
if (_this._stage == 'local') {
putEnvQ.push(utils.writeFile(path.join(_this._JAWS._meta.projectRootPath, '.env'), contents));
putEnvQ.push(utils.writeFile(path.join(_this._JAWS._projectRootPath, '.env'), contents));
} else {
putEnvQ.push(_this._JAWS.putEnvFile(mapForRegion.regionName, _this._stage, contents));
}

View File

@ -88,7 +88,7 @@ const CMD = class ModuleCreate extends ProjectCmd {
if (_this._module.pkgMgr == 'npm') {
let modulePath = path.join(
_this._JAWS._meta.projectRootPath, //TOOD: make this CWD if not in a JAWS project
_this._JAWS._projectRootPath, //TOOD: make this CWD if not in a JAWS project
'node_modules',
_this._module.name);
let templatesPath = path.join(__dirname, '..', 'templates');
@ -207,7 +207,7 @@ const CMD = class ModuleCreate extends ProjectCmd {
let templatesPath = path.join(__dirname, '..', 'templates');
let actionTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'action.awsm.json'));
let modulePath = path.join(
_this._JAWS._meta.projectRootPath, //TOOD: make this CWD if not in a JAWS project
_this._JAWS._projectRootPath, //TOOD: make this CWD if not in a JAWS project
'aws_modules',
_this._module.name);
let actionPath = path.join(modulePath, _this._module.action);

View File

@ -136,7 +136,7 @@ const CMD = class ModuleCreate extends ProjectCmd {
_installFiles(tempDirPath) {
let _this = this,
srcAwsmJsonPath = path.join(tempDirPath, 'awsm.json'),
awsModsPath = path.join(_this._JAWS._meta.projectRootPath, 'aws_modules');
awsModsPath = path.join(_this._JAWS._projectRootPath, 'aws_modules');
if (!utils.fileExistsSync(srcAwsmJsonPath)) {
return Promise.reject(new JawsError('Module missing awsm.json file in root of project', JawsError.errorCodes.UNKNOWN));

View File

@ -1,553 +0,0 @@
'use strict';
/**
* JAWS Command: project create
* - Asks the user for information about their new JAWS project
* - Creates a new project in the current working directory
* - Creates IAM resources via CloudFormation
*/
const GlobalCmd = require('./GlobalCmd'),
JawsError = require('../jaws-error'),
JawsCLI = require('../utils/cli'),
Promise = require('bluebird'),
path = require('path'),
os = require('os'),
AWSUtils = require('../utils/aws'),
utils = require('../utils'),
shortid = require('shortid');
let fs = require('fs');
Promise.promisifyAll(fs);
function generateShortId(maxLen) {
return shortid.generate().replace(/\W+/g, '').substring(0, maxLen).replace(/[_-]/g, '');
}
/**
* CMD Class
* @param name
* @param stage
* @param domain
* @param notificationEmail
* @param region
* @param profile
* @param noCf
* @param runtime
* @constructor
*/
const CMD = class ProjectCreate extends GlobalCmd {
constructor(name, stage, region, domain, notificationEmail, profile, noCf, runtime) {
super();
// Defaults
this._name = name ? name : null;
this._domain = domain ? domain : null;
this._stage = stage ? stage.toLowerCase().replace(/\W+/g, '').substring(0, 15) : null;
this._notificationEmail = notificationEmail;
this._region = region;
this._profile = profile;
this._runtime = runtime ? runtime : 'nodejs';
this._noCf = noCf;
this._prompts = {
properties: {},
};
this.Prompter = JawsCLI.prompt();
this.Prompter.override = {};
this._spinner = null;
}
/**
* Run
* @returns {*}
*/
run() {
let _this = this;
return Promise.try(function() {
// ASCII Greeting
JawsCLI.ascii();
})
.bind(_this)
.then(_this._prompt)
.then(_this._prepareProjectData)
.then(_this._createProjectDirectory)
.then(function() {
if (_this._noCf) {
let stackResourcesName = AWSUtils.cfGetResourcesStackName(
_this._stage,
_this._name
);
JawsCLI.log('Remember to run CloudFormation manually');
JawsCLI.log(`MAKE SURE to create stack with name: ${stackResourcesName}`);
JawsCLI.log('After creating CF stack, remember to put the IAM role outputs and jawsBucket in your '
+ 'project jaws.json in the right stage/region.');
return false;
} else {
return _this._createCfStack()
.bind(_this)
.then(function(cfData) {
if (_this._spinner) {
_this._spinner.stop(true);
}
_this._cfData = cfData;
})
.then(_this._putEnvFile)
.then(_this._putCfFile);
}
})
.then(_this._createProjectJson)
.then(_this._initRuntime)
.then(function() {
JawsCLI.log('Your project "' + _this._name
+ '" has been successfully created in the current directory.');
});
}
/**
* Prompt
* @private
*/
_prompt() {
utils.jawsDebug('Prompting for new project information');
let _this = this;
let nameDescription = 'Enter a project name: ';
// Prompt: name (project name)
_this.Prompter.override.name = _this._name;
_this._prompts.properties.name = {
description: nameDescription.yellow,
default: 'jaws-' + generateShortId(19),
message: 'Name must be only letters, numbers or dashes',
conform: function(name) {
let re = /^[a-zA-Z0-9-_]+$/;
return re.test(name);
},
};
// Prompt: domain - for AWS hosted zone and more
_this.Prompter.override.domain = _this._domain;
let domainDescription = 'Enter a project domain (You can change this at any time: ';
_this._prompts.properties.domain = {
description: domainDescription.yellow,
default: 'myapp.com',
message: 'Domain must only contain lowercase letters, numbers, periods and dashes',
conform: function(bucket) {
let re = /^[a-z0-9-.]+$/;
return re.test(bucket);
},
};
// Prompt: notification email - for AWS alerts
_this.Prompter.override.notificationEmail = _this._notificationEmail;
_this._prompts.properties.notificationEmail = {
description: 'Enter an email to use for AWS alarms: '.yellow,
required: true,
message: 'Please enter a valid email',
default: 'me@myapp.com',
conform: function(email) {
if (!email) return false;
return true;
},
};
// Prompt: stage
_this.Prompter.override.stage = _this._stage;
let stageDescription = 'Enter a stage for this project: ';
_this._prompts.properties.stage = {
description: stageDescription.yellow,
default: 'dev',
message: 'Stage must be letters only',
conform: function(stage) {
let re = /^[a-zA-Z]+$/;
return re.test(stage);
},
};
// Prompt: notification email - for AWS alerts
_this.Prompter.override.notificationEmail = _this._notificationEmail;
let notificationEmailDescription = 'Enter an email to use for AWS alarms: ';
_this._prompts.properties.notificationEmail = {
description: notificationEmailDescription.yellow,
required: true,
message: 'Please enter a valid email',
default: 'you@yourapp.com',
conform: function(email) {
if (!email) return false;
return true;
},
};
// Prompt: API Keys - Create an AWS profile by entering API keys
if (!utils.fileExistsSync(path.join(AWSUtils.getConfigDir(), 'credentials'))) {
_this.Prompter.override.awsAdminKeyId = _this._awsAdminKeyId;
let apiKeyDescription = 'Enter the ACCESS KEY ID for your Admin AWS IAM User: ';
_this._prompts.properties.awsAdminKeyId = {
description: apiKeyDescription.yellow,
required: true,
message: 'Please enter a valid access key ID',
conform: function(key) {
if (!key) return false;
return true;
},
};
_this.Prompter.override.awsAdminSecretKey = _this._awsAdminSecretKey;
let apiSecretDescription = 'Enter the SECRET ACCESS KEY for your Admin AWS IAM User: ';
_this._prompts.properties.awsAdminSecretKey = {
description: apiSecretDescription.yellow,
required: true,
message: 'Please enter a valid secret access key',
conform: function(key) {
if (!key) return false;
return true;
},
};
}
// Show Prompts
return _this.Prompter.getAsync(_this._prompts)
.then(function(answers) {
_this._name = answers.name;
_this._domain = answers.domain;
_this._stage = answers.stage.toLowerCase();
_this._notificationEmail = answers.notificationEmail;
_this._awsAdminKeyId = answers.awsAdminKeyId;
_this._awsAdminSecretKey = answers.awsAdminSecretKey;
// If region exists, skip select prompt
if (_this._region) return;
// Prompt: region select
let 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;
});
})
.then(function() {
// If profile exists, skip select prompt
if (_this._profile) return Promise.resolve();
// If aws credentials were passed, skip select prompt
if (_this._awsAdminKeyId && _this._awsAdminSecretKey) return Promise.resolve();
// Prompt: profile select
let profilesList = AWSUtils.profilesMap(),
profiles = Object.keys(profilesList),
choices = [];
for (let i = 0; i < profiles.length; i++) {
choices.push({
key: '',
value: profiles[i],
label: profiles[i],
});
}
return JawsCLI.select('Select an AWS profile for your project: ', choices, false)
.then(function(results) {
_this._profile = results[0].value;
});
});
}
/**
* Prepare Project Data
* @private
*/
_prepareProjectData() {
let _this = this;
// Validate: Ensure stage isn't "local"
if (_this._stage.toLowerCase() == 'local') {
throw new JawsError('Stage ' + _this._stage + ' is reserved');
}
// Validate: AWS only allows Alphanumeric and - in name
let nameOk = /^([a-zA-Z0-9-]+)$/.exec(_this._name);
if (!nameOk) {
throw new JawsError('Project names can only be alphanumeric and -');
}
// Append unique id if name is in use
if (utils.dirExistsSync(path.join(process.cwd(), _this._name))) {
_this._name = _this._name + '-' + generateShortId(19);
}
// Append unique id if domain is default
if (_this._domain === 'myapp.com') {
_this._domain = 'myapp-' + generateShortId(8) + '.com';
}
// Set JAWS Bucket
_this._jawsBucket = utils.generateJawsBucketName(_this._stage, _this._region, _this._domain);
// Validate: If no profile, ensure access keys, create profile
if (!_this._profile) {
if (!_this._awsAdminKeyId) {
throw new JawsError(
'An AWS Access Key ID is required',
JawsError.errorCodes.MISSING_AWS_CREDS);
}
if (!_this._awsAdminSecretKey) {
throw new JawsError(
'An AWS Secret Key is required',
JawsError.errorCodes.MISSING_AWS_CREDS);
}
// Set profile
AWSUtils.profilesSet('default', _this._region, _this._awsAdminKeyId, _this._awsAdminSecretKey);
_this._profile = 'default';
}
}
/**
* Create Project Directory
* @private
*/
_createProjectDirectory() {
let _this = this;
_this._projectRootPath = path.resolve(path.join(path.dirname('.'), _this._name));
// Prepare admin.env
let adminEnv = 'JAWS_ADMIN_AWS_PROFILE=' + _this._profile + os.EOL;
// Prepare README.md
let readme = '#' + _this._name;
// Create Project Scaffolding
return utils.writeFile(
path.join(_this._projectRootPath, '.env'),
'JAWS_STAGE=' + _this._stage
+ '\nJAWS_DATA_MODEL_STAGE=' + _this._stage
)
.then(function() {
return Promise.all([
fs.mkdirAsync(path.join(_this._projectRootPath, 'tests')),
fs.mkdirAsync(path.join(_this._projectRootPath, 'lib')),
fs.mkdirAsync(path.join(_this._projectRootPath, 'aws_modules')),
utils.writeFile(path.join(_this._projectRootPath, 'admin.env'), adminEnv),
utils.writeFile(path.join(_this._projectRootPath, 'README.md'), readme),
utils.generateResourcesCf(
_this._projectRootPath,
_this._name,
_this._domain,
_this._stage,
_this._region,
_this._notificationEmail,
_this._jawsBucket
),
fs.writeFileAsync(path.join(_this._projectRootPath, '.gitignore'), fs.readFileSync(__dirname + '/../templates/gitignore')),
]);
});
}
/**
* Put ENV File
* - Creates ENV file in JAWS stage/region bucket
* @private
*/
_putEnvFile() {
let _this = this,
stage = this.stage;
let envFileContents = `JAWS_STAGE=${stage}
JAWS_DATA_MODEL_STAGE=${stage}`;
return AWSUtils.putEnvFile(
_this._profile,
_this._region,
_this._jawsBucket,
_this._name,
_this._stage,
envFileContents);
}
/**
* Put CF File
* @returns {*}
* @private
*/
_putCfFile() {
let _this = this;
return AWSUtils.putCfFile(
_this._profile,
_this._projectRootPath,
_this._region,
_this._jawsBucket,
_this._name,
_this._stage,
'resources');
}
/**
* Create CloudFormation Stack
* @private
*/
_createCfStack() {
let _this = this;
JawsCLI.log('Creating CloudFormation Stack for your new project (~5 mins)...');
_this._spinner = JawsCLI.spinner();
_this._spinner.start();
// Create CF stack
return AWSUtils.cfCreateResourcesStack(
_this._profile,
_this._region,
_this._projectRootPath,
_this._name,
_this._stage,
_this._domain,
_this._notificationEmail,
_this._jawsBucket
)
.then(function(cfData) {
return AWSUtils.monitorCf(cfData, _this._profile, _this._region, 'create');
});
}
/**
* Create Project JSON
* @private
*/
_createProjectJson() {
let _this = this,
iamRoleArnLambda,
iamRoleArnApiGateway;
if (_this._cfData) {
for (let i = 0; i < _this._cfData.Outputs.length; i++) {
if (_this._cfData.Outputs[i].OutputKey === 'IamRoleArnLambda') {
iamRoleArnLambda = _this._cfData.Outputs[i].OutputValue;
}
if (_this._cfData.Outputs[i].OutputKey === 'IamRoleArnApiGateway') {
iamRoleArnApiGateway = _this._cfData.Outputs[i].OutputValue;
}
}
}
let templatesPath = path.join(__dirname, '..', 'templates'),
jawsJson = utils.readAndParseJsonSync(path.join(templatesPath, 'jaws.json'));
jawsJson.stages[_this._stage] = [{
region: _this._region,
iamRoleArnLambda: iamRoleArnLambda || '',
iamRoleArnApiGateway: iamRoleArnApiGateway || '',
jawsBucket: _this._jawsBucket,
}];
jawsJson.name = _this._name;
jawsJson.domain = _this._domain;
fs.writeFileSync(path.join(_this._projectRootPath, 'jaws.json'),
JSON.stringify(jawsJson, null, 2));
return jawsJson;
}
/**
* Init Runtime
* @private
*/
_initRuntime() {
let _this = this;
JawsCLI.log('Preparing your runtime and installing jaws-core module...');
if (_this._runtime === 'nodejs') {
let packageJsonTemplate = utils.readAndParseJsonSync(path.join(__dirname, '..', 'templates', 'nodejs', 'package.json'));
packageJsonTemplate.name = _this._name;
return fs.writeFileAsync(path.join(_this._projectRootPath, 'package.json'), JSON.stringify(packageJsonTemplate, null, 2))
.then(function() {
utils.jawsDebug('test_utils', 'Running NPM install...');
utils.npmInstall(_this._projectRootPath);
});
} else {
throw new JawsError('Unsupported runtime "' + _this.runtime + '"', JawsError.errorCodes.UNKNOWN);
}
}
};
/**************************************
* EXPORTS
**************************************/
/**
* Run
* @param name
* @param stage
* @param domain
* @param region
* @param notificationEmail
* @param profile
* @param noCf
* @param runtime defaults to 'nodejs'
* @returns {Promise}
*/
exports.run = function(name, stage, region, domain, notificationEmail, profile, noCf, runtime) {
utils.jawsDebug('Running new project:', name);
let command = new CMD(
name,
stage,
region,
domain,
notificationEmail,
profile,
noCf,
runtime);
return command.run();
};

View File

@ -7,13 +7,13 @@
*/
const ProjectCmd = require('./ProjectCmd.js'),
JawsError = require('../jaws-error'),
JawsCLI = require('../utils/cli'),
Promise = require('bluebird'),
os = require('os'),
path = require('path'),
AWSUtils = require('../utils/aws'),
utils = require('../utils');
JawsError = require('../jaws-error'),
JawsCLI = require('../utils/cli'),
Promise = require('bluebird'),
os = require('os'),
path = require('path'),
AWSUtils = require('../utils/aws'),
utils = require('../utils');
let fs = require('fs');
@ -22,10 +22,10 @@ Promise.promisifyAll(fs);
const CMD = class StageRegion extends ProjectCmd {
constructor(JAWS, type, stage, region, noCf) {
super(JAWS);
this._type = type;
this._stage = stage;
this._region = region;
this._noCf = noCf;
this._type = type;
this._stage = stage;
this._region = region;
this._noCf = noCf;
this._spinner = null;
this._prompts = {
properties: {},
@ -41,53 +41,55 @@ const CMD = class StageRegion extends ProjectCmd {
let _this = this;
return _this._JAWS.validateProject()
.bind(_this)
.then(function() {
// Report Status
if (_this._type === 'stage') JawsCLI.log('Creating new stage...');
if (_this._type === 'region') JawsCLI.log('Creating new region...');
})
.then(_this._promptStage)
.then(_this._promptRegion)
.then(_this._validate)
.then(function() {
return utils.generateResourcesCf(
_this._JAWS._meta.projectRootPath,
_this._JAWS._meta.projectJson.name,
_this._JAWS._meta.projectJson.domain,
_this._stage,
_this._region,
'',
_this._jawsBucket
.bind(_this)
.then(function() {
// Report Status
if (_this._type === 'stage') JawsCLI.log('Creating new stage...');
if (_this._type === 'region') JawsCLI.log('Creating new region...');
})
.then(_this._promptStage)
.then(_this._promptRegion)
.then(_this._validate)
.then(function() {
return utils.generateResourcesCf(
_this._JAWS._projectRootPath,
_this._JAWS._meta.projectJson.name,
_this._JAWS._meta.projectJson.domain,
_this._stage,
_this._region,
''
);
})
.then(() => {
return AWSUtils.createBucket(_this.Jaws._awsProfile, _this._region, _this.Jaws.getJawsBucket(_this._region, _this._stage));
})
.then(function() {
if (_this._noCf) {
let stackName = AWSUtils.cfGetResourcesStackName(
_this._stage,
_this._JAWS._meta.projectJson.name
);
})
.then(function() {
if (_this._noCf) {
let stackName = AWSUtils.cfGetResourcesStackName(
_this._stage,
_this._JAWS._meta.projectJson.name
);
JawsCLI.log('Remember to run CloudFormation manually');
JawsCLI.log(`!!MAKE SURE!! to create stack with name: ${stackName}`);
JawsCLI.log('After creating CF stack, remember to put the IAM role outputs and jawsBucket in your '
+ 'project jaws.json in the right stage/region.');
return false;
} else {
return _this._createCfStack()
.bind(_this)
.then(function(cfData) {
if (_this._spinner) {
_this._spinner.stop(true);
}
_this._cfData = cfData;
})
.then(_this._putEnvFile)
.then(_this._putCfFile);
}
})
.then(_this._updateProjectJson);
JawsCLI.log('Remember to run CloudFormation manually');
JawsCLI.log(`!!MAKE SURE!! to create stack with name: ${stackName}`);
JawsCLI.log('After creating CF stack, remember to put the IAM role outputs and jawsBucket in your '
+ 'project jaws.json in the right stage/region.');
return false;
} else {
return _this._createCfStack()
.bind(_this)
.then(function(cfData) {
if (_this._spinner) {
_this._spinner.stop(true);
}
_this._cfData = cfData;
})
.then(_this._putEnvFile)
.then(_this._putCfFile);
}
})
.then(_this._updateProjectJson);
}
_promptStage() {
@ -99,14 +101,14 @@ const CMD = class StageRegion extends ProjectCmd {
if (_this._type === 'stage') {
// User is creating a stage
_this.Prompter = JawsCLI.prompt();
_this.Prompter = JawsCLI.prompt();
let stageDescription = `Enter the name of the stage to be created: \n`;
_this._prompts.properties.stage = {
description: stageDescription.yellow,
default: 'dev',
message: 'Stage must be letters only',
conform: function(stage) {
default: 'dev',
message: 'Stage must be letters only',
conform: function(stage) {
let re = /^[a-zA-Z]+$/;
return re.test(stage);
},
@ -114,9 +116,9 @@ const CMD = class StageRegion extends ProjectCmd {
// Show Prompt
return _this.Prompter.getAsync(_this._prompts)
.then(function(answers) {
_this._stage = answers.stage;
});
.then(function(answers) {
_this._stage = answers.stage;
});
} else {
@ -138,22 +140,22 @@ const CMD = class StageRegion extends ProjectCmd {
let choices = [];
for (let i = 0; i < stages.length; i++) {
choices.push({
key: (i + 1) + ') ',
key: (i + 1) + ') ',
value: stages[i],
label: stages[i],
});
}
return JawsCLI.select('Which stage are you creating a region for: ', choices, false)
.then(function(results) {
_this._stage = results[0].value;
});
.then(function(results) {
_this._stage = results[0].value;
});
}
}
_promptRegion() {
let _this = this,
let _this = this,
validRegions = AWSUtils.validLambdaRegions;
if (validRegions.indexOf(_this._region) != -1) { //they specified region and its valid
@ -164,7 +166,7 @@ const CMD = class StageRegion extends ProjectCmd {
msg;
validRegions.forEach(function(r) {
choices.push({
key: '',
key: '',
value: r,
label: r,
});
@ -178,9 +180,9 @@ const CMD = class StageRegion extends ProjectCmd {
}
return JawsCLI.select(msg, choices, false)
.then(function(results) {
_this._region = results[0].value;
});
.then(function(results) {
_this._region = results[0].value;
});
}
_validate() {
@ -227,8 +229,8 @@ const CMD = class StageRegion extends ProjectCmd {
// Make sure region is not already defined
if (_this._JAWS._meta.projectJson.stages[_this._stage].some(function(r) {
return r.region == _this._region;
})) {
return r.region == _this._region;
})) {
return Promise.reject(new JawsError('Region "' + _this._region + '" is already defined in the stage "' + _this._stage + '"'));
}
}
@ -241,27 +243,27 @@ const CMD = class StageRegion extends ProjectCmd {
let _this = this;
JawsCLI.log('Creating CloudFormation stack for stage: "'
+ _this._stage
+ '" and region: "'
+ _this._region
+ '" (~5 mins)');
+ _this._stage
+ '" and region: "'
+ _this._region
+ '" (~5 mins)');
_this._spinner = JawsCLI.spinner();
_this._spinner.start();
// Create CF stack
return AWSUtils.cfCreateResourcesStack(
_this._JAWS._meta.profile,
_this._region,
_this._JAWS._meta.projectRootPath,
_this._JAWS._meta.projectJson.name,
_this._stage,
_this._domain,
'', // TODO: read email out of existing jaws-cf.json?
_this._jawsBucket
)
.then(function(cfData) {
return AWSUtils.monitorCf(cfData, _this._JAWS._meta.profile, _this._region, 'create');
});
_this._JAWS._awsProfile,
_this._region,
_this._JAWS._projectRootPath,
_this._JAWS._meta.projectJson.name,
_this._stage,
_this._domain,
'', // TODO: read email out of existing jaws-cf.json?
_this._jawsBucket
)
.then(function(cfData) {
return AWSUtils.monitorCf(cfData, _this._JAWS._awsProfile, _this._region, 'create');
});
}
@ -279,12 +281,12 @@ const CMD = class StageRegion extends ProjectCmd {
JAWS_DATA_MODEL_STAGE=${stage}`;
return AWSUtils.putEnvFile(
_this._JAWS._meta.profile,
_this._region,
_this._jawsBucket,
_this._JAWS._meta.projectJson.name,
_this._stage,
envFileContents);
_this._JAWS._awsProfile,
_this._region,
_this._jawsBucket,
_this._JAWS._meta.projectJson.name,
_this._stage,
envFileContents);
}
/**
@ -297,13 +299,13 @@ JAWS_DATA_MODEL_STAGE=${stage}`;
let _this = this;
return AWSUtils.putCfFile(
_this._JAWS._meta.profile,
_this._JAWS._meta.projectRootPath,
_this._region,
_this._jawsBucket,
_this._JAWS._meta.projectJson.name,
_this._stage,
'resources');
_this._JAWS._awsProfile,
_this._JAWS._projectRootPath,
_this._region,
_this._jawsBucket,
_this._JAWS._meta.projectJson.name,
_this._stage,
'resources');
}
@ -316,10 +318,10 @@ JAWS_DATA_MODEL_STAGE=${stage}`;
let _this = this;
let regionObj = {
region: _this._region,
iamRoleArnLambda: '',
region: _this._region,
iamRoleArnLambda: '',
iamRoleArnApiGateway: '',
jawsBucket: _this._jawsBucket,
jawsBucket: _this._jawsBucket,
};
if (_this._cfData) {
@ -341,8 +343,8 @@ JAWS_DATA_MODEL_STAGE=${stage}`;
}
return utils.writeFile(
path.join(_this._JAWS._meta.projectRootPath, 'jaws.json'),
JSON.stringify(_this._JAWS._meta.projectJson, null, 2));
path.join(_this._JAWS._projectRootPath, 'jaws.json'),
JSON.stringify(_this._JAWS._meta.projectJson, null, 2));
}
};

View File

@ -144,7 +144,7 @@ CMD.prototype.run = Promise.method(function() {
CMD.prototype._prepareResources = Promise.method(function() {
let _this = this;
return JawsUtils.findAllAwsmJsons(path.join(_this._JAWS._meta.projectRootPath, 'aws_modules'))
return JawsUtils.findAllAwsmJsons(path.join(_this._JAWS._projectRootPath, 'aws_modules'))
.then(function(jsonPaths) {
let hybrids = [];

View File

@ -76,7 +76,7 @@ const CMD = class Postinstall extends ProjectCmd {
return Promise.all(deferredDepInstalls);
})
.then(function() {
return utils.findAllEnvletsForAwsm(_this._JAWS._meta.projectRootPath, _this._moduleName);
return utils.findAllEnvletsForAwsm(_this._JAWS._projectRootPath, _this._moduleName);
})
.then(function(envlets) {
JawsCLI.log('Successfully installed ' + _this._moduleName);
@ -104,9 +104,9 @@ const CMD = class Postinstall extends ProjectCmd {
pkgMgrDir = 'node_modules';
}
let 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');
let srcAwsmPath = path.join(_this._JAWS._projectRootPath, pkgMgrDir, _this._moduleName, 'awsm'),
srcAwsmJsonPath = path.join(_this._JAWS._projectRootPath, pkgMgrDir, _this._moduleName, 'awsm.json'),
awsModsPath = path.join(_this._JAWS._projectRootPath, 'aws_modules');
if (!utils.fileExistsSync(srcAwsmJsonPath)) {
return Promise.reject(new JawsError('Module missing awsm.json file in root of project', JawsError.errorCodes.UNKNOWN));

View File

@ -66,7 +66,7 @@ const CMD = class Tag extends ProjectCmd {
return this._JAWS.validateProject()
.then(function() {
return utils[findAllFunc](path.join(_this._JAWS._meta.projectRootPath, 'aws_modules'));
return utils[findAllFunc](path.join(_this._JAWS._projectRootPath, 'aws_modules'));
})
.then(function(awsmJsonPaths) {
let tagQueue = [];
@ -95,7 +95,7 @@ const CMD = class Tag extends ProjectCmd {
return this._JAWS.validateProject()
.then(() => {
return utils[findAllFunc](path.join(_this._JAWS._meta.projectRootPath, 'aws_modules'));
return utils[findAllFunc](path.join(_this._JAWS._projectRootPath, 'aws_modules'));
})
.then(function(lAwsmJsonPaths) {

View File

@ -89,28 +89,28 @@ usage: jaws lambda alias <version> <aliasName>`,
JawsUtils.jawsDebug(`Preparing to version ${version} to alias ${aliasName} for stage:`, _this._stage);
return this._JAWS.validateProject()
.bind(_this)
.then(_this._promptStage)
.then(_this._setLambdaLogicalIds)
.then(_this._getRegions)
.each(region => {
//1) For each region, get all the lambdas for stack
let lStackName = AWSUtils.cfGetLambdasStackName(_this._stage, _this._JAWS._projectJson.name);
return AWSUtils.cfGetLambdaResourceSummaries(_this.Jaws._profile, region, lStackName)
.then(lambdaSummaries => {
//2) identify physical function names from logical
return AWSUtils.cfGetLambdaPhysicalsFromLogicals(_this._lambdaLogicalIdsToAlias, lambdaSummaries);
})
.then(lambdaNamesToAlias => {
//3) create alias (should only be one lambda name, as we only support aliasing one lambda at a time
return AWSUtils.lambdaCreateAlias(_this.Jaws._profile, region, lambdaNamesToAlias[0], version, aliasName);
});
})
.then(aliasedLambda => {
JawsCLI.log('Lambda Alias: Successfully aliased lambda in requested regions:');
JawsCLI.log(aliasedLambda);
return aliasedLambda;
});
.bind(_this)
.then(_this._promptStage)
.then(_this._setLambdaLogicalIds)
.then(_this._getRegions)
.each(region => {
//1) For each region, get all the lambdas for stack
let lStackName = AWSUtils.cfGetLambdasStackName(_this._stage, _this.Jaws._projectJson.name);
return AWSUtils.cfGetLambdaResourceSummaries(_this.Jaws._awsProfile, region, lStackName)
.then(lambdaSummaries => {
//2) identify physical function names from logical
return AWSUtils.cfGetLambdaPhysicalsFromLogicals(_this._lambdaLogicalIdsToAlias, lambdaSummaries);
})
.then(lambdaNamesToAlias => {
//3) create alias (should only be one lambda name, as we only support aliasing one lambda at a time
return AWSUtils.lambdaCreateAlias(_this.Jaws._awsProfile, region, lambdaNamesToAlias[0], version, aliasName);
});
})
.then(aliasedLambda => {
JawsCLI.log('Lambda Alias: Successfully aliased lambda in requested regions:');
JawsCLI.log(aliasedLambda);
return aliasedLambda;
});
}
/**
@ -124,7 +124,7 @@ usage: jaws lambda alias <version> <aliasName>`,
// If stage exists, skip
if (!this._stage) {
stages = Object.keys(_this.JAWS._projectJson.stages);
stages = Object.keys(_this.Jaws._projectJson.stages);
// If project only has 1 stage, skip prompt
if (stages.length === 1) {
@ -147,9 +147,9 @@ usage: jaws lambda alias <version> <aliasName>`,
}
return this.selectInput('AliasLambda: Choose a stage: ', choices, false)
.then(results => {
_this._stage = results[0].value;
});
.then(results => {
_this._stage = results[0].value;
});
}
/**
@ -166,7 +166,7 @@ usage: jaws lambda alias <version> <aliasName>`,
//Deploy to all regions in stage
let stage = this._stage,
projJson = this.JAWS._projectJson;
projJson = this.Jaws._projectJson;
let regionConfigs = projJson.stages[stage],
regions = regionConfigs.map(rCfg => {
@ -185,12 +185,12 @@ usage: jaws lambda alias <version> <aliasName>`,
_setLambdaLogicalIds() {
let _this = this;
return JawsUtils.getFullLambdaPaths(process.cwd(), [])
.then(fullAwsmJsonPaths => {
_this._lambdaLogicalIdsToAlias = fullAwsmJsonPaths.map(alp => {
let awsmJson = JawsUtils.readAndParseJsonSync(alp);
return JawsUtils.getLambdaName(awsmJson);
});
.then(fullAwsmJsonPaths => {
_this._lambdaLogicalIdsToAlias = fullAwsmJsonPaths.map(alp => {
let awsmJson = JawsUtils.readAndParseJsonSync(alp);
return JawsUtils.getLambdaName(awsmJson);
});
});
}
}

View File

@ -41,91 +41,91 @@ class Deployer {
awsmLambdas = [];
return BbPromise.try(function() {
})
.bind(_this)
.then(function() {
return _this._lambdaAwsmPaths;
})
.each(function(lambdaAwsmPath) {
let packager = new Packager(
_this._JAWS,
_this._stage,
_this._region,
lambdaAwsmPath
);
return BbPromise.try(function() {
})
.bind(_this)
.then(function() {
return packager.run();
})
.then(function(packagedLambda) {
let jawsBucket = _this._JAWS.getJawsBucket(_this._region, _this._stage);
JawsCLI.log('Lambda Deployer: Uploading ' + packagedLambda.lambdaName + ` to ${jawsBucket}`);
return AWSUtils.putLambdaZip(
_this._JAWS._profile,
_this._region,
jawsBucket,
projName,
_this._stage,
packagedLambda.lambdaName,
packagedLambda.zipBuffer
)
.then(function(s3Key) {
awsmLambdas.push({
awsmPath: lambdaAwsmPath,
Code: {
S3Bucket: jawsBucket,
S3Key: s3Key,
},
lambdaName: packagedLambda.lambdaName,
});
});
});
})
.then(function() {
//At this point all packages have been created and uploaded to s3
let lambdaRoleArn = JawsUtils
.getProjRegionConfigForStage(_this._JAWS._projectJson, _this._stage, _this._region).iamRoleArnLambda;
return [lambdaRoleArn, _this._generateLambdaCf(awsmLambdas, lambdaRoleArn)];
})
.spread(function(lambdaRoleArn, existingStack) {
if (_this._noExeCf) {
JawsCLI.log(`Lambda Deployer: not executing CloudFormation. Remember to set aaLambdaRoleArn parameter to ${lambdaRoleArn}`);
return false;
} else {
let createOrUpdate,
cfDeferred;
JawsUtils.jawsDebug(`Deploying with lambda role arn ${lambdaRoleArn}`);
if (existingStack) {
cfDeferred = AWSUtils.cfUpdateLambdasStack(_this._JAWS, _this._stage, _this._region, lambdaRoleArn);
createOrUpdate = 'update';
} else {
cfDeferred = AWSUtils.cfCreateLambdasStack(_this._JAWS, _this._stage, _this._region, lambdaRoleArn);
createOrUpdate = 'create';
}
JawsCLI.log('Running CloudFormation lambda deploy...');
let spinner = JawsCLI.spinner();
spinner.start();
return cfDeferred
.then(function(cfData) {
return AWSUtils.monitorCf(cfData, _this._JAWS._profile, _this._region, createOrUpdate);
})
.then(function() {
spinner.stop(true);
});
}
})
.then(function() {
JawsCLI.log('Lambda Deployer: Done deploying lambdas in ' + _this._region);
}
})
.bind(_this)
.then(function() {
return _this._lambdaAwsmPaths;
})
.each(function(lambdaAwsmPath) {
let packager = new Packager(
_this._JAWS,
_this._stage,
_this._region,
lambdaAwsmPath
);
return BbPromise.try(function() {
})
.bind(_this)
.then(function() {
return packager.run();
})
.then(function(packagedLambda) {
let jawsBucket = _this._JAWS.getJawsBucket(_this._region, _this._stage);
JawsCLI.log('Lambda Deployer: Uploading ' + packagedLambda.lambdaName + ` to ${jawsBucket}`);
return AWSUtils.putLambdaZip(
_this._JAWS._awsProfile,
_this._region,
jawsBucket,
projName,
_this._stage,
packagedLambda.lambdaName,
packagedLambda.zipBuffer
)
.then(function(s3Key) {
awsmLambdas.push({
awsmPath: lambdaAwsmPath,
Code: {
S3Bucket: jawsBucket,
S3Key: s3Key,
},
lambdaName: packagedLambda.lambdaName,
});
});
});
})
.then(function() {
//At this point all packages have been created and uploaded to s3
let lambdaRoleArn = JawsUtils
.getProjRegionConfigForStage(_this._JAWS._projectJson, _this._stage, _this._region).iamRoleArnLambda;
return [lambdaRoleArn, _this._generateLambdaCf(awsmLambdas, lambdaRoleArn)];
})
.spread(function(lambdaRoleArn, existingStack) {
if (_this._noExeCf) {
JawsCLI.log(`Lambda Deployer: not executing CloudFormation. Remember to set aaLambdaRoleArn parameter to ${lambdaRoleArn}`);
return false;
} else {
let createOrUpdate,
cfDeferred;
JawsUtils.jawsDebug(`Deploying with lambda role arn ${lambdaRoleArn}`);
if (existingStack) {
cfDeferred = AWSUtils.cfUpdateLambdasStack(_this._JAWS, _this._stage, _this._region, lambdaRoleArn);
createOrUpdate = 'update';
} else {
cfDeferred = AWSUtils.cfCreateLambdasStack(_this._JAWS, _this._stage, _this._region, lambdaRoleArn);
createOrUpdate = 'create';
}
JawsCLI.log('Running CloudFormation lambda deploy...');
let spinner = JawsCLI.spinner();
spinner.start();
return cfDeferred
.then(function(cfData) {
return AWSUtils.monitorCf(cfData, _this._JAWS._awsProfile, _this._region, createOrUpdate);
})
.then(function() {
spinner.stop(true);
});
}
})
.then(function() {
JawsCLI.log('Lambda Deployer: Done deploying lambdas in ' + _this._region);
}
);
}
/**
@ -150,84 +150,84 @@ class Deployer {
existingStack = true,
projName = this._JAWS._projectJson.name;
return AWSUtils.cfGetLambdasStackTemplate(_this._JAWS._profile, _this._region, _this._stage, projName)
.error(e => {
if (e && ['ValidationError', 'ResourceNotFoundException'].indexOf(e.code) == -1) { //ValidationError if DNE
console.error(
'Error trying to fetch existing lambda cf stack for region', _this._region, 'stage', _this._stage, e
);
throw new JawsError(e.message, JawsError.errorCodes.UNKNOWN);
}
JawsUtils.jawsDebug('no exsting lambda stack');
existingStack = false;
return false;
})
.then(cfTemplateBody => {
let templatesPath = path.join(__dirname, '..', 'templates'),
lambdaCf = JawsUtils.readAndParseJsonSync(path.join(templatesPath, 'lambdas-cf.json'));
delete lambdaCf.Resources.lTemplate;
lambdaCf.Description = projName + " lambdas";
lambdaCf.Parameters.aaLambdaRoleArn.Default = lambdaRoleArn;
//Always add lambdas tagged for deployment
taggedLambdaPkgs.forEach(function(pkg) {
let lResource = {
Type: "AWS::Lambda::Function",
Properties: {}
},
awsm = JawsUtils.readAndParseJsonSync(pkg.awsmPath);
lResource.Properties = awsm.lambda.cloudFormation;
lResource.Properties.Code = pkg.Code;
lResource.Properties.Role = {
Ref: "aaLambdaRoleArn"
};
JawsUtils.jawsDebug('adding Resource ' + pkg.lambdaName + ': ');
JawsUtils.jawsDebug(lResource);
lambdaCf.Resources[pkg.lambdaName] = lResource;
});
// If existing lambdas CF template
if (cfTemplateBody) {
JawsUtils.jawsDebug('existing stack detected');
// Find all lambdas in project, and copy ones that are in existing lambda-cf
let existingTemplate = JSON.parse(cfTemplateBody);
return JawsUtils.getAllLambdaNames(_this._JAWS._projectRootPath)
.then(allLambdaNames => {
Object.keys(existingTemplate.Resources).forEach(resource => {
if (!lambdaCf.Resources[resource] && allLambdaNames.indexOf(resource) != -1) {
JawsUtils.jawsDebug(`Adding exsiting lambda ${resource}`);
lambdaCf.Resources[resource] = existingTemplate.Resources[resource];
}
});
return lambdaCf;
});
} else {
return lambdaCf;
}
})
.then(lambdaCfTemplate => {
let lambdasCfPath = path.join(
_this._JAWS._projectRootPath,
'cloudformation',
_this._stage,
_this._region,
'lambdas-cf.json'
return AWSUtils.cfGetLambdasStackTemplate(_this._JAWS._awsProfile, _this._region, _this._stage, projName)
.error(e => {
if (e && ['ValidationError', 'ResourceNotFoundException'].indexOf(e.code) == -1) { //ValidationError if DNE
console.error(
'Error trying to fetch existing lambda cf stack for region', _this._region, 'stage', _this._stage, e
);
throw new JawsError(e.message, JawsError.errorCodes.UNKNOWN);
}
JawsUtils.jawsDebug(`Wrting to ${lambdasCfPath}`);
JawsUtils.jawsDebug('no exsting lambda stack');
existingStack = false;
return false;
})
.then(cfTemplateBody => {
let templatesPath = path.join(__dirname, '..', 'templates'),
lambdaCf = JawsUtils.readAndParseJsonSync(path.join(templatesPath, 'lambdas-cf.json'));
return JawsUtils.writeFile(lambdasCfPath, JSON.stringify(lambdaCfTemplate, null, 2))
.then(() => existingStack);
delete lambdaCf.Resources.lTemplate;
lambdaCf.Description = projName + " lambdas";
lambdaCf.Parameters.aaLambdaRoleArn.Default = lambdaRoleArn;
//Always add lambdas tagged for deployment
taggedLambdaPkgs.forEach(function(pkg) {
let lResource = {
Type: "AWS::Lambda::Function",
Properties: {}
},
awsm = JawsUtils.readAndParseJsonSync(pkg.awsmPath);
lResource.Properties = awsm.lambda.cloudFormation;
lResource.Properties.Code = pkg.Code;
lResource.Properties.Role = {
Ref: "aaLambdaRoleArn"
};
JawsUtils.jawsDebug('adding Resource ' + pkg.lambdaName + ': ');
JawsUtils.jawsDebug(lResource);
lambdaCf.Resources[pkg.lambdaName] = lResource;
});
// If existing lambdas CF template
if (cfTemplateBody) {
JawsUtils.jawsDebug('existing stack detected');
// Find all lambdas in project, and copy ones that are in existing lambda-cf
let existingTemplate = JSON.parse(cfTemplateBody);
return JawsUtils.getAllLambdaNames(_this._JAWS._projectRootPath)
.then(allLambdaNames => {
Object.keys(existingTemplate.Resources).forEach(resource => {
if (!lambdaCf.Resources[resource] && allLambdaNames.indexOf(resource) != -1) {
JawsUtils.jawsDebug(`Adding exsiting lambda ${resource}`);
lambdaCf.Resources[resource] = existingTemplate.Resources[resource];
}
});
return lambdaCf;
});
} else {
return lambdaCf;
}
})
.then(lambdaCfTemplate => {
let lambdasCfPath = path.join(
_this._JAWS._projectRootPath,
'cloudformation',
_this._stage,
_this._region,
'lambdas-cf.json'
);
JawsUtils.jawsDebug(`Wrting to ${lambdasCfPath}`);
return JawsUtils.writeFile(lambdasCfPath, JSON.stringify(lambdaCfTemplate, null, 2))
.then(() => existingStack);
});
}
}
@ -251,22 +251,22 @@ class Packager {
this._lambdaName = this.createLambdaName();
return this._createDistFolder()
.then(function() {
.then(function() {
// Package by runtime
switch (_this._awsmJson.lambda.cloudFormation.Runtime) {
case 'nodejs':
return _this._packageNodeJs()
.then(function(packageData) {
packageData.lambdaName = _this._lambdaName;
return packageData;
});
break;
default:
return Promise.reject(new JawsError('Unsupported lambda runtime ' + _this._awsmJson.lambda.cloudFormation.Runtime));
break;
}
});
// Package by runtime
switch (_this._awsmJson.cloudFormation.Lambda.Runtime) {
case 'nodejs':
return _this._packageNodeJs()
.then(function(packageData) {
packageData.lambdaName = _this._lambdaName;
return packageData;
});
break;
default:
return Promise.reject(new JawsError('Unsupported lambda runtime ' + _this._awsmJson.cloudFormation.Lambda.Runtime));
break;
}
});
}
/**
@ -304,44 +304,44 @@ class Packager {
JawsUtils.jawsDebug('copying', _this._JAWS._projectRootPath, 'to', _this._distDir);
// Copy entire test project to temp folder
_this._excludePatterns = _this._awsmJson.lambda.package.excludePatterns || [];
_this._excludePatterns = _this._awsmJson.package.excludePatterns || [];
wrench.copyDirSyncRecursive(
_this._JAWS._projectRootPath,
_this._distDir,
{
exclude: function(name, prefix) {
if (!_this._excludePatterns.length) {
return false;
_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);
let willExclude = (matches && matches.length > 0);
if (willExclude) {
JawsCLI.log(`Lambda Deployer: Excluding ${relPath}`);
}
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);
let willExclude = (matches && matches.length > 0);
if (willExclude) {
JawsCLI.log(`Lambda Deployer: Excluding ${relPath}`);
}
return willExclude;
});
},
}
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);
});
.then(function(s3ObjData) {
fs.writeFileSync(path.join(_this._distDir, '.env'), s3ObjData.Body);
});
}
/**
@ -354,47 +354,47 @@ class Packager {
let _this = this,
deferred = null;
if (_this._awsmJson.lambda.package
&& _this._awsmJson.lambda.package.optimize
&& _this._awsmJson.lambda.package.optimize.builder) {
if (_this._awsmJson.package
&& _this._awsmJson.package.optimize
&& _this._awsmJson.package.optimize.builder) {
deferred = _this._optimizeNodeJs()
.then(optimizedCodeBuffer => {
.then(optimizedCodeBuffer => {
// Lambda freaks out if code doesnt end in newline
let ocbWithNewline = optimizedCodeBuffer.concat(new Buffer('\n'));
let envData = fs.readFileSync(path.join(_this._distDir, '.env'));
// Lambda freaks out if code doesnt end in newline
let ocbWithNewline = optimizedCodeBuffer.concat(new Buffer('\n'));
let envData = fs.readFileSync(path.join(_this._distDir, '.env'));
let handlerFileName = _this._awsmJson.lambda.cloudFormation.Handler.split('.')[0],
compressPaths = [
let handlerFileName = _this._awsmJson.cloudFormation.Lambda.Handler.split('.')[0],
compressPaths = [
// handlerFileName is the full path lambda file including dir rel to back
{fileName: handlerFileName + '.js', data: ocbWithNewline},
{fileName: '.env', data: envData},
];
// handlerFileName is the full path lambda file including dir rel to back
{fileName: handlerFileName + '.js', data: ocbWithNewline},
{fileName: '.env', data: envData},
];
if (_this._awsmJson.lambda.package.optimize.includePaths.length) {
compressPaths = compressPaths.concat(_this._generateIncludePaths());
}
if (_this._awsmJson.package.optimize.includePaths.length) {
compressPaths = compressPaths.concat(_this._generateIncludePaths());
}
return _this._compressCode(compressPaths);
});
return _this._compressCode(compressPaths);
});
} else {
// User chose not to optimize, zip up whatever is in back
_this._awsmJson.lambda.package.optimize.includePaths = ['.'];
let compressPaths = _this._generateIncludePaths();
deferred = _this._compressCode(compressPaths);
_this._awsmJson.package.optimize.includePaths = ['.'];
let compressPaths = _this._generateIncludePaths();
deferred = _this._compressCode(compressPaths);
}
return deferred
.then(function(compressedCodeBuffer) {
let zippedFilePath = path.join(_this._distDir, 'package.zip'); // Save for auditing;
fs.writeFileSync(zippedFilePath, compressedCodeBuffer);
.then(function(compressedCodeBuffer) {
let zippedFilePath = path.join(_this._distDir, 'package.zip'); // Save for auditing;
fs.writeFileSync(zippedFilePath, compressedCodeBuffer);
JawsCLI.log(`Lambda Deployer: Compressed lambda written to ${zippedFilePath}`);
JawsCLI.log(`Lambda Deployer: Compressed lambda written to ${zippedFilePath}`);
return Promise.resolve({awsmFilePath: _this._lambdaPath, zipBuffer: compressedCodeBuffer});
});
return Promise.resolve({awsmFilePath: _this._lambdaPath, zipBuffer: compressedCodeBuffer});
});
}
/**
@ -406,12 +406,12 @@ class Packager {
let _this = this;
if (!_this._awsmJson.lambda.package.optimize
|| !_this._awsmJson.lambda.package.optimize.builder) {
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.lambda.package.optimize.builder.toLowerCase() == 'browserify') {
if (_this._awsmJson.package.optimize.builder.toLowerCase() == 'browserify') {
return _this._browserifyBundle();
} else {
return Promise.reject(new JawsError(`Unsupported builder ${builder}`));
@ -432,7 +432,7 @@ class Packager {
};
let b = browserify({
basedir: _this._distDir,
entries: [_this._awsmJson.lambda.cloudFormation.Handler.split('.')[0] + '.js'],
entries: [_this._awsmJson.cloudFormation.Lambda.Handler.split('.')[0] + '.js'],
standalone: 'lambda',
browserField: false, // Setup for node app (copy logic of --node in bin/args.js)
builtins: false,
@ -447,21 +447,21 @@ class Packager {
},
});
if (_this._awsmJson.lambda.package.optimize.babel) {
if (_this._awsmJson.package.optimize.babel) {
b.transform(babelify)
}
if (_this._awsmJson.lambda.package.optimize.transform) {
b.transform(_this._awsmJson.lambda.package.optimize.transform);
if (_this._awsmJson.package.optimize.transform) {
b.transform(_this._awsmJson.package.optimize.transform);
}
// optimize.exclude
_this._awsmJson.lambda.package.optimize.exclude.forEach(file => {
_this._awsmJson.package.optimize.exclude.forEach(file => {
b.exclude(file);
});
// optimize.ignore
_this._awsmJson.lambda.package.optimize.ignore.forEach(file => {
_this._awsmJson.package.optimize.ignore.forEach(file => {
b.ignore(file);
});
@ -478,7 +478,7 @@ class Packager {
fs.writeFileSync(bundledFilePath, bundledBuf);
JawsCLI.log(`Lambda Deployer: Bundled file written to ${bundledFilePath}`);
if (_this._awsmJson.lambda.package.optimize.exclude) {
if (_this._awsmJson.package.optimize.exclude) {
let result = UglifyJS.minify(bundledFilePath, uglyOptions);
if (!result || !result.code) {
@ -509,7 +509,7 @@ class Packager {
stats,
fullPath;
_this._awsmJson.lambda.package.optimize.includePaths.forEach(p => {
_this._awsmJson.package.optimize.includePaths.forEach(p => {
try {
fullPath = path.resolve(path.join(_this._distDir, p));
stats = fs.lstatSync(fullPath);
@ -524,20 +524,20 @@ class Packager {
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;
}
.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('Adding', pathInZip);
compressPaths.push({fileName: pathInZip, data: fs.readFileSync(filePath)});
}
});
let filePath = [fullPath, file].join('/');
if (fs.lstatSync(filePath).isFile()) {
let pathInZip = path.join(dirname, file);
JawsUtils.jawsDebug('Adding', pathInZip);
compressPaths.push({fileName: pathInZip, data: fs.readFileSync(filePath)});
}
});
}
});
@ -564,8 +564,8 @@ class Packager {
if (zippedData.length > 52428800) {
Promise.reject(new JawsError(
'Zip file is > the 50MB Lambda deploy limit (' + zippedData.length + ' bytes)',
JawsError.errorCodes.ZIP_TOO_BIG)
'Zip file is > the 50MB Lambda deploy limit (' + zippedData.length + ' bytes)',
JawsError.errorCodes.ZIP_TOO_BIG)
);
}
@ -644,22 +644,22 @@ usage: jaws lambda deploy [rel or abs path to lambda dirs. default is cwd]`,
this._noExeCf = (noExeCf == true || noExeCf == 'true');
return this._JAWS.validateProject()
.bind(_this)
.then(_this._promptStage)
.then(_this._validate)
.then(() => {
JawsUtils.jawsDebug('Deploying to stage:', _this._stage);
return _this._setLambdaAwsmPaths(lambdaPaths);
})
.then(_this._getRegions)
.each(region => {
let d = new Deployer(_this.JAWS, _this._lambdaAwsmPathsToDeploy, _this._stage, region, _this._noExeCf);
return d.run();
})
.then(lambdaAwsmPkgs => {
JawsCLI.log('Lambda Deployer: Successfully deployed lambdas to the requested regions!');
return lambdaAwsmPkgs;
});
.bind(_this)
.then(_this._promptStage)
.then(_this._validate)
.then(() => {
JawsUtils.jawsDebug('Deploying to stage:', _this._stage);
return _this._setLambdaAwsmPaths(lambdaPaths);
})
.then(_this._getRegions)
.each(region => {
let d = new Deployer(_this.Jaws, _this._lambdaAwsmPathsToDeploy, _this._stage, region, _this._noExeCf);
return d.run();
})
.then(lambdaAwsmPkgs => {
JawsCLI.log('Lambda Deployer: Successfully deployed lambdas to the requested regions!');
return lambdaAwsmPkgs;
});
}
/**
@ -673,7 +673,7 @@ usage: jaws lambda deploy [rel or abs path to lambda dirs. default is cwd]`,
// If stage exists, skip
if (!this._stage) {
stages = Object.keys(_this.JAWS._projectJson.stages);
stages = Object.keys(_this.Jaws._projectJson.stages);
// If project only has 1 stage, skip prompt
if (stages.length === 1) {
@ -696,9 +696,9 @@ usage: jaws lambda deploy [rel or abs path to lambda dirs. default is cwd]`,
}
return this.selectInput('Lambda Deployer: Choose a stage: ', choices, false)
.then(results => {
_this._stage = results[0].value;
});
.then(results => {
_this._stage = results[0].value;
});
}
/**
@ -715,7 +715,7 @@ usage: jaws lambda deploy [rel or abs path to lambda dirs. default is cwd]`,
//Deploy to all regions in stage
let stage = this._stage,
projJson = this.JAWS._projectJson;
projJson = this.Jaws._projectJson;
let regionConfigs = projJson.stages[stage],
regions = regionConfigs.map(rCfg => {
@ -734,7 +734,7 @@ usage: jaws lambda deploy [rel or abs path to lambda dirs. default is cwd]`,
*/
_validate() {
// Validate: Check stage exists within project
if (!this.JAWS._projectJson.stages[_this._stage]) {
if (!this.Jaws._projectJson.stages[_this._stage]) {
return Promise.reject(new JawsError(`Invalid stage ${stage}`, JawsError.errorCodes.UNKNOWN));
}
@ -750,9 +750,9 @@ usage: jaws lambda deploy [rel or abs path to lambda dirs. default is cwd]`,
_setLambdaAwsmPaths(lambdaPaths) {
let _this = this;
return JawsUtils.getFullLambdaPaths(process.cwd(), lambdaPaths)
.then(fullAwsmJsonPaths => {
_this._lambdaAwsmPathsToDeploy = fullAwsmJsonPaths;
});
.then(fullAwsmJsonPaths => {
_this._lambdaAwsmPathsToDeploy = fullAwsmJsonPaths;
});
}
}

View File

@ -0,0 +1,358 @@
'use strict';
/**
* Action: ModuleCreate
*/
const JawsPlugin = require('../../JawsPlugin'),
JawsError = require('../../jaws-error'),
JawsCLI = require('../../utils/cli'),
fs = require('fs'),
path = require('path'),
os = require('os'),
BbPromise = require('bluebird'),
AWSUtils = require('../../utils/aws'),
JawsUtils = require('../../utils');
let fs = require('fs');
BbPromise.promisifyAll(fs);
const supportedRuntimes = {
nodejs: {
defaultPkgMgr: 'npm',
validPkgMgrs: ['npm']
}
};
/**
* ModuleCreate Class
*/
class ModuleCreate extends JawsPlugin {
/**
* @param Jaws class object
* @param config object
*/
constructor(Jaws, config) {
super(Jaws, config);
this._templatesDir = path.join(__dirname, '..', '..', 'templates');
this._resource = "";
this._action = "";
this._pkgMgr = false;
this._createLambda = false;
this._createEndpoint = false;
this._runtime = 'nodejs';
}
/**
* Define your plugins name
*
* @returns {string}
*/
static getName() {
return 'jaws.core.' + ModuleCreate.name;
}
/**
* @returns {Promise} upon completion of all registrations
*/
registerActions() {
this.Jaws.action(this.createModule.bind(this), {
handler: 'moduleCreate',
description: `Creates scaffolding for new aws module.
usage: jaws module create <module resource> <action>`,
context: 'module',
contextAction: 'create',
options: [
{
option: 'runtime',
shortcut: 'r',
description: ''
},
{
option: 'lambda',
shortcut: 'l',
description: ''
},
{
option: 'endpoint',
shortcut: 'e',
description: ''
},
{
option: 'package-manager',
shortcut: 'p',
description: ''
},
],
});
return Promise.resolve();
}
/**
*
* @param runtime
* @param createLambda
* @param createEndpoint
* @param pkgMgr
* @param resourceAction <array> resource action
* @returns {Promise}
*/
createModule(runtime, createLambda, createEndpoint, pkgMgr) {
let _this = this,
resourceAction = Array.prototype.slice.call(arguments, 5);
if (!resourceAction || resourceAction.length !== 2) {
return Promise.reject(new JawsError('Must specify a resource and action'));
}
if (!createLambda && !createEndpoint) { //default is to create both
createEndpoint = true;
createLambda = true;
}
this._resource = resourceAction[0];
this._action = resourceAction[1];
this._createEndpoint = createEndpoint;
this._createLambda = createLambda;
this._runtime = runtime || 'nodejs';
if (!supportedRuntimes[this._runtime]) {
throw new JawsError('Unsupported runtime ' + _this._runtime, JawsError.errorCodes.UNKNOWN);
}
this._pkgMgr = pkgMgr || supportedRuntimes[this._runtime].defaultPkgMgr;
if (supportedRuntimes[this._runtime].validPkgMgrs.indexOf(this._pkgMgr) == -1) {
throw new JawsError('Unsupported package manger "' + this._pkgMgr + '"', JawsError.errorCodes.UNKNOWN);
}
return this._JAWS.validateProject()
.bind(_this)
.then(_this._sanitizeData)
.then(_this._createSkeleton)
.then(_this._createPackageMgrSkeleton)
.then(_this._initRuntime)
.then(function() {
JawsCLI.log('Successfully created '
+ _this._resourcee
+ '/'
+ _this._action);
});
}
_sanitizeData() {
this._action = this._action.toLowerCase().trim()
.replace(/\s/g, '-')
.replace(/[^a-zA-Z-\d:]/g, '')
.substring(0, 19);
this._resource = this._resource.toLowerCase().trim()
.replace(/\s/g, '-')
.replace(/[^a-zA-Z-\d:]/g, '')
.substring(0, 19);
}
_generateActionAwsmJson() {
let actionTemplateJson = utils.readAndParseJsonSync(path.join(_this._templatesDir, 'action.awsm.json'));
//We prefix with an l to make sure the CloudFormation resource map index is unique
actionTemplateJson.name = 'l' + this._resource.charAt(0).toUpperCase() + this._resource.slice(1) + this.action.charAt(0).toUpperCase() + this.action.slice(1);
if (this._createLambda) {
actionTemplateJson.cloudFormation.Lambda.Runtime = this._runtime;
// Create files for lambda actions
switch (this._runtime) {
case 'nodejs':
actionTemplateJson.cloudFormation.Lambda.Handler = path.join('aws_modules', this._resource, this._action, 'handler.handler');
break;
default:
throw new JawsError('This runtime is not supported "' + this._runtime + '"', JawsError.errorCodes.UNKNOWN);
break;
}
} else {
delete actionTemplateJson.lambda;
}
if (this._createEndpoint) {
actionTemplateJson.cloudFormation.APIGatewayEndpoint.Path = this._resource + '/' + this._action;
} else {
delete actionTemplateJson.cloudFormation.APIGatewayEndpoint;
}
//TODO: how do we support LambdaEventSourceMapping and LambdaAccessPolicyX
delete actionTemplateJson.cloudFormation.LambdaEventSourceMapping;
delete actionTemplateJson.cloudFormation.LambdaAccessPolicyX;
return actionTemplateJson;
}
_generateModuleAwsmJson() {
let moduleTemplateJson = utils.readAndParseJsonSync(path.join(this._templatesDir, 'module.awsm.json'));
moduleTemplateJson.name = this._resource;
return moduleTemplateJson;
};
/**
*
* @returns {Promise}
* @private
*/
_createPackageMgrSkeleton() {
let _this = this,
deferredWrites = [];
switch (_this.runtime) {
case 'nodejs':
if (_this.pkgMgr == 'npm') {
let modulePath = path.join(_this._JAWS._projectRootPath, 'node_modules', _this._resource);
// Create node_module if DNE in node_modules
if (!utils.dirExistsSync(modulePath)) {
deferredWrites.push(fs.mkdirAsync(modulePath));
}
// Create module package.json if DNE in node_module
if (!utils.fileExistsSync(path.join(modulePath, 'package.json'))) {
let packageJsonTemplate = utils.readAndParseJsonSync(path.join(_this._templatesDir, 'nodejs', 'package.json'));
packageJsonTemplate.name = _this._resource;
packageJsonTemplate.description = 'An aws-module';
packageJsonTemplate.dependencies = {};
deferredWrites.push(
fs.writeFileAsync(
path.join(modulePath, 'package.json'),
JSON.stringify(packageJsonTemplate, null, 2)
)
);
}
// Create module awsm.json if DNE in node_module
if (!utils.fileExistsSync(path.join(modulePath, 'awsm.json'))) {
let moduleTemplateJson = _this._generateModuleAwsmJson();
deferredWrites.push(
utils.writeFile(
path.join(modulePath, 'awsm.json'),
JSON.stringify(moduleTemplateJson, null, 2)));
}
// Create root lib folder if DNE in node_module
let modLibPath = path.join(modulePath, 'lib');
if (!utils.dirExistsSync(modLibPath)) {
deferredWrites.push(fs.mkdirAsync(modLibPath));
}
// Create awsm folder if DNE in node_module
if (!utils.dirExistsSync(path.join(modulePath, 'awsm'))) {
deferredWrites.push(fs.mkdirAsync(path.join(modulePath, 'awsm')));
}
// Create action if DNE in node_module
let actionPath = path.join(modulePath, 'awsm', _this.action);
if (!utils.dirExistsSync(actionPath)) {
let actionTemplateJson = this._generateActionAwsmJson(),
handlerJs = fs.readFileSync(path.join(_this._templatesDir, 'nodejs', 'handler.js')),
indexJs = fs.readFileSync(path.join(_this._templatesDir, 'nodejs', 'index.js'));
deferredWrites.push(
utils.writeFile(
path.join(actionPath, 'awsm.json'),
JSON.stringify(actionTemplateJson, null, 2)
),
utils.writeFile(path.join(actionPath, 'handler.js'), handlerJs),
utils.writeFile(path.join(actionPath, 'index.js'), indexJs),
utils.writeFile(path.join(actionPath, 'event.json'), '{}')
);
}
}
break;
default:
break;
}
return Promise.all(deferredWrites);
}
/**
*
* @returns {Promise}
* @private
*/
_createSkeleton() {
let _this = this,
writeFilesDeferred = [];
let modulePath = path.join(_this._JAWS._projectRootPath, 'aws_modules', _this._resource),
actionPath = path.join(modulePath, _this.action);
// If module/action already exists, throw error
if (utils.dirExistsSync(actionPath)) {
throw new JawsError(
actionPath + ' already exists',
JawsError.errorCodes.INVALID_PROJECT_JAWS
);
}
//module path will get created by util.writeFile if DNE
// If module awsm.json doesn't exist, create it
if (!utils.fileExistsSync(path.join(modulePath, 'awsm.json'))) {
let moduleTemplateJson = _this._generateModuleAwsmJson();
writeFilesDeferred.push(
utils.writeFile(
path.join(modulePath, 'awsm.json'),
JSON.stringify(moduleTemplateJson, null, 2)
)
);
}
// Create action folder
writeFilesDeferred.push(actionPath);
// Create action awsm.json
let actionTemplateJson = _this._generateActionAwsmJson();
let handlerJs = fs.readFileSync(path.join(this._templatesDir, 'nodejs', 'handler.js')),
indexJs = fs.readFileSync(path.join(this._templatesDir, 'nodejs', 'index.js'));
writeFilesDeferred.push(
utils.writeFile(path.join(actionPath, 'handler.js'), handlerJs),
utils.writeFile(path.join(actionPath, 'index.js'), indexJs),
utils.writeFile(path.join(actionPath, 'event.json'), '{}'),
utils.writeFile(path.join(actionPath, 'awsm.json'), JSON.stringify(actionTemplateJson, null, 2))
);
return Promise.all(writeFilesDeferred);
}
/**
*
* @returns {Promise}
* @private
*/
_initRuntime() {
let _this = this;
JawsCLI.log('Preparing your runtime..');
if (_this._runtime === 'nodejs') {
let packageJsonTemplate = JawsUtils.readAndParseJsonSync(path.join(_this._templatesDir, 'nodejs', 'package.json'));
packageJsonTemplate.name = _this.Jaws._projectJson.name;
return fs.writeFileAsync(path.join(_this.Jaws, _projectRootPath, 'package.json'), JSON.stringify(packageJsonTemplate, null, 2))
.then(function() {
JawsCLI.log('Installing jaws-core module...');
JawsUtils.npmInstall(_this._projectRootPath);
});
}
}
}
module.exports = ModuleCreate;

View File

@ -4,16 +4,17 @@
* Action: ProjectCreate
*/
const JawsPlugin = require('../../JawsPlugin'),
JawsError = require('../../jaws-error'),
JawsCLI = require('../../utils/cli'),
fs = require('fs'),
path = require('path'),
os = require('os'),
BbPromise = require('bluebird'),
AWSUtils = require('../../utils/aws'),
JawsUtils = require('../../utils'),
shortid = require('shortid');
const JawsPlugin = require('../../JawsPlugin'),
JawsError = require('../../jaws-error'),
JawsCLI = require('../../utils/cli'),
path = require('path'),
os = require('os'),
BbPromise = require('bluebird'),
AWSUtils = require('../../utils/aws'),
JawsUtils = require('../../utils');
let fs = require('fs');
BbPromise.promisifyAll(fs);
/**
* ProjectCreate Class
@ -28,6 +29,7 @@ class ProjectCreate extends JawsPlugin {
constructor(Jaws, config) {
super(Jaws, config);
this._templatesDir = path.join(__dirname, '..', '..', 'templates');
}
/**
@ -44,7 +46,7 @@ class ProjectCreate extends JawsPlugin {
*/
registerActions() {
this.Jaws.action(this._action.bind(this), {
this.Jaws.action(this.createProject.bind(this), {
handler: 'projectCreate',
description: 'Creates scaffolding for a new JAWS project',
context: 'project',
@ -85,269 +87,224 @@ class ProjectCreate extends JawsPlugin {
}
/**
* Action
* - Contains control flow
*
* @param name
* @param domain
* @param stage
* @param region
* @param notificationEmail
* @param runtime
* @param noCf
* @returns {Promise}
* @private
*/
_action(name, domain, stage, region, notificationEmail, runtime, noCf) {
createProject(name, domain, stage, region, notificationEmail, runtime, noCf) {
let _this = this;
/**
* Non-Interactive Validations
*/
return new Promise(resolve => {
/**
* Non-Interactive Validations
*/
if (!_this.Jaws._interactive) {
// Check API Keys
if (!_this.Jaws._awsAdminKeyId || !_this.Jaws._awsAdminSecretKey) {
throw new JawsError('Missing AWS API Key and/or AWS Secret Key');
}
// Check Params
if (!name || !stage || !region || !domain || !notificationEmail) {
throw new JawsError('Missing required properties');
}
if (!_this.Jaws._interactive && !this.Jaws._awsProfile) {
// Check API Keys
if (!_this.Jaws._awsAdminKeyId || !_this.Jaws._awsAdminSecretKey) {
throw new JawsError('Missing AWS API Key and/or AWS Secret Key');
}
/**
* Defaults
*/
_this._name = name || null;
_this._domain = domain || null;
_this._stage = stage ? stage.toLowerCase().replace(/\W+/g, '').substring(0, 15) : null;
_this._notificationEmail = notificationEmail;
_this._region = region;
_this._runtime = runtime || 'nodejs';
_this._noCf = noCf;
// Interactive Defaults
if (_this.Jaws._interactive) {
_this._prompts = {
properties: {},
};
_this.Prompter = JawsCLI.prompt();
_this.Prompter.override = {};
_this._spinner = null;
// Check Params
if (!name || !stage || !region || !domain || !notificationEmail) {
throw new JawsError('Missing required properties');
}
}
/**
* Control Flow
*/
/**
* Defaults
*/
return BbPromise.try(function() {
_this._name = name || null;
_this._domain = domain || null;
_this._stage = stage ? stage.toLowerCase().replace(/\W+/g, '').substring(0, 15) : null;
_this._notificationEmail = notificationEmail;
_this._region = region;
_this._runtime = runtime || 'nodejs';
_this._noCf = noCf;
// ASCII Greeting
if (_this.Jaws._interactive) JawsCLI.ascii();
// Interactive Defaults
if (_this.Jaws._interactive) {
_this._prompts = {
properties: {},
};
_this.Prompter = JawsCLI.prompt();
_this.Prompter.override = {};
_this._spinner = null;
}
/**
* Control Flow
*/
return BbPromise.try(function() {
if (_this.Jaws._interactive) {
JawsCLI.asciiGreeting();
}
})
.bind(_this)
.then(_this._prompt)
.then(_this._prepareProjectData)
.then(_this._createProjectDirectory)
.then(_this._putEnvFile)
.then(_this._putCfFile)
.then(_this._createCfStack)
.then(_this._createProjectJson)
.then(_this._initRuntime);
return resolve();
});
}
/**
* Generate ShortId
*/
_generateShortId(maxLen) {
return shortid.generate().replace(/\W+/g, '').substring(0, maxLen).replace(/[_-]/g, '');
.bind(_this)
.then(_this._prompt)
.then(_this._prepareProjectData)
.then(_this._createProjectDirectory)
.then(_this._createJawsBucket)
.then(_this._putEnvFile)
.then(_this._putCfFile)
.then(_this._createCfStack)
.then(_this._createProjectJson);
}
/**
* Prompt
* @returns {*}
* @returns {Promise}
* @private
*/
_prompt() {
let _this = this,
overrides = {};
// Skip, if not interactive
if (!this.Jaws._interactive) return Promise.resolve();
//Setup overrides based off of member var values
['name', 'domain', 'notificationEmail', 'stage', 'awsAdminKeyId', 'awsAdminSecretKey']
.forEach(memberVarKey => {
overrides[memberVarKey] = _this['_' + memberVarKey];
});
JawsUtils.jawsDebug('Prompting for new project information');
let _this = this;
let nameDescription = 'Enter a project name: ';
// Prompt: name (project name)
_this.Prompter.override.name = _this._name;
// Set default name
_this._name = 'jaws-' + _this._generateShortId(19);
_this._prompts.properties.name = {
description: nameDescription.yellow,
default: _this._name,
message: 'Name must be only letters, numbers or dashes',
conform: function (name) {
let re = /^[a-zA-Z0-9-_]+$/;
return re.test(name);
},
let prompts = {
properties: {
name: {
description: 'Enter a project name: '.yellow,
default: 'jaws-' + JawsUtils.generateShortId(19),
message: 'Name must be only letters, numbers or dashes',
required: true,
conform: function(name) {
let re = /^[a-zA-Z0-9-_]+$/;
return re.test(name);
},
},
domain: {
description: 'Enter a project domain (used for jaws s3 bucket name): '.yellow,
default: 'myapp.com',
message: 'Domain must only contain lowercase letters, numbers, periods and dashes',
required: true,
conform: function(bucket) {
let re = /^[a-z0-9-.]+$/;
return re.test(bucket);
},
},
notificationEmail: {
description: 'Enter an email to use for AWS alarms: '.yellow,
required: true,
message: 'Please enter a valid email',
default: 'me@myapp.com',
conform: function(email) {
return (!email) ? false : true;
},
},
stage: {
description: 'Enter a stage name for this project: '.yellow,
required: true,
default: 'dev',
message: 'Stage must be letters only',
conform: function(stage) {
let re = /^[a-zA-Z]+$/;
return re.test(stage);
},
},
}
};
// Prompt: domain - for AWS hosted zone and more
_this.Prompter.override.domain = _this._domain;
let domainDescription = 'Enter a project domain (You can change this at any time: ';
_this._prompts.properties.domain = {
description: domainDescription.yellow,
default: 'myapp.com',
message: 'Domain must only contain lowercase letters, numbers, periods and dashes',
conform: function (bucket) {
let re = /^[a-z0-9-.]+$/;
return re.test(bucket);
},
};
// Prompt: notification email - for AWS alerts
_this.Prompter.override.notificationEmail = _this._notificationEmail;
_this._prompts.properties.notificationEmail = {
description: 'Enter an email to use for AWS alarms: '.yellow,
required: true,
message: 'Please enter a valid email',
default: 'me@myapp.com',
conform: function (email) {
if (!email) return false;
return true;
},
};
// Prompt: stage
_this.Prompter.override.stage = _this._stage;
let stageDescription = 'Enter a stage for this project: ';
_this._prompts.properties.stage = {
description: stageDescription.yellow,
default: 'dev',
message: 'Stage must be letters only',
conform: function (stage) {
let re = /^[a-zA-Z]+$/;
return re.test(stage);
},
};
// Prompt: notification email - for AWS alerts
_this.Prompter.override.notificationEmail = _this._notificationEmail;
let notificationEmailDescription = 'Enter an email to use for AWS alarms: ';
_this._prompts.properties.notificationEmail = {
description: notificationEmailDescription.yellow,
required: true,
message: 'Please enter a valid email',
default: 'you@yourapp.com',
conform: function (email) {
if (!email) return false;
return true;
},
};
// Prompt: API Keys - Create an AWS profile by entering API keys
//Create aws credentials if DNE
if (!JawsUtils.fileExistsSync(path.join(AWSUtils.getConfigDir(), 'credentials'))) {
_this.Prompter.override.awsAdminKeyId = _this._awsAdminKeyId;
let apiKeyDescription = 'Enter the ACCESS KEY ID for your Admin AWS IAM User: ';
_this._prompts.properties.awsAdminKeyId = {
description: apiKeyDescription.yellow,
prompts.properties.awsAdminKeyId = {
description: 'Enter the ACCESS KEY ID for your Admin AWS IAM User: '.yellow,
required: true,
message: 'Please enter a valid access key ID',
conform: function (key) {
if (!key) return false;
return true;
conform: function(key) {
return (key) ? false : true;
},
};
_this.Prompter.override.awsAdminSecretKey = _this._awsAdminSecretKey;
let apiSecretDescription = 'Enter the SECRET ACCESS KEY for your Admin AWS IAM User: ';
_this._prompts.properties.awsAdminSecretKey = {
description: apiSecretDescription.yellow,
prompts.properties.awsAdminSecretKey = {
description: 'Enter the SECRET ACCESS KEY for your Admin AWS IAM User: '.yellow,
required: true,
message: 'Please enter a valid secret access key',
conform: function (key) {
conform: function(key) {
if (!key) return false;
return true;
},
};
}
// Show Prompts
return _this.Prompter.getAsync(_this._prompts)
.then(function (answers) {
_this._name = answers.name;
_this._domain = answers.domain;
_this._stage = answers.stage.toLowerCase();
_this._notificationEmail = answers.notificationEmail;
_this._awsAdminKeyId = answers.awsAdminKeyId;
_this._awsAdminSecretKey = answers.awsAdminSecretKey;
return this.promptInput(prompts, overrides)
.then(function(answers) {
_this._name = answers.name;
_this._domain = answers.domain;
_this._stage = answers.stage.toLowerCase();
_this._notificationEmail = answers.notificationEmail;
_this._awsAdminKeyId = answers.awsAdminKeyId;
_this._awsAdminSecretKey = answers.awsAdminSecretKey;
// If region exists, skip select prompt
if (_this._region) return;
// If region exists, skip select prompt
if (_this._region) {
return;
}
// Prompt: region select
let choices = [];
AWSUtils.validLambdaRegions.forEach(function (r) {
choices.push({
key: '',
value: r,
label: r,
});
// Prompt: region select
let 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;
});
})
.then(function () {
// If profile exists, skip select prompt
if (_this._profile) return Promise.resolve();
// If aws credentials were passed, skip select prompt
if (_this._awsAdminKeyId && _this._awsAdminSecretKey) return Promise.resolve();
// Prompt: profile select
let profilesList = AWSUtils.profilesMap(),
profiles = Object.keys(profilesList),
choices = [];
for (let i = 0; i < profiles.length; i++) {
choices.push({
key: '',
value: profiles[i],
label: profiles[i],
});
}
return JawsCLI.select('Select an AWS profile for your project: ', choices, false)
.then(function (results) {
_this._profile = results[0].value;
});
});
return _this.selectInput('Select a region for your project: ', choices, false)
.then(results => {
_this._region = results[0].value;
});
})
.then(function() {
// If profile exists, skip select prompt
if (_this._awsProfile) {
return;
}
// If aws credentials were passed, skip select prompt
if (_this._awsAdminKeyId && _this._awsAdminSecretKey) {
return;
}
// Prompt: profile select
let profilesList = AWSUtils.profilesMap(),
profiles = Object.keys(profilesList),
choices = [];
for (let i = 0; i < profiles.length; i++) {
choices.push({
key: '',
value: profiles[i],
label: profiles[i],
});
}
return _this.selectInput('Select an AWS profile for your project: ', choices, false)
.then(results => {
_this._awsProfile = results[0].value;
});
});
}
/**
* Prepare Project Data
* @returns {Promise}
* @private
*/
_prepareProjectData() {
@ -367,21 +324,21 @@ class ProjectCreate extends JawsPlugin {
// Append unique id if name is in use
if (JawsUtils.dirExistsSync(path.join(process.cwd(), _this._name))) {
_this._name = _this._name + '-' + _this._generateShortId(19);
_this._name = _this._name + '-' + JawsUtils.generateShortId(19);
}
// Append unique id if domain is default
if (_this._domain === 'myapp.com') {
_this._domain = 'myapp-' + _this._generateShortId(8) + '.com';
_this._domain = 'myapp-' + JawsUtils.generateShortId(8) + '.com';
}
// Set JAWS Bucket
_this._jawsBucket = JawsUtils.generateJawsBucketName(_this._stage, _this._region, _this._domain);
// If no profile, ensure access keys, create profile
if (!_this._profile) {
if (!_this._awsProfile) {
AWSUtils.profilesSet('default', _this._region, _this.Jaws._awsAdminKeyId, _this.Jaws._awsAdminSecretKey);
_this._profile = 'default';
_this._awsProfile = 'default';
}
return Promise.resolve();
@ -389,6 +346,7 @@ class ProjectCreate extends JawsPlugin {
/**
* Create Project Directory
* @returns {Private}
* @private
*/
@ -398,42 +356,52 @@ class ProjectCreate extends JawsPlugin {
_this._projectRootPath = path.resolve(path.join(path.dirname('.'), _this._name));
// Prepare admin.env
let adminEnv = 'JAWS_ADMIN_AWS_PROFILE=' + _this._profile + os.EOL;
let adminEnv = 'JAWS_ADMIN_AWS_PROFILE=' + _this._awsProfile + os.EOL;
// Prepare README.md
let readme = '#' + _this._name;
// Create Project Scaffolding
return JawsUtils.writeFile(
path.join(_this._projectRootPath, '.env'),
'JAWS_STAGE=' + _this._stage
+ '\nJAWS_DATA_MODEL_STAGE=' + _this._stage
)
.then(function () {
return Promise.all([
fs.mkdirAsync(path.join(_this._projectRootPath, 'tests')),
fs.mkdirAsync(path.join(_this._projectRootPath, 'lib')),
fs.mkdirAsync(path.join(_this._projectRootPath, 'aws_modules')),
JawsUtils.writeFile(path.join(_this._projectRootPath, 'admin.env'), adminEnv),
JawsUtils.writeFile(path.join(_this._projectRootPath, 'README.md'), readme),
JawsUtils.generateResourcesCf(
_this._projectRootPath,
_this._name,
_this._domain,
_this._stage,
_this._region,
_this._notificationEmail,
_this._jawsBucket
),
fs.writeFileAsync(path.join(_this._projectRootPath, '.gitignore'), fs.readFileSync(__dirname + '/../../templates/gitignore')),
]);
});
path.join(_this._projectRootPath, '.env'),
'JAWS_STAGE=' + _this._stage
+ '\nJAWS_DATA_MODEL_STAGE=' + _this._stage
)
.then(function() {
return Promise.all([
fs.mkdirAsync(path.join(_this._projectRootPath, 'tests')),
fs.mkdirAsync(path.join(_this._projectRootPath, 'lib')),
fs.mkdirAsync(path.join(_this._projectRootPath, 'aws_modules')),
JawsUtils.writeFile(path.join(_this._projectRootPath, 'admin.env'), adminEnv),
JawsUtils.writeFile(path.join(_this._projectRootPath, 'README.md'), readme),
JawsUtils.generateResourcesCf(
_this._projectRootPath,
_this._name,
_this._domain,
_this._stage,
_this._region,
_this._notificationEmail
),
fs.writeFileAsync(path.join(_this._projectRootPath, '.gitignore'), fs.readFileSync(path.join(_this._templatesDir, 'gitignore'))),
]);
});
}
/**
* Create jaws bucket if it does not exist
*
* @returns {Promise}
* @private
*/
_createJawsBucket() {
JawsUtils.jawsDebug('Creating jaws s3 bucket: ', this._jawsBucket);
return AWSUtils.createBucket(this._awsProfile, this._region, this._jawsBucket);
}
/**
* Put ENV File
* - Creates ENV file in JAWS stage/region bucket
* @returns {Private}
* @private
*/
@ -446,18 +414,18 @@ class ProjectCreate extends JawsPlugin {
JAWS_DATA_MODEL_STAGE=${stage}`;
return AWSUtils.putEnvFile(
_this._profile,
_this._region,
_this._jawsBucket,
_this._name,
_this._stage,
envFileContents);
_this._awsProfile,
_this._region,
_this._jawsBucket,
_this._name,
_this._stage,
envFileContents);
}
/**
* Put CF File
* @returns {*}
* @returns {Promise}
* @private
*/
@ -466,22 +434,26 @@ class ProjectCreate extends JawsPlugin {
let _this = this;
return AWSUtils.putCfFile(
_this._profile,
_this._projectRootPath,
_this._region,
_this._jawsBucket,
_this._name,
_this._stage,
'resources');
_this._awsProfile,
_this._projectRootPath,
_this._region,
_this._jawsBucket,
_this._name,
_this._stage,
'resources');
}
/**
* Create CloudFormation Stack
* @returns {Promise}
* @private
*/
_createCfStack() {
if (this._noCf) {
JawsUtils.jawsDebug('No execute CF was specified, skipping');
return Promise.resolve();
}
let _this = this;
@ -491,45 +463,42 @@ class ProjectCreate extends JawsPlugin {
// Create CF stack
return AWSUtils.cfCreateResourcesStack(
_this._profile,
_this._region,
_this._projectRootPath,
_this._name,
_this._stage,
_this._domain,
_this._notificationEmail,
_this._jawsBucket
)
.then(function (cfData) {
return AWSUtils.monitorCf(cfData, _this._profile, _this._region, 'create');
});
_this._awsProfile,
_this._region,
_this._projectRootPath,
_this._name,
_this._stage,
_this._domain,
_this._notificationEmail
)
.then(cfData => {
return AWSUtils.monitorCf(cfData, _this._awsProfile, _this._region, 'create');
});
}
/**
* Create Project JSON
* @private
*/
_createProjectJson() {
_createProjectJson(cfStackData) {
let _this = this,
iamRoleArnLambda,
iamRoleArnApiGateway;
if (_this._cfData) {
for (let i = 0; i < _this._cfData.Outputs.length; i++) {
if (_this._cfData.Outputs[i].OutputKey === 'IamRoleArnLambda') {
iamRoleArnLambda = _this._cfData.Outputs[i].OutputValue;
if (cfStackData) {
for (let i = 0; i < cfStackData.Outputs.length; i++) {
if (cfStackData.Outputs[i].OutputKey === 'IamRoleArnLambda') {
iamRoleArnLambda = cfStackData.Outputs[i].OutputValue;
}
if (_this._cfData.Outputs[i].OutputKey === 'IamRoleArnApiGateway') {
iamRoleArnApiGateway = _this._cfData.Outputs[i].OutputValue;
if (cfStackData.Outputs[i].OutputKey === 'IamRoleArnApiGateway') {
iamRoleArnApiGateway = cfStackData.Outputs[i].OutputValue;
}
}
}
let templatesPath = path.join(__dirname, '..', '..', 'templates'),
jawsJson = JawsUtils.readAndParseJsonSync(path.join(templatesPath, 'jaws.json'));
let jawsJson = JawsUtils.readAndParseJsonSync(path.join(_this._templatesDir, 'jaws.json'));
jawsJson.stages[_this._stage] = [{
region: _this._region,
@ -538,38 +507,14 @@ class ProjectCreate extends JawsPlugin {
jawsBucket: _this._jawsBucket,
}];
jawsJson.name = _this._name;
jawsJson.name = _this._name;
jawsJson.domain = _this._domain;
fs.writeFileSync(path.join(_this._projectRootPath, 'jaws.json'),
JSON.stringify(jawsJson, null, 2));
JSON.stringify(jawsJson, null, 2));
return jawsJson;
}
/**
* Init Runtime
* @private
*/
_initRuntime() {
let _this = this;
JawsCLI.log('Preparing your runtime and installing jaws-core module...');
if (_this._runtime === 'nodejs') {
let packageJsonTemplate = JawsUtils.readAndParseJsonSync(path.join(__dirname, '..', 'templates', 'nodejs', 'package.json'));
packageJsonTemplate.name = _this._name;
return fs.writeFileAsync(path.join(_this._projectRootPath, 'package.json'), JSON.stringify(packageJsonTemplate, null, 2))
.then(function () {
JawsUtils.jawsDebug('test_utils', 'Running NPM install...');
JawsUtils.npmInstall(_this._projectRootPath);
});
} else {
throw new JawsError('Unsupported runtime "' + _this.runtime + '"', JawsError.errorCodes.UNKNOWN);
}
}
}
module.exports = ProjectCreate;

View File

@ -78,32 +78,32 @@ class VersionLambda extends JawsPlugin {
this._stage = stage;
this._region = region; //may not be set
return this._JAWS.validateProject()
.bind(_this)
.then(_this._promptStage)
.then(() => {
JawsUtils.jawsDebug('publishing version for stage:', _this._stage);
return _this._setLambdaLogicalIds(lambdaPaths);
})
.then(_this._getRegions)
.each(region => {
//1) For each region, get all the lambdas for stack
let lStackName = AWSUtils.cfGetLambdasStackName(_this._stage, _this._JAWS._projectJson.name);
return AWSUtils.cfGetLambdaResourceSummaries(_this.Jaws._profile, region, lStackName)
.then(lambdaSummaries => {
//2) identify physical function names from logical
return AWSUtils.cfGetLambdaPhysicalsFromLogicals(_this._lambdaLogicalIdsToVersion, lambdaSummaries);
})
.then(lambdaNamesToVersion => {
//3) publishVersions
return AWSUtils.lambdaPublishVersions(_this.Jaws._profile, region, lambdaNamesToVersion);
});
})
.then(versionedLambdas => {
JawsCLI.log('Lambda VersionLambda: Successfully published following lambda versions to the requested regions:');
JawsCLI.log(versionedLambdas);
return versionedLambdas;
});
return this.Jaws.validateProject()
.bind(_this)
.then(_this._promptStage)
.then(() => {
JawsUtils.jawsDebug('publishing version for stage:', _this._stage);
return _this._setLambdaLogicalIds(lambdaPaths);
})
.then(_this._getRegions)
.each(region => {
//1) For each region, get all the lambdas for stack
let lStackName = AWSUtils.cfGetLambdasStackName(_this._stage, _this.Jaws._projectJson.name);
return AWSUtils.cfGetLambdaResourceSummaries(_this.Jaws._awsProfile, region, lStackName)
.then(lambdaSummaries => {
//2) identify physical function names from logical
return AWSUtils.cfGetLambdaPhysicalsFromLogicals(_this._lambdaLogicalIdsToVersion, lambdaSummaries);
})
.then(lambdaNamesToVersion => {
//3) publishVersions
return AWSUtils.lambdaPublishVersions(_this.Jaws._awsProfile, region, lambdaNamesToVersion);
});
})
.then(versionedLambdas => {
JawsCLI.log('Lambda VersionLambda: Successfully published following lambda versions to the requested regions:');
JawsCLI.log(versionedLambdas);
return versionedLambdas;
});
}
/**
@ -117,7 +117,7 @@ class VersionLambda extends JawsPlugin {
// If stage exists, skip
if (!this._stage) {
stages = Object.keys(_this.JAWS._projectJson.stages);
stages = Object.keys(_this.Jaws._projectJson.stages);
// If project only has 1 stage, skip prompt
if (stages.length === 1) {
@ -140,9 +140,9 @@ class VersionLambda extends JawsPlugin {
}
return this.selectInput('VersionLambda: Choose a stage: ', choices, false)
.then(results => {
_this._stage = results[0].value;
});
.then(results => {
_this._stage = results[0].value;
});
}
/**
@ -159,7 +159,7 @@ class VersionLambda extends JawsPlugin {
//Deploy to all regions in stage
let stage = this._stage,
projJson = this.JAWS._projectJson;
projJson = this.Jaws._projectJson;
let regionConfigs = projJson.stages[stage],
regions = regionConfigs.map(rCfg => {
@ -179,12 +179,12 @@ class VersionLambda extends JawsPlugin {
_setLambdaLogicalIds(lambdaPaths) {
let _this = this;
return JawsUtils.getFullLambdaPaths(process.cwd(), lambdaPaths)
.then(fullAwsmJsonPaths => {
_this._lambdaLogicalIdsToVersion = fullAwsmJsonPaths.map(alp => {
let awsmJson = JawsUtils.readAndParseJsonSync(alp);
return JawsUtils.getLambdaName(awsmJson);
});
.then(fullAwsmJsonPaths => {
_this._lambdaLogicalIdsToVersion = fullAwsmJsonPaths.map(alp => {
let awsmJson = JawsUtils.readAndParseJsonSync(alp);
return JawsUtils.getLambdaName(awsmJson);
});
});
}
}

View File

@ -5,5 +5,6 @@
"author": "",
"description": "",
"domain": "",
"stages": {}
"stages": {},
"plugins": []
}

View File

@ -4,7 +4,7 @@
"description": "A JAWS application",
"author": "me",
"license": "MIT",
"private": false,
"private": true,
"repository": {
"type": "git",
"url": "git://github.com/"

View File

@ -9,10 +9,6 @@
"Type": "String",
"Default": "myapp.com"
},
"aaJawsBucket": {
"Type": "String",
"Default": "myapp.com"
},
"aaStage": {
"Type": "String"
},
@ -139,7 +135,9 @@
":",
[
"arn:aws:logs",
{ "Ref" : "AWS::Region" },
{
"Ref": "AWS::Region"
},
"*:*"
]
]
@ -189,7 +187,9 @@
":",
[
"arn:aws:lambda",
{ "Ref" : "AWS::Region" },
{
"Ref": "AWS::Region"
},
"*:*"
]
]
@ -208,16 +208,6 @@
}
]
}
},
"JawsBucket" : {
"Type" : "AWS::S3::Bucket",
"Properties" : {
"AccessControl" : "Private",
"BucketName" : {
"Ref": "aaJawsBucket"
}
},
"DeletionPolicy" : "Retain"
}
},
"Outputs": {

View File

@ -53,8 +53,8 @@ module.exports.getConfigDir = function() {
let env = process.env;
let home = env.HOME ||
env.USERPROFILE ||
(env.HOMEPATH ? ((env.HOMEDRIVE || 'C:/') + env.HOMEPATH) : null);
env.USERPROFILE ||
(env.HOMEPATH ? ((env.HOMEDRIVE || 'C:/') + env.HOMEPATH) : null);
if (!home) {
throw new JawsError('Cant find homedir', JawsError.errorCodes.MISSING_HOMEDIR);
@ -94,16 +94,16 @@ module.exports.profilesSet = function(awsProfile, awsRegion, accessKeyId, secret
}
fs.appendFileSync(
credsPath,
`[${awsProfile}]
credsPath,
`[${awsProfile}]
aws_access_key_id = ${accessKeyId.trim()}
aws_secret_access_key = ${secretKey.trim()}\n`);
let profileNameForConfig = (awsProfile == 'default') ? 'default' : 'profile ' + awsProfile;
fs.appendFileSync(
configPath,
`[${profileNameForConfig}]
configPath,
`[${profileNameForConfig}]
region = ${awsRegion}\n`);
};
@ -152,8 +152,8 @@ exports.iamGetRole = function(awsProfile, awsRegion, roleName) {
if (error) {
return reject(new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN));
error.message,
JawsError.errorCodes.UNKNOWN));
} else {
return resolve(data);
}
@ -190,8 +190,8 @@ exports.cfDescribeStacks = function(awsProfile, awsRegion, stackName) {
if (error) {
return reject(new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN));
error.message,
JawsError.errorCodes.UNKNOWN));
} else {
return resolve(data);
}
@ -231,8 +231,8 @@ exports.cfDescribeStackResource = function(awsProfile, awsRegion, stackId, cfRes
if (error) {
return reject(new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN));
error.message,
JawsError.errorCodes.UNKNOWN));
} else {
return resolve(data);
}
@ -296,40 +296,40 @@ exports.cfGetLambdaResourceSummaries = function(awsProfile, awsRegion, stackName
return new Promise((resolve, reject)=> {
async.whilst(
function() {
return moreResources === true;
},
function(callback) {
_this.cfListStackResources(awsProfile, awsRegion, stackName, nextStackToken)
.then(function(lambdaCfResources) {
if (lambdaCfResources.StackResourceSummaries) {
lambdas = lambdas.concat(lambdaCfResources.StackResourceSummaries);
}
function() {
return moreResources === true;
},
function(callback) {
_this.cfListStackResources(awsProfile, awsRegion, stackName, nextStackToken)
.then(function(lambdaCfResources) {
if (lambdaCfResources.StackResourceSummaries) {
lambdas = lambdas.concat(lambdaCfResources.StackResourceSummaries);
}
// Check if more resources are available
if (!lambdaCfResources.NextToken) {
moreResources = false;
} else {
nextStackToken = lambdaCfResources.NextToken;
}
// Check if more resources are available
if (!lambdaCfResources.NextToken) {
moreResources = false;
} else {
nextStackToken = lambdaCfResources.NextToken;
}
callback();
})
.catch(function(error) {
JawsCli.log('Warning: JAWS could not find a deployed Cloudformation '
+ 'template containing lambda functions.');
console.log(error);
moreResources = false;
callback(error);
});
},
function(err) {
if (err) {
reject(err);
} else {
resolve(lambdas);
}
callback();
})
.catch(function(error) {
JawsCli.log('Warning: JAWS could not find a deployed Cloudformation '
+ 'template containing lambda functions.');
console.log(error);
moreResources = false;
callback(error);
});
},
function(err) {
if (err) {
reject(err);
} else {
resolve(lambdas);
}
}
);
});
};
@ -384,11 +384,11 @@ exports.lambdaPublishVersions = function(awsProfile, awsRegion, functionNames) {
});
return Promise.all(deferreds)
.then(data => {
return data.map(d => {
return {FunctionName: d.FunctionName, Version: d.Version, FunctionArn: d.FunctionArn};
});
.then(data => {
return data.map(d => {
return {FunctionName: d.FunctionName, Version: d.Version, FunctionArn: d.FunctionArn};
});
});
};
/**
@ -419,9 +419,9 @@ exports.lambdaCreateAlias = function(awsProfile, awsRegion, functionName, functi
};
return L.createAliasAsync(params)
.then(d => {
return {AliasArn: d.AliasArn, FunctionVersion: d.FunctionVersion};
});
.then(d => {
return {AliasArn: d.AliasArn, FunctionVersion: d.FunctionVersion};
});
};
/**
@ -466,11 +466,11 @@ exports.cfGetLambdasStackTemplate = function(awsProfile, awsRegion, stage, projN
}));
return CF.getTemplateAsync({
StackName: _this.cfGetLambdasStackName(stage, projName)
})
.then(function(data) {
return data.TemplateBody;
});
StackName: _this.cfGetLambdasStackName(stage, projName)
})
.then(function(data) {
return data.TemplateBody;
});
};
/**
@ -502,13 +502,13 @@ exports.putCfFile = function(awsProfile, projRootPath, awsRegion, bucketName, pr
};
return this.putS3Object(awsProfile, awsRegion, params)
.then(function() {
//Really AWS - TemplateURL is an https:// URL. You force us to lookup endpt vs bucket/key attrs!?!? wtf not cool
let s3 = new AWS.S3();
.then(function() {
//Really AWS - TemplateURL is an https:// URL. You force us to lookup endpt vs bucket/key attrs!?!? wtf not cool
let s3 = new AWS.S3();
//Seriously, not cool...
return 'https://' + s3.endpoint.hostname + `/${bucketName}/${key}`;
})
//Seriously, not cool...
return 'https://' + s3.endpoint.hostname + `/${bucketName}/${key}`;
})
};
/**
@ -550,10 +550,10 @@ exports.cfCreateLambdasStack = function(JAWS, stage, region, lambdaRoleArn) {
};
return _this.putCfFile(awsProfile, projRootPath, region, bucketName, projName, stage, 'lambdas')
.then(function(templateUrl) {
params.TemplateURL = templateUrl;
return CF.createStackAsync(params);
});
.then(function(templateUrl) {
params.TemplateURL = templateUrl;
return CF.createStackAsync(params);
});
};
/**
@ -591,10 +591,10 @@ exports.cfUpdateLambdasStack = function(JAWS, stage, region, lambdaRoleArn) {
};
return _this.putCfFile(awsProfile, projRootPath, region, bucketName, projName, stage, 'lambdas')
.then(function(templateUrl) {
params.TemplateURL = templateUrl;
return CF.updateStackAsync(params);
});
.then(function(templateUrl) {
params.TemplateURL = templateUrl;
return CF.updateStackAsync(params);
});
};
/**
@ -614,8 +614,7 @@ exports.cfCreateResourcesStack = function(awsProfile,
projName,
projStage,
projDomain,
projNotificationEmail,
jawsBucket) {
projNotificationEmail) {
let _this = this;
@ -643,10 +642,6 @@ exports.cfCreateResourcesStack = function(awsProfile,
ParameterKey: 'aaStage',
ParameterValue: projStage,
UsePreviousValue: false,
}, {
ParameterKey: 'aaJawsBucket',
ParameterValue: jawsBucket,
UsePreviousValue: false,
}, {
ParameterKey: 'aaDataModelStage',
ParameterValue: projStage,
@ -707,10 +702,10 @@ exports.cfUpdateResourcesStack = function(JAWS, stage, region) {
};
return _this.putCfFile(awsProfile, projRootPath, region, bucketName, projName, stage, 'resources')
.then(function(templateUrl) {
params.TemplateURL = templateUrl;
return CF.updateStackAsync(params);
});
.then(function(templateUrl) {
params.TemplateURL = templateUrl;
return CF.updateStackAsync(params);
});
};
/**
@ -746,31 +741,31 @@ exports.monitorCf = function(cfData, awsProfile, region, createOrUpdate, checkFr
stackData = null;
async.whilst(
function() {
return stackStatus !== stackStatusComplete;
},
function() {
return stackStatus !== stackStatusComplete;
},
function(callback) {
setTimeout(function() {
_this.cfDescribeStacks(awsProfile, region, cfData.StackId)
.then(function(data) {
stackData = data;
stackStatus = stackData.Stacks[0].StackStatus;
function(callback) {
setTimeout(function() {
_this.cfDescribeStacks(awsProfile, region, cfData.StackId)
.then(function(data) {
stackData = data;
stackStatus = stackData.Stacks[0].StackStatus;
if (!stackStatus || validStatuses.indexOf(stackStatus) === -1) {
console.log((data.Stacks && data.Stacks.length ? data.Stacks[0] : data));
return reject(new JawsError(
`Something went wrong while ${createOrUpdate}ing your cloudformation`));
} else {
return callback();
}
});
}, checkFreq);
},
if (!stackStatus || validStatuses.indexOf(stackStatus) === -1) {
console.log((data.Stacks && data.Stacks.length ? data.Stacks[0] : data));
return reject(new JawsError(
`Something went wrong while ${createOrUpdate}ing your cloudformation`));
} else {
return callback();
}
});
}, checkFreq);
},
function() {
return resolve(stackData.Stacks[0]);
}
function() {
return resolve(stackData.Stacks[0]);
}
);
});
};
@ -780,30 +775,30 @@ exports.monitorCf = function(cfData, awsProfile, region, createOrUpdate, checkFr
* @param awsProfile
* @param awsRegion
* @param bucketName
* @returns {*}
* @returns {Promise}
*/
exports.createBucket = function(awsProfile, awsRegion, bucketName) {
this.configAWS(awsProfile, awsRegion);
let s3 = Promise.promisifyAll(new AWS.S3());
return s3.getBucketAclAsync({Bucket: bucketName})
.then(function() {
})
.error(function(err) {
if (err.code == 'AccessDenied') {
throw new JawsError(
`Bucket ${bucketName} already exists and you do not have permissions to use it`,
JawsError.errorCodes.ACCESS_DENIED
);
}
.then(function() {
//we are good, bucket already exists and we own it!
})
.error(function(err) {
if (err.code == 'AccessDenied') {
throw new JawsError(
`Bucket ${bucketName} already exists and you do not have permissions to use it`,
JawsError.errorCodes.ACCESS_DENIED
);
}
return s3.createBucketAsync({
Bucket: bucketName,
ACL: 'private',
});
return s3.createBucketAsync({
Bucket: bucketName,
ACL: 'private',
});
});
};
/**
@ -900,9 +895,9 @@ exports.putLambdaZip = function(awsProfile, awsRegion, bucketName, projectName,
utils.jawsDebug(`lambda zip s3 key: ${key}`);
return this.putS3Object(awsProfile, awsRegion, params)
.then(function() {
return key;
});
.then(function() {
return key;
});
};
/**
@ -931,8 +926,8 @@ exports.cwGetLogStreams = function(logGroupName, limit) {
if (error) {
return reject(new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN));
error.message,
JawsError.errorCodes.UNKNOWN));
} else {
return resolve(data);
}
@ -963,8 +958,8 @@ exports.cwGetStreamEvents = function(logGroupName, logStreamName) {
cwLogs.getLogEvents(params, function(err, data) {
if (error) {
return reject(new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN));
error.message,
JawsError.errorCodes.UNKNOWN));
} else {
return resolve(data);
}

View File

@ -4,16 +4,16 @@
* JAWS Services: CLI
*/
let Promise = require('bluebird'),
prompt = require('prompt'),
path = require('path'),
os = require('os'),
let Promise = require('bluebird'),
prompt = require('prompt'),
path = require('path'),
os = require('os'),
JawsError = require('../jaws-error/index'),
utils = require('../utils'),
fs = require('fs'),
chalk = require('chalk'),
Spinner = require('cli-spinner').Spinner,
keypress = require('keypress');
utils = require('../utils'),
fs = require('fs'),
chalk = require('chalk'),
Spinner = require('cli-spinner').Spinner,
keypress = require('keypress');
Promise.promisifyAll(fs);
Promise.promisifyAll(prompt);
@ -21,16 +21,17 @@ Promise.promisifyAll(prompt);
/**
* ASCII
*/
exports.ascii = function() {
exports.asciiGreeting = function() {
let ver = require('../../package.json').version;
let art = '';
art = art + ' ____ _____ __ __ _________ ' + os.EOL;
art = art + ' | | / _ \\/ \\ / \\/ _____/ ' + os.EOL;
art = art + ' | |/ /_\\ \\ \\/\\/ /\\_____ \\ ' + os.EOL;
art = art + ' /\\__| / | \\ / / \\ ' + os.EOL;
art = art + ' \\________\\____|__ /\\__/\\__/ /_________/ v1 (BETA)' + os.EOL;
art = art + '' + os.EOL;
art = art + ' *** The Server-Less Framework *** ' + os.EOL;
art = art + ' ____ _____ __ __ _________ ' + os.EOL;
art = art + ' | | / _ \\/ \\ / \\/ _____/ ' + os.EOL;
art = art + ' | |/ /_\\ \\ \\/\\/ /\\_____ \\ ' + os.EOL;
art = art + ' /\\__| / | \\ / / \\ ' + os.EOL;
art = art + ' \\________\\____|__ /\\__/\\__/ /_________/ v' + ver + os.EOL;
art = art + '' + os.EOL;
art = art + ' *** The Server-Less Framework *** ' + os.EOL;
console.log(chalk.yellow(art));
};
@ -50,11 +51,11 @@ exports.spinner = function(message) {
// Non-interactive spinner object
spinner = {
start: function (message) {
start: function(message) {
message = message || 'Loading... ';
process.stdout.write(`JAWS: ${message}`);
},
stop: function (message) {
stop: function(message) {
// Because of how spinner is used with normal library
// we do a small hack and still allow for setting message
@ -85,7 +86,7 @@ exports.log = function(message) {
exports.prompt = function() {
prompt.start();
prompt.delimiter = '';
prompt.message = 'JAWS: ';
prompt.message = 'JAWS: ';
return prompt;
};
@ -138,7 +139,7 @@ Select._render = function() {
for (let i = 0; i < _this.state.choices.length; i++) {
let choice = _this.state.choices[i],
line = '';
line = '';
// Increment line count
_this.state.lines++;
@ -216,7 +217,7 @@ exports.select = function(message, choices, multi, doneLabel) {
if (!_this.isInteractive()) {
throw new JawsError('You must specify all necessary options when in a non-interactive mode.',
JawsError.errorCodes.UNKNOWN);
JawsError.errorCodes.UNKNOWN);
}
// Set keypress listener, if not set
@ -264,7 +265,7 @@ 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') {
&& Select.state.choices[Select.state.index - 1].action.toLowerCase() === 'done') {
process.stdin.removeListener('keypress', keypressHandler);
return Select._close();
} else {
@ -293,21 +294,21 @@ exports.select = function(message, choices, multi, doneLabel) {
// Update CheckList
Select.state = {
choices: choices,
index: (choices[0] && choices[0].spacer) ? 2 : 1,
lines: 0,
multi: multi,
choices: choices,
index: (choices[0] && choices[0].spacer) ? 2 : 1,
lines: 0,
multi: multi,
doneLabel: doneLabel ? doneLabel : 'Done',
};
// Add Done and Cancel to choices
if (Select.state.multi) {
Select.state.choices.push(
{spacer: '- - - - -'},
{
action: 'Done',
label: Select.state.doneLabel,
});
{spacer: '- - - - -'},
{
action: 'Done',
label: Select.state.doneLabel,
});
}
// Log Message

View File

@ -11,7 +11,8 @@ let Promise = require('bluebird'),
readdirp = require('readdirp'),
JawsError = require('../jaws-error'),
fs = require('fs'),
mkdirpAsync = require('mkdirp-then');
mkdirpAsync = require('mkdirp-then'),
shortid = require('shortid');
Promise.promisifyAll(fs);
@ -37,26 +38,26 @@ exports.findAllAwsmPathsOfType = function(startPath, type) {
}
return _this.readRecursively(startPath, '*awsm.json')
.then(function(jsonPaths) {
return new Promise(function(resolve, reject) {
.then(function(jsonPaths) {
return new Promise(function(resolve, reject) {
let jawsPathsOfType = [];
let jawsPathsOfType = [];
// Check each file to ensure it is a lambda
async.eachLimit(jsonPaths, 10, function(jsonPath, cb) {
let lambdaJawsPath = path.join(startPath, jsonPath),
json = require(lambdaJawsPath);
// Check each file to ensure it is a lambda
async.eachLimit(jsonPaths, 10, function(jsonPath, cb) {
let lambdaJawsPath = path.join(startPath, jsonPath),
json = require(lambdaJawsPath);
if (typeof json[jawsJsonAttr] !== 'undefined') jawsPathsOfType.push(lambdaJawsPath);
return cb();
},
if (typeof json[jawsJsonAttr] !== 'undefined') jawsPathsOfType.push(lambdaJawsPath);
return cb();
},
function(error) {
if (error) reject(error);
resolve(jawsPathsOfType);
});
});
function(error) {
if (error) reject(error);
resolve(jawsPathsOfType);
});
});
});
};
/**
@ -102,15 +103,15 @@ exports.findProjectRootPath = function(startDir) {
exports.execute = function(promise) {
promise
.catch(JawsError, function(e) {
console.error(e);
process.exit(e.messageId);
})
.error(function(e) {
console.error(e);
process.exit(1);
})
.done();
.catch(JawsError, function(e) {
console.error(e);
process.exit(e.messageId);
})
.error(function(e) {
console.error(e);
process.exit(1);
})
.done();
};
/**
@ -128,15 +129,15 @@ exports.readRecursively = function(path, filter) {
root: path,
fileFilter: filter,
})
.on('data', function(entry) {
files.push(entry.path);
})
.on('error', function(error) {
reject(error);
})
.on('end', function() {
resolve(files);
});
.on('data', function(entry) {
files.push(entry.path);
})
.on('error', function(error) {
reject(error);
})
.on('end', function() {
resolve(files);
});
});
};
@ -171,24 +172,24 @@ exports.findAllEnvletsForAwsm = function(projectRootPath, modName) {
let _this = this;
return this.findAllAwsmPathsOfType(path.join(projectRootPath, 'aws_modules', modName), 'lambda')
.then(function(awsmJsonPaths) {
let envletKeys = [];
.then(function(awsmJsonPaths) {
let envletKeys = [];
awsmJsonPaths.forEach(function(awsmJsonPath) {
let awsmJson = _this.readAndParseJsonSync(awsmJsonPath);
awsmJsonPaths.forEach(function(awsmJsonPath) {
let awsmJson = _this.readAndParseJsonSync(awsmJsonPath);
//TODO: change to es6 set...
if (awsmJson.lambda && awsmJson.lambda.envlets) {
awsmJson.lambda.envlets.forEach(function(envlet) {
if (envletKeys.indexOf(envlet) == -1) {
envletKeys.push(envlet);
}
});
}
});
return envletKeys;
//TODO: change to es6 set...
if (awsmJson.lambda && awsmJson.lambda.envlets) {
awsmJson.lambda.envlets.forEach(function(envlet) {
if (envletKeys.indexOf(envlet) == -1) {
envletKeys.push(envlet);
}
});
}
});
return envletKeys;
});
};
/**
@ -199,11 +200,11 @@ exports.findAllEnvletsForAwsm = function(projectRootPath, modName) {
*/
exports.findAllAwsmJsons = function(startPath) {
return this.readRecursively(startPath, '*awsm.json')
.then(function(jsonPaths) {
return jsonPaths.map(function(jsonPath) {
return path.resolve(path.join(startPath, jsonPath));
});
.then(function(jsonPaths) {
return jsonPaths.map(function(jsonPath) {
return path.resolve(path.join(startPath, jsonPath));
});
});
};
/**
@ -250,9 +251,13 @@ exports.writeFile = function(filePath, contents) {
}
return mkdirpAsync(path.dirname(filePath))
.then(function() {
return fs.writeFileAsync(filePath, contents);
});
.then(function() {
return fs.writeFileAsync(filePath, contents);
});
};
exports.generateShortId = function(maxLen) {
return shortid.generate().replace(/\W+/g, '').substring(0, maxLen).replace(/[_-]/g, '');
};
/**
@ -294,16 +299,16 @@ exports.getAllLambdaNames = function(projectRootPath) {
lambdaNames = [];
return this.findAllLambdas(projectRootPath)
.then(function(lambdaAwsmPaths) {
lambdaAwsmPaths.forEach(function(ljp) {
let awsm = _this.readAndParseJsonSync(ljp),
lambdaName = _this.getLambdaName(awsm);
.then(function(lambdaAwsmPaths) {
lambdaAwsmPaths.forEach(function(ljp) {
let awsm = _this.readAndParseJsonSync(ljp),
lambdaName = _this.getLambdaName(awsm);
lambdaNames.push(lambdaName);
});
return lambdaNames;
lambdaNames.push(lambdaName);
});
return lambdaNames;
});
};
/**
@ -371,7 +376,7 @@ exports.npmInstall = function(dir) {
process.chdir(cwd);
};
exports.generateResourcesCf = function(projRootPath, projName, projDomain, stage, region, notificationEmail, jawsBucket) {
exports.generateResourcesCf = function(projRootPath, projName, projDomain, stage, region, notificationEmail) {
let cfTemplate = require('../templates/resources-cf');
cfTemplate.Parameters.aaProjectName.Default = projName;
@ -379,13 +384,12 @@ exports.generateResourcesCf = function(projRootPath, projName, projDomain, stage
cfTemplate.Parameters.aaProjectDomain.Default = projDomain;
cfTemplate.Parameters.aaStage.Default = stage;
cfTemplate.Parameters.aaDataModelStage.Default = stage; //to simplify bootstrap use same stage
cfTemplate.Parameters.aaJawsBucket.Default = jawsBucket;
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)
path.join(projRootPath, 'cloudformation', stage, region, 'resources-cf.json'),
JSON.stringify(cfTemplate, null, 2)
);
};