@bowtie/sls
Version:
Serverless helpers & utilities
189 lines (160 loc) • 5.19 kB
JavaScript
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