UNPKG

@bowtie/sls

Version:

Serverless helpers & utilities

189 lines (160 loc) 5.19 kB
const uuidv1 = require('uuid/v1') // const merge = require('deepmerge') const { verifySchema } = require('@bowtie/utils') const { dynamoose, defaults, helpers } = require('../config') const { parseServiceConfig, scanRecursive, findDeploymentHost, findDeploymentLinks } = require('../utils') const pubnub = require('../pubnub') // [HIGH] TODO: WTF?!? // const Build = require('./Build') const { Schema } = dynamoose const { envGetAlias, tagIsRelease } = helpers const DeploySchema = new Schema({ id: { type: String, required: true, hashKey: true, default: (model) => uuidv1() }, service_name: String, deploy_timestamp: Number, deploy_status: { type: String, required: true, enum: [ 'CREATED', 'IN_PROGRESS', 'SUCCEEDED', 'FAILED', 'STOPPED' ], default: 'CREATED' }, tag: String, env: String, repo: String, rev: String, user: String, host: String, stack: String, links: Array, target: String, release: Boolean, approved: Boolean, verified: Boolean, build_id: String, build_number: Number, health_check: String, }, { timestamps: true, throughput: 'ON_DEMAND' }) const DeployModel = dynamoose.model(process.env.DEPLOYS_TABLE_NAME, DeploySchema); DeployModel.methods.set('scanAll', async function (params = {}) { return scanRecursive(this, params) }) DeployModel.methods.document.set('hasBuild', function (options = {}) { return (this.build_id && this.build_id.trim() !== '') }) DeployModel.methods.document.set('getBuild', async function (options = {}) { const Build = require('./Build') if (this.hasBuild()) { return await Build.get(this.build_id) } else { return await Build.findOne({ source_version: this.rev }, { deploy: this }) } }) DeployModel.methods.document.set('sync', async function (params = {}) { await this.validate({ env: envGetAlias(this.stack), host: findDeploymentHost(this.stack), repo: `${parseServiceConfig('source')['repo']}/${parseServiceConfig('source')['repo']}`, links: findDeploymentLinks(this.stack), target: parseServiceConfig('target'), release: tagIsRelease(this.tag), approved: !!this.approved, verified: !!this.verified, service_name: process.env.SERVICE_NAME, health_check: '/', deploy_timestamp: Date.now(), }) return await this.save() }) DeployModel.methods.document.set('validate', async function (updates = {}) { Object.assign(this, updates) if ((this.approved || this.verified) && this.deploy_status !== 'SUCCEEDED') { throw new Error(`FAIL: Deploy#validate() (deploy_status: ${this.deploy_status})`) } const build = await this.getBuild() const tag = this.tag || build.release || build.build_number || 'latest' if (!build) { throw new Error(`FAIL: Deploy#validate() (build: null or undefined)`) } if (build.build_status !== 'SUCCEEDED') { throw new Error(`FAIL: Deploy#validate() (build_status: ${build.build_status})`) } // Skip tag validate, not 100% functional for static/s3 target builds ... // if (!(build.build_tags && build.build_tags.includes(this.tag))) { // throw new Error(`FAIL: Deploy#validate() '${this.tag}' (build_tags: '${build.build_tags.join(',')})`) // } if (!build.build_sender) { throw new Error(`FAIL: Deploy#validate() '${this.tag}' (build_sender: '${build.build_sender})`) } Object.assign(this, { tag, user: build.build_sender, build_id: build.id, build_number: build.build_number }) verifySchema(this, { tag: 'string', env: 'string', user: 'string', stack: 'string', target: 'string', build_id: 'string', build_number: 'number', approved: 'boolean', verified: 'boolean', service_name: 'string', health_check: 'string', deploy_timestamp: 'number', }) return true }) DeployModel.methods.document.set('publish', function (data = {}, action = 'saved') { return new Promise( (resolve, reject) => { const payload = { channel: 'deploys', message: { data, action, service: process.env.SERVICE_NAME, subject: this } } console.log('Deploy publish', { payload, data }, this) if (pubnub) { pubnub.publish(payload, (status, response) => { console.log('PN Publish', { status, response }) if (status.error) { const { operation, statusCode } = status reject(new Error(`FAIL: Deploy#publish() failed: (${operation} -> ${statusCode}})`)) } else { resolve(this) } }) } else { console.log('PubNub is not configured, not publishing updates') resolve(this) } } ) }) DeployModel.methods.document.set('saveNotify', async function (options = {}) { const data = await this.sync() return await this.publish(data, 'saved') }) DeployModel.methods.document.set('parse', function (options = {}) { return { id: this.id, tag: this.tag, env: this.env, rev: this.rev, user: this.user, repo: this.repo, stack: this.stack, status: this.deploy_status, timestamp: this.deploy_timestamp } }) module.exports = DeployModel