basic deployCore functionality

This commit is contained in:
Eslam A. Hefnawy 2016-05-30 13:29:23 +02:00
parent 0a1c19b02a
commit 26f534f481
6 changed files with 165 additions and 158 deletions

View File

@ -1,55 +1,13 @@
'use strict';
const AWS = require('aws-sdk');
const BbPromise = require('bluebird');
const Zip = require('node-zip');
const deployCore = require('./lib/deployCore');
class Deploy {
constructor(serverless) {
class awsDeploy {
constructor(serverless, options) {
this.serverless = serverless;
this.commands = {
deploy: {
usage: 'deploy lambda zip.',
lifecycleEvents: [
'deploy',
],
},
};
this.hooks = {
'deploy:deploy': this.deploy,
};
const config = {
region: 'us-east-1',
};
this.Lambda = new AWS.Lambda(config);
BbPromise.promisifyAll(this.Lambda, { suffix: 'Promised' });
}
deploy(options) {
this.options = options;
const allPromises = [];
this.serverless.service.getAllFunctions().forEach((f) => {
const fConfig = this.serverless.service.getFunction(f);
const configParams = {
FunctionName: `${this.serverless.service.service}-${f}`,
Description: fConfig.description,
Handler: fConfig.handler,
MemorySize: fConfig.memory_size,
Timeout: fConfig.timeout,
};
allPromises.push(this.Lambda.updateFunctionConfigurationPromised(configParams));
const codeParams = {
FunctionName: `${this.serverless.service.service}-${f}`,
ZipFile: new Zip(),
};
allPromises.push(this.Lambda.updateFunctionCodePromised(codeParams));
});
return BbPromise.all(allPromises);
Object.assign(this, deployCore);
}
}
module.exports = Deploy;
module.exports = awsDeploy;

View File

@ -1 +1,124 @@
//
'use strict';
/*
* check if stack already exists. resolve if it is.
* get core cf template and add variables
* create stack
* monitor stack
* add output vars
*
* TO MOCK:
* this.options.region
* this.options.stage
* service name
* serverlessEnvYaml
*/
const BbPromise = require('bluebird');
const path = require('path');
const _ = require('lodash');
const async = require('async');
const AWS = require('aws-sdk');
module.exports = {
createStack() {
const config = {
region: this.options.region,
};
this.CloudFormation = new AWS.CloudFormation(config);
BbPromise.promisifyAll(this.CloudFormation, { suffix: 'Promised' });
const stackName = `${this.serverless.service.service}-${this.options.stage}`;
const coreCFTemplate = this.serverless.utils.readFileSync(
path.join(this.serverless.config.serverlessPath, 'templates', 'core-cf.json')
);
// set the necessary variables before creating stack
coreCFTemplate
.Resources
.coreBucket
.Properties
.BucketName = `${this.serverless.service.service}-${this.options.region}`;
coreCFTemplate
.Resources
.IamPolicyLambda
.Properties
.PolicyName = `${this.options.stage}-${this.serverless.service.service}-lambda`;
coreCFTemplate
.Resources
.IamPolicyLambda
.Properties
.PolicyDocument
.Statement[0]
.Resource = `arn:aws:logs:${this.options.region}:*:*`;
const params = {
StackName: stackName,
OnFailure: 'DELETE',
Capabilities: [
'CAPABILITY_IAM',
],
Parameters: [],
TemplateBody: JSON.stringify(coreCFTemplate),
Tags: [{
Key: 'STAGE',
Value: this.options.stage,
}],
};
return this.CloudFormation.createStackPromised(params);
},
monitor(cfData, frequency) {
const validStatuses = [
'CREATE_COMPLETE',
'CREATE_IN_PROGRESS',
];
return new BbPromise((resolve, reject) => {
let stackStatus = null;
let stackData = null;
async.whilst(() => (stackStatus !== 'CREATE_COMPLETE'),
(callback) => {
setTimeout(() => {
const params = {
StackName: cfData.StackId,
};
return this.CloudFormation.describeStacksPromised(params)
.then((data) => {
stackData = data;
stackStatus = stackData.Stacks[0].StackStatus;
if (!stackStatus || validStatuses.indexOf(stackStatus) === -1) {
return reject(new this.serverless.classes
.Error(`An error occurred while provisioning your cloudformation: ${stackData
.Stacks[0].StackStatusReason}`));
}
return callback();
});
}, frequency || 5000);
}, () => resolve(stackData.Stacks[0]));
});
},
addOutputVars() {
const serverlessEnvYamlPath = path
.join(this.serverless.config.servicePath, 'serverless.env.yaml');
return this.serverless.yamlParser.parse(serverlessEnvYamlPath).then(parsedServerlessEnvYaml => {
const serverlessEnvYaml = parsedServerlessEnvYaml;
cfData.Outputs.forEach((output) => {
const varName = _.lowerFirst(output.OutputKey);
serverlessEnvYaml.stages[this.options.stage]
.regions[this.options.region].vars[varName] = output.OutputValue;
});
this.serverless.utils.writeFileSync(serverlessEnvYamlPath, serverlessEnvYaml);
return BbPromise.resolve();
});
},
deployCore() {
// check if stack exists
return BbPromise.bind(this)
.then(this.createStack)
.then(this.monitor)
.then(this.addOutputVars);
},
};

View File

@ -0,0 +1,12 @@
'use strict';
const AwsDeploy = require('../awsDeploy');
const Serverless = require('../../../Serverless');
const awsDeploy = new AwsDeploy();
describe('test', () => {
it('test case', () => {
awsDeploy.deployCore();
});
});

View File

@ -1,93 +0,0 @@
'use strict';
const expect = require('chai').expect;
const sinon = require('sinon');
const Deploy = require('../deploy');
const Serverless = require('../../../Serverless');
const serverless = new Serverless();
describe('Deploy', () => {
let deploy;
let configStub;
let codeStub;
beforeEach(() => {
deploy = new Deploy(serverless);
configStub = sinon.stub(deploy.Lambda, 'updateFunctionConfigurationPromised');
codeStub = sinon.stub(deploy.Lambda, 'updateFunctionCodePromised');
serverless.service.service = 'myService';
serverless.service.functions = {
create: {
handler: 'users.create',
description: 'fake function',
memory_size: 512,
timeout: 6,
},
list: {
handler: 'users.list',
description: 'fake function',
memory_size: 1024,
timeout: 6,
},
};
});
afterEach(() => {
deploy.Lambda.updateFunctionConfigurationPromised.restore();
deploy.Lambda.updateFunctionCodePromised.restore();
});
describe('#constructor()', () => {
it('should have commands', () => expect(deploy.commands).to.be.not.empty);
it('should have hooks', () => expect(deploy.hooks).to.be.not.empty);
});
describe('#deploy()', () => {
it('should update lambda', () => {
deploy.deploy().then(() => {
// both config calls
expect(configStub.calledTwice).to.be.equal(true);
// first config call args
expect(configStub.args[0][0].FunctionName)
.to.be.equal('myService-create');
expect(configStub.args[0][0].Description)
.to.be.equal(serverless.service.functions.create.description);
expect(configStub.args[0][0].Handler)
.to.be.equal(serverless.service.functions.create.handler);
expect(configStub.args[0][0].MemorySize)
.to.be.equal(serverless.service.functions.create.memory_size);
expect(configStub.args[0][0].Timeout)
.to.be.equal(serverless.service.functions.create.timeout);
// second config call args
expect(configStub.args[1][0].FunctionName)
.to.be.equal('myService-list');
expect(configStub.args[1][0].Description)
.to.be.equal(serverless.service.functions.list.description);
expect(configStub.args[1][0].Handler)
.to.be.equal(serverless.service.functions.list.handler);
expect(configStub.args[1][0].MemorySize)
.to.be.equal(serverless.service.functions.list.memory_size);
expect(configStub.args[1][0].Timeout)
.to.be.equal(serverless.service.functions.list.timeout);
// both code calls
expect(codeStub.calledTwice).to.be.equal(true);
// first code call args
expect(codeStub.args[0][0].FunctionName)
.to.be.equal('myService-create');
expect(typeof codeStub.args[0][0].ZipFile)
.to.not.be.equal('undefined');
// second code call args
expect(codeStub.args[1][0].FunctionName)
.to.be.equal('myService-list');
expect(typeof codeStub.args[1][0].ZipFile)
.to.not.be.equal('undefined');
});
});
});
});

View File

@ -2,6 +2,12 @@
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway",
"Resources": {
"coreBucket": {
"Type" : "AWS::S3::Bucket",
"Properties": {
"BucketName": ""
}
},
"IamRoleLambda": {
"Type": "AWS::IAM::Role",
"Properties": {

View File

@ -3,20 +3,21 @@
process.env.DEBUG = '*';
require('./config');
// Serverless Core Tests
require('./classes/Serverless');
require('./classes/PluginManager');
require('./classes/Utils');
require('./classes/Config');
require('./classes/Service');
require('./classes/YamlParser');
require('./classes/CLI');
// Integration Tests
require('./integration/Serverless');
// Core Plugins Tests
require('../lib/plugins/create/tests/create');
require('../lib/plugins/deploy/tests/deploy');
require('../lib/plugins/awsResourcesDeploy/tests/awsResourcesDeploy');
require('../lib/plugins/awsCompileFunctionsToResources/tests/awsCompileFunctionsToResources');
// // Serverless Core Tests
// require('./classes/Serverless');
// require('./classes/PluginManager');
// require('./classes/Utils');
// require('./classes/Config');
// require('./classes/Service');
// require('./classes/YamlParser');
// require('./classes/CLI');
//
// // Integration Tests
// require('./integration/Serverless');
//
// // Core Plugins Tests
// require('../lib/plugins/create/tests/create');
// require('../lib/plugins/deploy/tests/deploy');
// require('../lib/plugins/awsResourcesDeploy/tests/awsResourcesDeploy');
// require('../lib/plugins/awsCompileFunctionsToResources/tests/awsCompileFunctionsToResources');
require('../lib/plugins/awsDeploy/tests/awsDeploy');