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

Commit c5ebd2e

Browse files
authored
Merge pull request #52 from dhollinger/slack_as_plugin
ChatOps Plugin Support
2 parents b58c717 + 178770e commit c5ebd2e

File tree

8 files changed

+270
-67
lines changed

8 files changed

+270
-67
lines changed

README.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,43 @@ Any configuration option is placed in `/etc/puppet_webhook/server.yml` or `/etc/
8080
* `--ssl-key FILE`: Specify the SSL Private key to use. Pair with `--ssl-cert`. Requires `--ssl` or `ssl_enable: true` in config file.
8181
* `-c FILE, --configfile FILE`: Specifies a config file to use. Must be a `.yml` file in YAML format.
8282

83+
#### Chatops Configuration
84+
85+
Puppet_webhook can post to chatops tools via various APIs and Clients. At this time, only `Slack` is supported.
86+
87+
To enable ChatOps support simply add the following to your `/etc/puppet_webhook/app.yml` file:
88+
``` yaml
89+
chatops: true
90+
```
91+
92+
##### Slack Configuration
93+
94+
You can enable Slack notifications for the webhook. You will need a Slack webhook URL and the slack-notifier gem installed.
95+
96+
To get the Slack webhook URL you need to:
97+
98+
Go to https://slack.com/apps/A0F7XDUAZ-incoming-webhooks.
99+
Choose your team, press Configure.
100+
In configurations press Add configuration.
101+
Choose channel, press Add Incoming WebHooks integration.
102+
Then configure the webhook to add your Slack Webhook URL:
103+
104+
``` yaml
105+
chatops: true
106+
chatops_service: 'slack' # Required so the app knows that you're sending to Slack.
107+
chatops_channel: '#channel' # deftaults to #general
108+
chatops_user: 'r10k' # defaults to puppet_webhook
109+
chatops_options:
110+
icon_emoji: ':ocean:'
111+
http_options:
112+
proxy_address: 'http://proxy.example.com'
113+
proxy_port: '3128'
114+
proxy_from_env: false
115+
```
116+
117+
**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**
118+
119+
### Reference
83120

84121
#### Server Configuration File
85122

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

230+
##### chatops
231+
232+
Enable the use of notifications to Slack or other ChatOps tool.
233+
* Valid options: [ `true`, `false` ]
234+
* Default: `false`
235+
236+
##### chatops_service
237+
238+
Name of ChatOps tool to send notifications to.
239+
* Valid options: [ `slack` ]
240+
* Default: `slack`
241+
242+
##### chatops_url
243+
*Replaces `slack_webhook`*
244+
245+
URL of the API or Webhook to send notifications to. See Documentation of your tool for details.
246+
* Default: `''`
247+
248+
##### chatops_user
249+
*Replaces `slack_user`*
250+
251+
User to post notification as.
252+
* Default: `puppet_webhook`
253+
254+
##### chatops_channel
255+
*Replaces `slack_channel`*
256+
257+
Channel/Team/Area to post to.
258+
* Default: `#general`
259+
260+
##### chatops_options
261+
262+
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.
263+
* Default: `{}`
264+
193265
##### slack_webhook
266+
***DEPRECATED* - Please use `chatops_url` instead**
194267

195268
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.
196269
* Valid options: [ `https://hooks.slack.com/services/<generated_hash>`, `false` ]
197270
* Default: `false`
198271

199272
##### slack_channel
273+
***DEPRECATED* - Please use `chatops_channel` instead**
200274

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

204278
##### slack_user
279+
***DEPRECATED* - Please use `chatops_user` instead**
205280

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

209284
##### slack_emoji
285+
***DEPRECATED* - Please use `chatops_options` instead.**
286+
**Example for new config ONLY:**
287+
``` yaml
288+
chatops_options:
289+
icon_emoji: ':ocean:'
290+
```
210291

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

214295
##### slack_proxy_url
296+
***DEPRECATED* - Please use `chatops_options` instead.**
297+
**Example for new config ONLY:**
298+
``` yaml
299+
chatops_options:
300+
http_options:
301+
proxy_address: 'http://proxy.example.com'
302+
proxy_port: '3128'
303+
proxy_from_env: false
304+
```
215305

216306
The proxy URL for Slack if used.
217307
* MUST BE A VALID URL.

config/app.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ use_mco_ruby: false
1010
use_mcollective: false
1111
discovery_timeout: '10'
1212

