Skip to content
This repository has been archived by the owner on Jan 29, 2022. It is now read-only.

Commit

Permalink
Merge pull request #52 from dhollinger/slack_as_plugin
Browse files Browse the repository at this point in the history
ChatOps Plugin Support
  • Loading branch information
dhollinger authored May 1, 2018
2 parents b58c717 + 178770e commit c5ebd2e
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 67 deletions.
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,43 @@ Any configuration option is placed in `/etc/puppet_webhook/server.yml` or `/etc/
* `--ssl-key FILE`: Specify the SSL Private key to use. Pair with `--ssl-cert`. Requires `--ssl` or `ssl_enable: true` in config file.
* `-c FILE, --configfile FILE`: Specifies a config file to use. Must be a `.yml` file in YAML format.

#### Chatops Configuration

Puppet_webhook can post to chatops tools via various APIs and Clients. At this time, only `Slack` is supported.

To enable ChatOps support simply add the following to your `/etc/puppet_webhook/app.yml` file:
``` yaml
chatops: true
```
##### Slack Configuration
You can enable Slack notifications for the webhook. You will need a Slack webhook URL and the slack-notifier gem installed.
To get the Slack webhook URL you need to:
Go to https://slack.com/apps/A0F7XDUAZ-incoming-webhooks.
Choose your team, press Configure.
In configurations press Add configuration.
Choose channel, press Add Incoming WebHooks integration.
Then configure the webhook to add your Slack Webhook URL:
``` yaml
chatops: true
chatops_service: 'slack' # Required so the app knows that you're sending to Slack.
chatops_channel: '#channel' # deftaults to #general
chatops_user: 'r10k' # defaults to puppet_webhook
chatops_options:
icon_emoji: ':ocean:'
http_options:
proxy_address: 'http://proxy.example.com'
proxy_port: '3128'
proxy_from_env: false
```
**NOTE: The legacy `slack_webhook`, `slack_user`, `slack_channel`, `slack_emoji`, and `slack_proxy_url` still work, but will be removed in 3.0.0**

### Reference

#### Server Configuration File

Expand Down Expand Up @@ -190,28 +227,81 @@ Whether or not to use MCollective CLI command. REQUIRES MCOLLECTIVE AND MCOLLECT
MCollective Ruby discovery timeout. REQUIRES `use_mco_ruby` TO BE `true`.
* Default: `'10'`

##### chatops

Enable the use of notifications to Slack or other ChatOps tool.
* Valid options: [ `true`, `false` ]
* Default: `false`

##### chatops_service

Name of ChatOps tool to send notifications to.
* Valid options: [ `slack` ]
* Default: `slack`

##### chatops_url
*Replaces `slack_webhook`*

URL of the API or Webhook to send notifications to. See Documentation of your tool for details.
* Default: `''`

##### chatops_user
*Replaces `slack_user`*

User to post notification as.
* Default: `puppet_webhook`

##### chatops_channel
*Replaces `slack_channel`*

Channel/Team/Area to post to.
* Default: `#general`

##### chatops_options

Hash of options to pass to the Chatops plugin. Each set of options are unique to each tool, so please see your tool's documentation for more information.
* Default: `{}`

##### slack_webhook
***DEPRECATED* - Please use `chatops_url` instead**

URL of your Slack Webhook receiver, if you wish not to use a Slack Webhook, then simply leave the option on `false`, otherwise use the full Wwebhook URL for your community as per https://api.slack.com/incoming-webhooks.
* Valid options: [ `https://hooks.slack.com/services/<generated_hash>`, `false` ]
* Default: `false`

##### slack_channel
***DEPRECATED* - Please use `chatops_channel` instead**

Name of the Slack channel to post to. Ignored if `slack_webhook` is disabled.
Default: `#general`

##### slack_user
***DEPRECATED* - Please use `chatops_user` instead**

Name of the Slack user to post as. Ignored if `slack_webhook` is disabled.
Default: `puppet_webhook`

##### slack_emoji
***DEPRECATED* - Please use `chatops_options` instead.**
**Example for new config ONLY:**
``` yaml
chatops_options:
icon_emoji: ':ocean:'
```

