fluentd 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c973680de36bad1981b1cc6bd3c23c391a04fc66
4
- data.tar.gz: eb6b2c87bd06d19c2d90050355059e6030f47c14
3
+ metadata.gz: ab82be7292539aa23259062e0a5fb6fbe9d6a5ee
4
+ data.tar.gz: f2114ce7960c197f57693e8c84e377cb2ee2edb4
5
5
  SHA512:
6
- metadata.gz: 390c4766aca79e9e3e235fad17b66e5ab0d979df6d77e91ac7f03b4be9a831b60214bef63a5cf0811548a8020f4954df4f27beda58dedeb5948a0f804b6a1c42
7
- data.tar.gz: cef9e1fcf326748d4a72420c5ae4f6b50f3ed8af8178ba0f405ee667a675435121de90099a27170d96ac9592ed421e71655a40b2060a1967f58f62e1c311c047
6
+ metadata.gz: f4cffdc9ff288294bf1ed8f93dac35045f488309b8d303765fdce93434077e820f10c326f9af4437ba26b63b7f1c1636677b03c1514fa61c0a20dcb3d3f69361
7
+ data.tar.gz: d7fa53ec4203b5b818d4bffea29102f295824c7120ce50217ec45d473f225e152647f6d0ed20049dd0e0a58f79e626012a48724620b203cb8b2aed54f27a06b5
@@ -1,5 +1,28 @@
1
1
  # v1.3
2
2
 
3
+ ## Release v1.3.1 - 2018/11/27
4
+
5
+ ### Enhancements
6
+
7
+ * out_forward: Separate parameter names for certificate
8
+ https://github.com/fluent/fluentd/pull/2181
9
+ https://github.com/fluent/fluentd/pull/2190
10
+ * out_forward: Add `verify_connection_at_startup` parameter to check connection setting at startup phase
11
+ https://github.com/fluent/fluentd/pull/2184
12
+ * config: Check right slash position in regexp type
13
+ https://github.com/fluent/fluentd/pull/2176
14
+ * parser_nginx: Support multiple IPs in `http_x_forwarded_for` field
15
+ https://github.com/fluent/fluentd/pull/2171
16
+
17
+ ### Bug fixes
18
+
19
+ * fluent-cat: Fix retry limit handling
20
+ https://github.com/fluent/fluentd/pull/2193
21
+ * record_accessor helper: Delete top level field with bracket style
22
+ https://github.com/fluent/fluentd/pull/2192
23
+ * filter_record_transformer: Keep `class` methond to avoid undefined method error
24
+ https://github.com/fluent/fluentd/pull/2186
25
+
3
26
  ## Release v1.3.0 - 2018/11/10
4
27
 
5
28
  ### New features
@@ -34,6 +34,7 @@ config_path = Fluent::DEFAULT_CONFIG_PATH
34
34
  format = 'json'
35
35
  message_key = 'message'
36
36
  time_as_integer = false
37
+ retry_limit = 5
37
38
 
38
39
  op.on('-p', '--port PORT', "fluent tcp port (default: #{port})", Integer) {|i|
39
40
  port = i
@@ -75,6 +76,10 @@ op.on('--time-as-integer', "Send time as integer for v0.12 or earlier", TrueClas
75
76
  time_as_integer = true
76
77
  }
77
78
 
79
+ op.on('--retry-limit N', "Specify the number of retry limit (default: #{retry_limit})", Integer) {|n|
80
+ retry_limit = n
81
+ }
82
+
78
83
  singleton_class.module_eval do
79
84
  define_method(:usage) do |msg|
80
85
  puts op.to_s
@@ -107,6 +112,8 @@ require 'msgpack'
107
112
  class Writer
108
113
  include MonitorMixin
109
114
 
115
+ RetryLimitError = Class.new(StandardError)
116
+
110
117
  class TimerThread
111
118
  def initialize(writer)
112
119
  @writer = writer
@@ -130,7 +137,7 @@ class Writer
130
137
  end
131
138
  end
132
139
 
133
- def initialize(tag, connector, time_as_integer: false)
140
+ def initialize(tag, connector, time_as_integer: false, retry_limit: 5)
134
141
  @tag = tag
135
142
  @connector = connector
136
143
  @socket = false
@@ -142,7 +149,7 @@ class Writer
142
149
  @pending = []
143
150
  @pending_limit = 1024 # TODO
144
151
  @retry_wait = 1
145
- @retry_limit = 5 # TODO
152
+ @retry_limit = retry_limit
146
153
  @time_as_integer = time_as_integer
147
154
 
148
155
  super()
@@ -236,21 +243,24 @@ class Writer
236
243
  end
237
244
 
238
245
  def try_connect
239
- now = Time.now.to_i
240
-
241
- unless @error_history.empty?
242
- # wait before re-connecting
243
- wait = @retry_wait * (2 ** (@error_history.size-1))
244
- if now <= @socket_time + wait
245
- return false
246
+ begin
247
+ now = Time.now.to_i
248
+
249
+ unless @error_history.empty?
250
+ # wait before re-connecting
251
+ wait = 1 #@retry_wait * (2 ** (@error_history.size-1))
252
+ if now <= @socket_time + wait
253
+ sleep(wait)
254
+ try_connect
255
+ end
246
256
  end
247
- end
248
257
 
249
- begin
250
258
  @socket = @connector.call
251
259
  @error_history.clear
252
260
  return true
253
261
 
262
+ rescue RetryLimitError => ex
263
+ raise ex
254
264
  rescue
255
265
  $stderr.puts "connect failed: #{$!}"
256
266
  @error_history << $!
@@ -263,9 +273,10 @@ class Writer
263
273
  }
