Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Slack example storing secrets in Lockr #144

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions slack_notification_lockr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Slack Integration with Lockr #

This script shows how easy it is to integrate Slack notifications from your Pantheon project using Quicksilver. You will need a [Slack Webhook URL](https://api.slack.com/incoming-webhooks), which should not be exposed publicly.

We **do not** recommend tracking sensitive information, such as API keys, in version control. Instead consider using a secure, managed service for storing sensitive credentials. This example uses [Lockr](https://www.lockr.io/), which has a free tier, but feel free to use your own solution.

## Instructions ##

1. [Enable Incoming Webhooks](https://my.slack.com/services/new/incoming-webhook/) for your Slack instance.
1. Install and activate the [Lockr WordPress plugin](https://wordpress.org/plugins/lockr/).
1. Save your webhook URL as `slack_url` in Lockr.
1. Save the Slack channel to post messages to as `slack_channel` in Lockr.
1. Add, and update as needed, the example `slack_notification.php` script to the `private` directory in the root of your site's codebase, that is under version control.
1. Add Quicksilver operations to your `pantheon.yml`
1. Test a deploy out!
- Note that using the `cache_clear` Quicksilver hook makes testing easy. Once things are working well change to the hooks below.

Optionally, you may want to use the `terminus workflow:watch` command to get immediate debugging feedback. You may also want to customize your notifications further. The [Slack API](https://api.slack.com/incoming-webhooks) documentation has more information on formatting options.

### Example `pantheon.yml` ###

Here's an example of what your `pantheon.yml` would look like if this were the only Quicksilver operation you wanted to use. Pick and choose the exact workflows that you would like to see notifications for.

```yaml
api_version: 1

workflows:
deploy_product:
after:
- type: webphp
description: Post to Slack after site creation
script: private/scripts/slack_notification_lockr.php
create_cloud_development_environment:
after:
- type: webphp
description: Post to Slack after Multidev creation
script: private/scripts/slack_notification_lockr.php
deploy:
after:
- type: webphp
description: Post to Slack after deploy
script: private/scripts/slack_notification_lockr.php
sync_code:
after:
- type: webphp
description: Post to Slack after code commit
script: private/scripts/slack_notification_lockr.php
clear_cache:
after:
- type: webphp
description: Someone is clearing the cache again
script: private/scripts/slack_notification_lockr.php
```

196 changes: 196 additions & 0 deletions slack_notification_lockr/slack_notification_lockr.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<?php

// Important constants :)
$pantheon_yellow = '#EFD01B';

// Default values for parameters - this will assume the channel you define the webhook for.
// The full Slack Message API allows you to specify other channels and enhance the messagge further
// if you like: https://api.slack.com/docs/messages/builder
$defaults = array(
'slack_username' => 'Pantheon-Quicksilver',
'always_show_text' => false,
);

// Load our hidden credentials.
// See the README.md for instructions on storing secrets.
$secret_keys = array( 'slack_url', 'slack_channel' );
$secrets = _get_lockr_key($secret_keys, $defaults);

// Build an array of fields to be rendered with Slack Attachments as a table
// attachment-style formatting:
// https://api.slack.com/docs/attachments
$fields = array(
array(
'title' => 'Site',
'value' => $_ENV['PANTHEON_SITE_NAME'],
'short' => 'true'
),
array( // Render Environment name with link to site, <http://{ENV}-{SITENAME}.pantheon.io|{ENV}>
'title' => 'Environment',
'value' => '<http://' . $_ENV['PANTHEON_ENVIRONMENT'] . '-' . $_ENV['PANTHEON_SITE_NAME'] . '.pantheonsite.io|' . $_ENV['PANTHEON_ENVIRONMENT'] . '>',
'short' => 'true'
),
array( // Render Name with link to Email from Commit message
'title' => 'By',
'value' => $_POST['user_email'],
'short' => 'true'
),
array( // Render workflow phase that the message was sent
'title' => 'Workflow',
'value' => ucfirst($_POST['stage']) . ' ' . str_replace('_', ' ', $_POST['wf_type']),
'short' => 'true'
),
array(
'title' => 'View Dashboard',
'value' => '<https://dashboard.pantheon.io/sites/'. PANTHEON_SITE .'#'. PANTHEON_ENVIRONMENT .'/deploys|View Dashboard>',
'short' => 'true'
),
);

// Customize the message based on the workflow type. Note that slack_notification.php
// must appear in your pantheon.yml for each workflow type you wish to send notifications on.
switch($_POST['wf_type']) {
case 'deploy':
// Find out what tag we are on and get the annotation.
$deploy_tag = `git describe --tags`;
$deploy_message = $_POST['deploy_message'];

// Prepare the slack payload as per:
// https://api.slack.com/incoming-webhooks
$text = 'Deploy to the '. $_ENV['PANTHEON_ENVIRONMENT'];
$text .= ' environment of '. $_ENV['PANTHEON_SITE_NAME'] .' by '. $_POST['user_email'] .' complete!';
$text .= ' <https://dashboard.pantheon.io/sites/'. PANTHEON_SITE .'#'. PANTHEON_ENVIRONMENT .'/deploys|View Dashboard>';
// Build an array of fields to be rendered with Slack Attachments as a table
// attachment-style formatting:
// https://api.slack.com/docs/attachments
$fields[] = array(
'title' => 'Details',
'value' => $text,
'short' => 'false'
);
$fields[] = array(
'title' => 'Deploy Note',
'value' => $deploy_message,
'short' => 'false'
);
break;

case 'sync_code':
// Get the committer, hash, and message for the most recent commit.
$committer = `git log -1 --pretty=%cn`;
$email = `git log -1 --pretty=%ce`;
$message = `git log -1 --pretty=%B`;
$hash = `git log -1 --pretty=%h`;

// Prepare the slack payload as per:
// https://api.slack.com/incoming-webhooks
$text = 'Code sync to the ' . $_ENV['PANTHEON_ENVIRONMENT'] . ' environment of ' . $_ENV['PANTHEON_SITE_NAME'] . ' by ' . $_POST['user_email'] . "!\n";
$text .= 'Most recent commit: ' . rtrim($hash) . ' by ' . rtrim($committer) . ': ' . $message;
// Build an array of fields to be rendered with Slack Attachments as a table
// attachment-style formatting:
// https://api.slack.com/docs/attachments
$fields += array(
array(
'title' => 'Commit',
'value' => rtrim($hash),
'short' => 'true'
),
array(
'title' => 'Commit Message',
'value' => $message,
'short' => 'false'
)
);
break;

case 'clear_cache':
$fields[] = array(
'title' => 'Cleared caches',
'value' => 'Cleared caches on the ' . $_ENV['PANTHEON_ENVIRONMENT'] . ' environment of ' . $_ENV['PANTHEON_SITE_NAME'] . "!\n",
'short' => 'false'
);
break;

default:
$text = $_POST['qs_description'];
break;
}

$attachment = array(
'fallback' => $text,
'pretext' => ($_POST['wf_type'] == 'clear_cache') ? 'Caches cleared :construction:' : 'Deploying :rocket:',
'color' => $pantheon_yellow, // Can either be one of 'good', 'warning', 'danger', or any hex color code
'fields' => $fields
);

_slack_notification($secrets['slack_url'], $secrets['slack_channel'], $secrets['slack_username'], $text, $attachment, $secrets['always_show_text']);


/**
* Takes an existing array of keys and attempts to
* add a new key from Lockr
*
* @param string $key the machine name of the key to retrieve from Lockr
* @param array $incoming_keys existing array of items
* @return array array with Locker key added if it is found
*/
function _get_lockr_key($secret_keys,$defaults=array()){
$status = false;
$wp_plugin_list = $lockr_keys = $current_keys = array();

exec("wp plugin list --name=lockr --format=count --status=active --no-color", $wp_plugin_list);
if( empty($wp_plugin_list) || $wp_plugin_list[0] == "0" ){
die( 'The Lockr plugin is not active. Please install and activate the plugin.' );
return $defaults;
}

foreach( $secret_keys as $key ){

echo "Getting $key key from Lockr" . PHP_EOL;
exec("wp lockr get key $key --no-color", $current_keys, $status);

if( 0 == $status && !empty($current_keys) ){
echo "Key $key found in Lockr" . PHP_EOL;
// Use preg_replace to only remove Success: from the start
$lockr_keys[$key] = trim( preg_replace('/^Success: /', '', $current_keys[0]) );
} else {
die( "Required key $key NOT found in Lockr. Make sure it exists" );
}

}

return array_merge( $defaults, $lockr_keys );
}

/**
* Send a notification to slack
*/
function _slack_notification($slack_url, $channel, $username, $text, $attachment, $alwaysShowText = false)
{
$attachment['fallback'] = $text;
$post = array(
'username' => $username,
'channel' => $channel,
'icon_emoji' => ':lightning_cloud:',
'attachments' => array($attachment)
);
if ($alwaysShowText) {
$post['text'] = $text;
}
$payload = json_encode($post);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $slack_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
// Watch for messages with `terminus workflows watch --site=SITENAME`
print("\n==== Posting to Slack ====\n");
$result = curl_exec($ch);
print("RESULT: $result");
// $payload_pretty = json_encode($post,JSON_PRETTY_PRINT); // Uncomment to debug JSON
// print("JSON: $payload_pretty"); // Uncomment to Debug JSON
print("\n===== Post Complete! =====\n");
curl_close($ch);
}