Icon emoji for the Webhook to use when posting. Ignored if `slack_webhook` is disabled.
Default: `:ocean:`

##### slack_proxy_url
***DEPRECATED* - Please use `chatops_options` instead.**
**Example for new config ONLY:**
``` yaml
chatops_options:
http_options:
proxy_address: 'http://proxy.example.com'
proxy_port: '3128'
proxy_from_env: false
```

The proxy URL for Slack if used.
* MUST BE A VALID URL.
Expand Down
9 changes: 7 additions & 2 deletions config/app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ use_mco_ruby: false
use_mcollective: false
discovery_timeout: '10'

# Slack Notifications
slack_webhook: false
# Chatops Notification
chatops: false
# Slack Example
# chatops_service: 'slack'
# chatops_channel: '#general'
# chatops_user: 'r10k'
# chatops_url: 'https://hooks.slack.com/services/<hash>/<hash>/<hash>'

# R10k
default_branch: production
Expand Down
15 changes: 8 additions & 7 deletions lib/helpers/deployments.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'plugins/mcollective'
require 'plugins/chatops'

module Deployments # rubocop:disable Style/Documentation
def deploy(branch, deleted)
Expand All @@ -23,18 +24,18 @@ def deploy(branch, deleted)
command = "#{settings.command_prefix} r10k deploy environment #{branch} #{settings.r10k_deploy_arguments}"
message = run_command(command)
end
status_message = { status: :success, message: message.to_s, branch: branch, status_code: 200 }
status_message = { status: :success, message: message.to_s, branch: branch, status_code: 202 }
LOGGER.info("message: #{message} branch: #{branch}")
unless deleted
generate_types(branch) if types?
end
notify_slack(status_message) if slack?
notification(status_message)
[status_message[:status_code], status_message.to_json]
rescue StandardError => e
status_message = { status: :fail, message: e.message, trace: e.backtrace, branch: branch, status_code: 500 }
LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
status 500
notify_slack(status_message) if slack?
notification(status_message)
status_message.to_json
end

Expand All @@ -61,14 +62,14 @@ def deploy_module(module_name)
message = run_command(command)
end
LOGGER.info("message: #{message} module_name: #{module_name}")
status_message = { status: :success, message: message.to_s, module_name: module_name, status_code: 200 }
notify_slack(status_message) if slack?
status_message = { status: :success, message: message.to_s, module_name: module_name, status_code: 202 }
notification(status_message)
status_message.to_json
rescue StandardError => e
status_message = { status: :fail, message: e.message, trace: e.backtrace, branch: branch, status_code: 500 }
LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
status 500
status_message = { status: :fail, message: e.message, trace: e.backtrace, module_name: module_name, status_code: 500 }
notify_slack(status_message) if slack?
notification(status_message)
status_message.to_json
end
end
93 changes: 40 additions & 53 deletions lib/helpers/tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def run_prefix_command(payload)

def run_command(command)
message = "forked: #{command}"
exec "#{command} &"
system "#{command} &"
message
end

Expand All @@ -58,66 +58,53 @@ def generate_types(environment)
message = run_command(command)
LOGGER.info("message: #{message} environment: #{environment}")
status_message = { status: :success, message: message.to_s, environment: environment, status_code: 200 }
notify_slack(status_message) if slack?
notification(status_message)
rescue StandardError => e
LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
status_message = { status: :fail, message: e.message, trace: e.backtrace, environment: environment, status_code: 500 }
notify_slack(status_message) if slack?
notification(status_message)
end

def notify_slack(status_message)
return unless settings.slack_webhook

if settings.slack_proxy_url
uri = URI(settings.slack_proxy_url)
http_options = {
proxy_address: uri.hostname,
proxy_port: uri.port,
proxy_from_env: false
}
else
http_options = {}
end

notifier = Slack::Notifier.new settings.slack_webhook do
defaults channel: settings.slack_channel,
username: settings.slack_user,
icon_emoji: settings.slack_emoji,
http_options: http_options
end

if status_message[:branch]
target = status_message[:branch]
elsif status_message[:module]
target = status_message[:module]
end