264
274
  @pending.clear
265
275
  @error_history.clear
276
+ raise RetryLimitError, "exceed retry limit"
277
+ else
278
+ retry
266
279
  end
267
-
268
- return false
269
280
  end
270
281
  end
271
282
 
@@ -285,7 +296,7 @@ else
285
296
  }
286
297
  end
287
298
 
288
- w = Writer.new(tag, connector, time_as_integer: time_as_integer)
299
+ w = Writer.new(tag, connector, time_as_integer: time_as_integer, retry_limit: retry_limit)
289
300
  w.start
290
301
 
291
302
  case format
@@ -74,6 +74,9 @@ module Fluent
74
74
  return nil unless str
75
75
  return Regexp.compile(str) unless str.start_with?("/")
76
76
  right_slash_position = str.rindex("/")
77
+ if right_slash_position < str.size - 3
78
+ raise Fluent::ConfigError, "invalid regexp: missing right slash: #{str}"
79
+ end
77
80
  options = str[(right_slash_position + 1)..-1]
78
81
  option = 0
79
82
  option |= Regexp::IGNORECASE if options.include?("i")
@@ -316,7 +316,7 @@ module Fluent::Plugin
316
316
  end
317
317
 
318
318
  (Object.instance_methods).each do |m|
319
- undef_method m unless m.to_s =~ /^__|respond_to_missing\?|object_id|public_methods|instance_eval|method_missing|define_singleton_method|respond_to\?|new_ostruct_member/
319
+ undef_method m unless m.to_s =~ /^__|respond_to_missing\?|object_id|public_methods|instance_eval|method_missing|define_singleton_method|respond_to\?|new_ostruct_member|^class$/
320
320
  end
321
321
  end
322
322
  end
@@ -77,6 +77,9 @@ module Fluent::Plugin
77
77
  desc 'Ignore DNS resolution and errors at startup time.'
78
78
  config_param :ignore_network_errors_at_startup, :bool, default: false
79
79
 
80
+ desc 'Verify that a connection can be made with one of out_forward nodes at the time of startup.'
81
+ config_param :verify_connection_at_startup, :bool, default: false
82
+
80
83
  desc 'Compress buffered data.'
81
84
  config_param :compress, :enum, list: [:text, :gzip], default: :text
82
85
 
@@ -91,6 +94,8 @@ module Fluent::Plugin
91
94
  desc 'Verify hostname of servers and certificates or not in TLS transport.'
92
95
  config_param :tls_verify_hostname, :bool, default: true
93
96
  desc 'The additional CA certificate path for TLS.'
97
+ config_param :tls_ca_cert_path, :array, value_type: :string, default: nil
98
+ desc 'The additional certificate path for TLS.'
94
99
  config_param :tls_cert_path, :array, value_type: :string, default: nil
95
100
 
96
101
  config_section :security, required: false, multi: false do
@@ -166,8 +171,12 @@ module Fluent::Plugin
166
171
  end
167
172
 
168
173
  if @transport == :tls
174
+ # socket helper adds CA cert or signed certificate to same cert store internally so unify it in this place.
169
175
  if @tls_cert_path && !@tls_cert_path.empty?
170
- @tls_cert_path.each do |path|
176
+ @tls_ca_cert_path = @tls_cert_path
177
+ end
178
+ if @tls_ca_cert_path && !@tls_ca_cert_path.empty?
179
+ @tls_ca_cert_path.each do |path|
171
180
  raise Fluent::ConfigError, "specified cert path does not exist:#{path}" unless File.exist?(path)
