Configuring Notifications for Third-Party Services

When your Container Builder builds change states, you can send notifications about these changes to third-party messaging services and via email.

Services such as Slack offer incoming webhooks, which enable users to have messages from external services sent to their clients. Cloud Functions allows users to write single-use, programmatic functions which listen for cloud events, such as builds. When a cloud event occurs, a trigger responds by executing an action, such as sending a Cloud Pub/Sub message.

There are many third-party services built with cross-application messaging functionality, such as SendGrid and IFTTT. You can use this tutorial as a template for connecting with services like these.

This tutorial demonstrates how you can use Cloud Functions and Cloud Pub/Sub to send notifications about {{ short_product_name }) to Slack and via email using Mailgun.

Objectives

  • Either deploy a Slack application or set up Mailgun to receive external notifications from Container Builder.
  • Write a Cloud Function for sending Pub/Sub notifications to Slack or Mailgun.

Costs

This tutorial uses billable components of Cloud Platform, including:

  • Cloud Functions
  • Cloud Pub/Sub
  • Container Engine

Use the Pricing Calculator to generate a cost estimate based on your projected usage.

New Cloud Platform users might be eligible for a free trial.

Before you begin

  1. Sign in to your Google account.

    If you don't already have one, sign up for a new account.

  2. Select or create a Cloud Platform project.

    Go to the Manage resources page

  3. Enable billing for your project.

    Enable billing

  4. Enable the Cloud Functions, Cloud Pub/Sub, and Container Engine APIs.

    Enable the APIs

  5. Install and initialize the Cloud SDK.
  6. Update and install gcloud components:
    gcloud components update &&
    gcloud components install alpha beta

You may also want to complete the Container Engine quickstart to become familiar with this product.

Slack notifications

The following sections walk you through how to set up Slack notifications.

Preparing the Slack application

  1. Download Slack for your computer. You also need to join a Slack team, either by registering with your email or by using an invitation sent by a Team Admin.

  2. Sign in to Slack using your team name and your Slack account credentials.

  3. Create a new Slack app:

    1. Choose the app's name and your Slack team. Click Create.
    2. Click Incoming Webhooks.
    3. Enable incoming webhooks.
    4. Click Add New Webhook to Team. An authorization page opens.
    5. From the drop down menu, select the channel to which you would like notifications sent.
    6. Click Authorize.
    7. A webhook for your Slack application has been created. Copy the webhook and save it for later use.

Writing the Cloud Function

Create a Cloud Storage bucket to stage your Cloud Functions files, where [STAGING_BUCKET_NAME] is a globally-unique bucket name (such as [PROJECT-ID]_cloudbuilds):

gsutil mb gs://[STAGING_BUCKET_NAME]

You should see the following output:

Creating gs://[PROJECT-ID]_cloudbuilds/[STAGING_BUCKET_NAME]...

Next, create a directory on your local system for the application code:

Linux / macOS

mkdir ~/gcb_slack
cd ~/gcb_slack

Windows

mkdir %HOMEPATH%\gcb_slack
cd %HOMEPATH%\gcb_slack

Then, create the following files in the gcb_slack directory:

index.js

const IncomingWebhook = require('@slack/client').IncomingWebhook;
const SLACK_WEBHOOK_URL = "[SLACK_WEBHOOK]"

const webhook = new IncomingWebhook(SLACK_WEBHOOK_URL);

// subscribe is the main function called by Cloud Functions.
module.exports.subscribe = (event, callback) => {
 const build = eventToBuild(event.data.data);

// Skip if the current status is not in the status list.
// Add additional statues to list if you'd like:
// QUEUED, WORKING, SUCCESS, FAILURE,
// INTERNAL_ERROR, TIMEOUT, CANCELLED
  const status = ['SUCCESS', 'FAILURE', 'INTERNAL_ERROR', 'TIMEOUT'];
  if (status.indexOf(build.status) === -1) {
    return callback();
  }

  // Send message to Slack.
  const message = createSlackMessage(build);
  webhook.send(message, callback);
};

// eventToBuild transforms pubsub event message to a build object.
const eventToBuild = (data) => {
  return JSON.parse(new Buffer(data, 'base64').toString());
}

// createSlackMessage create a message from a build object.
const createSlackMessage = (build) => {
  let message = {
   text: 'Build \`${build.id}\`',
    mrkdwn: true,
    attachments: [
      {
        title: 'Build logs',
        title_link: build.logUrl,
        fields: [{
          title: 'Status',
          value: build.status
        }]
      }
    ]
  };
  return message
}

[SLACK-WEBHOOK] is your Slack app's incoming webhook.

This program uses Slack's webhook to listen for and receive messages from the Cloud Function. When an event is fired, a message is sent to the webhook then relayed to Slack.