13-
# Slack Notifications
14-
slack_webhook: false
13+
# Chatops Notification
14+
chatops: false
15+
# Slack Example
16+
# chatops_service: 'slack'
17+
# chatops_channel: '#general'
18+
# chatops_user: 'r10k'
19+
# chatops_url: 'https://hooks.slack.com/services/<hash>/<hash>/<hash>'
1520

1621
# R10k
1722
default_branch: production

lib/helpers/deployments.rb

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'plugins/mcollective'
2+
require 'plugins/chatops'
23

34
module Deployments # rubocop:disable Style/Documentation
45
def deploy(branch, deleted)
@@ -23,18 +24,18 @@ def deploy(branch, deleted)
2324
command = "#{settings.command_prefix} r10k deploy environment #{branch} #{settings.r10k_deploy_arguments}"
2425
message = run_command(command)
2526
end
26-
status_message = { status: :success, message: message.to_s, branch: branch, status_code: 200 }
27+
status_message = { status: :success, message: message.to_s, branch: branch, status_code: 202 }
2728
LOGGER.info("message: #{message} branch: #{branch}")
2829
unless deleted
2930
generate_types(branch) if types?
3031
end
31-
notify_slack(status_message) if slack?
32+
notification(status_message)
3233
[status_message[:status_code], status_message.to_json]
3334
rescue StandardError => e
3435
status_message = { status: :fail, message: e.message, trace: e.backtrace, branch: branch, status_code: 500 }
3536
LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
3637
status 500
37-
notify_slack(status_message) if slack?
38+
notification(status_message)
3839
status_message.to_json
3940
end
4041

@@ -61,14 +62,14 @@ def deploy_module(module_name)
6162
message = run_command(command)
6263
end
6364
LOGGER.info("message: #{message} module_name: #{module_name}")
64-
status_message = { status: :success, message: message.to_s, module_name: module_name, status_code: 200 }
65-
notify_slack(status_message) if slack?
65+
status_message = { status: :success, message: message.to_s, module_name: module_name, status_code: 202 }
66+
notification(status_message)
6667
status_message.to_json
6768
rescue StandardError => e
69+
status_message = { status: :fail, message: e.message, trace: e.backtrace, branch: branch, status_code: 500 }
6870
LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
6971
status 500
70-
status_message = { status: :fail, message: e.message, trace: e.backtrace, module_name: module_name, status_code: 500 }
71-
notify_slack(status_message) if slack?
72+
notification(status_message)
7273
status_message.to_json
7374
end
7475
end

lib/helpers/tasks.rb

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def run_prefix_command(payload)
4848

4949
def run_command(command)
5050
message = "forked: #{command}"
51-
exec "#{command} &"
51+
system "#{command} &"
5252
message
5353
end
5454

@@ -58,66 +58,53 @@ def generate_types(environment)
5858
message = run_command(command)
5959
LOGGER.info("message: #{message} environment: #{environment}")
6060
status_message = { status: :success, message: message.to_s, environment: environment, status_code: 200 }
61-
notify_slack(status_message) if slack?
61+
notification(status_message)
6262
rescue StandardError => e
6363
LOGGER.error("message: #{e.message} trace: #{e.backtrace}")
6464
status_message = { status: :fail, message: e.message, trace: e.backtrace, environment: environment, status_code: 500 }
65-
notify_slack(status_message) if slack?
65+
notification(status_message)
6666
end
6767

68-
def notify_slack(status_message)
69-
return unless settings.slack_webhook
70-
71-
if settings.slack_proxy_url
72-
uri = URI(settings.slack_proxy_url)
73-
http_options = {
74-
proxy_address: uri.hostname,
75-
proxy_port: uri.port,
76-
proxy_from_env: false
77-
}
78-
else
79-
http_options = {}
80-
end
81-
82-
notifier = Slack::Notifier.new settings.slack_webhook do
83-
defaults channel: settings.slack_channel,
84-
username: settings.slack_user,
85-
icon_emoji: settings.slack_emoji,
86-
http_options: http_options
87-
end
88-
89-
if status_message[:branch]
90-
target = status_message[:branch]
91-
elsif status_message[:module]
92-
target = status_message[:module]
93-
end
94-
95-
message = {
96-
author: 'r10k for Puppet',
97-
title: "r10k deployment of Puppet environment #{target}"
98-
}
99-
100-
case status_message[:status_code]
101-
when 200
102-
message.merge!(
103-
color: 'good',
104-
text: "Successfully deployed #{target}",
105-
fallback: "Successfully deployed #{target}"
106-
)
107-
when 500
108-
message.merge!(
109-
color: 'bad',
110-
text: "Failed to deploy #{target}",
111-
fallback: "Failed to deploy #{target}"
112-
)
113-
end
68+
def notification(message)
69+
return unless settings.chatops || settings.slack_webhook
70+
slack_settings if settings.chatops == false && settings.slack_webhook != false
71+
PuppetWebhook::Chatops.new(settings.chatops_service,
72+
settings.chatops_url,
73+
settings.chatops_channel,
74+
settings.chatops_user,
75+
settings.chatops_options).notify(message)
76+
end
11477