message = {
author: 'r10k for Puppet',
title: "r10k deployment of Puppet environment #{target}"
}

case status_message[:status_code]
when 200
message.merge!(
color: 'good',
text: "Successfully deployed #{target}",
fallback: "Successfully deployed #{target}"
)
when 500
message.merge!(
color: 'bad',
text: "Failed to deploy #{target}",
fallback: "Failed to deploy #{target}"
)
end
def notification(message)
return unless settings.chatops || settings.slack_webhook
slack_settings if settings.chatops == false && settings.slack_webhook != false
PuppetWebhook::Chatops.new(settings.chatops_service,
settings.chatops_url,
settings.chatops_channel,
settings.chatops_user,
settings.chatops_options).notify(message)
end

notifier.post text: message[:fallback], attachments: [message]
# Deprecated
# TODO: Remove in release 3.0.0
def slack_settings
settings.chatops_service = 'slack'
LOGGER.warn('settings.slack_webhook is deprecated and will be removed in puppet_webhook 3.0.0')
settings.chatops_url = settings.slack_webhook
LOGGER.warn('settings.slack_user is deprecated and will be removed in puppet_webhook 3.0.0')
settings.chatops_user = settings.slack_user
LOGGER.warn('settings.slack_channel is deprecated and will be removed in puppet_webhook 3.0.0')
settings.chatops_channel = settings.slack_channel
LOGGER.warn('settings.slack_emoji is deprecated and will be removed in puppet_webhook 3.0.0')
settings.chatops_options[:icon_emoji] = settings.slack_emoji
LOGGER.warn('settings.slack_proxy_url is deprecated and will be removed in puppet_webhook 3.0.0')
settings.chatops_options[:http_options] = if settings.slack_proxy_url
slack_proxy
else
{}
end
end

def slack?
return false if settings.slack_webhook.nil?
settings.slack_webhook
# Deprecated
# TODO: Remove in release 3.0.0
def slack_proxy
uri = URI(settings.slack_proxy_url)
http_options = {
proxy_address: uri.hostname,
proxy_port: uri.port,
proxy_from_env: false
}
http_options
end

def types?
Expand Down
29 changes: 29 additions & 0 deletions lib/plugins/chatops.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'plugins/chatops/slack'

class PuppetWebhook
# Chatops object for sending webhook notifications to chatops tools
class Chatops
def initialize(service, url, channel, user, options = {})
@service = service
@url = url
@channel = channel
@user = user
@args = options
end

def notify(message)
case @service
when 'slack'
LOGGER.info("Sending Slack webhook message to #{@url}")
Chatops::Slack.new(@channel,
@url,
@user,
message,
http_options: @args[:http_options] || {},
icon_emoji: @args[:icon_emoji]).notify
else
LOGGER.error("Service #{@service} is not currently supported")
end
end
end
end
60 changes: 60 additions & 0 deletions lib/plugins/chatops/slack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require 'slack-notifier'

class PuppetWebhook
class Chatops
# Sets up Slack object that will send notifications to Slack via a webhook.
class Slack
def initialize(channel, url, user, message, options = {})
@channel = channel
@url = url
@user = user
@message = message
@options = options
end

def notify
notifier = ::Slack::Notifier.new @url, http_options: @options[:http_options]

target = if @message[:branch]
@message[:branch]
elsif @message[:module]
@message[:module]
end

msg = format_message(target)

notifier.post text: msg[:fallback],
channel: @channel,
username: @user,
icon_emoji: @options[:icon_emoji],
attachments: [msg]
end

private

def format_message(target)
message = {
author: 'r10k for Puppet',
title: "r10k deployment of Puppet environment #{target}"
}

case @message[:status_code]
when 202
message.merge!(
color: 'good',
text: "Successfully started deployment of #{target}",
fallback: "Successfully started deployment of #{target}"
)
when 500
message.merge!(
color: 'bad',
text: "Failed to deploy #{target}",
fallback: "Failed to deploy #{target}"
)
end

message
end
end
end
end
Loading

0 comments on commit c5ebd2e

Please sign in to comment.