The createSlackMessage function has been simplified for this example. You can expand the message to include much more, including text colors, images, and build duration.

package.json

{
  "name": "google-container-slack",
  "version": "0.0.1",
  "description": "Slack integration for Google Cloud Container Builder, using Google Cloud Functions",
  "main": "index.js",
  "dependencies": {
    "@slack/client": "3.9.0"
  }
}

package.json describes:

  • the program's name, version, and description
  • its primary runtime file(s)
  • its dependencies

This file is deceptively simple: you can add more dependencies, requirements, and other information as needed.

You should now have index.js and package.json in the gcb_slack directory.

Deploying the Cloud Function

To deploy the subscribe function with a Cloud Pub/Sub trigger, run the following command in the gcb_slack directory:

gcloud beta functions deploy subscribe --stage-bucket [STAGING_BUCKET_NAME] --trigger-topic cloud-builds

where [STAGING_BUCKET_NAME] is the name of your staging Cloud Storage Bucket, such as [PROJECT-ID]_cloudbuilds.

You should see something like the following output:

Copying file:///var/folders/../.../fun.zip [Content-Type=application/zip]...
/ [1 files][  1.5 KiB/  1.5 KiB]
Operation completed over 1 objects/1.5 KiB.
Waiting for operation to finish...done.
Deploying function (may take a while - up to 2 minutes)...done.
availableMemoryMb: 256
entryPoint: subscribe
eventTrigger:
  eventType: providers/cloud.pubsub/eventTypes/topic.publish
  resource: projects/[PROJECT-ID]/topics/cloud-builds
latestOperation: operations/...
name: projects/[PROJECT-ID]/locations/us-central1/functions/subscribe
status: READY
timeout: 60s

After you've completed deployment of the Cloud Function, when a build event occurs, you receive a Slack notification.

Email notifications

The following sections walk you through setting up email notifications using the Mailgun API.

If you are new to Mailgun, follow their quickstart documentation.

Prepare Mailgun settings

  1. If you haven't already, create a Mailgun account.
  2. Add a new domain, then follow Mailgun's instructions for verifying your domain. This may take up to 48 hours.
  3. From the domains page, click your domain to open its settings page.
  4. From your domain's settings page, copy the domain's API key and save it for later use.

You must also have the following:

  • An email address for sending email. This email address can belong to the domain you provide. If you are not sure which email address to use, check with your domain provider. You can also provide an nonexistent email if you do not expect a response from the recipient, such as noreply@mydomain.com.
  • An email address for receiving email. The recipient can be any email address.

Writing the Cloud Function

Create a Cloud Storage bucket to stage your Cloud Functions files, where [STAGING_BUCKET_NAME] is a globally-unique bucket name (such as [PROJECT-ID]_cloudbuilds):

gsutil mb gs://[STAGING_BUCKET_NAME]

You should see the following output:

Creating gs://[PROJECT-ID]_cloudbuilds/[STAGING_BUCKET_NAME]...

Next, create a directory on your local system for the application code:

Linux / macOS

mkdir ~/gcb_email
cd ~/gcb_email

Windows

mkdir %HOMEPATH%\gcb_email
cd %HOMEPATH%\gcb_email

Then, create the following files in the gcb_email directory:

index.js

const Mailgun = require('mailgun-js');
const humanizeDuration = require('humanize-duration');
const config = require('./config.json');

module.exports.mailgun = new Mailgun({
  apiKey: config.MAILGUN_API_KEY,
  domain: config.MAILGUN_DOMAIN,
});

// subscribe is the main function called by Cloud Functions.
module.exports.subscribe = (event, callback) => {
  const build = module.exports.eventToBuild(event.data.data);

  // Skip if the current status is not in the status list.
  const status = ['SUCCESS', 'FAILURE', 'INTERNAL_ERROR', 'TIMEOUT'];
  if (status.indexOf(build.status) === -1) {
    return callback();
  }

  // Send email.
  const message = module.exports.createEmail(build);
  module.exports.mailgun.messages().send(message, callback);
};

// eventToBuild transforms pubsub event message to a build object.
module.exports.eventToBuild = (data) => {
  return JSON.parse(new Buffer(data, 'base64').toString());
}

// createEmail create an email message from a build object.
module.exports.createEmail = (build) => {
  let duration = humanizeDuration(new Date(build.finishTime) - new Date(build.startTime));
  let content = `<p>Build ${build.id} finished with status ${build.status}, in ${duration}.</p>`
    + `<p><a href="${build.logUrl}">Build logs</a></p>`;
  if (build.images) {
    let images = build.images.join(',');
    content += `<p>Images: ${images}</p>`;
  }
  let message = {
    from: config.MAILGUN_FROM,
    to: config.MAILGUN_TO,
    subject: `Build ${build.id} finished`,
    text: content
  };
  return message
}

