new awsm.json format, alias lambda, version lambda

This commit is contained in:
doapp-ryanp 2015-10-20 22:17:50 -05:00
parent 806ced38a8
commit bc072fb925
16 changed files with 1323 additions and 931 deletions

View File

@ -6,3 +6,6 @@
## Running test cases
1. Set env vars defined in `tests/config.js`. By default if you do not set `TEST_JAWS_EXE_CF` no AWS resources will be created.
1. Make sure you have run `npm install` from the jaws project root
1. Run the mocha test from the CLI (`mocha tests/all.js`) or setup mocha test from your IDE. WebStorm allows you to run a debugger in the IDE for your test cases which is really handy to track down issues.

View File

@ -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);
});
}

View File

@ -55,23 +55,23 @@ class ApiDeployer {
let _this = this;
return this._findTaggedEndpoints()
.bind(_this)
.then(_this._validateAndSantizeTaggedEndpoints)
.then(_this._fetchDeployedLambdas)
.then(_this._findOrCreateApi)
.then(_this._saveApiId)
.then(_this._listApiResources)
.then(_this._buildEndpoints)
.then(_this._createDeployment)
.then(function() {
return 'https://'
+ _this._restApiId
+ '.execute-api.'
+ _this._regionJson.region
+ '.amazonaws.com/'
+ _this._stage
+ '/';
});
.bind(_this)
.then(_this._validateAndSantizeTaggedEndpoints)
.then(_this._fetchDeployedLambdas)
.then(_this._findOrCreateApi)
.then(_this._saveApiId)
.then(_this._listApiResources)
.then(_this._buildEndpoints)
.then(_this._createDeployment)
.then(function() {
return 'https://'
+ _this._restApiId
+ '.execute-api.'
+ _this._regionJson.region
+ '.amazonaws.com/'
+ _this._stage
+ '/';
});
}
/**
@ -83,26 +83,26 @@ class ApiDeployer {
let _this = this;
return JawsUtils.findAllEndpoints(_this._prjRootPath)
.each(function(endpoint) {
.each(function(endpoint) {
let eJson = require(endpoint);
if (eJson.apiGateway.deploy) _this._endpoints.push(eJson);
let eJson = require(endpoint);
if (eJson.apiGateway.deploy) _this._endpoints.push(eJson);
}).then(function() {
}).then(function() {
if (!_this._endpoints.length) {
throw new JawsError(
'You have no tagged endpoints',
JawsError.errorCodes.UNKNOWN);
}
if (!_this._endpoints.length) {
throw new JawsError(
'You have no tagged endpoints',
JawsError.errorCodes.UNKNOWN);
}
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": found '
+ _this._endpoints.length + ' endpoints to deploy');
});
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": found '
+ _this._endpoints.length + ' endpoints to deploy');
});
}
/**
@ -113,14 +113,14 @@ class ApiDeployer {
_fetchDeployedLambdas() {
let _this = this;
return AWSUtils.cfGetLambdaNames(
_this._JAWS._profile,
_this._regionJson.region,
_this._stage + '-' + _this._JAWS._projectJson.name + '-l'
)
.then(lambdas => {
this._lambdas = lambdas;
});
return AWSUtils.cfGetLambdaResourceSummaries(
_this._JAWS._profile,
_this._regionJson.region,
AWSUtils.cfGetLambdasStackName(_this._stage, _this._JAWS._projectJson.name)
)
.then(lambdas => {
this._lambdas = lambdas;
});
}
/**
@ -140,13 +140,13 @@ class ApiDeployer {
// Validate attributes
if (!e.Type
|| !e.Path
|| !e.Method
|| !e.AuthorizationType
|| typeof e.ApiKeyRequired === 'undefined') {
|| !e.Path
|| !e.Method
|| !e.AuthorizationType
|| typeof e.ApiKeyRequired === 'undefined') {
return Promise.reject(new JawsError(
'Missing one of many required endpoint attributes: Type, Path, Method, AuthorizationType, ApiKeyRequired',
JawsError.errorCodes.UNKNOWN));
'Missing one of many required endpoint attributes: Type, Path, Method, AuthorizationType, ApiKeyRequired',
JawsError.errorCodes.UNKNOWN));
}
// Sanitize path
@ -194,16 +194,16 @@ class ApiDeployer {
// Show existing REST API
return this.ApiClient.showRestApi(_this._restApiId)
.then(function(response) {
.then(function(response) {
_this._restApiId = response.id;
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": found existing REST API on AWS API Gateway with ID: '
+ response.id);
});
_this._restApiId = response.id;
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": found existing REST API on AWS API Gateway with ID: '
+ response.id);
});
} else {
// Create REST API
@ -218,11 +218,11 @@ class ApiDeployer {
_this._restApiId = response.id;
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": created a new REST API on AWS API Gateway with ID: '
+ response.id);
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": created a new REST API on AWS API Gateway with ID: '
+ response.id);
});
}
}
@ -238,27 +238,27 @@ class ApiDeployer {
// List all Resources for this REST API
return this.ApiClient.listResources(_this._restApiId)
.then(function(response) {
.then(function(response) {
// Parse API Gateway's HAL response
_this._resources = response._embedded.item;
if (!Array.isArray(_this._resources)) _this._resources = [_this._resources];
// Parse API Gateway's HAL response
_this._resources = response._embedded.item;
if (!Array.isArray(_this._resources)) _this._resources = [_this._resources];
// Get Parent Resource ID
for (let i = 0; i < _this._resources.length; i++) {
if (_this._resources[i].path === '/') {
_this._parentResourceId = _this._resources[i].id;
// Get Parent Resource ID
for (let i = 0; i < _this._resources.length; i++) {
if (_this._resources[i].path === '/') {
_this._parentResourceId = _this._resources[i].id;
}
}
}
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": found '
+ _this._resources.length
+ ' existing resources on API Gateway');
});
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ '": found '
+ _this._resources.length
+ ' existing resources on API Gateway');
});
}
/**
@ -275,18 +275,18 @@ class ApiDeployer {
}).each(function(endpoint) {
return _this._createEndpointResources(endpoint)
.bind(_this)
.then(_this._createEndpointMethod)
.then(_this._createEndpointIntegration)
.then(_this._manageLambdaAccessPolicy)
.then(_this._createEndpointMethodResponses)
.then(_this._createEndpointMethodIntegResponses)
.then(function() {
.bind(_this)
.then(_this._createEndpointMethod)
.then(_this._createEndpointIntegration)
.then(_this._manageLambdaAccessPolicy)
.then(_this._createEndpointMethodResponses)
.then(_this._createEndpointMethodIntegResponses)
.then(function() {
// Clean-up hack
// TODO figure out how "apig" temp property is being written to the awsm's json and remove that
if (endpoint.apiGateway.apig) delete endpoint.apiGateway.apig;
});
// Clean-up hack
// TODO figure out how "apig" temp property is being written to the awsm's json and remove that
if (endpoint.apiGateway.apig) delete endpoint.apiGateway.apig;
});
});
}
@ -358,21 +358,21 @@ class ApiDeployer {
// Create Resource
return _this.ApiClient.createResource(
_this._restApiId,
endpoint.apiGateway.apig.parentResourceId,
eResource)
.then(function(response) {
_this._restApiId,
endpoint.apiGateway.apig.parentResourceId,
eResource)
.then(function(response) {
// Add resource to _this.resources and callback
_this._resources.push(response);
JawsCli.log(
'Endpoint Deployer: "' +
_this._stage + ' - '
+ _this._regionJson.region
+ ' - ' + endpoint.apiGateway.cloudFormation.Path + '": '
+ 'created resource: '
+ response.pathPart);
});
// Add resource to _this.resources and callback
_this._resources.push(response);
JawsCli.log(
'Endpoint Deployer: "' +
_this._stage + ' - '
+ _this._regionJson.region
+ ' - ' + endpoint.apiGateway.cloudFormation.Path + '": '
+ 'created resource: '
+ response.pathPart);
});
}).then(function() {
@ -411,42 +411,42 @@ class ApiDeployer {
}
return _this.ApiClient.showMethod(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method)
.then(function() {
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method)
.then(function() {
return _this.ApiClient.deleteMethod(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method)
.then(function() {
_this.ApiClient.putMethod(
return _this.ApiClient.deleteMethod(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method)
.then(function() {
_this.ApiClient.putMethod(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
methodBody);
});
}, function() {
return _this.ApiClient.putMethod(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
methodBody);
});
}, function() {
})
.delay(250) // API Gateway takes time to delete Methods. Might have to increase this.
.then(function(response) {
return _this.ApiClient.putMethod(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
methodBody);
})
.delay(250) // API Gateway takes time to delete Methods. Might have to increase this.
.then(function(response) {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ ' - ' + endpoint.apiGateway.cloudFormation.Path + '": '
+ 'created method: '
+ endpoint.apiGateway.cloudFormation.Method);
return endpoint;
});
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ ' - ' + endpoint.apiGateway.cloudFormation.Path + '": '
+ 'created method: '
+ endpoint.apiGateway.cloudFormation.Method);
return endpoint;
});
}
/**
@ -464,7 +464,7 @@ class ApiDeployer {
if (endpoint.type === 'lambda' || typeof endpoint.lambda !== 'undefined') {
// Find Deployed Lambda and its function name
let cfLogicalResourceId = JawsUtils.generateLambdaName(endpoint);
let cfLogicalResourceId = JawsUtils.getLambdaName(endpoint);
let lambda = null;
for (let i = 0; i < _this._lambdas.length; i++) {
@ -476,7 +476,7 @@ class ApiDeployer {
// If no deployed lambda found, throw error
if (!lambda) {
return Promise.reject(new JawsError('Could not find a lambda deployed in this stage/region with this function name: '
+ cfLogicalResourceId));
+ cfLogicalResourceId));
}
endpoint.apiGateway.apig.lambda = lambda;
@ -513,28 +513,28 @@ class ApiDeployer {
// Create Integration
return _this.ApiClient.putIntegration(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
integrationBody)
.then(function(response) {
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
integrationBody)
.then(function(response) {
// Save integration to apig property
endpoint.apiGateway.apig.integration = response;
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ ' - ' + endpoint.apiGateway.cloudFormation.Path + '": '
+ 'created integration with the type: '
+ endpoint.apiGateway.cloudFormation.Type);
return endpoint;
})
.catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
// Save integration to apig property
endpoint.apiGateway.apig.integration = response;
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage + ' - '
+ _this._regionJson.region
+ ' - ' + endpoint.apiGateway.cloudFormation.Path + '": '
+ 'created integration with the type: '
+ endpoint.apiGateway.cloudFormation.Type);
return endpoint;
})
.catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
}
/**
@ -549,65 +549,65 @@ class ApiDeployer {
return Promise.try(function() {
// Collect Response Keys
if (endpoint.apiGateway.cloudFormation.Responses) return Object.keys(endpoint.apiGateway.cloudFormation.Responses);
else return [];
})
.each(function(responseKey) {
// Collect Response Keys
if (endpoint.apiGateway.cloudFormation.Responses) return Object.keys(endpoint.apiGateway.cloudFormation.Responses);
else return [];
})
.each(function(responseKey) {
let thisResponse = endpoint.apiGateway.cloudFormation.Responses[responseKey];
let methodResponseBody = {};
let thisResponse = endpoint.apiGateway.cloudFormation.Responses[responseKey];
let methodResponseBody = {};
// If Request Params, add them
if (thisResponse.responseParameters) {
// If Request Params, add them
if (thisResponse.responseParameters) {
methodResponseBody.responseParameters = {};
methodResponseBody.responseParameters = {};
// Format Response Parameters per APIG API's Expectations
for (let prop in thisResponse.responseParameters) {
methodResponseBody.responseParameters[prop] = true;
// Format Response Parameters per APIG API's Expectations
for (let prop in thisResponse.responseParameters) {
methodResponseBody.responseParameters[prop] = true;
}
}
}
// If Request models, add them
if (thisResponse.responseModels) {
// If Request models, add them
if (thisResponse.responseModels) {
methodResponseBody.responseModels = {};
methodResponseBody.responseModels = {};
// Format Response Models per APIG API's Expectations
for (let name in thisResponse.responseModels) {
let value = thisResponse.responseModels[name];
methodResponseBody.responseModels[name] = value;
// Format Response Models per APIG API's Expectations
for (let name in thisResponse.responseModels) {
let value = thisResponse.responseModels[name];
methodResponseBody.responseModels[name] = value;
}
}
}
// Create Method Response
return _this.ApiClient.putMethodResponse(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
thisResponse.statusCode,
methodResponseBody)
.then(function() {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": '
+ 'created method response');
})
.catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
})
.then(function() {
return endpoint;
});
// Create Method Response
return _this.ApiClient.putMethodResponse(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
thisResponse.statusCode,
methodResponseBody)
.then(function() {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": '
+ 'created method response');
})
.catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
})
.then(function() {
return endpoint;
});
}
/**
@ -622,47 +622,47 @@ class ApiDeployer {
return Promise.try(function() {
// Collect Response Keys
if (endpoint.apiGateway.cloudFormation.Responses) return Object.keys(endpoint.apiGateway.cloudFormation.Responses);
else return [];
})
.each(function(responseKey) {
// Collect Response Keys
if (endpoint.apiGateway.cloudFormation.Responses) return Object.keys(endpoint.apiGateway.cloudFormation.Responses);
else return [];
})
.each(function(responseKey) {
let thisResponse = endpoint.apiGateway.cloudFormation.Responses[responseKey];
let integrationResponseBody = {};
let thisResponse = endpoint.apiGateway.cloudFormation.Responses[responseKey];
let integrationResponseBody = {};
// Add Response Parameters
integrationResponseBody.responseParameters = thisResponse.responseParameters || {};
// Add Response Parameters
integrationResponseBody.responseParameters = thisResponse.responseParameters || {};
// Add Response Templates
integrationResponseBody.responseTemplates = thisResponse.responseTemplates || {};
// Add Response Templates
integrationResponseBody.responseTemplates = thisResponse.responseTemplates || {};
// Add SelectionPattern
integrationResponseBody.selectionPattern = thisResponse.selectionPattern || (responseKey === 'default' ? null : responseKey);
// Add SelectionPattern
integrationResponseBody.selectionPattern = thisResponse.selectionPattern || (responseKey === 'default' ? null : responseKey);
// Create Integration Response
return _this.ApiClient.putIntegrationResponse(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
thisResponse.statusCode,
integrationResponseBody)
.then(function() {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": '
+ 'created method integration response');
}).catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
});
// Create Integration Response
return _this.ApiClient.putIntegrationResponse(
_this._restApiId,
endpoint.apiGateway.apig.resource.id,
endpoint.apiGateway.cloudFormation.Method,
thisResponse.statusCode,
integrationResponseBody)
.then(function() {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": '
+ 'created method integration response');
}).catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
});
}
/**
@ -679,9 +679,9 @@ class ApiDeployer {
if (!endpoint.apiGateway.apig.lambda) return Promise.resolve(endpoint);
return this._getLambdaAccessPolicy(endpoint)
.bind(_this)
.then(_this._removeLambdaAccessPolicy)
.then(_this._updateLambdaAccessPolicy);
.bind(_this)
.then(_this._removeLambdaAccessPolicy)
.then(_this._updateLambdaAccessPolicy);
}
/**
@ -701,16 +701,16 @@ class ApiDeployer {
let _this = this;
return AWSUtils.lambdaGetPolicy(
_this._JAWS._meta.profile,
_this._regionJson.region,
endpoint.apiGateway.apig.lambda.PhysicalResourceId)
.then(function(data) {
endpoint.apiGateway.apig.lambda.Policy = JSON.parse(data.Policy);
return endpoint;
})
.catch(function(error) {
return endpoint;
});
_this._JAWS._meta.profile,
_this._regionJson.region,
endpoint.apiGateway.apig.lambda.PhysicalResourceId)
.then(function(data) {
endpoint.apiGateway.apig.lambda.Policy = JSON.parse(data.Policy);
return endpoint;
})
.catch(function(error) {
return endpoint;
});
}
/**
@ -737,27 +737,27 @@ class ApiDeployer {
if (!statement) return Promise.resolve(endpoint);
return AWSUtils.lambdaRemovePermission(
_this._JAWS._meta.profile,
_this._regionJson.region,
endpoint.apiGateway.apig.lambda.PhysicalResourceId,
'jaws-apigateway-access')
.then(function(data) {
_this._JAWS._meta.profile,
_this._regionJson.region,
endpoint.apiGateway.apig.lambda.PhysicalResourceId,
'jaws-apigateway-access')
.then(function(data) {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": removed existing lambda access policy statement');
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": removed existing lambda access policy statement');
return endpoint;
})
.catch(function(error) {
console.log(error);
return endpoint;
});
return endpoint;
})
.catch(function(error) {
console.log(error);
return endpoint;
});
}
/**
@ -781,35 +781,35 @@ class ApiDeployer {
statement.Principal = 'apigateway.amazonaws.com';
statement.StatementId = 'jaws-apigateway-access';
statement.SourceArn = 'arn:aws:execute-api:'
+ _this._regionJson.region
+ ':'
+ _this._awsAccountNumber
+ ':'
+ _this._restApiId
+ '/*/'
+ endpoint.apiGateway.cloudFormation.Method
+ '/'
+ endpoint.apiGateway.cloudFormation.Path;
+ _this._regionJson.region
+ ':'
+ _this._awsAccountNumber
+ ':'
+ _this._restApiId
+ '/*/'
+ endpoint.apiGateway.cloudFormation.Method
+ '/'
+ endpoint.apiGateway.cloudFormation.Path;
return AWSUtils.lambdaAddPermission(
_this._JAWS._meta.profile,
_this._regionJson.region,
statement)
.then(function(data) {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": created new lambda access policy statement');
return endpoint;
})
.catch(function(error) {
console.log(error);
return endpoint;
});
_this._JAWS._meta.profile,
_this._regionJson.region,
statement)
.then(function(data) {
JawsCli.log(
'Endpoint Deployer: "'
+ _this._stage
+ ' - '
+ _this._regionJson.region
+ ' - '
+ endpoint.apiGateway.cloudFormation.Path
+ '": created new lambda access policy statement');
return endpoint;
})
.catch(function(error) {
console.log(error);
return endpoint;
});
}
/**
@ -828,14 +828,14 @@ class ApiDeployer {
};
return _this.ApiClient.createDeployment(_this._restApiId, deployment)
.then(function(response) {
return response;
})
.catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
.then(function(response) {
return response;
})
.catch(function(error) {
throw new JawsError(
error.message,
JawsError.errorCodes.UNKNOWN);
});
}
}
@ -863,46 +863,46 @@ const CMD = class DeployEndpoint extends ProjectCmd {
let _this = this;
return this._JAWS.validateProject()
.bind(_this)
.then(function() {
// If !allTagged, tag current directory
if (!_this._allTagged) {
return Tag.tag('endpoint', null, false);
}
})
.then(_this._promptStage)
.then(_this._promptRegions)
.then(function() {
return _this._regions;
})
.each(function(regionJson) {
.bind(_this)
.then(function() {
// If !allTagged, tag current directory
if (!_this._allTagged) {
return Tag.tag('endpoint', null, false);
}
})
.then(_this._promptStage)
.then(_this._promptRegions)
.then(function() {
return _this._regions;
})
.each(function(regionJson) {
JawsCli.log(`Endpoint Deployer: Deploying endpoint(s) to region "${regionJson.region}"...`);
JawsCli.log(`Endpoint Deployer: Deploying endpoint(s) to region "${regionJson.region}"...`);
let deployer = new ApiDeployer(
_this._JAWS,
_this._stage,
regionJson,
_this._prjRootPath,
_this._prjJson,
_this._prjCreds
);
let deployer = new ApiDeployer(
_this._JAWS,
_this._stage,
regionJson,
_this._prjRootPath,
_this._prjJson,
_this._prjCreds
);
return deployer.deploy()
.then(function(url) {
JawsCli.log('Endpoint Deployer: Endpoints for stage "'
+ _this._stage
+ '" successfully deployed to API Gateway in the region "'
+ regionJson.region
+ '". Access them @ '
+ url);
});
})
.then(function() {
// Untag All tagged endpoints
let CmdTag = new Tag(_this._JAWS, 'endpoint');
return _this._allTagged ? CmdTag.tagAll(true) : Tag.tag('endpoint', null, true);
});
return deployer.deploy()
.then(function(url) {
JawsCli.log('Endpoint Deployer: Endpoints for stage "'
+ _this._stage
+ '" successfully deployed to API Gateway in the region "'
+ regionJson.region
+ '". Access them @ '
+ url);
});
})
.then(function() {
// Untag All tagged endpoints
let CmdTag = new Tag(_this._JAWS, 'endpoint');
return _this._allTagged ? CmdTag.tagAll(true) : Tag.tag('endpoint', null, true);
});
}
_promptStage() {
@ -933,11 +933,11 @@ const CMD = class DeployEndpoint extends ProjectCmd {
}
return JawsCli.select('Select a stage to deploy to: ', choices, false)
.then(function(selectedStages) {
if (selectedStages && (selectedStages.length > 0)) {
_this._stage = selectedStages[0].value;
}
});
.then(function(selectedStages) {
if (selectedStages && (selectedStages.length > 0)) {
_this._stage = selectedStages[0].value;
}
});
}
_promptRegions() {

View File

@ -15,7 +15,7 @@ const GlobalCmd = require('./GlobalCmd'),
function simulateNodeJs(awsmJson, handler, event) {
return new Promise(function(resolve, reject) {
let lambdaName = utils.generateLambdaName(awsmJson);
let lambdaName = utils.getLambdaName(awsmJson);
utils.jawsDebug('Testing', lambdaName);
handler(event, context(lambdaName, function(err, result) {

View File

@ -1,24 +1,23 @@
'use strict';
//TODO: make this extend GlobalCmd but it will require some decent sized refactoring in bin/jaws
//plus need to handle if CWD is in a jaws project, to intelligently create it in aws_modules
//TODO: account for changes in new awsm.json structure
/**
* Create jaws module
*
*/
const ProjectCmd = require('./ProjectCmd.js'),
JawsError = require('../jaws-error'),
JawsCLI = require('../utils/cli'),
Promise = require('bluebird'),
fs = require('fs'),
path = require('path'),
utils = require('../utils');
JawsError = require('../jaws-error'),
JawsCLI = require('../utils/cli'),
Promise = require('bluebird'),
fs = require('fs'),
path = require('path'),
utils = require('../utils');
let supportedRuntimes = {
nodejs: {
defaultPkgMgr: 'npm',
validPkgMgrs: ['npm']
validPkgMgrs: ['npm']
}
};
@ -38,20 +37,20 @@ const CMD = class ModuleCreate extends ProjectCmd {
throw new JawsError(`Unsupported runtime "${runtime}"`, JawsError.errorCodes.UNKNOWN);
}
let _this = this,
let _this = this,
supportedRuntimeObj = supportedRuntimes[runtime];
this._module = {
name: name,
this._module = {
name: name,
runtime: runtime,
action: action,
pkgMgr: pkgMgr,
action: action,
pkgMgr: pkgMgr,
modType: modType,
};
this._prompts = {
this._prompts = {
properties: {},
};
this.Prompter = JawsCLI.prompt();
this.Prompter = JawsCLI.prompt();
this.Prompter.override = {};
if (pkgMgr && supportedRuntimeObj.validPkgMgrs.indexOf(_this._module.pkgMgr) == -1) {
@ -81,14 +80,14 @@ const CMD = class ModuleCreate extends ProjectCmd {
* @private
*/
_createPackageMgrSkeleton() {
let _this = this,
let _this = this,
deferredWrites = [];
switch (_this._module.runtime) {
case 'nodejs':
if (_this._module.pkgMgr == 'npm') {
let modulePath = path.join(
let modulePath = path.join(
_this._JAWS._meta.projectRootPath, //TOOD: make this CWD if not in a JAWS project
'node_modules',
_this._module.name);
@ -101,9 +100,9 @@ const CMD = class ModuleCreate extends ProjectCmd {
// Create module package.json if DNE in node_module
if (!utils.fileExistsSync(path.join(modulePath, 'package.json'))) {
let packageJsonTemplate = utils.readAndParseJsonSync(path.join(templatesPath, 'nodejs', 'package.json'));
packageJsonTemplate.name = _this._name;
packageJsonTemplate.description = 'An aws-module';
let packageJsonTemplate = utils.readAndParseJsonSync(path.join(templatesPath, 'nodejs', 'package.json'));
packageJsonTemplate.name = _this._name;
packageJsonTemplate.description = 'An aws-module';
packageJsonTemplate.dependencies = {};
if (packageJsonTemplate.private) delete packageJsonTemplate.private;
deferredWrites.push(
@ -116,7 +115,7 @@ const CMD = class ModuleCreate extends ProjectCmd {
// Create module awsm.json if DNE in node_module
if (!utils.fileExistsSync(path.join(modulePath, 'awsm.json'))) {
let moduleTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'module.awsm.json'));
let moduleTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'module.awsm.json'));
moduleTemplateJson.name = _this._module.name;
deferredWrites.push(
utils.writeFile(
@ -145,11 +144,11 @@ const CMD = class ModuleCreate extends ProjectCmd {
'action.awsm.json'));
// Create action awsm.json
actionTemplateJson.apiGateway.cloudFormation.Path = _this._module.name + '/' + _this._module.action;
actionTemplateJson.apiGateway.cloudFormation.Path = _this._module.name + '/' + _this._module.action;
actionTemplateJson.apiGateway.cloudFormation.Method = 'GET';
actionTemplateJson.apiGateway.cloudFormation.Type = 'AWS';
actionTemplateJson.lambda.cloudFormation.Runtime = 'nodejs';
actionTemplateJson.lambda.cloudFormation.Handler = path.join(
actionTemplateJson.apiGateway.cloudFormation.Type = 'AWS';
actionTemplateJson.lambda.cloudFormation.Runtime = 'nodejs';
actionTemplateJson.lambda.cloudFormation.Handler = path.join(
'aws_modules',
_this._module.name,
_this._module.action,
@ -157,7 +156,7 @@ const CMD = class ModuleCreate extends ProjectCmd {
// Create handler.js, index.js, event.json, package.json
let handlerJs = fs.readFileSync(path.join(templatesPath, 'nodejs', 'handler.js'));
let indexJs = fs.readFileSync(path.join(templatesPath, 'nodejs', 'index.js'));
let indexJs = fs.readFileSync(path.join(templatesPath, 'nodejs', 'index.js'));
deferredWrites.push(
utils.writeFile(
@ -201,17 +200,17 @@ const CMD = class ModuleCreate extends ProjectCmd {
* @private
*/
_createSkeleton() {
let _this = this,
let _this = this,
writeFilesDeferred = [];
// Fetch skeleton resources
let templatesPath = path.join(__dirname, '..', 'templates');
let templatesPath = path.join(__dirname, '..', 'templates');
let actionTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'action.awsm.json'));
let modulePath = path.join(
let modulePath = path.join(
_this._JAWS._meta.projectRootPath, //TOOD: make this CWD if not in a JAWS project
'aws_modules',
_this._module.name);
let actionPath = path.join(modulePath, _this._module.action);
let actionPath = path.join(modulePath, _this._module.action);
// If module/action already exists, throw error
if (utils.dirExistsSync(actionPath)) {
@ -225,7 +224,7 @@ const CMD = class ModuleCreate extends ProjectCmd {
// If module awsm.json doesn't exist, create it
if (!utils.fileExistsSync(path.join(modulePath, 'awsm.json'))) {
let moduleTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'module.awsm.json'));
let moduleTemplateJson = utils.readAndParseJsonSync(path.join(templatesPath, 'module.awsm.json'));
moduleTemplateJson.name = _this._module.name;
writeFilesDeferred.push(
utils.writeFile(
@ -237,11 +236,17 @@ const CMD = class ModuleCreate extends ProjectCmd {
writeFilesDeferred.push(actionPath);
// Create action awsm.json
actionTemplateJson.apiGateway.cloudFormation.Path = _this._module.name + '/' + _this._module.action;
actionTemplateJson.apiGateway.cloudFormation.Path = _this._module.name + '/' + _this._module.action;
actionTemplateJson.apiGateway.cloudFormation.Method = 'GET';
actionTemplateJson.apiGateway.cloudFormation.Type = 'AWS';
actionTemplateJson.apiGateway.cloudFormation.Type = 'AWS';
if (['lambda', 'both'].indexOf(_this._module.modType) != -1) {
//We prefix with an l to make sure the CloudFormation resource map index is unique
//we will probably have API gateway resources in their own CF JSON but we dont want to
//make that decsision until CF has API gateway support
actionTemplateJson.lambda.name = 'l' + _this._module.name.charAt(0).toUpperCase() + _this._module.name.slice(1) + _this._module.action.charAt(0).toUpperCase() + _this._module.action.slice(1);
actionTemplateJson.lambda.cloudFormation.Runtime = _this._module.runtime;
// Create files for lambda actions
switch (_this._module.runtime) {
case 'nodejs':
@ -256,7 +261,7 @@ const CMD = class ModuleCreate extends ProjectCmd {
// Create handler.js, index.js, event.json, package.json
let handlerJs = fs.readFileSync(path.join(templatesPath, 'nodejs', 'handler.js'));
let indexJs = fs.readFileSync(path.join(templatesPath, 'nodejs', 'index.js'));
let indexJs = fs.readFileSync(path.join(templatesPath, 'nodejs', 'index.js'));
writeFilesDeferred.push(
utils.writeFile(path.join(actionPath, 'handler.js'), handlerJs),

View File

@ -164,7 +164,7 @@ CMD.prototype._prepareResources = Promise.method(function() {
key: ' L) ',
value: jsonPaths[i],
type: 'lambda',
label: JawsUtils.generateLambdaName(json),
label: JawsUtils.getLambdaName(json),
};
// Create path

View File

@ -0,0 +1,196 @@
'use strict';
/**
* Action: AliasLambda
*/
const JawsPlugin = require('../../JawsPlugin'),
JawsError = require('../../jaws-error'),
JawsCLI = require('../../utils/cli'),
BbPromise = require('bluebird'),
path = require('path'),
os = require('os'),
AWSUtils = require('../../utils/aws'),
JawsUtils = require('../../utils/index');
let fs = require('fs');
BbPromise.promisifyAll(fs);
class AliasLambda extends JawsPlugin {
/**
* @param Jaws class object
* @param config object
*/
constructor(Jaws, config) {
super(Jaws, config);
this._stage = null;
this._region = null;
this._lambdaLogicalIdsToAlias = [];
}
/**
* Define your plugins name
*
* @returns {string}
*/
static getName() {
return 'jaws.core.' + AliasLambda.name;
}
/**
* @returns {Promise} upon completion of all registrations
*/
registerActions() {
this.Jaws.action(this.lambdaCreateAlias.bind(this), {
handler: 'lambdaCreateAlias',
description: `Version lambda at CWD
usage: jaws lambda alias <version> <aliasName>`,
context: 'lambda',
contextAction: 'alias',
options: [
{
option: 'stage',
shortcut: 's',
description: 'Optional if only one stage is defined in project'
}, {
option: 'region',
shortcut: 'r',
description: 'Optional. Default is to version lambda in all regions defined in stage'
}
],
});
return Promise.resolve();
}
/**
*
* @param stage Optional if only one stage is defined in project
* @param region Optional. Default is to version lambda in all regions defined in stage
* @param verAndAliasName <Array> version, aliasName
* @returns {Promise.<Array>}
*/
lambdaCreateAlias(stage, region) {
let _this = this,
verAliasName = Array.prototype.slice.call(arguments, 3);
if (!verAliasNameAndPaths || verAliasName.length !== 2) {
return Promise.reject(new JawsError('Must specify a lambda version and alias name'));
}
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._lambdaLogicalIdsToAlias, lambdaSummaries);
})
.then(lambdaNamesToVersion => {
//3) publishVersions
return AWSUtils.lambdaCreateAliass(_this.Jaws._profile, region, lambdaNamesToVersion);
});
})
.then(versionedLambdas => {
JawsCLI.log('Lambda AliasLambda: Successfully published following lambda versions to the requested regions:');
JawsCLI.log(versionedLambdas);
return versionedLambdas;
});
}
/**
*
* @returns {Promise}
* @private
*/
_promptStage() {
let stages = [],
_this = this;
// If stage exists, skip
if (!this._stage) {
stages = Object.keys(_this.JAWS._projectJson.stages);
// If project only has 1 stage, skip prompt
if (stages.length === 1) {
this._stage = stages[0];
}
}
if (this._stage) { //User specified stage or only one stage
return Promise.resolve();
}
// Create Choices
let choices = [];
for (let i = 0; i < stages.length; i++) {
choices.push({
key: '',
value: stages[i],
label: stages[i],
});
}
return this.selectInput('AliasLambda: Choose a stage: ', choices, false)
.then(results => {
_this._stage = results[0].value;
});
}
/**
* this._stage must be set before calling this method
* @returns {Promise} list of regions
* @private
*/
_getRegions() {
if (this._region) { //user specified specific region to deploy to
JawsUtils.jawsDebug('Deploying to region: ' + this._region);
return BbPromise.resolve([region]);
}
//Deploy to all regions in stage
let stage = this._stage,
projJson = this.JAWS._projectJson;
let regionConfigs = projJson.stages[stage],
regions = regionConfigs.map(rCfg => {
return rCfg.region;
});
JawsUtils.jawsDebug('Publishing version to regions:', regions);
return BbPromise.resolve(regions);
}
/**
*
* @param lambdaPaths [] optional abs or rel (to cwd) paths to lambda dirs. If ommitted deploys lambda @ cwd
* @return {Promise}
* @private
*/
_setLambdaLogicalIds(lambdaPaths) {
let _this = this;
return JawsUtils.getFullLambdaPaths(process.cwd(), lambdaPaths)
.then(fullAwsmJsonPaths => {
_this._lambdaLogicalIdsToAlias = fullAwsmJsonPaths.map(alp => {
let awsmJson = JawsUtils.readAndParseJsonSync(alp);
return JawsUtils.getLambdaName(awsmJson);
});
});
}
}
module.exports = AliasLambda;

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,
})
.bind(_this)
.then(function() {
return _this._lambdaAwsmPaths;
})
.each(function(lambdaAwsmPath) {
let packager = new Packager(
_this._JAWS,
_this._stage,
packagedLambda.lambdaName,
packagedLambda.zipBuffer
)
.then(function(s3Key) {
awsmLambdas.push({
awsmPath: lambdaAwsmPath,
Code: {
S3Bucket: jawsBucket,
S3Key: s3Key,
},
lambdaName: packagedLambda.lambdaName,
});
_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';
})
.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 {
cfDeferred = AWSUtils.cfCreateLambdasStack(_this._JAWS, _this._stage, _this._region, lambdaRoleArn);
createOrUpdate = 'create';
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);
});
}
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);
}
);
})
.then(function() {
JawsCLI.log('Lambda Deployer: Done deploying lambdas in ' + _this._region);
}
);
}
/**
@ -151,83 +151,83 @@ class Deployer {
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
.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'
);
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'));
JawsUtils.jawsDebug(`Wrting to ${lambdasCfPath}`);
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;
return JawsUtils.writeFile(lambdasCfPath, JSON.stringify(lambdaCfTemplate, null, 2))
.then(() => existingStack);
});
// 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.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;
}
});
}
/**
@ -277,7 +277,7 @@ class Packager {
*/
createLambdaName() {
let _this = this,
name = JawsUtils.generateLambdaName(_this._awsmJson);
name = JawsUtils.getLambdaName(_this._awsmJson);
JawsUtils.jawsDebug(`computed lambdaName: ${name}`);
return name;
@ -306,42 +306,42 @@ class Packager {
// Copy entire test project to temp folder
_this._excludePatterns = _this._awsmJson.lambda.package.excludePatterns || [];
wrench.copyDirSyncRecursive(
_this._JAWS._projectRootPath,
_this._distDir,
{
exclude: function(name, prefix) {
if (!_this._excludePatterns.length) {
return false;
}
let relPath = path.join(
prefix.replace(_this._distDir, ''), name);
return _this._excludePatterns.some(sRegex => {
relPath = (relPath.charAt(0) == path.sep) ? relPath.substr(1) : relPath;
let re = new RegExp(sRegex),
matches = re.exec(relPath);
let willExclude = (matches && matches.length > 0);
if (willExclude) {
JawsCLI.log(`Lambda Deployer: Excluding ${relPath}`);
_this._JAWS._projectRootPath,
_this._distDir,
{
exclude: function(name, prefix) {
if (!_this._excludePatterns.length) {
return false;
}
return willExclude;
});
},
}
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;
});
},
}
);
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);
});
}
/**
@ -355,30 +355,30 @@ class Packager {
deferred = null;
if (_this._awsmJson.lambda.package
&& _this._awsmJson.lambda.package.optimize
&& _this._awsmJson.lambda.package.optimize.builder) {
&& _this._awsmJson.lambda.package.optimize
&& _this._awsmJson.lambda.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.lambda.cloudFormation.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.lambda.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 = ['.'];
@ -387,14 +387,14 @@ class Packager {
}
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});
});
}
/**
@ -407,7 +407,7 @@ class Packager {
let _this = this;
if (!_this._awsmJson.lambda.package.optimize
|| !_this._awsmJson.lambda.package.optimize.builder) {
|| !_this._awsmJson.lambda.package.optimize.builder) {
return Promise.reject(new JawsError('Cant optimize for nodejs. lambda jaws.json does not have optimize.builder set'));
}
@ -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)
);
}
@ -604,7 +604,8 @@ class DeployLambda extends JawsPlugin {
registerActions() {
this.Jaws.action(this.deployLambda.bind(this), {
handler: 'lambdaDeploy',
description: 'Deploy lambda at CWD or lambdas at specified paths',
description: `Deploy lambda at CWD or lambdas at specified paths
usage: jaws lambda deploy [rel or abs path to lambda dirs. default is cwd]`,
context: 'lambda',
contextAction: 'deploy',
options: [
@ -643,22 +644,22 @@ class DeployLambda extends JawsPlugin {
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;
});
}
/**
@ -695,9 +696,9 @@ class DeployLambda extends JawsPlugin {
}
return this.selectInput('Lambda Deployer: Choose a stage: ', choices, false)
.then(results => {
_this._stage = results[0].value;
});
.then(results => {
_this._stage = results[0].value;
});
}
/**
@ -749,9 +750,9 @@ class DeployLambda extends JawsPlugin {
_setLambdaAwsmPaths(lambdaPaths) {
let _this = this;
return JawsUtils.getFullLambdaPaths(process.cwd(), lambdaPaths)
.then(fullAwsmJsonPaths => {
_this._lambdaAwsmPathsToDeploy = fullAwsmJsonPaths;
});
.then(fullAwsmJsonPaths => {
_this._lambdaAwsmPathsToDeploy = fullAwsmJsonPaths;
});
}
}

View File

@ -10,7 +10,6 @@ const JawsPlugin = require('../../JawsPlugin'),
BbPromise = require('bluebird'),
path = require('path'),
os = require('os'),
AWS = require('aws-sdk'),
AWSUtils = require('../../utils/aws'),
JawsUtils = require('../../utils/index');
@ -26,10 +25,9 @@ class VersionLambda extends JawsPlugin {
constructor(Jaws, config) {
super(Jaws, config);
this._stage = null;
this._region = null;
this._noExeCf = false;
this._lambdaAwsmPathsToVersion = [];
this._stage = null;
this._region = null;
this._lambdaLogicalIdsToVersion = [];
}
/**
@ -71,7 +69,7 @@ class VersionLambda extends JawsPlugin {
* @param stage Optional if only one stage is defined in project
* @param region Optional. Default is to version lambda in all regions defined in stage
* @param lambdaPaths [] optional abs or rel (to cwd) paths to lambda dirs. If ommitted versions lambda @ cwd
* @returns {Promise.<T>}
* @returns {Promise.<Array>}
*/
lambdaPublishVersion(stage, region) {
let _this = this,
@ -81,21 +79,31 @@ class VersionLambda extends JawsPlugin {
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._setLambdaAwsmPaths(lambdaPaths);
})
.then(_this._getRegions)
.each(region => {
})
.then(versionedLambdas => {
JawsCLI.log('Lambda Deployer: Successfully published lambda versions to the requested regions!');
JawsCLI.log(versionedLambdas);
return versionedLambdas;
});
.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;
});
}
/**
@ -132,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;
});
}
/**
@ -168,12 +176,15 @@ class VersionLambda extends JawsPlugin {
* @return {Promise}
* @private
*/
_setLambdaAwsmPaths(lambdaPaths) {
_setLambdaLogicalIds(lambdaPaths) {
let _this = this;
return JawsUtils.getFullLambdaPaths(process.cwd(), lambdaPaths)
.then(fullAwsmJsonPaths => {
_this._lambdaAwsmPathsToVersion = fullAwsmJsonPaths;
});
.then(fullAwsmJsonPaths => {
_this._lambdaLogicalIdsToVersion = fullAwsmJsonPaths.map(alp => {
let awsmJson = JawsUtils.readAndParseJsonSync(alp);
return JawsUtils.getLambdaName(awsmJson);
});
});
}
}

View File

@ -1,31 +1,55 @@
{
"lambda": {
"envVars": [],
"deploy": false,
"package": {
"optimize": {
"builder": "browserify",
"minify": true,
"ignore": [],
"exclude": [
"aws-sdk"
],
"includePaths": []
},
"excludePatterns": []
"name": "",
"envVars": [],
"package": {
"optimize": {
"builder": "browserify",
"minify": true,
"ignore": [],
"exclude": [
"aws-sdk"
],
"includePaths": []
},
"cloudFormation": {
"Description": "",
"Handler": "",
"MemorySize": 1024,
"Runtime": "nodejs",
"Timeout": 6
}
"excludePatterns": []
},
"apiGateway": {
"deploy": false,
"cloudFormation": {
"Type": "",
"plugins": [],
"cloudFormation": {
"Lambda": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "",
"Role": {
"Ref": "aaLambdaRoleArn"
},
"Code": {
"S3Bucket": "",
"S3Key": ""
},
"Runtime": "nodejs",
"Timeout": 6,
"MemorySize": 1024
}
},
"LambdaEventSourceMapping": {
"Type": "AWS::Lambda::EventSourceMapping",
"Properties": {
"EventSourceArn": {},
"FunctionName": "",
"StartingPosition": ""
}
},
"LambdaAccessPolicyX": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": "",
"Action": "",
"Principal": "",
"SourceAccount": ""
}
},
"APIGatewayEndpoint": {
"Type": "AWS::ApiGateway::Endpoint",
"Path": "",
"Method": "GET",
"AuthorizationType": "none",
@ -33,16 +57,15 @@
"RequestTemplates": {},
"RequestParameters": {},
"Responses": {
"400": {
"statusCode": "400"
},
"default": {
"statusCode": "200",
"responseParameters": {},
"responseModels": {},
"responseTemplates": {
"application/json": ""
}
},
"400": {
"statusCode": "400"
}
}
}

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);
}
@ -264,48 +264,131 @@ exports.cfListStackResources = function(awsProfile, awsRegion, stackName, nextTo
return CF.listStackResourcesAsync(params);
};
exports.cfGetLambdaNames = function(awsProfile, awsRegion, stackName) {
/**
* Returns data like:
* [
{
"LogicalResourceId": "lChannelWxLatlng",
"PhysicalResourceId": "prod-pushChannelSearch-l-lChannelWxLatlng-AS845QCZ8J1L",
"ResourceType": "AWS::Lambda::Function",
"LastUpdatedTimestamp": "2015-10-15T19:06:55.134Z",
"ResourceStatus": "UPDATE_COMPLETE"
},
{
"LogicalResourceId": "lChannelWxTypeahead",
"PhysicalResourceId": "prod-pushChannelSearch-l-lChannelWxTypeahead-15NUNJF0O22HA",
"ResourceType": "AWS::Lambda::Function",
"LastUpdatedTimestamp": "2015-10-19T21:35:24.357Z",
"ResourceStatus": "UPDATE_COMPLETE"
}
]
* @param awsProfile
* @param awsRegion
* @param stackName
* @returns {Promise.<array>}
*/
exports.cfGetLambdaResourceSummaries = function(awsProfile, awsRegion, stackName) {
let _this = this,
moreResources = true,
nextStackToken,
lambdas = [];
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);
}
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);
}
// Check if more resources are available
if (!lambdaCfResources.NextToken) {
moreResources = false;
// 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 {
nextStackToken = lambdaCfResources.NextToken;
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) {
return Promise.reject(err);
} else {
return Promise.resolve(lambdas);
}
/**
* Given a list of lambda LogicalResourceId's and a list of lambdaResourceSummaries
* return a corresponding list of lambda PhysicalResourceId's
*
* @param logicalIds
* @param lambdaResourceSummaries
*/
exports.cfGetLambdaPhysicalsFromLogicals = function(logicalIds, lambdaResourceSummaries) {
let lambdaPhysicalIds = [];
for (let lid of logicalIds) {
let foundLambda = lambdaResourceSummaries.find(element=> {
return element.LogicalResourceId == lid;
});
if (!foundLambda) {
throw new JawsError(`unable to find lambda with logical id ${lid}`, JawsError.errorCodes.UNKNOWN);
}
);
lambdaPhysicalIds.push(foundLambda.PhysicalResourceId);
}
};
/**
* Retrns [{FunctionName: d.FunctionName, Version: d.Version},...]
* @param awsProfile
* @param awsRegion
* @param functionNames
* @returns {Promise.<Array>}
*/
exports.lambdaPublishVersions = function(awsProfile, awsRegion, functionNames) {
this.configAWS(awsProfile, awsRegion);
let L = Promise.promisifyAll(new AWS.Lambda({
apiVersion: '2015-03-31',
}));
let d = new Date(),
ds = `versioned at ${d}`,
deferreds = [];
functionNames.forEach(fn => {
let params = {
FunctionName: stackName,
Description: ds
};
deferreds.push(L.publishVersionAsync(params));
});
return Promise.all(deferreds)
.then(data => {
return data.map(d => {
return {FunctionName: d.FunctionName, Version: d.Version};
});
});
};
/**
@ -350,11 +433,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;
});
};
/**
@ -386,13 +469,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}`;
})
};
/**
@ -434,10 +517,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);
});
};
/**
@ -475,10 +558,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);
});
};
/**
@ -591,10 +674,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);
});
};
/**
@ -630,31 +713,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]);
}
);
});
};
@ -673,21 +756,21 @@ exports.createBucket = function(awsProfile, awsRegion, bucketName) {
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() {
})
.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',
});
});
});
};
/**
@ -784,9 +867,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;
});
};
/**
@ -815,8 +898,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);
}
@ -847,8 +930,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

@ -37,26 +37,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 +102,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 +128,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 +171,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);
}
});
}
//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;
});
return envletKeys;
});
};
/**
@ -199,11 +199,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,27 +250,19 @@ exports.writeFile = function(filePath, contents) {
}
return mkdirpAsync(path.dirname(filePath))
.then(function() {
return fs.writeFileAsync(filePath, contents);
});
.then(function() {
return fs.writeFileAsync(filePath, contents);
});
};
/**
* Generate Lambda Name
* Get Lambda Name
* @param awsmJson
* @returns {string}
*/
exports.generateLambdaName = function(awsmJson) {
let handlerName = awsmJson.lambda.cloudFormation.Handler.replace('aws_modules', ''),
resourceAction = handlerName.substr(0, handlerName.lastIndexOf('/'));
//We prefix with an l to make sure the CloudFormation resource map index is unique
//we will probably have API gateway resources in their own CF JSON but we dont want to
//make that decsision until CF has API gateway support
return 'l' + resourceAction.replace(/(\/|-|_)([a-z])/g, function(g) {
return g[1].toUpperCase();
});
exports.getLambdaName = function(awsmJson) {
return awsmJson.name;
};
/**
@ -302,16 +294,16 @@ exports.getAllLambdaNames = function(projectRootPath) {
lambdaNames = [];
return this.findAllLambdas(projectRootPath)
.then(function(lambdaAwsmPaths) {
lambdaAwsmPaths.forEach(function(ljp) {
let awsm = _this.readAndParseJsonSync(ljp),
lambdaName = _this.generateLambdaName(awsm);
.then(function(lambdaAwsmPaths) {
lambdaAwsmPaths.forEach(function(ljp) {
let awsm = _this.readAndParseJsonSync(ljp),
lambdaName = _this.getLambdaName(awsm);
lambdaNames.push(lambdaName);
lambdaNames.push(lambdaName);
});
return lambdaNames;
});
return lambdaNames;
});
};
/**
@ -392,8 +384,8 @@ exports.generateResourcesCf = function(projRootPath, projName, projDomain, stage
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)
);
};

View File

@ -1,7 +1,5 @@
'use strict';
//TODO: doc on needing to set env vars
//TODO: must setup an env var file for unittest
require('./config'); //init config
describe('All Tests', function() {
@ -15,7 +13,7 @@ describe('All Tests', function() {
});
//require tests vs inline so we can run sequentially
require('./tests/TestPluginCustom');
//require('./tests/TestPluginCustom');
//require('./cli/tag');
//require('./cli/env');
//require('./cli/module_create');
@ -24,10 +22,11 @@ describe('All Tests', function() {
/**
* Tests below create AWS Resources
*/
//require('./cli/dash');
//require('./cli/deploy_lambda');
//require('./cli/deploy_resources');
//require('./cli/deploy_endpoint');
//require('./cli/new_stage_region');
require('./tests/TestActionProjectCreate');
//require('./cli/dash');
//require('./cli/deploy_lambda');
//require('./cli/deploy_resources');
//require('./cli/deploy_endpoint');
//require('./cli/new_stage_region');
//require('./tests/actions/ProjectCreate');
require('./tests/actions/VersionLambda');
});

View File

@ -1,25 +1,25 @@
'use strict';
const path = require('path'),
AWS = require('aws-sdk');
AWS = require('aws-sdk');
// Require ENV lets, can also set ENV lets in your IDE
require('dotenv').config({path: path.join(__dirname, '.env'), silent: true});
process.env.JAWS_VERBOSE = true;
process.env.DEBUG = '*';
let config = {
name: 'test-prj',
domain: 'test-prj.com',
notifyEmail: 'tester@jawsstack.com',
stage: 'unittest',
region: 'us-east-1',
stage2: 'unittest2',
region2: 'us-west-2',
name: 'test-prj',
domain: process.env.TEST_JAWS_DOMAIN,
notifyEmail: 'tester@jawsstack.com',
stage: 'unittest',
region: 'us-east-1',
stage2: 'unittest2',
region2: 'us-west-2',
iamRoleArnApiGateway: process.env.TEST_JAWS_APIGATEWAY_ROLE,
iamRoleArnLambda: process.env.TEST_JAWS_LAMBDA_ROLE,
profile: process.env.TEST_JAWS_PROFILE,
noExecuteCf: (process.env.TEST_JAWS_NO_EXE_CF != "false"),
iamRoleArnLambda: process.env.TEST_JAWS_LAMBDA_ROLE,
profile: process.env.TEST_JAWS_AWS_PROFILE,
noExecuteCf: process.env.TEST_JAWS_EXE_CF != "true",
};
AWS.config.credentials = new AWS.SharedIniFileCredentials({

View File

@ -6,20 +6,20 @@
* - Deletes the CF stack created by the project
*/
let JAWS = require('../../lib/jaws.js'),
JawsError = require('../../lib/jaws-error'),
let JAWS = require('../../../lib/Jaws.js'),
JawsError = require('../../../lib/jaws-error/index'),
path = require('path'),
os = require('os'),
utils = require('../../lib/utils'),
utils = require('../../../lib/utils/index'),
assert = require('chai').assert,
shortid = require('shortid'),
config = require('../config');
config = require('../../config');
// Instantiate JAWS
let Jaws = new JAWS({
awsAdminKeyId: '123',
awsAdminKeyId: '123',
awsAdminSecretKey: '123',
interactive: false,
interactive: false,
});
describe('Test Plugin: Project Create', function() {
@ -38,17 +38,17 @@ describe('Test Plugin: Project Create', function() {
this.timeout(0);
let name = 'jaws-test-' + shortid.generate().replace('_', '');
let name = config.name + shortid.generate().replace('_', '');
Jaws.actions.projectCreate(
name,
name + '.com',
'test',
'us-east-1',
'test@test.com',
config.domain,
config.stage,
config.region,
config.notifyEmail,
'nodejs',
true
)
config.noExecuteCf
)
.then(function() {
let jawsJson = utils.readAndParseJsonSync(path.join(os.tmpdir(), name, 'jaws.json'));

View File

@ -0,0 +1,79 @@
'use strict';
/**
* Test: Project Create Action
* - Creates a new project in your system's temp directory
* - Deletes the CF stack created by the project
*/
//let JAWS = require('../../../lib/Jaws.js'),
// JawsError = require('../../../lib/jaws-error/index'),
// path = require('path'),
// os = require('os'),
// utils = require('../../../lib/utils/index'),
// assert = require('chai').assert,
// shortid = require('shortid'),
// config = require('../../config');
//let Jaws = new JAWS();
let AwsUtils = require('../../../lib/utils/aws');
describe('Test Plugin: Version Lambda', function() {
before(function(done) {
this.timeout(0);
done();
});
after(function(done) {
done();
});
describe('Test Plugin: Version lambda', function() {
it('should create a new project in temp directory', function(done) {
this.timeout(0);
//let name = config.name + shortid.generate().replace('_', '');
//
//Jaws.actions.projectCreate(
// name,
// config.domain,
// config.stage,
// config.region,
// config.notifyEmail,
// 'nodejs',
// config.noExecuteCf
// )
// .then(function() {
// let jawsJson = utils.readAndParseJsonSync(path.join(os.tmpdir(), name, 'jaws.json'));
//
// let region = false;
//
// for (let i = 0; i < jawsJson.stages[config.stage].length; i++) {
// let stage = jawsJson.stages[config.stage][i];
// if (stage.region === config.region) {
// region = stage.region;
// }
// }
// assert.isTrue(region !== false);
// done();
// })
// .catch(JawsError, function(e) {
// done(e);
// })
// .error(function(e) {
// done(e);
// });
});
});
//it('Delete Cloudformation stack from new project', function(done) {
// this.timeout(0);
// let CF = new config.AWS.CloudFormation();
// CF.deleteStack({ StackName: config.stage + '-' + config.name }, function(err, data) {
// if (err) console.log(err, err.stack);
// done();
// });
//});
});