172
181
  raise Fluent::ConfigError, "specified cert path is not readable:#{path}" unless File.readable?(path)
173
182
  end
@@ -253,6 +262,17 @@ module Fluent::Plugin
253
262
  @sock_ack_waiting = []
254
263
  thread_create(:out_forward_receiving_ack, &method(:ack_reader))
255
264
  end
265
+
266
+ if @verify_connection_at_startup
267
+ @nodes.each do |node|
268
+ begin
269
+ node.verify_connection
270
+ rescue StandardError => e
271
+ log.fatal "forward's connection setting error: #{e.message}"
272
+ raise Fluent::UnrecoverableError, e.message
273
+ end
274
+ end
275
+ end
256
276
  end
257
277
 
258
278
  def close
@@ -324,7 +344,7 @@ module Fluent::Plugin
324
344
  verify_fqdn: @tls_verify_hostname,
325
345
  fqdn: hostname,
326
346
  allow_self_signed_cert: @tls_allow_self_signed_cert,
327
- cert_paths: @tls_cert_path,
347
+ cert_paths: @tls_ca_cert_path,
328
348
  linger_timeout: @send_timeout,
329
349
  send_timeout: @send_timeout,
330
350
  recv_timeout: @ack_response_timeout,
@@ -568,6 +588,19 @@ module Fluent::Plugin
568
588
  @standby
569
589
  end
570
590
 
591
+ def verify_connection
592
+ sock = @sender.create_transfer_socket(resolved_host, port, @hostname)
593
+ begin
594
+ ri = RequestInfo.new(@sender.security ? :helo : :established)
595
+ if ri.state != :established
596
+ establish_connection(sock, ri)
597
+ raise if ri.state != :established
598
+ end
599
+ ensure
600
+ sock.close
601
+ end
602
+ end
603
+
571
604
  def establish_connection(sock, ri)
572
605
  while available? && ri.state != :established
573
606
  begin
@@ -21,7 +21,7 @@ module Fluent
21
21
  class NginxParser < RegexpParser
22
22
  Plugin.register_parser("nginx", self)
23
23
 
24
- config_set_default :expression, /^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)"(?:\s+(?<http_x_forwarded_for>[^ ]+))?)?$/
24
+ config_set_default :expression, /^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)"(?:\s+\"?(?<http_x_forwarded_for>[^\"]*)\"?)?)?$/
25
25
  config_set_default :time_format, "%d/%b/%Y:%H:%M:%S %z"
26
26
  end
27
27
  end
@@ -40,8 +40,14 @@ module Fluent
40
40
  if @keys.is_a?(Array)
41
41
  @last_key = @keys.last
42
42
  @dig_keys = @keys[0..-2]
43
- mcall = method(:call_dig)
44
- mdelete = method(:delete_nest)
43
+ if @dig_keys.empty?
44
+ @keys = @keys.first
45
+ mcall = method(:call_index)
46
+ mdelete = method(:delete_top)
47
+ else
48
+ mcall = method(:call_dig)
49
+ mdelete = method(:delete_nest)
50
+ end
45
51
  else
46
52
  # Call [] for single key to reduce dig overhead
47
53
  mcall = method(:call_index)
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.3.0'
19
+ VERSION = '1.3.1'
20
20
 
21
21
  end
@@ -79,6 +79,14 @@ class TestConfigTypes < ::Test::Unit::TestCase
79
79
  test 'w/o slashes' do |(expected, str)|
80
80
  assert_equal(expected, Config.regexp_value(str))
81
81
  end
82
+
83
+ data("missing right slash" => "/regexp",
84
+ "too many options" => "/regexp/imx",)
85
+ test 'invalid regexp' do |(str)|
86
+ assert_raise(Fluent::ConfigError.new("invalid regexp: missing right slash: #{str}")) do
87
+ Config.regexp_value(str)
88
+ end
89
+ end
82
90
  end
83
91
 
84
92
  sub_test_case 'type converters for config_param definitions' do
@@ -9,6 +9,8 @@ require 'fluent/plugin/in_forward'
9
9
  class ForwardOutputTest < Test::Unit::TestCase
10
10
  def setup
11
11
  Fluent::Test.setup
12
+ FileUtils.rm_rf(TMP_DIR)
13
+ FileUtils.mkdir_p(TMP_DIR)
12
14
  @d = nil
13
15
  end
14
16
 
@@ -16,6 +18,8 @@ class ForwardOutputTest < Test::Unit::TestCase
16
18
  @d.instance_shutdown if @d
17
19
  end
18
20
 
