@bowtie/sls
Version:
Serverless helpers & utilities
283 lines (231 loc) • 13.3 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>deployer.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading"><a href="global.html">Globals</a></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#actionFailureNotifySlack">actionFailureNotifySlack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#bitbucketWebhook">bitbucketWebhook</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildChange">buildChange</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildChangeNotifyBitbucket">buildChangeNotifyBitbucket</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildChangeNotifyGithub">buildChangeNotifyGithub</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#buildChangeNotifySlack">buildChangeNotifySlack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#decodeBody">decodeBody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#deployBuild">deployBuild</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#deployEcr">deployEcr</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#deploymentNotifyAirbrake">deploymentNotifyAirbrake</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#deployments">deployments</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#deployS3">deployS3</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#describeStack">describeStack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#findClusterName">findClusterName</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#findClusterStack">findClusterStack</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#findMigrationTask">findMigrationTask</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#getStatusColor">getStatusColor</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#githubWebhook">githubWebhook</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#init">init</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#initMigration">initMigration</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#parseBody">parseBody</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#parsePayload">parsePayload</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#prepareBuild">prepareBuild</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#runMigration">runMigration</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#slackCommand">slackCommand</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#slackResponse">slackResponse</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#stackChange">stackChange</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#startBuild">startBuild</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="global.html#trackBuild">trackBuild</a></span></li>
</nav>
<div id="main">
<h1 class="page-title">deployer.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>// Include the AWS SDK
const AWS = require('aws-sdk')
const async = require('async')
const { Deploy } = require('./models')
// Create instances for ECR & CloudFormation
const ECR = new AWS.ECR()
const CloudFormation = new AWS.CloudFormation()
/**
* Handle a stack change event (as published from SNS topic)
* @param {object} event
*/
module.exports.stackChange = (event) => {
// Return a promise for the promise chain
return new Promise(
(resolve, reject) => {
// Ensure the SNS records have been parsed into messages
if (!event.parsed.messages || event.parsed.messages.length === 0) {
reject(new Error('No messages to be sent.'))
return
}
console.log('Messages:', JSON.stringify(event.parsed.messages, null, 2))
// Loop through all messages (async)
async.each(event.parsed.messages, (msg, next) => {
// Flag to determine whether this message is for parent stack or a stack resource
const isStackMessage = (msg.ResourceType === 'AWS::CloudFormation::Stack' && msg.LogicalResourceId === msg.StackName)
if (isStackMessage) {
console.log('Tracking deploy status from msg:', msg)
console.log('Current event object', JSON.stringify(event, null, 2))
next()
} else {
console.log('Not tracking deploy status for msg:', msg)
next()
}
}, err => {
if (err) {
// Reject on error
reject(err)
} else {
// Resolve with event
resolve(event)
}
})
}
)
}
/**
* Deploy S3
* @param {object} event
* @param {object} deployment
* @param {function} done
*/
const deployS3 = (event, deployment, done) => {
console.log('Deploy to S3:', deployment)
if (event.parsed.build.found) {
done(new Error(`Skip deploy, build exists. Rebuilding`));
} else {
done(null, 'SUCCEEDED');
}
}
/**
* Deploy ECR
* @param {object} event
* @param {object} deployment
* @param {function} done
*/
const deployEcr = (event, deployment, done) => {
// Construct ECR parameters to search for the specified tag
const ecrParams = {
// Use repo as specified in the parsed service parameters
repositoryName: process.env.ECR_REPO_NAME,
imageIds: [
{
// Search for an image tag as specified in the parsed service parameters
imageTag: deployment.tag
}
]
}
// Check if repo & tag exist
ECR.batchGetImage(ecrParams, (err, data) => {
// Reject on error
if (err) {
return done(err)
}
// Skip deployment if tag does not exist in ECR
if (data.images.length === 0) {
console.log('Unable to find ECR image with params:', JSON.stringify(ecrParams))
return done(null, 'FAILED')
}
// Ensure at least 1 image, and no failures
// if (!data.images || data.images.length == 0 || data.failures.length > 0) {
// return done(data)
// }
// Describe the stack (verifies it exists, and can build parameter list)
CloudFormation.describeStacks({
// Get the stack name from the parsed service parameters
StackName: deployment.stack
}, (err, data) => {
// Reject on error
if (err) {
console.warn(err)
console.log('Unable to find CF Stack with name:', deployment.stack)
return done(null, 'FAILED')
}
// This should be impossible, but ensure only a single stack is being returned
if (data.Stacks.length > 1) {
return done(new Error(`More than 1 stack matching name: ${deployment.stack}`))
}
// Filter current stack parameters except for "Tag"
const params = data.Stacks[0].Parameters.map(p => {
return {
ParameterKey: p.ParameterKey,
UsePreviousValue: true
}
}).filter(p => p.ParameterKey !== 'Tag')
// Add the "Tag" parameter with the tag as specified by the parsed service parameters
params.push({
ParameterKey: 'Tag',
ParameterValue: deployment.tag
})
// Update the CloudFormation stack with the reconstructed parameters
CloudFormation.updateStack({
// Get the stack name from the parsed service parameters
StackName: deployment.stack,
// Should include all previous parameters, and an updated "Tag" parameter
Parameters: params,
// Reuse the previous stack template
UsePreviousTemplate: true,
// Execute this stack update with the given role ARN
// - This is passed into the environment by serverless (see "serverless.yml")
RoleARN: process.env.UPDATE_ROLE_ARN,
// A stack that creates/changes IAM roles/policies/etc MUST provide this capability flag
Capabilities: [ 'CAPABILITY_IAM' ],
// Send stack change notifications to the SNS topic ARN
// - This is passed into the environment by serverless (see "serverless.yml")
// - This SNS topic is automatically hooked up to the "stack-change" function to send messages to Slack
NotificationARNs: [ process.env.NOTIFY_SNS_ARN ]
}, (err) => {
if (err) {
return done(err)
}
done(null, 'IN_PROGRESS')
})
})
})
}
/**
* Deploy build (from parsed deployments)
* @param {object} event
*/
module.exports.deployBuild = (event) => {
// Return a promise for the promise chain
return new Promise(
(resolve, reject) => {
if (event.builds.prepared) {
console.log('Event contains prepared build (new build). Skipping deployments ...')
resolve(event)
return
}
// Ensure the deployments have been parsed
if (!event.parsed.deployments) {
console.log('Event does not contain parsed deployment details.')
resolve(event)
return
}
// Debug logs for deployments being handled
console.log('Parsed deployments', JSON.stringify(event.parsed.deployments, null, 2))
console.log('Current event object', JSON.stringify(event, null, 2))
// Loop through parsed deployments (async) and update CF stack(s)
async.each(event.parsed.deployments, (deployment, next) => {
const deployMethod = deployment.target === 's3' ? deployS3 : deployEcr
deployMethod(event, deployment, (err, status) => {
if (err) return next(err)
const newDeploy = new Deploy(Object.assign({}, deployment, {
service_name: process.env.SERVICE_NAME,
deploy_status: status,
deploy_timestamp: Date.now()
}))
newDeploy.saveNotify().then(data => {
console.log('Saved deploy data:', data)
next()
}).catch(next)
})
}, err => {
if (err) {
console.warn(err)
}
resolve(event)
})
}
)
}
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.4</a> on Mon Jun 22 2020 11:46:50 GMT-0600 (Mountain Daylight Time) using the Minami theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
</body>
</html>