fluent-plugin-slack-stakater 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +95 -0
- data/Gemfile +3 -0
- data/Gemfile.fluentd.0.12 +4 -0
- data/README.md +126 -0
- data/Rakefile +16 -0
- data/VERSION +1 -0
- data/example.conf +18 -0
- data/fluent-plugin-slack.gemspec +28 -0
- data/lib/fluent/plugin/out_buffered_slack.rb +1 -0
- data/lib/fluent/plugin/out_slack.rb +382 -0
- data/lib/fluent/plugin/slack_client.rb +289 -0
- data/lib/fluent/plugin/slack_client/error.rb +24 -0
- data/test.sh +2 -0
- data/test/plugin/test_out_slack.rb +566 -0
- data/test/plugin/test_slack_client.rb +282 -0
- data/test/test_helper.rb +32 -0
- metadata +176 -0
@@ -0,0 +1,289 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
4
|
+
require 'logger'
|
5
|
+
require_relative 'slack_client/error'
|
6
|
+
|
7
|
+
module Fluent
|
8
|
+
module SlackClient
|
9
|
+
# The base framework of slack client
|
10
|
+
class Base
|
11
|
+
attr_accessor :log, :debug_dev
|
12
|
+
attr_reader :endpoint, :https_proxy
|
13
|
+
|
14
|
+
# @param [String] endpoint
|
15
|
+
#
|
16
|
+
# (Incoming Webhook) required
|
17
|
+
# https://hooks.slack.com/services/XXX/XXX/XXX
|
18
|
+
#
|
19
|
+
# (Slackbot) required
|
20
|
+
# https://xxxx.slack.com/services/hooks/slackbot?token=XXXXX
|
21
|
+
#
|
22
|
+
# (Web API) optional and default to be
|
23
|
+
# https://slack.com/api/
|
24
|
+
#
|
25
|
+
# @param [String] https_proxy (optional)
|
26
|
+
#
|
27
|
+
# https://proxy.foo.bar:port
|
28
|
+
#
|
29
|
+
def initialize(endpoint = nil, https_proxy = nil)
|
30
|
+
self.endpoint = endpoint if endpoint
|
31
|
+
self.https_proxy = https_proxy if https_proxy
|
32
|
+
@log = Logger.new('/dev/null')
|
33
|
+
end
|
34
|
+
|
35
|
+
def endpoint=(endpoint)
|
36
|
+
@endpoint = URI.parse(endpoint)
|
37
|
+
end
|
38
|
+
|
39
|
+
def https_proxy=(https_proxy)
|
40
|
+
@https_proxy = URI.parse(https_proxy)
|
41
|
+
@proxy_class = Net::HTTP.Proxy(@https_proxy.host, @https_proxy.port)
|
42
|
+
end
|
43
|
+
|
44
|
+
def proxy_class
|
45
|
+
@proxy_class ||= Net::HTTP
|
46
|
+
end
|
47
|
+
|
48
|
+
def post(endpoint, params)
|
49
|
+
http = proxy_class.new(endpoint.host, endpoint.port)
|
50
|
+
http.use_ssl = (endpoint.scheme == 'https')
|
51
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
52
|
+
http.set_debug_output(debug_dev) if debug_dev
|
53
|
+
|
54
|
+
req = Net::HTTP::Post.new(endpoint.request_uri)
|
55
|
+
req['Host'] = endpoint.host
|
56
|
+
req['Accept'] = 'application/json; charset=utf-8'
|
57
|
+
req['User-Agent'] = 'fluent-plugin-slack'
|
58
|
+
req.body = encode_body(params)
|
59
|
+
|
60
|
+
res = http.request(req)
|
61
|
+
response_check(res, params)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def encode_body(params)
|
67
|
+
raise NotImplementedError
|
68
|
+
end
|
69
|
+
|
70
|
+
def response_check(res, params)
|
71
|
+
if res.code != "200"
|
72
|
+
raise Error.new(res, params)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def filter_params(params)
|
77
|
+
params.dup.tap {|p| p[:token] = '[FILTERED]' if p[:token] }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Required to implement to use #with_channels_create
|
81
|
+
# channels.create API is available from only Slack Web API
|
82
|
+
def api
|
83
|
+
raise NotImplementedError
|
84
|
+
end
|
85
|
+
|
86
|
+
def with_channels_create(params = {}, opts = {})
|
87
|
+
retries = 1
|
88
|
+
begin
|
89
|
+
yield
|
90
|
+
rescue ChannelNotFoundError => e
|
91
|
+
if params[:token] and opts[:auto_channels_create]
|
92
|
+
log.warn "out_slack: channel \"#{params[:channel]}\" is not found. try to create the channel, and then retry to post the message."
|
93
|
+
api.channels_create({name: params[:channel], token: params[:token]})
|
94
|
+
retry if (retries -= 1) >= 0 # one time retry
|
95
|
+
else
|
96
|
+
raise e
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_json_with_scrub!(params)
|
102
|
+
retries = 1
|
103
|
+
begin
|
104
|
+
params.to_json
|
105
|
+
rescue Encoding::UndefinedConversionError => e
|
106
|
+
recursive_scrub!(params)
|
107
|
+
if (retries -= 1) >= 0 # one time retry
|
108
|
+
log.warn "out_slack: to_json `#{params}` failed. retry after scrub!. #{e.message}"
|
109
|
+
retry
|
110
|
+
else
|
111
|
+
raise e
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def recursive_scrub!(params)
|
117
|
+
case params
|
118
|
+
when Hash
|
119
|
+
params.each {|k, v| recursive_scrub!(v)}
|
120
|
+
when Array
|
121
|
+
params.each {|elm| recursive_scrub!(elm)}
|
122
|
+
when String
|
123
|
+
params.force_encoding(Encoding::UTF_8) if params.encoding == Encoding::ASCII_8BIT
|
124
|
+
params.scrub!('?') if params.respond_to?(:scrub!)
|
125
|
+
else
|
126
|
+
params
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Slack client for Incoming Webhook
|
132
|
+
# https://api.slack.com/incoming-webhooks
|
133
|
+
class IncomingWebhook < Base
|
134
|
+
def initialize(endpoint, https_proxy = nil)
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def post_message(params = {}, opts = {})
|
139
|
+
log.info { "out_slack: post_message #{params}" }
|
140
|
+
post(endpoint, params)
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def encode_body(params = {})
|
146
|
+
# https://api.slack.com/docs/formatting
|
147
|
+
to_json_with_scrub!(params).gsub(/&/, '&').gsub(/</, '<').gsub(/>/, '>')
|
148
|
+
end
|
149
|
+
|
150
|
+
def response_check(res, params)
|
151
|
+
super
|
152
|
+
unless res.body == 'ok'
|
153
|
+
raise Error.new(res, params)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Slack client for Slackbot Remote Control
|
159
|
+
# https://api.slack.com/slackbot
|
160
|
+
class Slackbot < Base
|
161
|
+
def initialize(endpoint, https_proxy = nil)
|
162
|
+
super
|
163
|
+
end
|
164
|
+
|
165
|
+
def api
|
166
|
+
@api ||= WebApi.new(nil, https_proxy)
|
167
|
+
end
|
168
|
+
|
169
|
+
def post_message(params = {}, opts = {})
|
170
|
+
raise ArgumentError, "channel parameter is required" unless params[:channel]
|
171
|
+
with_channels_create(params, opts) do
|
172
|
+
log.info { "out_slack: post_message #{filter_params(params)}" }
|
173
|
+
post(slackbot_endpoint(params), params)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def slackbot_endpoint(params)
|
180
|
+
endpoint.dup.tap {|e| e.query += "&channel=#{URI.encode(params[:channel])}" }
|
181
|
+
end
|
182
|
+
|
183
|
+
def encode_body(params = {})
|
184
|
+
return params[:text]if params[:text]
|
185
|
+
unless params[:attachments]
|
186
|
+
raise ArgumentError, 'params[:text] or params[:attachments] is required'
|
187
|
+
end
|
188
|
+
# handle params[:attachments]
|
189
|
+
attachment = Array(params[:attachments]).first # see only the first for now
|
190
|
+
# {
|
191
|
+
# attachments: [{
|
192
|
+
# text: "HERE",
|
193
|
+
# }]
|
194
|
+
# }
|
195
|
+
text = attachment[:text]
|
196
|
+
# {
|
197
|
+
# attachments: [{
|
198
|
+
# fields: [{
|
199
|
+
# title: "title",
|
200
|
+
# value: "HERE",
|
201
|
+
# }]
|
202
|
+
# }]
|
203
|
+
# }
|
204
|
+
if text.nil? and attachment[:fields]
|
205
|
+
text = Array(attachment[:fields]).first[:value] # see only the first for now
|
206
|
+
end
|
207
|
+
text
|
208
|
+
end
|
209
|
+
|
210
|
+
def response_check(res, params)
|
211
|
+
if res.body == 'channel_not_found'
|
212
|
+
raise ChannelNotFoundError.new(res, params)
|
213
|
+
elsif res.body != 'ok'
|
214
|
+
raise Error.new(res, params)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Slack client for Web API
|
220
|
+
class WebApi < Base
|
221
|
+
DEFAULT_ENDPOINT = "https://slack.com/api/".freeze
|
222
|
+
|
223
|
+
def api
|
224
|
+
self
|
225
|
+
end
|
226
|
+
|
227
|
+
def endpoint
|
228
|
+
@endpoint ||= URI.parse(DEFAULT_ENDPOINT)
|
229
|
+
end
|
230
|
+
|
231
|
+
def post_message_endpoint
|
232
|
+
@post_message_endpoint ||= URI.join(endpoint, "chat.postMessage")
|
233
|
+
end
|
234
|
+
|
235
|
+
def channels_create_endpoint
|
236
|
+
@channels_create_endpoint ||= URI.join(endpoint, "channels.create")
|
237
|
+
end
|
238
|
+
|
239
|
+
# Sends a message to a channel.
|
240
|
+
#
|
241
|
+
# @see https://api.slack.com/methods/chat.postMessage
|
242
|
+
# @see https://github.com/slackhq/slack-api-docs/blob/master/methods/chat.postMessage.md
|
243
|
+
# @see https://github.com/slackhq/slack-api-docs/blob/master/methods/chat.postMessage.json
|
244
|
+
def post_message(params = {}, opts = {})
|
245
|
+
with_channels_create(params, opts) do
|
246
|
+
log.info { "out_slack: post_message #{filter_params(params)}" }
|
247
|
+
post(post_message_endpoint, params)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# Creates a channel.
|
252
|
+
#
|
253
|
+
# NOTE: Bot user can not create a channel. Token must be issued by Normal User Account
|
254
|
+
# @see https://api.slack.com/bot-users
|
255
|
+
#
|
256
|
+
# @see https://api.slack.com/methods/channels.create
|
257
|
+
# @see https://github.com/slackhq/slack-api-docs/blob/master/methods/channels.create.md
|
258
|
+
# @see https://github.com/slackhq/slack-api-docs/blob/master/methods/channels.create.json
|
259
|
+
def channels_create(params = {}, opts = {})
|
260
|
+
log.info { "out_slack: channels_create #{filter_params(params)}" }
|
261
|
+
post(channels_create_endpoint, params)
|
262
|
+
end
|
263
|
+
|
264
|
+
private
|
265
|
+
|
266
|
+
def encode_body(params = {})
|
267
|
+
body = params.dup
|
268
|
+
if params[:attachments]
|
269
|
+
body[:attachments] = to_json_with_scrub!(params[:attachments])
|
270
|
+
end
|
271
|
+
URI.encode_www_form(body)
|
272
|
+
end
|
273
|
+
|
274
|
+
def response_check(res, params)
|
275
|
+
super
|
276
|
+
res_params = JSON.parse(res.body)
|
277
|
+
return if res_params['ok']
|
278
|
+
case res_params['error']
|
279
|
+
when 'channel_not_found'
|
280
|
+
raise ChannelNotFoundError.new(res, params)
|
281
|
+
when 'name_taken'
|
282
|
+
raise NameTakenError.new(res, params)
|
283
|
+
else
|
284
|
+
raise Error.new(res, params)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
module SlackClient
|
5
|
+
class Error < StandardError
|
6
|
+
attr_reader :res, :req_params
|
7
|
+
|
8
|
+
def initialize(res, req_params = {})
|
9
|
+
@res = res
|
10
|
+
@req_params = req_params.dup
|
11
|
+
end
|
12
|
+
|
13
|
+
def message
|
14
|
+
@req_params[:token] = '[FILTERED]' if @req_params[:token]
|
15
|
+
"res.code:#{@res.code}, res.body:#{@res.body}, req_params:#{@req_params}"
|
16
|
+
end
|
17
|
+
|
18
|
+
alias :to_s :message
|
19
|
+
end
|
20
|
+
|
21
|
+
class ChannelNotFoundError < Error; end
|
22
|
+
class NameTakenError < Error; end
|
23
|
+
end
|
24
|
+
end
|
data/test.sh
ADDED
@@ -0,0 +1,566 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require 'fluent/plugin/out_slack'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
class SlackOutputTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
super
|
9
|
+
Fluent::Test.setup
|
10
|
+
@icon_url = 'http://www.google.com/s2/favicons?domain=www.google.de'
|
11
|
+
end
|
12
|
+
|
13
|
+
CONFIG = %[
|
14
|
+
channel channel
|
15
|
+
webhook_url https://hooks.slack.com/services/XXXX/XXXX/XXX
|
16
|
+
]
|
17
|
+
|
18
|
+
def default_payload
|
19
|
+
{
|
20
|
+
channel: '#channel',
|
21
|
+
mrkdwn: true,
|
22
|
+
link_names: true,
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_attachment
|
27
|
+
{
|
28
|
+
mrkdwn_in: %w[text fields],
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_driver(conf = CONFIG)
|
33
|
+
Fluent::Test::BufferedOutputTestDriver.new(Fluent::SlackOutput).configure(conf)
|
34
|
+
end
|
35
|
+
|
36
|
+
# old version compatibility with v0.4.0"
|
37
|
+
def test_old_config
|
38
|
+
# default check
|
39
|
+
d = create_driver
|
40
|
+
assert_equal true, d.instance.localtime
|
41
|
+
assert_equal nil, d.instance.username # 'fluentd' break lower version compatibility
|
42
|
+
assert_equal nil, d.instance.color # 'good' break lower version compatibility
|
43
|
+
assert_equal nil, d.instance.icon_emoji # ':question:' break lower version compatibility
|
44
|
+
assert_equal nil, d.instance.icon_url
|
45
|
+
assert_equal true, d.instance.mrkdwn
|
46
|
+
assert_equal true, d.instance.link_names
|
47
|
+
assert_equal nil, d.instance.parse
|
48
|
+
|
49
|
+
assert_nothing_raised do
|
50
|
+
create_driver(CONFIG + %[api_key testtoken])
|
51
|
+
end
|
52
|
+
|
53
|
+
# incoming webhook endpoint was changed. team option should be ignored
|
54
|
+
assert_nothing_raised do
|
55
|
+
create_driver(CONFIG + %[team sowasowa])
|
56
|
+
end
|
57
|
+
|
58
|
+
# rtm? it was not calling `rtm.start`. rtm option was removed and should be ignored
|
59
|
+
assert_nothing_raised do
|
60
|
+
create_driver(CONFIG + %[rtm true])
|
61
|
+
end
|
62
|
+
|
63
|
+
# channel should be URI.unescape-ed
|
64
|
+
d = create_driver(CONFIG + %[channel %23test])
|
65
|
+
assert_equal '#test', d.instance.channel
|
66
|
+
|
67
|
+
# timezone should work
|
68
|
+
d = create_driver(CONFIG + %[timezone Asia/Tokyo])
|
69
|
+
assert_equal 'Asia/Tokyo', d.instance.timezone
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_configure
|
73
|
+
d = create_driver(%[
|
74
|
+
channel channel
|
75
|
+
time_format %Y/%m/%d %H:%M:%S
|
76
|
+
username username
|
77
|
+
color bad
|
78
|
+
icon_emoji :ghost:
|
79
|
+
token XX-XX-XX
|
80
|
+
title slack notice!
|
81
|
+
message %s
|
82
|
+
message_keys message
|
83
|
+
])
|
84
|
+
assert_equal '#channel', d.instance.channel
|
85
|
+
assert_equal '%Y/%m/%d %H:%M:%S', d.instance.time_format
|
86
|
+
assert_equal 'username', d.instance.username
|
87
|
+
assert_equal 'bad', d.instance.color
|
88
|
+
assert_equal ':ghost:', d.instance.icon_emoji
|
89
|
+
assert_equal 'XX-XX-XX', d.instance.token
|
90
|
+
assert_equal '%s', d.instance.message
|
91
|
+
assert_equal ['message'], d.instance.message_keys
|
92
|
+
|
93
|
+
# Allow DM
|
94
|
+
d = create_driver(CONFIG + %[channel @test])
|
95
|
+
assert_equal '@test', d.instance.channel
|
96
|
+
|
97
|
+
assert_raise(Fluent::ConfigError) do
|
98
|
+
create_driver(CONFIG + %[title %s %s\ntitle_keys foo])
|
99
|
+
end
|
100
|
+
|
101
|
+
assert_raise(Fluent::ConfigError) do
|
102
|
+
create_driver(CONFIG + %[message %s %s\nmessage_keys foo])
|
103
|
+
end
|
104
|
+
|
105
|
+
assert_raise(Fluent::ConfigError) do
|
106
|
+
create_driver(CONFIG + %[channel %s %s\nchannel_keys foo])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_slack_configure
|
111
|
+
# One of webhook_url or slackbot_url, or token is required
|
112
|
+
assert_raise(Fluent::ConfigError) do
|
113
|
+
create_driver(%[channel foo])
|
114
|
+
end
|
115
|
+
|
116
|
+
# webhook_url is an empty string
|
117
|
+
assert_raise(Fluent::ConfigError) do
|
118
|
+
create_driver(%[channel foo\nwebhook_url])
|
119
|
+
end
|
120
|
+
|
121
|
+
# webhook without channel (it works because webhook has a default channel)
|
122
|
+
assert_nothing_raised do
|
123
|
+
create_driver(%[webhook_url https://example.com/path/to/webhook])
|
124
|
+
end
|
125
|
+
|
126
|
+
# slackbot_url is an empty string
|
127
|
+
assert_raise(Fluent::ConfigError) do
|
128
|
+
create_driver(%[channel foo\nslackbot_url])
|
129
|
+
end
|
130
|
+
|
131
|
+
# slackbot without channel
|
132
|
+
assert_raise(Fluent::ConfigError) do
|
133
|
+
create_driver(%[slackbot_url https://example.com/path/to/slackbot])
|
134
|
+
end
|
135
|
+
|
136
|
+
# token is an empty string
|
137
|
+
assert_raise(Fluent::ConfigError) do
|
138
|
+
create_driver(%[channel foo\ntoken])
|
139
|
+
end
|
140
|
+
|
141
|
+
# slack webapi token without channel
|
142
|
+
assert_raise(Fluent::ConfigError) do
|
143
|
+
create_driver(%[token some_token])
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_timezone_configure
|
148
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
149
|
+
|
150
|
+
d = create_driver(CONFIG + %[localtime])
|
151
|
+
with_timezone('Asia/Tokyo') do
|
152
|
+
assert_equal true, d.instance.localtime
|
153
|
+
assert_equal "07:00:00", d.instance.timef.format(time)
|
154
|
+
end
|
155
|
+
|
156
|
+
d = create_driver(CONFIG + %[utc])
|
157
|
+
with_timezone('Asia/Tokyo') do
|
158
|
+
assert_equal false, d.instance.localtime
|
159
|
+
assert_equal "22:00:00", d.instance.timef.format(time)
|
160
|
+
end
|
161
|
+
|
162
|
+
d = create_driver(CONFIG + %[timezone Asia/Taipei])
|
163
|
+
with_timezone('Asia/Tokyo') do
|
164
|
+
assert_equal "Asia/Taipei", d.instance.timezone
|
165
|
+
assert_equal "06:00:00", d.instance.timef.format(time)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_time_format_configure
|
170
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
171
|
+
|
172
|
+
d = create_driver(CONFIG + %[time_format %Y/%m/%d %H:%M:%S])
|
173
|
+
with_timezone('Asia/Tokyo') do
|
174
|
+
assert_equal "2014/01/02 07:00:00", d.instance.timef.format(time)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_buffer_configure
|
179
|
+
assert_nothing_raised do
|
180
|
+
create_driver(CONFIG + %[buffer_type file\nbuffer_path tmp/])
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def test_icon_configure
|
185
|
+
# default
|
186
|
+
d = create_driver(CONFIG)
|
187
|
+
assert_equal nil, d.instance.icon_emoji
|
188
|
+
assert_equal nil, d.instance.icon_url
|
189
|
+
|
190
|
+
# either of icon_emoji or icon_url can be specified
|
191
|
+
assert_raise(Fluent::ConfigError) do
|
192
|
+
d = create_driver(CONFIG + %[icon_emoji :ghost:\nicon_url #{@icon_url}])
|
193
|
+
end
|
194
|
+
|
195
|
+
# icon_emoji
|
196
|
+
d = create_driver(CONFIG + %[icon_emoji :ghost:])
|
197
|
+
assert_equal ':ghost:', d.instance.icon_emoji
|
198
|
+
assert_equal nil, d.instance.icon_url
|
199
|
+
|
200
|
+
# icon_url
|
201
|
+
d = create_driver(CONFIG + %[icon_url #{@icon_url}])
|
202
|
+
assert_equal nil, d.instance.icon_emoji
|
203
|
+
assert_equal @icon_url, d.instance.icon_url
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_link_names_configure
|
207
|
+
# default
|
208
|
+
d = create_driver(CONFIG)
|
209
|
+
assert_equal true, d.instance.link_names
|
210
|
+
|
211
|
+
# true
|
212
|
+
d = create_driver(CONFIG + %[link_names true])
|
213
|
+
assert_equal true, d.instance.link_names
|
214
|
+
|
215
|
+
# false
|
216
|
+
d = create_driver(CONFIG + %[link_names false])
|
217
|
+
assert_equal false, d.instance.link_names
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_parse_configure
|
221
|
+
# default
|
222
|
+
d = create_driver(CONFIG)
|
223
|
+
assert_equal nil, d.instance.parse
|
224
|
+
|
225
|
+
# none
|
226
|
+
d = create_driver(CONFIG + %[parse none])
|
227
|
+
assert_equal 'none', d.instance.parse
|
228
|
+
|
229
|
+
# full
|
230
|
+
d = create_driver(CONFIG + %[parse full])
|
231
|
+
assert_equal 'full', d.instance.parse
|
232
|
+
|
233
|
+
# invalid
|
234
|
+
assert_raise(Fluent::ConfigError) do
|
235
|
+
d = create_driver(CONFIG + %[parse invalid])
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_mrkwn_configure
|
240
|
+
# default
|
241
|
+
d = create_driver(CONFIG)
|
242
|
+
assert_equal true, d.instance.mrkdwn
|
243
|
+
assert_equal %w[text fields], d.instance.mrkdwn_in
|
244
|
+
|
245
|
+
# true
|
246
|
+
d = create_driver(CONFIG + %[mrkdwn true])
|
247
|
+
assert_equal true, d.instance.mrkdwn
|
248
|
+
assert_equal %w[text fields], d.instance.mrkdwn_in
|
249
|
+
|
250
|
+
# false
|
251
|
+
d = create_driver(CONFIG + %[mrkdwn false])
|
252
|
+
assert_equal false, d.instance.mrkdwn
|
253
|
+
assert_equal nil, d.instance.mrkdwn_in
|
254
|
+
end
|
255
|
+
|
256
|
+
def test_https_proxy_configure
|
257
|
+
# default
|
258
|
+
d = create_driver(CONFIG)
|
259
|
+
assert_equal nil, d.instance.slack.https_proxy
|
260
|
+
assert_equal Net::HTTP, d.instance.slack.proxy_class
|
261
|
+
|
262
|
+
# https_proxy
|
263
|
+
d = create_driver(CONFIG + %[https_proxy https://proxy.foo.bar:443])
|
264
|
+
assert_equal URI.parse('https://proxy.foo.bar:443'), d.instance.slack.https_proxy
|
265
|
+
assert_not_equal Net::HTTP, d.instance.slack.proxy_class # Net::HTTP.Proxy
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_auto_channels_create_configure
|
269
|
+
# default
|
270
|
+
d = create_driver(CONFIG)
|
271
|
+
assert_equal false, d.instance.auto_channels_create
|
272
|
+
assert_equal({}, d.instance.post_message_opts)
|
273
|
+
|
274
|
+
# require `token`
|
275
|
+
assert_raise(Fluent::ConfigError) do
|
276
|
+
d = create_driver(CONFIG + %[auto_channels_create true])
|
277
|
+
end
|
278
|
+
|
279
|
+
# auto_channels_create
|
280
|
+
d = create_driver(CONFIG + %[auto_channels_create true\ntoken XXX-XX-XXX])
|
281
|
+
assert_equal true, d.instance.auto_channels_create
|
282
|
+
assert_equal({auto_channels_create: true}, d.instance.post_message_opts)
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_default_incoming_webhook
|
286
|
+
d = create_driver(%[
|
287
|
+
channel channel
|
288
|
+
webhook_url https://hooks.slack.com/services/XXX/XXX/XXX
|
289
|
+
])
|
290
|
+
assert_equal Fluent::SlackClient::IncomingWebhook, d.instance.slack.class
|
291
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
292
|
+
d.tag = 'test'
|
293
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
294
|
+
text: "sowawa1\nsowawa2\n",
|
295
|
+
}), {})
|
296
|
+
with_timezone('Asia/Tokyo') do
|
297
|
+
d.emit({message: 'sowawa1'}, time)
|
298
|
+
d.emit({message: 'sowawa2'}, time)
|
299
|
+
d.run
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def test_default_slackbot
|
304
|
+
d = create_driver(%[
|
305
|
+
channel channel
|
306
|
+
slackbot_url https://xxxxx.slack.com/services/hooks/slackbot?token=XXXXXXX
|
307
|
+
])
|
308
|
+
assert_equal Fluent::SlackClient::Slackbot, d.instance.slack.class
|
309
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
310
|
+
d.tag = 'test'
|
311
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
312
|
+
text: "sowawa1\nsowawa2\n",
|
313
|
+
}), {})
|
314
|
+
with_timezone('Asia/Tokyo') do
|
315
|
+
d.emit({message: 'sowawa1'}, time)
|
316
|
+
d.emit({message: 'sowawa2'}, time)
|
317
|
+
d.run
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def test_default_slack_api
|
322
|
+
d = create_driver(%[
|
323
|
+
channel channel
|
324
|
+
token XX-XX-XX
|
325
|
+
])
|
326
|
+
assert_equal Fluent::SlackClient::WebApi, d.instance.slack.class
|
327
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
328
|
+
d.tag = 'test'
|
329
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
330
|
+
token: 'XX-XX-XX',
|
331
|
+
text: "sowawa1\nsowawa2\n",
|
332
|
+
}), {})
|
333
|
+
with_timezone('Asia/Tokyo') do
|
334
|
+
d.emit({message: 'sowawa1'}, time)
|
335
|
+
d.emit({message: 'sowawa2'}, time)
|
336
|
+
d.run
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def test_title_payload
|
341
|
+
title = "mytitle"
|
342
|
+
d = create_driver(CONFIG + %[title #{title}])
|
343
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
344
|
+
d.tag = 'test'
|
345
|
+
# attachments field should be changed to show the title
|
346
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
347
|
+
attachments: [default_attachment.merge({
|
348
|
+
fallback: title,
|
349
|
+
fields: [
|
350
|
+
{
|
351
|
+
title: title,
|
352
|
+
value: "sowawa1\nsowawa2\n",
|
353
|
+
}
|
354
|
+
],
|
355
|
+
})]
|
356
|
+
}), {})
|
357
|
+
with_timezone('Asia/Tokyo') do
|
358
|
+
d.emit({message: 'sowawa1'}, time)
|
359
|
+
d.emit({message: 'sowawa2'}, time)
|
360
|
+
d.run
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def test_title_payload_with_verbose_fallback_option
|
365
|
+
title = "mytitle"
|
366
|
+
d = create_driver(CONFIG + %[title #{title}\nverbose_fallback true])
|
367
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
368
|
+
d.tag = 'test'
|
369
|
+
# attachments field should be changed to show the title
|
370
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
371
|
+
attachments: [default_attachment.merge({
|
372
|
+
fallback: "#{title} sowawa1\nsowawa2\n",
|
373
|
+
fields: [
|
374
|
+
{
|
375
|
+
title: title,
|
376
|
+
value: "sowawa1\nsowawa2\n",
|
377
|
+
}
|
378
|
+
],
|
379
|
+
})]
|
380
|
+
}), {})
|
381
|
+
with_timezone('Asia/Tokyo') do
|
382
|
+
d.emit({message: 'sowawa1'}, time)
|
383
|
+
d.emit({message: 'sowawa2'}, time)
|
384
|
+
d.run
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def test_color_payload
|
389
|
+
color = 'good'
|
390
|
+
d = create_driver(CONFIG + %[color #{color}])
|
391
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
392
|
+
d.tag = 'test'
|
393
|
+
# attachments field should be changed to show the title
|
394
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
395
|
+
attachments: [default_attachment.merge({
|
396
|
+
color: color,
|
397
|
+
fallback: "sowawa1\nsowawa2\n",
|
398
|
+
text: "sowawa1\nsowawa2\n",
|
399
|
+
})]
|
400
|
+
}), {})
|
401
|
+
with_timezone('Asia/Tokyo') do
|
402
|
+
d.emit({message: 'sowawa1'}, time)
|
403
|
+
d.emit({message: 'sowawa2'}, time)
|
404
|
+
d.run
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def test_plain_payload
|
409
|
+
d = create_driver(CONFIG)
|
410
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
411
|
+
d.tag = 'test'
|
412
|
+
# attachments field should be changed to show the title
|
413
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
414
|
+
text: "sowawa1\nsowawa2\n",
|
415
|
+
}), {})
|
416
|
+
with_timezone('Asia/Tokyo') do
|
417
|
+
d.emit({message: 'sowawa1'}, time)
|
418
|
+
d.emit({message: 'sowawa2'}, time)
|
419
|
+
d.run
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def test_title_keys
|
424
|
+
d = create_driver(CONFIG + %[title [%s] %s\ntitle_keys time,tag])
|
425
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
426
|
+
d.tag = 'test'
|
427
|
+
# attachments field should be changed to show the title
|
428
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
429
|
+
attachments: [default_attachment.merge({
|
430
|
+
fallback: "[07:00:00] #{d.tag}",
|
431
|
+
fields: [
|
432
|
+
{
|
433
|
+
title: "[07:00:00] #{d.tag}",
|
434
|
+
value: "sowawa1\nsowawa2\n",
|
435
|
+
}
|
436
|
+
],
|
437
|
+
})]
|
438
|
+
}), {})
|
439
|
+
with_timezone('Asia/Tokyo') do
|
440
|
+
d.emit({message: 'sowawa1'}, time)
|
441
|
+
d.emit({message: 'sowawa2'}, time)
|
442
|
+
d.run
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def test_message_keys
|
447
|
+
d = create_driver(CONFIG + %[message [%s] %s %s\nmessage_keys time,tag,message])
|
448
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
449
|
+
d.tag = 'test'
|
450
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
451
|
+
text: "[07:00:00] test sowawa1\n[07:00:00] test sowawa2\n",
|
452
|
+
}), {})
|
453
|
+
with_timezone('Asia/Tokyo') do
|
454
|
+
d.emit({message: 'sowawa1'}, time)
|
455
|
+
d.emit({message: 'sowawa2'}, time)
|
456
|
+
d.run
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def test_channel_keys
|
461
|
+
d = create_driver(CONFIG + %[channel %s\nchannel_keys channel])
|
462
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
463
|
+
d.tag = 'test'
|
464
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
465
|
+
channel: '#channel1',
|
466
|
+
text: "sowawa1\n",
|
467
|
+
}), {})
|
468
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
469
|
+
channel: '#channel2',
|
470
|
+
text: "sowawa2\n",
|
471
|
+
}), {})
|
472
|
+
with_timezone('Asia/Tokyo') do
|
473
|
+
d.emit({message: 'sowawa1', channel: 'channel1'}, time)
|
474
|
+
d.emit({message: 'sowawa2', channel: 'channel2'}, time)
|
475
|
+
d.run
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
def test_icon_emoji
|
480
|
+
d = create_driver(CONFIG + %[icon_emoji :ghost:])
|
481
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
482
|
+
d.tag = 'test'
|
483
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
484
|
+
icon_emoji: ':ghost:',
|
485
|
+
text: "foo\n",
|
486
|
+
}), {})
|
487
|
+
with_timezone('Asia/Tokyo') do
|
488
|
+
d.emit({message: 'foo'}, time)
|
489
|
+
d.run
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
def test_icon_url
|
494
|
+
d = create_driver(CONFIG + %[icon_url #{@icon_url}])
|
495
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
496
|
+
d.tag = 'test'
|
497
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
498
|
+
icon_url: @icon_url,
|
499
|
+
text: "foo\n",
|
500
|
+
}), {})
|
501
|
+
with_timezone('Asia/Tokyo') do
|
502
|
+
d.emit({message: 'foo'}, time)
|
503
|
+
d.run
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
def test_mrkdwn
|
508
|
+
d = create_driver(CONFIG + %[mrkdwn true])
|
509
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
510
|
+
d.tag = 'test'
|
511
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
512
|
+
mrkdwn: true,
|
513
|
+
text: "foo\n",
|
514
|
+
}), {})
|
515
|
+
with_timezone('Asia/Tokyo') do
|
516
|
+
d.emit({message: 'foo'}, time)
|
517
|
+
d.run
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
def test_mrkdwn_in
|
522
|
+
d = create_driver(CONFIG + %[mrkdwn true\ncolor good])
|
523
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
524
|
+
d.tag = 'test'
|
525
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
526
|
+
attachments: [default_attachment.merge({
|
527
|
+
color: "good",
|
528
|
+
fallback: "foo\n",
|
529
|
+
text: "foo\n",
|
530
|
+
mrkdwn_in: ["text", "fields"],
|
531
|
+
})]
|
532
|
+
}), {})
|
533
|
+
with_timezone('Asia/Tokyo') do
|
534
|
+
d.emit({message: 'foo'}, time)
|
535
|
+
d.run
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
def test_link_names
|
540
|
+
d = create_driver(CONFIG + %[link_names true])
|
541
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
542
|
+
d.tag = 'test'
|
543
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
544
|
+
link_names: true,
|
545
|
+
text: "foo\n",
|
546
|
+
}), {})
|
547
|
+
with_timezone('Asia/Tokyo') do
|
548
|
+
d.emit({message: 'foo'}, time)
|
549
|
+
d.run
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
def test_parse
|
554
|
+
d = create_driver(CONFIG + %[parse full])
|
555
|
+
time = Time.parse("2014-01-01 22:00:00 UTC").to_i
|
556
|
+
d.tag = 'test'
|
557
|
+
mock(d.instance.slack).post_message(default_payload.merge({
|
558
|
+
parse: "full",
|
559
|
+
text: "foo\n",
|
560
|
+
}), {})
|
561
|
+
with_timezone('Asia/Tokyo') do
|
562
|
+
d.emit({message: 'foo'}, time)
|
563
|
+
d.run
|
564
|
+
end
|
565
|
+
end
|
566
|
+
end
|