115-
notifier.post text: message[:fallback], attachments: [message]
78+
# Deprecated
79+
# TODO: Remove in release 3.0.0
80+
def slack_settings
81+
settings.chatops_service = 'slack'
82+
LOGGER.warn('settings.slack_webhook is deprecated and will be removed in puppet_webhook 3.0.0')
83+
settings.chatops_url = settings.slack_webhook
84+
LOGGER.warn('settings.slack_user is deprecated and will be removed in puppet_webhook 3.0.0')
85+
settings.chatops_user = settings.slack_user
86+
LOGGER.warn('settings.slack_channel is deprecated and will be removed in puppet_webhook 3.0.0')
87+
settings.chatops_channel = settings.slack_channel
88+
LOGGER.warn('settings.slack_emoji is deprecated and will be removed in puppet_webhook 3.0.0')
89+
settings.chatops_options[:icon_emoji] = settings.slack_emoji
90+
LOGGER.warn('settings.slack_proxy_url is deprecated and will be removed in puppet_webhook 3.0.0')
91+
settings.chatops_options[:http_options] = if settings.slack_proxy_url
92+
slack_proxy
93+
else
94+
{}
95+
end
11696
end
11797

118-
def slack?
119-
return false if settings.slack_webhook.nil?
120-
settings.slack_webhook
98+
# Deprecated
99+
# TODO: Remove in release 3.0.0
100+
def slack_proxy
101+
uri = URI(settings.slack_proxy_url)
102+
http_options = {
103+
proxy_address: uri.hostname,
104+
proxy_port: uri.port,
105+
proxy_from_env: false
106+
}
107+
http_options
121108
end
122109

123110
def types?

lib/plugins/chatops.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require 'plugins/chatops/slack'
2+
3+
class PuppetWebhook
4+
# Chatops object for sending webhook notifications to chatops tools
5+
class Chatops
6+
def initialize(service, url, channel, user, options = {})
7+
@service = service
8+
@url = url
9+
@channel = channel
10+
@user = user
11+
@args = options
12+
end
13+
14+
def notify(message)
15+
case @service
16+
when 'slack'
17+
LOGGER.info("Sending Slack webhook message to #{@url}")
18+
Chatops::Slack.new(@channel,
19+
@url,
20+
@user,
21+
message,
22+
http_options: @args[:http_options] || {},
23+
icon_emoji: @args[:icon_emoji]).notify
24+
else
25+
LOGGER.error("Service #{@service} is not currently supported")
26+
end
27+
end
28+
end
29+
end

lib/plugins/chatops/slack.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require 'slack-notifier'
2+
3+
class PuppetWebhook
4+
class Chatops
5+
# Sets up Slack object that will send notifications to Slack via a webhook.
6+
class Slack
7+
def initialize(channel, url, user, message, options = {})
8+
@channel = channel
9+
@url = url
10+
@user = user
11+
@message = message
12+
@options = options
13+
end
14+
15+
def notify
16+
notifier = ::Slack::Notifier.new @url, http_options: @options[:http_options]
17+
18+
target = if @message[:branch]
19+
@message[:branch]
20+
elsif @message[:module]
21+
@message[:module]
22+
end
23+
24+
msg = format_message(target)
25+
26+
notifier.post text: msg[:fallback],
27+
channel: @channel,
28+
username: @user,
29+
icon_emoji: @options[:icon_emoji],
30+
attachments: [msg]
31+
end
32+
33+
private
34+
35+
def format_message(target)
36+
message = {
37+
author: 'r10k for Puppet',
38+
title: "r10k deployment of Puppet environment #{target}"
39+
}
40+
41+
case @message[:status_code]
42+
when 202
43+
message.merge!(
44+
color: 'good',
45+
text: "Successfully started deployment of #{target}",
46+
fallback: "Successfully started deployment of #{target}"
47+
)
48+
when 500
49+
message.merge!(
50+
color: 'bad',
51+
text: "Failed to deploy #{target}",
52+
fallback: "Failed to deploy #{target}"
53+
)
54+
end
55+
56+
message
57+
end
58+
end
59+
end
60+
end

0 commit comments

Comments
 (0)