Notifications During Deployment

Adding Notifications

You might want to add notifications to your deployment process. I use Slack, and wanted to know when a deployment was ran, as well as it's outcome.

To do this, I added more code into the Python script. This has the benefit of keeping the NodeJS listener as simple as possible - it is only responsible for listening to webhooks and adding jobs to the queue.

Back in our Python virtualenv, we can add another library for make using Slack's web API easy.

# as user deployer
cd ~/build-server
source .venv/bin/activate

pip install requests

Next, we can create a new python file, responsible for making notifications. I'll call this file notify.py:

import requests
import json

def slack(results, error=False):
    # Good candidate for an environment variable
    slackUrl = 'https://hooks.slack.com/services/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'

    color = 'good'
    msg = 'Success'

    if error:
        color = 'danger'
        msg = 'Error'

    title = 'Deployment %s' % msg

    fields = []

    for host in results:
        fields.append({
            'title': 'host',
            'value': host,
            'short': True
        })

    # Go through each host?

    payload = {
        'username': 'Deploy Bot',
        'icon_emoji': ':cloud:',
        'channel': '#builds',
        'attachments' : [{
            'fallback': title,
            'color': color,
            'title': title,
            'title_link': "http://45.55.209.211",
            'text': 'Deployment Status',
            'fields': fields
        }]
    }

    response = requests.post(slackUrl, data=json.dumps(payload))

    return response

And then we can use it in our deploy.py script:

import sys
import json
from datetime import datetime
from StringIO import StringIO

import fabfile
from fabric.api import execute

import boto3

import notify

sqs = boto3.resource('sqs')
deploy_queue = sqs.get_queue_by_name(QueueName='serial-deploy')

def run_deploy(message):
    try:
        iomsg = StringIO(message.body)
        job_data = json.load(iomsg)

        # job_data['job']['time']
        # job_data['job']['reference']

        # We're not using job_data, yet
        result = execute(fabfile.deploy)

        # Check if successful
        has_errors = False
        for host in result:
            if result[host]:
                has_errors = True
                print "Error: %s" % result[host]

        if not has_errors:
            message.delete()

        notify.slack(result, has_errors)
    except Exception as e:
        print "Unexpected error:", sys.exc_info()[0]

while True:
    messages = deploy_queue.receive_messages(
        MaxNumberOfMessages=1,  # Single message at a time
        VisibilityTimeout=60,   # 60 seconds
        WaitTimeSeconds=10      # Wait for 10 seconds for next job
    )

    if len(messages) > 0:
        print "Starting deployment at %s" % datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
        run_deploy(messages[0])

Now we'll see the results of a deployment in a slack channel!