briareus
Version:
Briareus assists with Feature Branch deploys to ECS
213 lines (189 loc) • 5.37 kB
JavaScript
const _ = require('lodash');
const async = require('async');
const EventEmitter = require('events');
const uuidv4 = require('uuid/v4');
const jsonpatch = require('fast-json-patch');
const crypto = require('crypto');
const Actions = require('./actions');
const persistance = require('./persistance');
const recon = require('./recon');
class Deployment {
constructor(ctx, data) {
this.ctx = ctx;
this.events = new EventEmitter();
if (!data.partitionKey) {
// If this is a new deployment; initialize
let id = uuidv4();
let hashedSlug = crypto.createHash('md5').update(data.slug).digest("hex").substr(0, 22);
let name = `briareus-${hashedSlug}`;
_.defaults(data, {
partitionKey: `deployment-${id}`,
sortKey: 'details',
uuid: id,
hashedSlug: hashedSlug,
name: name,
endpoint: {
protocol: 'https',
hostname: `${name}.${data.baseHostname}`
},
createdAt: Math.floor(new Date() / 1000),
events: [],
assets: {
dnsRecords: []
},
status: 'created'
});
}
this.data = data;
this.ctx.logData.deploymentId = this.data.uuid;
}
recon(cb) {
this.ctx.log.info(`Starting Recon`);
let deploymentAware = _.map(_.keys(recon), (reconName) => {
let fn = recon[reconName];
return (done) => {
this.ctx.log.info(`Running Recon: ${reconName}`);
fn(this, done);
}
});
async.parallel(deploymentAware, (err, results) => {
if (err) return cb(err);
this.data = jsonpatch.applyPatch(this.data, _.flatten(results)).newDocument;
cb();
});
}
provision(cb) {
async.series([
(next) => this.recon(next),
(next) => {
this.data.status = 'provisioning';
this.save(next)
},
(next) => {
let pipeline = this.createProvisionPipeline();
this.runPipeline(pipeline, next);
},
(next) => {
this.data.status = 'provisioned';
this.save(next)
}
], cb);
}
createProvisionPipeline() {
let phases = [];
if (!this.get('/assets/ecsService')) {
phases.push({
name: 'Creating Build Infrastructure',
actions: [
'createAcmCertificate',
'createCertificateVerificationDnsRecords',
'waitForAcmCertificateVerification',
'addAcmCertificateToAlbListener',
'createPlaceholderEcsTaskDefinition',
'createAlbTargetGroup',
'setAlbTargetGroupAttributes',
'createAlbRule',
'createDnsRecordsEndpoint',
'createEcsService',
]
});
}
// phases.push({
// name: 'Creating Build Release',
// actions: [
// 'createBuildEcsTaskDefinition',
// 'deployBuildTaskDefinition'
// ]
// });
// phases.push({
// name: 'Monitoring Deploy',
// actions: [
// 'monitorDeploy'
// ]
// });
return phases;
}
destroy(cb) {
async.series([
(next) => {
this.data.status = 'destroying';
this.save(next)
},
(next) => {
let pipeline = this.createDestroyPipeline();
this.runPipeline(pipeline, next);
},
(next) => {
this.data.status = 'destroyed';
this.save(next)
}
], cb);
}
createDestroyPipeline() {
let phases = [];
phases.push({
name: 'Destroying Resources',
actions: [
// 'destroyDnsRecords',
// 'removeAcmCertificateFromAlbListener',
// 'destroyAcmCertificate',
'scaleDownEcsService',
'destroyEcsService',
'destroyAlbRule',
'destroyAlbTargetGroup',
]
});
return phases;
}
get(path) {
return jsonpatch.getValueByPointer(this.data, path);
}
save(cb) {
this.ctx.log.info(`Saving Deployment`);
persistance.putItem(this.data, cb)
}
emit(event, data) {
let decoratedEvent = {
id: uuidv4(),
createdAt: Math.floor(new Date() / 1000),
name: event,
data
};
this.ctx.log.info({ event: decoratedEvent }, `Deployment Event`);
this.data.events.push(decoratedEvent);
this.events.emit(event);
}
runPipeline(phases, cb) {
this.emit('pipeline:start');
async.eachSeries(phases, (phase, next) => {
this.emit('phase:start', { phase: phase.name });
async.series(this.makeActions(phase.actions), (err) => {
if (err) return next(err);
this.emit('phase:complete', { phase: phase.name });
});
}, (err) => {
if (err) return cb(err);
this.emit('pipeline:complete');
cb();
});
}
makeActions(actions) {
return _.map(actions, (actionName) => {
let action = Actions[actionName];
return (cb) => {
this.emit('action:start', { action: actionName, description: action.description });
async.waterfall([
(next) => action(this, next),
(patches, next) => {
this.ctx.log.debug({ patches }, `Output patch for action:${actionName}`);
this.data = jsonpatch.applyPatch(this.data, patches).newDocument;
this.emit('action:complete', { action: actionName });
this.save(next)
}
], cb);
}
});
}
}
module.exports = Deployment;