21
+ TMP_DIR = File.join(__dir__, "../tmp/out_forward#{ENV['TEST_ENV_NUMBER']}")
22
+
19
23
  TARGET_HOST = '127.0.0.1'
20
24
  TARGET_PORT = unused_port
21
25
  CONFIG = %[
@@ -153,6 +157,27 @@ EOL
153
157
  assert{ logs.any?{|log| log.include?(expected_log) && log.include?(expected_detail) } }
154
158
  end
155
159
 
160
+ data('CA cert' => 'tls_ca_cert_path',
161
+ 'non CA cert' => 'tls_cert_path')
162
+ test 'configure tls_cert_path/tls_ca_cert_path' do |param|
163
+ dummy_cert_path = File.join(TMP_DIR, "dummy_cert.pem")
164
+ FileUtils.touch(dummy_cert_path)
165
+ conf = %[
166
+ send_timeout 5
167
+ transport tls
168
+ tls_insecure_mode true
169
+ #{param} #{dummy_cert_path}
170
+ <server>
171
+ host #{TARGET_HOST}
172
+ port #{TARGET_PORT}
173
+ </server>
174
+ ]
175
+
176
+ @d = d = create_driver(conf)
177
+ # In the plugin, tls_ca_cert_path is used for both cases
178
+ assert_equal([dummy_cert_path], d.instance.tls_ca_cert_path)
179
+ end
180
+
156
181
  test 'compress_default_value' do
157
182
  @d = d = create_driver
158
183
  assert_equal :text, d.instance.compress
@@ -217,6 +242,18 @@ EOL
217
242
  assert_equal 2, d.instance.ack_response_timeout
218
243
  end
219
244
 
245
+ test 'verify_connection_at_startup is disabled in default' do
246
+ @d = d = create_driver(CONFIG)
247
+ assert_false d.instance.verify_connection_at_startup
248
+ end
249
+
250
+ test 'verify_connection_at_startup can be enabled' do
251
+ @d = d = create_driver(CONFIG + %[
252
+ verify_connection_at_startup true
253
+ ])
254
+ assert_true d.instance.verify_connection_at_startup
255
+ end
256
+
220
257
  test 'send tags in str (utf-8 strings)' do
221
258
  target_input_driver = create_target_input_driver
222
259
 
@@ -774,4 +811,113 @@ EOL
774
811
  i.configure(conf)
775
812
  end
776
813
  end
814
+
815
+ sub_test_case 'verify_connection_at_startup' do
816
+ test 'nodes are not available' do
817
+ @d = d = create_driver(CONFIG + %[
818
+ verify_connection_at_startup true
819
+ <buffer tag>
820
+ flush_mode immediate
821
+ retry_type periodic
822
+ retry_wait 30s
823
+ flush_at_shutdown false # suppress errors in d.instance_shutdown
824
+ </buffer>
825
+ ])
826
+ assert_raise Fluent::UnrecoverableError do
827
+ d.instance_start
828
+ end
829
+ d.instance_shutdown
830
+ end
831
+
832
+ test 'nodes_shared_key_miss_match' do
833
+ input_conf = TARGET_CONFIG + %[
834
+ <security>
835
+ self_hostname in.localhost
836
+ shared_key fluentd-sharedkey
837
+ </security>
838
+ ]
839
+ target_input_driver = create_target_input_driver(conf: input_conf)
840
+ output_conf = %[
841
+ send_timeout 30
842
+ heartbeat_type transport
843
+ transport tls
844
+ tls_verify_hostname false
845
+ verify_connection_at_startup true
846
+ require_ack_response true
847
+ ack_response_timeout 5s
848
+ <security>
849
+ self_hostname localhost
850
+ shared_key key_miss_match
851
+ </security>
852
+ <buffer tag>
853
+ flush_mode immediate
854
+ retry_type periodic
855
+ retry_wait 30s
856
+ flush_at_shutdown false # suppress errors in d.instance_shutdown
857
+ flush_thread_interval 31s
858
+ </buffer>
859
+
860
+ <server>
861
+ host #{TARGET_HOST}
862
+ port #{TARGET_PORT}
863
+ </server>
864
+ ]
865
+ @d = d = create_driver(output_conf)
866
+
867
+ target_input_driver.run(expect_records: 1, timeout: 15) do
868
+ assert_raise Fluent::UnrecoverableError do
869
+ d.instance_start
870
+ end
871
+ d.instance_shutdown
872
+ end
873
+ end
874
+
875
+ test 'nodes_shared_key_match' do
876
+ input_conf = TARGET_CONFIG + %[
877
+ <security>
878
+ self_hostname in.localhost
879
+ shared_key fluentd-sharedkey
880
+ <client>
881
+ host 127.0.0.1
882
+ </client>
883
+ </security>
884
+ ]
885
+ target_input_driver = create_target_input_driver(conf: input_conf)
886
+
887
+ output_conf = %[
888
+ send_timeout 51
889
+ verify_connection_at_startup true
890
+ <security>
891
+ self_hostname localhost
892
+ shared_key fluentd-sharedkey
893
+ </security>
894
+ <server>
895
+ name test
896
+ host #{TARGET_HOST}
897
+ port #{TARGET_PORT}
898
+ shared_key fluentd-sharedkey
899
+ </server>
900
+ ]
901
+ @d = d = create_driver(output_conf)
902
+
903
+ time = event_time("2011-01-02 13:14:15 UTC")
904
+ records = [
905
+ {"a" => 1},
906
+ {"a" => 2}
907
+ ]
908
+
909
+ target_input_driver.run(expect_records: 2, timeout: 15) do
910
+ d.run(default_tag: 'test') do
911
+ records.each do |record|
912
+ d.feed(time, record)
913
+ end
914
+ end
915
+ end
916
+
917
+ events = target_input_driver.events
918
+ assert{ events != [] }
919
+ assert_equal(['test', time, records[0]], events[0])
920
+ assert_equal(['test', time, records[1]], events[1])
921
+ end
922
+ end
777
923
  end
@@ -28,6 +28,18 @@ class NginxParserTest < ::Test::Unit::TestCase
28
28
  'agent' => 'Opera/12.0',
29
29
  'http_x_forwarded_for' => '-'
30
30
  }
