This commit is contained in:
ac360 2016-01-01 15:43:41 -08:00
parent 0f9f6f247b
commit ec17ebd758
2 changed files with 234 additions and 221 deletions

View File

@ -7,7 +7,7 @@
* - WARNING: This Action runs concurrently.
*/
module.exports = function(SPlugin, serverlessPath) {
module.exports = function(SPlugin, serverlessPath) {
const path = require('path'),
SError = require(path.join(serverlessPath, 'ServerlessError')),
SUtils = require(path.join(serverlessPath, 'utils/index')),
@ -43,14 +43,14 @@ module.exports = function(SPlugin, serverlessPath) {
* Deploy Code Lambda Node.Js
*/
codeDeployLambdaNodejs(evt) {
codeDeployLambdaNodejs(options) {
let deployer = new Deployer(this.S);
return deployer.deploy(evt);
return deployer.deploy(options);
}
}
/**
* Deployer
* Deployer Class
* - Necessary for this action to run concurrently
*/
@ -60,73 +60,79 @@ module.exports = function(SPlugin, serverlessPath) {
this.S = S;
}
deploy(evt) {
deploy(options) {
let _this = this;
_this.options = options;
// Load AWS Service Instances
let awsConfig = {
region: evt.region.region,
region: this.options.region,
accessKeyId: _this.S.config.awsAdminKeyId,
secretAccessKey: _this.S.config.awsAdminSecretKey,
secretAccessKey: _this.S.config.awsAdminSecretKey
};
_this.S3 = require('../utils/aws/S3')(awsConfig);
_this.Lambda = require('../utils/aws/Lambda')(awsConfig);
_this.AwsMisc = require('../utils/aws/Misc');
_this.S3 = require('../utils/aws/S3')(awsConfig);
_this.Lambda = require('../utils/aws/Lambda')(awsConfig);
_this.AwsMisc = require('../utils/aws/Misc');
// Instantiate Classes
_this.project = _this.S.classes.Project(_this.S);
_this.meta = _this.S.classes.Meta(_this.S);
// Flow
return _this._validateAndPrepare(evt)
.bind(_this)
.then(_this._compress)
.then(_this._upload)
.then(_this._deploy)
.then(function() {
return evt;
});
return _this._validateAndPrepare()
.bind(_this)
.then(_this._compress)
.then(_this._upload)
.then(_this._deploy)
.then(function() {
return options;
});
}
/**
* Validate And Prepare
*/
_validateAndPrepare(evt) {
return BbPromise.resolve(evt);
_validateAndPrepare() {
return BbPromise.resolve();
}
/**
* Compress
*/
_compress(evt) {
_compress() {
let zip = new Zip();
evt.function.pathsPackaged.forEach(nc => {
this.options.function.pathsPackaged.forEach(nc => {
zip.file(nc.fileName, nc.data);
});
let zipBuffer = zip.generate({
type: 'nodebuffer',
compression: 'DEFLATE',
compression: 'DEFLATE'
});
if (zipBuffer.length > 52428800) {
Promise.reject(new SError(
'Zip file is > the 50MB Lambda queued limit (' + zipBuffer.length + ' bytes)',
SError.errorCodes.ZIP_TOO_BIG)
'Zip file is > the 50MB Lambda queued limit (' + zipBuffer.length + ' bytes)',
SError.errorCodes.ZIP_TOO_BIG)
);
}
// Set path of compressed package
evt.function.pathCompressed = path.join(evt.function.pathDist, 'package.zip');
this.pathCompressed = path.join(this.options.pathDist, 'package.zip');
// Create compressed package
fs.writeFileSync(
evt.function.pathCompressed,
zipBuffer);
this.pathCompressed,
zipBuffer);
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Compressed file created - ${evt.function.pathCompressed}`);
SUtils.sDebug(`"${this.options.stage} - ${this.options.region.region} - ${this.function.data.name}": Compressed file created - ${this.pathCompressed}`);
return BbPromise.resolve(evt);
return BbPromise.resolve();
}
/**
@ -138,22 +144,22 @@ module.exports = function(SPlugin, serverlessPath) {
let _this = this;
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Uploading to - ${evt.region.regionBucket}`);
SUtils.sDebug(`"${this.options.stage} - ${this.options.region} - ${this.function.data.name}": Uploading to project bucket...`);
return _this.S3.sPutLambdaZip(
evt.region.regionBucket,
_this.S.data.project.get('name'),
evt.stage,
evt.function.name,
fs.createReadStream(evt.function.pathCompressed))
.then(function(s3Key) {
this.projectBucket,
_this.S.data.project.get('name'),
this.options.stage,
this.function.name,
fs.createReadStream(this.pathCompressed))
.then(function(s3Key) {
// Store S3 Data
evt.function.s3Bucket = evt.region.regionBucket;
evt.function.s3Key = s3Key;
// Store S3 Data
this.function.s3Bucket = this.region.regionBucket;
this.function.s3Key = s3Key;
return BbPromise.resolve(evt);
});
return BbPromise.resolve();
});
}
/**
@ -161,11 +167,11 @@ module.exports = function(SPlugin, serverlessPath) {
* - Deploy Lambda from S3 to Lambda
*/
_deploy(evt) {
_deploy() {
let _this = this,
lambda,
lambdaVersion;
lambda,
lambdaVersion;
var params = {
FunctionName: _this.Lambda.sGetLambdaName(_this.S.data.project.get('name'), evt.function.name),
@ -173,141 +179,141 @@ module.exports = function(SPlugin, serverlessPath) {
};
return _this.Lambda.getFunctionPromised(params)
.catch(function(e) {
lambda = null;
})
.then(function(data) {
lambda = data;
})
.then(function() {
.catch(function(e) {
lambda = null;
})
.then(function(data) {
lambda = data;
})
.then(function() {
// Create or Update Lambda
// Create or Update Lambda
if (!lambda) {
if (!lambda) {
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Creating Lambda function...`);
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Creating Lambda function...`);
// Create Lambda
let params = {
Code: {
S3Bucket: evt.function.s3Bucket,
S3Key: evt.function.s3Key
},
FunctionName: _this.Lambda.sGetLambdaName(_this.S.data.project.get('name'), evt.function.name), /* required */
Handler: evt.function.handler, /* required */
Role: evt.region.iamRoleArnLambda, /* required */
Runtime: evt.function.module.runtime, /* required */
Description: 'Serverless Lambda function for project: ' + _this.S.data.project.get('name'),
MemorySize: evt.function.memorySize,
Publish: true, // Required by Serverless Framework & recommended by AWS
Timeout: evt.function.timeout
};
return _this.Lambda.createFunctionPromised(params)
// Create Lambda
let params = {
Code: {
S3Bucket: evt.function.s3Bucket,
S3Key: evt.function.s3Key
},
FunctionName: _this.Lambda.sGetLambdaName(_this.S.data.project.get('name'), evt.function.name), /* required */
Handler: evt.function.handler, /* required */
Role: evt.region.iamRoleArnLambda, /* required */
Runtime: evt.function.module.runtime, /* required */
Description: 'Serverless Lambda function for project: ' + _this.S.data.project.get('name'),
MemorySize: evt.function.memorySize,
Publish: true, // Required by Serverless Framework & recommended by AWS
Timeout: evt.function.timeout
};
return _this.Lambda.createFunctionPromised(params)
.then(function(data) {
// Save Version
lambdaVersion = data.Version;
return data;
})
.catch(function(e){
console.log(e)
})
} else {
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Updating Lambda configuration...`);
let params = {
FunctionName: lambda.Configuration.FunctionName, /* required */
Description: 'Serverless Lambda function for project: ' + _this.S._project.name,
Handler: evt.function.handler,
MemorySize: evt.function.memorySize,
Role: evt.region.iamRoleArnLambda,
Timeout: evt.function.timeout
};
return _this.Lambda.updateFunctionConfigurationPromised(params)
.then(function(){
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Updating Lambda function...`);
// Update Lambda Code
let params = {
FunctionName: lambda.Configuration.FunctionName, /* required */
Publish: true, // Required by Serverless Framework & recommended by AWS
S3Bucket: evt.region.regionBucket,
S3Key: evt.function.s3Key,
};
return _this.Lambda.updateFunctionCodePromised(params)
.then(function(data) {
// Save Version
lambdaVersion = data.Version;
return data;
})
.catch(function(e){
console.log(e)
})
return( data );
});
});
}
})
.then(function(data) {
} else {
// Alias Lambda w/ Stage
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Updating Lambda configuration...`);
let aliasedLambda = false;
let params = {
FunctionName: lambda.Configuration.FunctionName, /* required */
Description: 'Serverless Lambda function for project: ' + _this.S._project.name,
Handler: evt.function.handler,
MemorySize: evt.function.memorySize,
Role: evt.region.iamRoleArnLambda,
Timeout: evt.function.timeout
};
var params = {
FunctionName: data.FunctionName, /* required */
Name: evt.stage.toLowerCase() /* required */
};
return _this.Lambda.updateFunctionConfigurationPromised(params)
.then(function(){
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Updating Lambda function...`);
return _this.Lambda.getAliasPromised(params)
.then(function() {
aliasedLambda = true;
})
.catch(function(e) {
aliasedLambda = false;
})
.then(function() {
// Update Lambda Code
let params = {
FunctionName: lambda.Configuration.FunctionName, /* required */
Publish: true, // Required by Serverless Framework & recommended by AWS
S3Bucket: evt.region.regionBucket,
S3Key: evt.function.s3Key,
};
return _this.Lambda.updateFunctionCodePromised(params)
.then(function(data) {
if (aliasedLambda) {
// Save Version
lambdaVersion = data.Version;
// Update Existing Alias
return( data );
});
});
}
})
.then(function(data) {
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Updating Lambda Alias for version - ${lambdaVersion}`);
// Alias Lambda w/ Stage
let params = {
FunctionName: data.FunctionName, /* required */
FunctionVersion: lambdaVersion, /* required */
Name: evt.stage, /* required */
Description: 'Project: ' + _this.S._project.name + ' Stage: ' + evt.stage
};
let aliasedLambda = false;
return _this.Lambda.updateAliasPromised(params);
var params = {
FunctionName: data.FunctionName, /* required */
Name: evt.stage.toLowerCase() /* required */
};
} else {
return _this.Lambda.getAliasPromised(params)
.then(function() {
aliasedLambda = true;
})
.catch(function(e) {
aliasedLambda = false;
})
.then(function() {
// Create New Alias
if (aliasedLambda) {
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Creating New Lambda Alias for version - ${lambdaVersion}`);
// Update Existing Alias
let params = {
FunctionName: data.FunctionName, /* required */
FunctionVersion: lambdaVersion, /* required */
Name: evt.stage, /* required */
Description: 'Project: ' + _this.S._project.name + ' Stage: ' + evt.stage
};
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Updating Lambda Alias for version - ${lambdaVersion}`);
let params = {
FunctionName: data.FunctionName, /* required */
FunctionVersion: lambdaVersion, /* required */
Name: evt.stage, /* required */
Description: 'Project: ' + _this.S._project.name + ' Stage: ' + evt.stage
};
return _this.Lambda.updateAliasPromised(params);
} else {
// Create New Alias
SUtils.sDebug(`"${evt.stage} - ${evt.region.region} - ${evt.function.name}": Creating New Lambda Alias for version - ${lambdaVersion}`);
let params = {
FunctionName: data.FunctionName, /* required */
FunctionVersion: lambdaVersion, /* required */
Name: evt.stage, /* required */
Description: 'Project: ' + _this.S._project.name + ' Stage: ' + evt.stage
};
return _this.Lambda.createAliasPromised(params);
}
})
.then(function(data) {
// Add Version & Alias information to evt
evt.function.deployedVersion = data.FunctionVersion;
evt.function.deployedAlias = evt.stage;
evt.function.deployedAliasArn = data.AliasArn;
return evt
});
})
return _this.Lambda.createAliasPromised(params);
}
})
.then(function(data) {
// Add Version & Alias information to evt
evt.function.deployedVersion = data.FunctionVersion;
evt.function.deployedAlias = evt.stage;
evt.function.deployedAliasArn = data.AliasArn;
return evt
});
})
}
}

View File

@ -11,12 +11,12 @@
module.exports = function(SPlugin, serverlessPath) {
const path = require('path'),
SError = require(path.join(serverlessPath, 'ServerlessError')),
SUtils = require(path.join(serverlessPath, 'utils/index')),
BbPromise = require('bluebird'),
fs = require('fs'),
os = require('os'),
wrench = require('wrench');
SError = require(path.join(serverlessPath, 'ServerlessError')),
SUtils = require(path.join(serverlessPath, 'utils/index')),
BbPromise = require('bluebird'),
fs = require('fs'),
os = require('os'),
wrench = require('wrench');
// Promisify fs module
BbPromise.promisifyAll(fs);
@ -81,12 +81,12 @@ module.exports = function(SPlugin, serverlessPath) {
// Flow
return _this._validateAndPrepare()
.bind(_this)
.then(_this._createDistFolder)
.then(_this._package)
.then(function() {
return options;
});
.bind(_this)
.then(_this._createDistFolder)
.then(_this._package)
.then(function() {
return options;
});
}
/**
@ -136,48 +136,48 @@ module.exports = function(SPlugin, serverlessPath) {
let excludePatterns = this.function.data.custom.excludePatterns || [];
wrench.copyDirSyncRecursive(
path.join(_this.S.config.projectPath, 'back'),
path.join(_this.S.config.projectPath, 'back'),
_this.pathDist,
{
exclude: function(name, prefix) {
{
exclude: function(name, prefix) {
if (!excludePatterns.length) {
return false;
if (!excludePatterns.length) {
return false;
}
let relPath = path.join(
prefix.replace(_this.pathDist, ''), name);
return excludePatterns.some(sRegex => {
relPath = (relPath.charAt(0) == path.sep) ? relPath.substr(1) : relPath;
let re = new RegExp(sRegex),
matches = re.exec(relPath),
willExclude = (matches && matches.length > 0);
if (willExclude) {
SUtils.sDebug(`"${_this.options.stage} - ${_this.options.region} - ${_this.function.data.name}": Excluding - ${relPath}`);
}
let relPath = path.join(
prefix.replace(_this.pathDist, ''), name);
return excludePatterns.some(sRegex => {
relPath = (relPath.charAt(0) == path.sep) ? relPath.substr(1) : relPath;
let re = new RegExp(sRegex),
matches = re.exec(relPath),
willExclude = (matches && matches.length > 0);
if (willExclude) {
SUtils.sDebug(`"${_this.options.stage} - ${_this.options.region} - ${_this.function.data.name}": Excluding - ${relPath}`);
}
return willExclude;
});
}
return willExclude;
});
}
}
);
// Get ENV file from S3
return _this.S3.sGetEnvFile(
this.regionBucket,
_this.project.data.name,
this.options.stage,
this.options.region)
.then(function(s3ObjData) {
this.regionBucket,
_this.project.data.name,
this.options.stage,
this.options.region)
.then(function(s3ObjData) {
fs.writeFileSync(
path.join(this.pathDist,'.env'),
s3ObjData.Body);
fs.writeFileSync(
path.join(this.pathDist,'.env'),
s3ObjData.Body);
});
});
}
/**
@ -190,23 +190,23 @@ module.exports = function(SPlugin, serverlessPath) {
this.function.data.custom.includePaths = ['.'];
// Create pathsPackaged for each file ready to compress
this.pathsPackaged = _this._generateIncludePaths(evt);
this.pathsPackaged = _this._generateIncludePaths();
return BbPromise.resolve(evt);
return BbPromise.resolve();
}
/**
* Generate Include Paths
*/
_generateIncludePaths(evt) {
_generateIncludePaths() {
let compressPaths = [],
ignore = ['.DS_Store'],
stats,
fullPath;
ignore = ['.DS_Store'],
stats,
fullPath;
this.function.data.custom.includePaths.forEach(p => {
this.function.data.custom.includePaths.forEach(p => {
try {
fullPath = path.resolve(path.join(this.pathDist, p));
@ -217,29 +217,36 @@ module.exports = function(SPlugin, serverlessPath) {
}
if (stats.isFile()) {
compressPaths.push({fileName: p, data: fs.readFileSync(fullPath)});
compressPaths.push({
fileName: p,
data: fs.readFileSync(fullPath)
});
} else if (stats.isDirectory()) {
let dirname = path.basename(p);
wrench
.readdirSyncRecursive(fullPath)
.forEach(file => {
.readdirSyncRecursive(fullPath)
.forEach(file => {
// Ignore certain files
for (let i = 0; i < ignore.length; i++) {
if (file.toLowerCase().indexOf(ignore[i]) > -1) return;
}
// 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);
compressPaths.push({fileName: pathInZip, data: fs.readFileSync(filePath)});
}
});
let filePath = [fullPath, file].join('/');
if (fs.lstatSync(filePath).isFile()) {
let pathInZip = path.join(dirname, file);
compressPaths.push({
fileName: pathInZip,
data: fs.readFileSync(filePath)
});
}
});
}
});
return compressPaths;
}
}