serverless/lib/defaults/actions/CodeProvisionLambdaNodejs.js
2015-11-19 14:02:47 -08:00

232 lines
6.7 KiB
JavaScript

'use strict';
/**
* Action: Code Provision: Lambda: Nodejs
* - Collects and optimizes Lambda code in a temp folder
*/
const JawsPlugin = require('../../JawsPlugin'),
JawsError = require('../../jaws-error'),
JawsUtils = require('../../utils/index'),
extend = require('util')._extend,
BbPromise = require('bluebird'),
path = require('path'),
fs = require('fs'),
os = require('os');
// Promisify fs module
BbPromise.promisifyAll(fs);
class CodeProvisionLambdaNodejs extends JawsPlugin {
/**
* Constructor
*/
constructor(Jaws, config) {
super(Jaws, config);
}
/**
* Get Name
*/
static getName() {
return 'jaws.core.' + CodeProvisionLambdaNodejs.name;
}
/**
* Register Plugin Actions
*/
registerActions() {
this.Jaws.addAction(this.codeProvisionLambdaNodejs.bind(this), {
handler: 'codeProvisionLambdaNodejs',
description: 'Deploys the code or endpoint of a function, or both'
});
return BbPromise.resolve();
}
/**
* Function Deploy
*/
codeProvisionLambdaNodejs(evt) {
let _this = this;
_this.evt = evt;
// Load AWS Service Instances
let awsConfig = {
region: _this.evt.region.region,
accessKeyId: _this.Jaws._awsAdminKeyId,
secretAccessKey: _this.Jaws._awsAdminSecretKey,
};
_this.Lambda = require('../../utils/aws/Lambda')(awsConfig);
_this.CloudFormation = require('../../utils/aws/Lambda')(awsConfig);
_this.S3 = require('../../utils/aws/S3')(awsConfig);
_this.AwsMisc = require('../../utils/aws/Misc');
// Flow
return BbPromise.try(function() {})
.bind(_this)
.then(_this._validateAndPrepare)
.then(_this._upload)
.then(function() {
return _this.evt;
})
.catch(function(e) {
console.log(e.stack)
});
}
/**
* Validate And Prepare
* - If CLI, maps CLI input to event object
*/
_validateAndPrepare() {
Promise.resolve();
}
/**
* Upload
* - Upload zip file to S3
*/
_upload() {
let _this = this,
lambdaName = _this.evt.function.name;
JawsUtils.jawsDebug(`Uploading ${lambdaName} to ${_this.evt.region.jawsBucket}`);
return _this.S3.sPutLambdaZip(
_this.evt.region.jawsBucket,
_this.Jaws._projectJson.name,
_this.evt.stage,
lambdaName,
fs.createReadStream(_this.evt.function.pathCompressed))
.then(function(s3Key) {
_this.evt.function.s3Key = s3Key;
return BbPromise.resolve();
});
}
/**
* Generate lambda-cf.json file
* - Always put in entries for lambdas marked as queued
* - If no existing lambda CF, just generate entries for lambdas intended for deployment
* - If existing lambda CF, find all awsm.json's in current project, and put in ones that are already in
* - existing lambda-cf.json. Making sure to use the existing CF obj for the lambda to not trigger an update
* @returns {Promise} true if there was an existing stack, false if not
* @private
*/
_generateLambdaCf() {
let _this = this,
existingStack = true;
let params = {
StackName: _this.CloudFormation.sGetLambdasStackName(
_this.evt.stage,
_this.Jaws._projectJson.name) /* required */
};
_this.CloudFormation.getTemplatePromised(params)
.then(function(data) {
return data.TemplateBody;
})
.error(e => {
// ValidationError if does not exist
if (e && ['ValidationError', 'ResourceNotFoundException'].indexOf(e.code) == -1) {
console.error(
'Error trying to fetch existing lambda cf stack for region',
_this.evt.region,
'stage',
_this.evt.stage,
e
);
throw new JawsError(e.message);
}
JawsUtils.jawsDebug('no existing 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
awsmLambdasToDeploy.forEach(pkg => {
let lambdaAwsmJson = JawsUtils.readAndParseJsonSync(pkg.awsmPath),
lambdaName = pkg.lambdaName;
Object.keys(lambdaAwsmJson.cloudFormation.lambda).forEach(cfEleIdx => {
let cfEle = lambdaAwsmJson.cloudFormation.lambda[cfEleIdx];
//If its not a lambda CF resource, prefix it with the lambda name to prevent collisions
let resourceIdx = (cfEle.Type == 'AWS::Lambda::Function') ? lambdaName : lambdaName + '-' + cfEleIdx;
cfEle.Properties.Code = pkg.Code;
lambdaCf.Resources[resourceIdx] = cfEle;
JawsUtils.jawsDebug(`adding Resource ${resourceIdx}`, cfEle);
});
});
// 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',
'lambdas-cf.json'
);
JawsUtils.jawsDebug(`Wrting to ${lambdasCfPath}`);
return JawsUtils.writeFile(lambdasCfPath, JSON.stringify(lambdaCfTemplate, null, 2))
.then(() => existingStack);
});
}
}
module.exports = CodeProvisionLambdaNodejs;