fluent-plugin-out-kivera 1.0.1
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/.github/workflows/linux.yml +26 -0
- data/.github/workflows/macos.yml +26 -0
- data/.gitignore +8 -0
- data/CHANGELOG.md +80 -0
- data/Gemfile +4 -0
- data/ISSUE_TEMPLATE.md +21 -0
- data/LICENSE.txt +11 -0
- data/README.md +58 -0
- data/Rakefile +11 -0
- data/fluent-plugin-out-kivera.gemspec +26 -0
- data/lib/fluent/plugin/out_kivera.rb +378 -0
- data/lib/fluent/test/http_output_test.rb +42 -0
- data/test/plugin/script/plugin/formatter_test.rb +21 -0
- data/test/plugin/test_out_kivera.rb +872 -0
- metadata +150 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'test/unit'
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
|
17
|
+
require 'fluent/test'
|
18
|
+
|
19
|
+
unless ENV.has_key?('VERBOSE')
|
20
|
+
nulllogger = Object.new
|
21
|
+
nulllogger.instance_eval {|obj|
|
22
|
+
def method_missing(method, *args)
|
23
|
+
# pass
|
24
|
+
end
|
25
|
+
}
|
26
|
+
$log = nulllogger
|
27
|
+
end
|
28
|
+
|
29
|
+
class Test::Unit::TestCase
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'webrick'
|
33
|
+
require 'webrick/https'
|
34
|
+
|
35
|
+
# to handle POST/PUT/DELETE ...
|
36
|
+
module WEBrick::HTTPServlet
|
37
|
+
class ProcHandler < AbstractServlet
|
38
|
+
alias do_POST do_GET
|
39
|
+
alias do_PUT do_GET
|
40
|
+
alias do_DELETE do_GET
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'fluent/plugin/formatter'
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
module Plugin
|
5
|
+
class TestFormatter < Formatter
|
6
|
+
Fluent::Plugin.register_formatter('test', self)
|
7
|
+
|
8
|
+
def configure(conf)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def format(tag, time, record)
|
13
|
+
output = {
|
14
|
+
"wrapped" => true,
|
15
|
+
"record" => record
|
16
|
+
}
|
17
|
+
output
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,872 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'yajl'
|
5
|
+
require 'fluent/test/http_output_test'
|
6
|
+
require 'fluent/plugin/out_kivera'
|
7
|
+
require 'fluent/test/driver/output'
|
8
|
+
require 'fluent/test/helpers'
|
9
|
+
require_relative "./script/plugin/formatter_test"
|
10
|
+
|
11
|
+
module OS
|
12
|
+
# ref. http://stackoverflow.com/questions/170956/how-can-i-find-which-operating-system-my-ruby-program-is-running-on
|
13
|
+
def OS.windows?
|
14
|
+
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def OS.mac?
|
18
|
+
(/darwin/ =~ RUBY_PLATFORM) != nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def OS.unix?
|
22
|
+
!OS.windows?
|
23
|
+
end
|
24
|
+
|
25
|
+
def OS.linux?
|
26
|
+
OS.unix? and not OS.mac?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class HTTPOutputTestBase < Test::Unit::TestCase
|
31
|
+
include Fluent::Test::Helpers
|
32
|
+
|
33
|
+
def self.port
|
34
|
+
5126
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.server_config
|
38
|
+
config = {BindAddress: '127.0.0.1', Port: port}
|
39
|
+
if ENV['VERBOSE']
|
40
|
+
logger = WEBrick::Log.new(STDOUT, WEBrick::BasicLog::DEBUG)
|
41
|
+
config[:Logger] = logger
|
42
|
+
config[:AccessLog] = []
|
43
|
+
end
|
44
|
+
config
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.test_http_client(**opts)
|
48
|
+
opts = opts.merge(open_timeout: 1, read_timeout: 1)
|
49
|
+
Net::HTTP.start('127.0.0.1', port, **opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
# setup / teardown for servers
|
53
|
+
def setup
|
54
|
+
Fluent::Test.setup
|
55
|
+
@posts = []
|
56
|
+
@puts = []
|
57
|
+
@prohibited = 0
|
58
|
+
@requests = 0
|
59
|
+
@auth = false
|
60
|
+
@headers = {}
|
61
|
+
@dummy_server_thread = Thread.new do
|
62
|
+
srv = WEBrick::HTTPServer.new(self.class.server_config)
|
63
|
+
begin
|
64
|
+
allowed_methods = %w(POST PUT)
|
65
|
+
srv.mount_proc('/api') { |req,res|
|
66
|
+
@requests += 1
|
67
|
+
unless allowed_methods.include? req.request_method
|
68
|
+
res.status = 405
|
69
|
+
res.body = 'request method mismatch'
|
70
|
+
next
|
71
|
+
end
|
72
|
+
req.each do |key, value|
|
73
|
+
@headers[key] = value
|
74
|
+
end
|
75
|
+
if @auth and req.header['authorization'][0] == 'Basic YWxpY2U6c2VjcmV0IQ==' # pattern of user='alice' passwd='secret!'
|
76
|
+
# ok, authorized
|
77
|
+
# pattern of bear #{Base64.encode64('secret token!')}
|
78
|
+
elsif @auth and req.header['authorization'][0] == 'bearer c2VjcmV0IHRva2VuIQ=='
|
79
|
+
# pattern of jwt
|
80
|
+
# header: {
|
81
|
+
# "alg": "HS256",
|
82
|
+
# "typ": "JWT"
|
83
|
+
# }
|
84
|
+
# payload: {
|
85
|
+
# "iss": "Hoge Publisher",
|
86
|
+
# "sub": "Hoge User"
|
87
|
+
# }
|
88
|
+
# signature:
|
89
|
+
# HS256(base64UrlEncode(header) + "." +
|
90
|
+
# base64UrlEncode(payload) + "." +
|
91
|
+
# secret)
|
92
|
+
elsif @auth and req.header['authorization'][0] == 'jwt eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIb2dlIFB1Ymxpc2hlciIsInN1YiI6IkhvZ2UgVXNlciJ9.V2NL7YgCWNt5d3vTXFrcRLpRImO2cU2JZ4mQglqw3rE'
|
93
|
+
elsif @auth
|
94
|
+
res.status = 403
|
95
|
+
@prohibited += 1
|
96
|
+
next
|
97
|
+
else
|
98
|
+
# ok, authorization not required
|
99
|
+
end
|
100
|
+
|
101
|
+
expander = -> (req) {
|
102
|
+
if req["Content-Encoding"] == "gzip"
|
103
|
+
StringIO.open(req.body, 'rb'){|sio|
|
104
|
+
Zlib::GzipReader.wrap(sio).read
|
105
|
+
}
|
106
|
+
else
|
107
|
+
req.body
|
108
|
+
end
|
109
|
+
}
|
110
|
+
|
111
|
+
record = {:auth => nil}
|
112
|
+
if req.content_type == 'application/json'
|
113
|
+
record[:json] = Yajl.load(expander.call(req))
|
114
|
+
elsif req.content_type == 'text/plain'
|
115
|
+
puts req
|
116
|
+
record[:data] = expander.call(req)
|
117
|
+
elsif req.content_type == 'application/octet-stream'
|
118
|
+
record[:data] = expander.call(req)
|
119
|
+
elsif req.content_type == 'application/x-ndjson'
|
120
|
+
data = []
|
121
|
+
expander.call(req).each_line { |l|
|
122
|
+
data << Yajl.load(l)
|
123
|
+
}
|
124
|
+
record[:x_ndjson] = data
|
125
|
+
else
|
126
|
+
record[:form] = Hash[*(req.body.split('&').map{|kv|kv.split('=')}.flatten)]
|
127
|
+
end
|
128
|
+
|
129
|
+
instance_variable_get("@#{req.request_method.downcase}s").push(record)
|
130
|
+
|
131
|
+
res.status = 200
|
132
|
+
}
|
133
|
+
srv.mount_proc('/modified-api') { |req,res|
|
134
|
+
res.status = 303
|
135
|
+
res.body = 'See other'
|
136
|
+
}
|
137
|
+
srv.mount_proc('/') { |req,res|
|
138
|
+
res.status = 200
|
139
|
+
res.body = 'running'
|
140
|
+
}
|
141
|
+
srv.start
|
142
|
+
ensure
|
143
|
+
srv.shutdown
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# to wait completion of dummy server.start()
|
148
|
+
require 'thread'
|
149
|
+
cv = ConditionVariable.new
|
150
|
+
watcher = Thread.new {
|
151
|
+
connected = false
|
152
|
+
while not connected
|
153
|
+
begin
|
154
|
+
client = self.class.test_http_client
|
155
|
+
client.request_get('/')
|
156
|
+
connected = true
|
157
|
+
rescue Errno::ECONNREFUSED
|
158
|
+
sleep 0.1
|
159
|
+
rescue StandardError => e
|
160
|
+
p e
|
161
|
+
sleep 0.1
|
162
|
+
end
|
163
|
+
end
|
164
|
+
cv.signal
|
165
|
+
}
|
166
|
+
mutex = Mutex.new
|
167
|
+
mutex.synchronize {
|
168
|
+
cv.wait(mutex)
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_dummy_server
|
173
|
+
client = self.class.test_http_client
|
174
|
+
post_header = { 'Content-Type' => 'application/x-www-form-urlencoded' }
|
175
|
+
|
176
|
+
assert_equal '200', client.request_get('/').code
|
177
|
+
assert_equal '200', client.request_post('/api/service/metrics/hoge', 'number=1&mode=gauge', post_header).code
|
178
|
+
|
179
|
+
assert_equal 1, @posts.size
|
180
|
+
|
181
|
+
assert_equal '1', @posts[0][:form]['number']
|
182
|
+
assert_equal 'gauge', @posts[0][:form]['mode']
|
183
|
+
assert_nil @posts[0][:auth]
|
184
|
+
|
185
|
+
assert_equal '303', client.request_get('/modified-api').code
|
186
|
+
|
187
|
+
@auth = true
|
188
|
+
|
189
|
+
assert_equal '403', client.request_post('/api/service/metrics/pos', 'number=30&mode=gauge', post_header).code
|
190
|
+
|
191
|
+
req_with_auth = lambda do |number, mode, user, pass|
|
192
|
+
req = Net::HTTP::Post.new("/api/service/metrics/pos")
|
193
|
+
req.content_type = 'application/x-www-form-urlencoded'
|
194
|
+
req.basic_auth user, pass
|
195
|
+
req.set_form_data({'number'=>number, 'mode'=>mode})
|
196
|
+
req
|
197
|
+
end
|
198
|
+
|
199
|
+
assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
|
200
|
+
|
201
|
+
assert_equal '403', client.request(req_with_auth.call(500, 'count', 'alice', 'wrong password!')).code
|
202
|
+
|
203
|
+
assert_equal 1, @posts.size
|
204
|
+
|
205
|
+
assert_equal '200', client.request(req_with_auth.call(500, 'count', 'alice', 'secret!')).code
|
206
|
+
|
207
|
+
assert_equal 2, @posts.size
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
def teardown
|
212
|
+
@dummy_server_thread.kill
|
213
|
+
@dummy_server_thread.join
|
214
|
+
end
|
215
|
+
|
216
|
+
def create_driver(conf)
|
217
|
+
Fluent::Test::Driver::Output.new(Fluent::Plugin::HTTPOutput).configure(conf)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class HTTPOutputTest < HTTPOutputTestBase
|
222
|
+
CONFIG = %[
|
223
|
+
endpoint_url http://127.0.0.1:#{port}/api/
|
224
|
+
]
|
225
|
+
|
226
|
+
CONFIG_QUERY_PARAM = %[
|
227
|
+
endpoint_url http://127.0.0.1:#{port}/api?foo=bar&baz=qux
|
228
|
+
]
|
229
|
+
|
230
|
+
CONFIG_JSON = %[
|
231
|
+
endpoint_url http://127.0.0.1:#{port}/api/
|
232
|
+
serializer json
|
233
|
+
]
|
234
|
+
|
235
|
+
CONFIG_TEXT = %[
|
236
|
+
endpoint_url http://127.0.0.1:#{port}/api/
|
237
|
+
serializer text
|
238
|
+
]
|
239
|
+
|
240
|
+
CONFIG_RAW = %[
|
241
|
+
endpoint_url http://127.0.0.1:#{port}/api/
|
242
|
+
serializer raw
|
243
|
+
]
|
244
|
+
|
245
|
+
CONFIG_PUT = %[
|
246
|
+
endpoint_url http://127.0.0.1:#{port}/api/
|
247
|
+
http_method put
|
248
|
+
]
|
249
|
+
|
250
|
+
CONFIG_HTTP_ERROR = %[
|
251
|
+
endpoint_url https://127.0.0.1:#{port - 1}/api/
|
252
|
+
]
|
253
|
+
|
254
|
+
CONFIG_HTTP_ERROR_SUPPRESSED = %[
|
255
|
+
endpoint_url https://127.0.0.1:#{port - 1}/api/
|
256
|
+
raise_on_error false
|
257
|
+
]
|
258
|
+
|
259
|
+
RATE_LIMIT_MSEC = 1200
|
260
|
+
|
261
|
+
CONFIG_RATE_LIMIT = %[
|
262
|
+
endpoint_url http://127.0.0.1:#{port}/api/
|
263
|
+
rate_limit_msec #{RATE_LIMIT_MSEC}
|
264
|
+
]
|
265
|
+
|
266
|
+
def test_configure
|
267
|
+
d = create_driver CONFIG
|
268
|
+
assert_equal "http://127.0.0.1:#{self.class.port}/api/", d.instance.endpoint_url
|
269
|
+
assert_equal :form, d.instance.serializer
|
270
|
+
assert_equal [503], d.instance.recoverable_status_codes
|
271
|
+
|
272
|
+
d = create_driver CONFIG_JSON
|
273
|
+
assert_equal "http://127.0.0.1:#{self.class.port}/api/", d.instance.endpoint_url
|
274
|
+
assert_equal :json, d.instance.serializer
|
275
|
+
end
|
276
|
+
|
277
|
+
test 'lack of tag in chunk_keys' do
|
278
|
+
assert_raise_message(/'tag' in chunk_keys is required./) do
|
279
|
+
create_driver(Fluent::Config::Element.new(
|
280
|
+
'ROOT', '', {
|
281
|
+
'@type' => 'http',
|
282
|
+
'endpoint_url' => "http://127.0.0.1:#{self.class.port}/api/",
|
283
|
+
'buffered' => true,
|
284
|
+
}, [
|
285
|
+
Fluent::Config::Element.new('buffer', 'mykey', {
|
286
|
+
'chunk_keys' => 'mykey'
|
287
|
+
}, [])
|
288
|
+
]
|
289
|
+
))
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_emit_form
|
294
|
+
d = create_driver CONFIG
|
295
|
+
d.run(default_tag: 'test.metrics') do
|
296
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
|
297
|
+
end
|
298
|
+
|
299
|
+
assert_equal 1, @posts.size
|
300
|
+
record = @posts[0]
|
301
|
+
|
302
|
+
assert_equal '50', record[:form]['field1']
|
303
|
+
assert_equal '20', record[:form]['field2']
|
304
|
+
assert_equal '10', record[:form]['field3']
|
305
|
+
assert_equal '1', record[:form]['otherfield']
|
306
|
+
assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
|
307
|
+
assert_nil record[:auth]
|
308
|
+
|
309
|
+
d.run(default_tag: 'test.metrics') do
|
310
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
311
|
+
end
|
312
|
+
|
313
|
+
assert_equal 2, @posts.size
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_emit_form_with_query_params
|
317
|
+
d = create_driver CONFIG_QUERY_PARAM
|
318
|
+
d.run(default_tag: 'test.metrics') do
|
319
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
|
320
|
+
end
|
321
|
+
|
322
|
+
assert_equal 1, @posts.size
|
323
|
+
record = @posts[0]
|
324
|
+
|
325
|
+
assert_equal '50', record[:form]['field1']
|
326
|
+
assert_equal '20', record[:form]['field2']
|
327
|
+
assert_equal '10', record[:form]['field3']
|
328
|
+
assert_equal '1', record[:form]['otherfield']
|
329
|
+
assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
|
330
|
+
assert_nil record[:auth]
|
331
|
+
|
332
|
+
d.run(default_tag: 'test.metrics') do
|
333
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
334
|
+
end
|
335
|
+
|
336
|
+
assert_equal 2, @posts.size
|
337
|
+
end
|
338
|
+
|
339
|
+
def test_emit_form_with_custom_headers
|
340
|
+
d = create_driver CONFIG + %[custom_headers {"key":"custom","token":"arbitrary"}]
|
341
|
+
d.run(default_tag: 'test.metrics') do
|
342
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
|
343
|
+
end
|
344
|
+
|
345
|
+
assert_true @headers.has_key?("key")
|
346
|
+
assert_equal "custom", @headers["key"]
|
347
|
+
assert_true @headers.has_key?("token")
|
348
|
+
assert_equal "arbitrary", @headers["token"]
|
349
|
+
|
350
|
+
assert_equal 1, @posts.size
|
351
|
+
record = @posts[0]
|
352
|
+
|
353
|
+
assert_equal '50', record[:form]['field1']
|
354
|
+
assert_equal '20', record[:form]['field2']
|
355
|
+
assert_equal '10', record[:form]['field3']
|
356
|
+
assert_equal '1', record[:form]['otherfield']
|
357
|
+
assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
|
358
|
+
assert_nil record[:auth]
|
359
|
+
|
360
|
+
d.run(default_tag: 'test.metrics') do
|
361
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
362
|
+
end
|
363
|
+
|
364
|
+
assert_equal 2, @posts.size
|
365
|
+
end
|
366
|
+
|
367
|
+
class BufferedEmitTest < self
|
368
|
+
def test_emit_form
|
369
|
+
d = create_driver CONFIG + %[buffered true]
|
370
|
+
d.run(default_tag: 'test.metrics', shutdown: false) do
|
371
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit") })
|
372
|
+
end
|
373
|
+
|
374
|
+
assert_equal 1, @posts.size
|
375
|
+
record = @posts[0]
|
376
|
+
|
377
|
+
assert_equal '50', record[:form]['field1']
|
378
|
+
assert_equal '20', record[:form]['field2']
|
379
|
+
assert_equal '10', record[:form]['field3']
|
380
|
+
assert_equal '1', record[:form]['otherfield']
|
381
|
+
assert_equal URI.encode_www_form_component("あ").upcase, record[:form]['binary'].upcase
|
382
|
+
assert_nil record[:auth]
|
383
|
+
|
384
|
+
d.run(default_tag: 'test.metrics', shutdown: false) do
|
385
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
386
|
+
end
|
387
|
+
|
388
|
+
assert_equal 2, @posts.size
|
389
|
+
end
|
390
|
+
|
391
|
+
def test_emit_form_with_placeholders
|
392
|
+
d = create_driver(Fluent::Config::Element.new(
|
393
|
+
'ROOT', '' ,
|
394
|
+
{"endpoint_url" => "${endpoint}",
|
395
|
+
"buffered" => true},
|
396
|
+
[Fluent::Config::Element.new('buffer', 'tag, endpoint', {"@type" => "memory"} ,[])]))
|
397
|
+
|
398
|
+
d.run(default_tag: 'test.metrics', shutdown: false) do
|
399
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => "\xe3\x81\x82".force_encoding("ascii-8bit"), 'endpoint' => "http://127.0.0.1:#{self.class.port}/modified-api/" })
|
400
|
+
end
|
401
|
+
|
402
|
+
assert_equal 0, @posts.size # post into other URI
|
403
|
+
assert_equal "http://127.0.0.1:#{self.class.port}/modified-api/", d.instance.endpoint_url
|
404
|
+
end
|
405
|
+
|
406
|
+
def test_emit_form_put
|
407
|
+
d = create_driver CONFIG_PUT + %[buffered true]
|
408
|
+
d.run(default_tag: 'test.metrics', shutdown: false) do
|
409
|
+
d.feed({ 'field1' => 50 })
|
410
|
+
end
|
411
|
+
|
412
|
+
assert_equal 0, @posts.size
|
413
|
+
assert_equal 1, @puts.size
|
414
|
+
record = @puts[0]
|
415
|
+
|
416
|
+
assert_equal '50', record[:form]['field1']
|
417
|
+
assert_nil record[:auth]
|
418
|
+
|
419
|
+
d.run(default_tag: 'test.metrics', shutdown: false) do
|
420
|
+
d.feed({ 'field1' => 50 })
|
421
|
+
end
|
422
|
+
|
423
|
+
assert_equal 0, @posts.size
|
424
|
+
assert_equal 2, @puts.size
|
425
|
+
end
|
426
|
+
|
427
|
+
def test_emit_json
|
428
|
+
binary_string = "\xe3\x81\x82"
|
429
|
+
d = create_driver CONFIG_JSON + %[buffered true]
|
430
|
+
d.run(default_tag: 'test.metrics') do
|
431
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
|
432
|
+
end
|
433
|
+
|
434
|
+
assert_equal 1, @posts.size
|
435
|
+
record = @posts[0]
|
436
|
+
|
437
|
+
assert_equal 50, record[:json]['field1']
|
438
|
+
assert_equal 20, record[:json]['field2']
|
439
|
+
assert_equal 10, record[:json]['field3']
|
440
|
+
assert_equal 1, record[:json]['otherfield']
|
441
|
+
assert_equal binary_string, record[:json]['binary']
|
442
|
+
assert_nil record[:auth]
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_emit_json_with_compression
|
446
|
+
binary_string = "\xe3\x81\x82"
|
447
|
+
d = create_driver CONFIG_JSON + %[buffered true\ncompress_request true]
|
448
|
+
d.run(default_tag: 'test.metrics') do
|
449
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
|
450
|
+
end
|
451
|
+
|
452
|
+
assert_equal 1, @posts.size
|
453
|
+
record = @posts[0]
|
454
|
+
|
455
|
+
assert_equal 50, record[:json]['field1']
|
456
|
+
assert_equal 20, record[:json]['field2']
|
457
|
+
assert_equal 10, record[:json]['field3']
|
458
|
+
assert_equal 1, record[:json]['otherfield']
|
459
|
+
assert_equal binary_string, record[:json]['binary']
|
460
|
+
assert_nil record[:auth]
|
461
|
+
end
|
462
|
+
|
463
|
+
def test_emit_x_ndjson
|
464
|
+
binary_string = "\xe3\x81\x82"
|
465
|
+
d = create_driver CONFIG_JSON + %[buffered true\nbulk_request]
|
466
|
+
d.run(default_tag: 'test.metrics') do
|
467
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
|
468
|
+
d.feed({ 'field1' => 70, 'field2' => 30, 'field3' => 20, 'otherfield' => 2, 'binary' => binary_string })
|
469
|
+
end
|
470
|
+
|
471
|
+
assert_equal 1, @posts.size
|
472
|
+
record = @posts[0]
|
473
|
+
|
474
|
+
expected =[
|
475
|
+
{
|
476
|
+
"binary" => "\u3042",
|
477
|
+
"field1" => 50,
|
478
|
+
"field2" => 20,
|
479
|
+
"field3" => 10,
|
480
|
+
"otherfield" => 1
|
481
|
+
},
|
482
|
+
{
|
483
|
+
"binary" => "\u3042",
|
484
|
+
"field1" => 70,
|
485
|
+
"field2" => 30,
|
486
|
+
"field3" => 20,
|
487
|
+
"otherfield" => 2
|
488
|
+
}
|
489
|
+
]
|
490
|
+
|
491
|
+
assert_equal expected, record[:x_ndjson]
|
492
|
+
assert_nil record[:auth]
|
493
|
+
end
|
494
|
+
|
495
|
+
def test_emit_x_ndjson_with_compression
|
496
|
+
binary_string = "\xe3\x81\x82"
|
497
|
+
d = create_driver CONFIG_JSON + %[buffered true\nbulk_request true\ncompress_request true]
|
498
|
+
d.run(default_tag: 'test.metrics') do
|
499
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
|
500
|
+
d.feed({ 'field1' => 70, 'field2' => 30, 'field3' => 20, 'otherfield' => 2, 'binary' => binary_string })
|
501
|
+
end
|
502
|
+
|
503
|
+
assert_equal 1, @posts.size
|
504
|
+
record = @posts[0]
|
505
|
+
|
506
|
+
expected =[
|
507
|
+
{
|
508
|
+
"binary" => "\u3042",
|
509
|
+
"field1" => 50,
|
510
|
+
"field2" => 20,
|
511
|
+
"field3" => 10,
|
512
|
+
"otherfield" => 1
|
513
|
+
},
|
514
|
+
{
|
515
|
+
"binary" => "\u3042",
|
516
|
+
"field1" => 70,
|
517
|
+
"field2" => 30,
|
518
|
+
"field3" => 20,
|
519
|
+
"otherfield" => 2
|
520
|
+
}
|
521
|
+
]
|
522
|
+
|
523
|
+
assert_equal expected, record[:x_ndjson]
|
524
|
+
assert_nil record[:auth]
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
def test_emit_form_put
|
529
|
+
d = create_driver CONFIG_PUT
|
530
|
+
d.run(default_tag: 'test.metrics') do
|
531
|
+
d.feed({ 'field1' => 50 })
|
532
|
+
end
|
533
|
+
|
534
|
+
assert_equal 0, @posts.size
|
535
|
+
assert_equal 1, @puts.size
|
536
|
+
record = @puts[0]
|
537
|
+
|
538
|
+
assert_equal '50', record[:form]['field1']
|
539
|
+
assert_nil record[:auth]
|
540
|
+
|
541
|
+
d.run(default_tag: 'test.metrics') do
|
542
|
+
d.feed({ 'field1' => 50 })
|
543
|
+
end
|
544
|
+
|
545
|
+
assert_equal 0, @posts.size
|
546
|
+
assert_equal 2, @puts.size
|
547
|
+
end
|
548
|
+
|
549
|
+
def test_emit_json
|
550
|
+
binary_string = "\xe3\x81\x82"
|
551
|
+
d = create_driver CONFIG_JSON
|
552
|
+
d.run(default_tag: 'test.metrics') do
|
553
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
|
554
|
+
end
|
555
|
+
|
556
|
+
assert_equal 1, @posts.size
|
557
|
+
record = @posts[0]
|
558
|
+
|
559
|
+
assert_equal 50, record[:json]['field1']
|
560
|
+
assert_equal 20, record[:json]['field2']
|
561
|
+
assert_equal 10, record[:json]['field3']
|
562
|
+
assert_equal 1, record[:json]['otherfield']
|
563
|
+
assert_equal binary_string, record[:json]['binary']
|
564
|
+
assert_nil record[:auth]
|
565
|
+
end
|
566
|
+
|
567
|
+
def test_emit_json_with_compression
|
568
|
+
binary_string = "\xe3\x81\x82"
|
569
|
+
d = create_driver CONFIG_JSON + %[compress_request true]
|
570
|
+
d.run(default_tag: 'test.metrics') do
|
571
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1, 'binary' => binary_string })
|
572
|
+
end
|
573
|
+
|
574
|
+
assert_equal 1, @posts.size
|
575
|
+
record = @posts[0]
|
576
|
+
|
577
|
+
assert_equal 50, record[:json]['field1']
|
578
|
+
assert_equal 20, record[:json]['field2']
|
579
|
+
assert_equal 10, record[:json]['field3']
|
580
|
+
assert_equal 1, record[:json]['otherfield']
|
581
|
+
assert_equal binary_string, record[:json]['binary']
|
582
|
+
assert_nil record[:auth]
|
583
|
+
end
|
584
|
+
|
585
|
+
def test_emit_text
|
586
|
+
binary_string = "\xe3\x81\x82"
|
587
|
+
d = create_driver CONFIG_TEXT
|
588
|
+
d.run(default_tag: 'test.metrics') do
|
589
|
+
d.feed({ "message" => "hello" })
|
590
|
+
end
|
591
|
+
assert_equal 1, @posts.size
|
592
|
+
record = @posts[0]
|
593
|
+
assert_equal 'hello', record[:data]
|
594
|
+
assert_nil record[:auth]
|
595
|
+
end
|
596
|
+
|
597
|
+
def test_emit_text_with_compression
|
598
|
+
d = create_driver CONFIG_TEXT + %[compress_request true]
|
599
|
+
d.run(default_tag: 'test.metrics') do
|
600
|
+
d.feed({ "message" => "hello" })
|
601
|
+
end
|
602
|
+
assert_equal 1, @posts.size
|
603
|
+
record = @posts[0]
|
604
|
+
assert_equal 'hello', record[:data]
|
605
|
+
assert_nil record[:auth]
|
606
|
+
end
|
607
|
+
|
608
|
+
def test_emit_raw
|
609
|
+
binary_string = "\xe3\x81\x82"
|
610
|
+
d = create_driver CONFIG_RAW + %[format msgpack]
|
611
|
+
d.run(default_tag: 'test.metrics') do
|
612
|
+
d.feed({ "message" => "hello" })
|
613
|
+
end
|
614
|
+
assert_equal 1, @posts.size
|
615
|
+
record = @posts[0]
|
616
|
+
assert_equal ({ "message" => "hello" }).to_msgpack, record[:data]
|
617
|
+
assert_nil record[:auth]
|
618
|
+
end
|
619
|
+
|
620
|
+
def test_emit_raw_with_compression
|
621
|
+
binary_string = "\xe3\x81\x82"
|
622
|
+
d = create_driver CONFIG_RAW + %[format msgpack\ncompress_request true]
|
623
|
+
d.run(default_tag: 'test.metrics') do
|
624
|
+
d.feed({ "message" => "hello" })
|
625
|
+
end
|
626
|
+
assert_equal 1, @posts.size
|
627
|
+
record = @posts[0]
|
628
|
+
assert_equal ({ "message" => "hello" }).to_msgpack, record[:data].force_encoding("ascii-8bit")
|
629
|
+
assert_nil record[:auth]
|
630
|
+
end
|
631
|
+
|
632
|
+
def test_http_error_is_raised
|
633
|
+
d = create_driver CONFIG_HTTP_ERROR
|
634
|
+
assert_raise Errno::ECONNREFUSED do
|
635
|
+
d.run(default_tag: 'test.metrics') do
|
636
|
+
d.feed({ 'field1' => 50 })
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
def test_http_error_is_suppressed_with_raise_on_error_false
|
642
|
+
d = create_driver CONFIG_HTTP_ERROR_SUPPRESSED
|
643
|
+
d.run(default_tag: 'test.metrics') do
|
644
|
+
d.feed({ 'field1' => 50 })
|
645
|
+
end
|
646
|
+
# drive asserts the next output chain is called;
|
647
|
+
# so no exception means our plugin handled the error
|
648
|
+
|
649
|
+
assert_equal 0, @requests
|
650
|
+
end
|
651
|
+
|
652
|
+
def test_rate_limiting
|
653
|
+
d = create_driver CONFIG_RATE_LIMIT
|
654
|
+
record = { :k => 1 }
|
655
|
+
|
656
|
+
last_emit = _current_msec
|
657
|
+
d.run(default_tag: 'test.metrics') do
|
658
|
+
d.feed(record)
|
659
|
+
end
|
660
|
+
|
661
|
+
assert_equal 1, @posts.size
|
662
|
+
|
663
|
+
d.run(default_tag: 'test.metrics') do
|
664
|
+
d.feed({})
|
665
|
+
end
|
666
|
+
assert last_emit + RATE_LIMIT_MSEC > _current_msec, "Still under rate limiting interval"
|
667
|
+
assert_equal 1, @posts.size
|
668
|
+
|
669
|
+
wait_msec = 500
|
670
|
+
sleep (last_emit + RATE_LIMIT_MSEC - _current_msec + wait_msec) * 0.001
|
671
|
+
|
672
|
+
assert last_emit + RATE_LIMIT_MSEC < _current_msec, "No longer under rate limiting interval"
|
673
|
+
d.run(default_tag: 'test.metrics') do
|
674
|
+
d.feed(record)
|
675
|
+
end
|
676
|
+
assert_equal 2, @posts.size
|
677
|
+
end
|
678
|
+
|
679
|
+
def _current_msec
|
680
|
+
Time.now.to_f * 1000
|
681
|
+
end
|
682
|
+
|
683
|
+
def test_auth
|
684
|
+
@auth = true # enable authentication of dummy server
|
685
|
+
|
686
|
+
d = create_driver(CONFIG)
|
687
|
+
d.run(default_tag: 'test.metrics') do
|
688
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
689
|
+
end # failed in background, and output warn log
|
690
|
+
|
691
|
+
assert_equal 0, @posts.size
|
692
|
+
assert_equal 1, @prohibited
|
693
|
+
|
694
|
+
d = create_driver(CONFIG + %[
|
695
|
+
authentication basic
|
696
|
+
username alice
|
697
|
+
password wrong_password
|
698
|
+
])
|
699
|
+
d.run(default_tag: 'test.metrics') do
|
700
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
701
|
+
end # failed in background, and output warn log
|
702
|
+
|
703
|
+
assert_equal 0, @posts.size
|
704
|
+
assert_equal 2, @prohibited
|
705
|
+
|
706
|
+
d = create_driver(CONFIG + %[
|
707
|
+
authentication basic
|
708
|
+
username alice
|
709
|
+
password secret!
|
710
|
+
])
|
711
|
+
d.run(default_tag: 'test.metrics') do
|
712
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
713
|
+
end # failed in background, and output warn log
|
714
|
+
|
715
|
+
assert_equal 1, @posts.size
|
716
|
+
assert_equal 2, @prohibited
|
717
|
+
|
718
|
+
require 'base64'
|
719
|
+
d = create_driver(CONFIG + %[
|
720
|
+
authentication bearer
|
721
|
+
token #{Base64.encode64('secret token!')}
|
722
|
+
])
|
723
|
+
d.run(default_tag: 'test.metrics') do
|
724
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
725
|
+
end # failed in background, and output warn log
|
726
|
+
|
727
|
+
assert_equal 2, @posts.size
|
728
|
+
assert_equal 2, @prohibited
|
729
|
+
|
730
|
+
d = create_driver(CONFIG + %[
|
731
|
+
authentication jwt
|
732
|
+
token eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJIb2dlIFB1Ymxpc2hlciIsInN1YiI6IkhvZ2UgVXNlciJ9.V2NL7YgCWNt5d3vTXFrcRLpRImO2cU2JZ4mQglqw3rE
|
733
|
+
])
|
734
|
+
d.run(default_tag: 'test.metrics') do
|
735
|
+
d.feed({ 'field1' => 50, 'field2' => 20, 'field3' => 10, 'otherfield' => 1 })
|
736
|
+
end # failed in background, and output warn log
|
737
|
+
|
738
|
+
assert_equal 3, @posts.size
|
739
|
+
assert_equal 2, @prohibited
|
740
|
+
end
|
741
|
+
|
742
|
+
class CustomFormatterTest < self
|
743
|
+
def test_new_config
|
744
|
+
config = Fluent::Config::Element.new(
|
745
|
+
'ROOT', '',
|
746
|
+
{"@type" => "http",
|
747
|
+
"endpoint_url" => "http://127.0.0.1:#{self.class.port}/api/",
|
748
|
+
"serializer" => "json"}, [
|
749
|
+
Fluent::Config::Element.new('format', '', {
|
750
|
+
"@type" => "test"
|
751
|
+
}, [])
|
752
|
+
])
|
753
|
+
d = create_driver config
|
754
|
+
payload = {"field" => 1}
|
755
|
+
d.run(default_tag: 'test.metrics') do
|
756
|
+
d.feed(payload)
|
757
|
+
end
|
758
|
+
|
759
|
+
record = @posts[0]
|
760
|
+
expected = {"wrapped" => true, "record" => payload}
|
761
|
+
assert_equal expected, record[:json]
|
762
|
+
end
|
763
|
+
|
764
|
+
def test_legacy_config
|
765
|
+
config = %[
|
766
|
+
endpoint_url http://127.0.0.1:#{self.class.port}/api/
|
767
|
+
serializer json
|
768
|
+
format test
|
769
|
+
]
|
770
|
+
|
771
|
+
d = create_driver config
|
772
|
+
payload = {"field" => 1}
|
773
|
+
d.run(default_tag: 'test.metrics') do
|
774
|
+
d.feed(payload)
|
775
|
+
end
|
776
|
+
|
777
|
+
record = @posts[0]
|
778
|
+
expected = {"wrapped" => true, "record" => payload}
|
779
|
+
assert_equal expected, record[:json]
|
780
|
+
end
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
class HTTPSOutputTest < HTTPOutputTestBase
|
785
|
+
def self.port
|
786
|
+
5127
|
787
|
+
end
|
788
|
+
|
789
|
+
def self.server_config
|
790
|
+
config = super
|
791
|
+
config[:SSLEnable] = true
|
792
|
+
config[:SSLCertName] = [["CN", WEBrick::Utils::getservername]]
|
793
|
+
config
|
794
|
+
end
|
795
|
+
|
796
|
+
def self.test_http_client
|
797
|
+
super(
|
798
|
+
use_ssl: true,
|
799
|
+
verify_mode: OpenSSL::SSL::VERIFY_NONE,
|
800
|
+
)
|
801
|
+
end
|
802
|
+
|
803
|
+
def test_configure
|
804
|
+
test_uri = URI.parse("https://127.0.0.1/")
|
805
|
+
|
806
|
+
ssl_config = %[
|
807
|
+
endpoint_url https://127.0.0.1:#{self.class.port}/api/
|
808
|
+
]
|
809
|
+
d = create_driver ssl_config
|
810
|
+
expected_endpoint_url = "https://127.0.0.1:#{self.class.port}/api/"
|
811
|
+
assert_equal expected_endpoint_url, d.instance.endpoint_url
|
812
|
+
http_opts = d.instance.http_opts(test_uri)
|
813
|
+
assert_equal true, http_opts[:use_ssl]
|
814
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, http_opts[:verify_mode]
|
815
|
+
|
816
|
+
no_verify_config = %[
|
817
|
+
endpoint_url https://127.0.0.1:#{self.class.port}/api/
|
818
|
+
ssl_no_verify true
|
819
|
+
]
|
820
|
+
d = create_driver no_verify_config
|
821
|
+
http_opts = d.instance.http_opts(test_uri)
|
822
|
+
assert_equal true, http_opts[:use_ssl]
|
823
|
+
assert_equal OpenSSL::SSL::VERIFY_NONE, http_opts[:verify_mode]
|
824
|
+
|
825
|
+
cacert_file_config = %[
|
826
|
+
endpoint_url https://127.0.0.1:#{self.class.port}/api/
|
827
|
+
ssl_no_verify true
|
828
|
+
cacert_file /tmp/ssl.cert
|
829
|
+
]
|
830
|
+
d = create_driver cacert_file_config
|
831
|
+
FileUtils::touch '/tmp/ssl.cert'
|
832
|
+
http_opts = d.instance.http_opts(test_uri)
|
833
|
+
assert_equal true, http_opts[:use_ssl]
|
834
|
+
assert_equal OpenSSL::SSL::VERIFY_NONE, http_opts[:verify_mode]
|
835
|
+
assert_equal true, File.file?('/tmp/ssl.cert')
|
836
|
+
puts http_opts
|
837
|
+
assert_equal File.join('/tmp/ssl.cert'), http_opts[:ca_file]
|
838
|
+
end
|
839
|
+
|
840
|
+
def test_emit_form_ssl
|
841
|
+
config = %[
|
842
|
+
endpoint_url https://127.0.0.1:#{self.class.port}/api/
|
843
|
+
ssl_no_verify true
|
844
|
+
]
|
845
|
+
d = create_driver config
|
846
|
+
d.run(default_tag: 'test.metrics') do
|
847
|
+
d.feed({ 'field1' => 50 })
|
848
|
+
end
|
849
|
+
|
850
|
+
assert_equal 1, @posts.size
|
851
|
+
record = @posts[0]
|
852
|
+
|
853
|
+
assert_equal '50', record[:form]['field1']
|
854
|
+
end
|
855
|
+
|
856
|
+
def test_emit_form_ssl_ca
|
857
|
+
config = %[
|
858
|
+
endpoint_url https://127.0.0.1:#{self.class.port}/api/
|
859
|
+
ssl_no_verify true
|
860
|
+
cacert_file /tmp/ssl.cert
|
861
|
+
]
|
862
|
+
d = create_driver config
|
863
|
+
d.run(default_tag: 'test.metrics') do
|
864
|
+
d.feed({ 'field1' => 50 })
|
865
|
+
end
|
866
|
+
|
867
|
+
assert_equal 1, @posts.size
|
868
|
+
record = @posts[0]
|
869
|
+
|
870
|
+
assert_equal '50', record[:form]['field1']
|
871
|
+
end
|
872
|
+
end
|