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.
@@ -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