31
+ @expected_extended_multiple_ip = {
32
+ 'remote' => '127.0.0.1',
33
+ 'host' => '192.168.0.1',
34
+ 'user' => '-',
35
+ 'method' => 'GET',
36
+ 'path' => '/',
37
+ 'code' => '200',
38
+ 'size' => '777',
39
+ 'referer' => '-',
40
+ 'agent' => 'Opera/12.0',
41
+ 'http_x_forwarded_for' => '127.0.0.1, 192.168.0.1'
42
+ }
31
43
  end
32
44
 
33
45
  def create_driver
@@ -65,4 +77,12 @@ class NginxParserTest < ::Test::Unit::TestCase
65
77
  assert_equal(@expected_extended, record)
66
78
  }
67
79
  end
80
+
81
+ def test_parse_with_http_x_forwarded_for_multiple_ip
82
+ d = create_driver
83
+ d.instance.parse('127.0.0.1 192.168.0.1 - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777 "-" "Opera/12.0" "127.0.0.1, 192.168.0.1"') { |time, record|
84
+ assert_equal(event_time('28/Feb/2013:12:00:00 +0900', format: '%d/%b/%Y:%H:%M:%S %z'), time)
85
+ assert_equal(@expected_extended_multiple_ip, record)
86
+ }
87
+ end
68
88
  end
@@ -110,6 +110,12 @@ class RecordAccessorHelperTest < Test::Unit::TestCase
110
110
  assert_equal r[param], accessor.call(r)
111
111
  end
112
112
 
113
+ test "access single dot key using bracket style" do
114
+ r = {'key1' => 'v1', 'ke y2' => 'v2', 'this.is.key3' => 'v3'}
115
+ accessor = @d.record_accessor_create('$["this.is.key3"]')
116
+ assert_equal 'v3', accessor.call(r)
117
+ end
118
+
113
119
  test "nested bracket keys with dot" do
114
120
  r = {'key1' => {'this.is.key3' => 'value'}}
115
121
  accessor = @d.record_accessor_create("$['key1']['this.is.key3']")
@@ -164,6 +170,13 @@ class RecordAccessorHelperTest < Test::Unit::TestCase
164
170
  assert_not_include(r, param)
165
171
  end
166
172
 
173
+ test "delete top key using bracket style" do
174
+ r = {'key1' => 'v1', 'ke y2' => 'v2', 'this.is.key3' => 'v3'}
175
+ accessor = @d.record_accessor_create('$["this.is.key3"]')
176
+ accessor.delete(r)
177
+ assert_not_include(r, 'this.is.key3')
178
+ end
179
+
167
180
  data('bracket' => "$['key1'][0]['ke y2']",
168
181
  'bracket w/ double quotes' => '$["key1"][0]["ke y2"]')
169
182
  test "delete nested keys ['key1', 0, 'ke y2']" do |param|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluentd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sadayuki Furuhashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-10 00:00:00.000000000 Z
11
+ date: 2018-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack