UNPKG

briareus

Version:

Briareus assists with Feature Branch deploys to ECS

213 lines (189 loc) 5.37 kB
'use strict' 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;