config.json

  {
     "MAILGUN_API_KEY":"[API_KEY]",
     "MAILGUN_DOMAIN":"email.com",
     "MAILGUN_FROM":"me@email.com",
     "MAILGUN_TO":"someone@email.com"
  }

In this file, you must provide the following information:

  • MAILGUN_API_KEY, the API key you collected
  • MAILGUN_DOMAIN, the domain you added
  • MAILGUN_FROM, the email address belonging to the domain from which email is sent (even one that doesn't exist)
  • MAILGUN_TO, the email address to which email is sent

package.json

  {
    "name": "google-container-email",
    "version": "0.0.1",
    "description": "Email integration for Google Cloud Container Builder, using Google Cloud Functions",
    "main": "index.js"
    },
    "dependencies": {
      "humanize-duration": "3.10.0",
      "mailgun-js": "~0.11.2"
    },
    "devDependencies": {
      "async": "^2.1.5",
      "mocha": "3.2.0",
      "should": "11.2.1"
    },
  }

package.json describes:

  • the program's name, version, and description
  • its primary runtime file
  • its dependencies

You should now have index.js, config.json, and package.json in the gcb_email directory.

Deploying the Cloud Function

To deploy the function, run the following command in the gcb_email directory:

gcloud beta functions deploy subscribe --stage-bucket [STAGING_BUCKET_NAME] --trigger-topic cloud-builds

where [STAGING_BUCKET_NAME] is the name of your staging Cloud Storage Bucket, such as [PROJECT-ID]_cloudbuilds.

After you've completed deployment of the Cloud Function, you rshould eceive an email notification when a build event occurs.

Testing the function

After configuration, you should test your implementation to ensure that it is working as expected. You can test by submitting a build to Container Builder.

If you would like to quickly test your implementation from your local environment, create a file called request.json containing the following code. This request uploads a demonstration image to your repository.

  {
    "source": {
      "storageSource": {
        "bucket": "container-builder-examples",
        "object": "node-docker-example.tar.gz"
      }
    },
    "steps": [
      {
        "name": "gcr.io/cloud-builders/docker",
        "args": [
          "build", "-t", "gcr.io/$PROJECT_ID/cb-demo-img", "."
        ]
      }
    ],
    "images": [
      "gcr.io/$PROJECT_ID/cb-demo-img"
    ]
  }
  

Then, run the following command from your shell or terminal window:

curl -X POST -T /PATH/TO/request.json -H "Authorization: Bearer $(gcloud auth print-access-token)" https://cloudbuild.googleapis.com/v1/projects/[PROJECT-ID]/builds

where

  • [PROJECT-ID] is your project ID
  • /PATH/TO/request.json is the file path

You should see something like the following output:

{
  "name": "operations/build/[PROJECT-ID]/...,
  "metadata": {
    "@type": "type.googleapis.com/google.devtools.cloudbuild.v1.BuildOperationMetadata",
    "build": {
      "id": "...",
      "status": "QUEUED",
      "source": {
        "storageSource": {
          "bucket": "container-builder-examples",
          "object": "node-docker-example.tar.gz"
        }
      },
      "createTime": "...",
      "steps": [
        {
          "name": "gcr.io/cloud-builders/docker",
          "args": [
            "build",
            "-t",
            "gcr.io/[PROJECT-ID]/cb-demo-img",
            "."
          ]
        }
      ],
      "timeout": "600s",
      "images": [
        "gcr.io/[PROJECT-ID]/cb-demo-img"
      ],
      "projectId": "[PROJECT-ID]",

If you followed the Slack instructions, you should see a Slack notification like this one:

If you followed the Mailgun instructions, you should receive an email about the build.

Deleting the demonstration image

To delete the demonstration image, follow these steps:

  1. Use your web browser to visit https://gcr.io/[PROJECT-ID]/cb-demo-img, where [PROJECT-ID] is your project ID.
  2. Check the box beside the image.
  3. Click Delete.

Cleaning up

To avoid incurring charges to your Google Cloud Platform account for the resources used in this tutorial:

Deleting the project

The easiest way to eliminate billing is to delete the project you created for the tutorial.

To delete the project:

  1. In the Cloud Platform Console, go to the Projects page.

    Go to the Projects page

  2. In the project list, select the project you want to delete and click Delete project. After selecting the checkbox next to the project name, click
      Delete project
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Deleting the Cloud Functions

Deleting the Cloud Functions removes all Container Engine resources, but you need to manually remove any resources in Cloud Storage and Cloud Pub/Sub.

Use the following command to delete a Cloud Function:

gcloud beta functions delete [NAME_OF_FUNCTION]

You can also delete Cloud Functions from the Google Cloud Platform Console.

What's next

Monitor your resources on the go

Get the Google Cloud Console app to help you manage your projects.

Send feedback about...

Cloud Container Builder Documentation