fluentd 1.8.1 → 1.9.0.rc1

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.

Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -17
  3. data/CHANGELOG.md +23 -0
  4. data/Gemfile +1 -4
  5. data/README.md +2 -2
  6. data/fluentd.gemspec +2 -3
  7. data/lib/fluent/command/plugin_generator.rb +1 -1
  8. data/lib/fluent/config.rb +19 -0
  9. data/lib/fluent/config/literal_parser.rb +13 -8
  10. data/lib/fluent/engine.rb +60 -9
  11. data/lib/fluent/plugin/base.rb +5 -0
  12. data/lib/fluent/plugin/buf_file.rb +10 -6
  13. data/lib/fluent/plugin/buf_file_single.rb +10 -6
  14. data/lib/fluent/plugin/buffer.rb +35 -19
  15. data/lib/fluent/plugin/in_http.rb +9 -9
  16. data/lib/fluent/plugin/in_tail.rb +8 -6
  17. data/lib/fluent/plugin/out_http.rb +2 -2
  18. data/lib/fluent/plugin/output.rb +1 -1
  19. data/lib/fluent/plugin/parser.rb +6 -0
  20. data/lib/fluent/plugin_helper/http_server.rb +0 -1
  21. data/lib/fluent/plugin_helper/record_accessor.rb +0 -8
  22. data/lib/fluent/plugin_helper/server.rb +1 -16
  23. data/lib/fluent/plugin_id.rb +9 -4
  24. data/lib/fluent/static_config_analysis.rb +194 -0
  25. data/lib/fluent/supervisor.rb +101 -26
  26. data/lib/fluent/test/driver/base.rb +4 -3
  27. data/lib/fluent/variable_store.rb +40 -0
  28. data/lib/fluent/version.rb +1 -1
  29. data/test/config/test_system_config.rb +4 -4
  30. data/test/plugin/test_in_http.rb +35 -3
  31. data/test/plugin/test_out_http.rb +8 -2
  32. data/test/plugin/test_output.rb +3 -3
  33. data/test/plugin/test_output_as_buffered_secondary.rb +2 -2
  34. data/test/test_config.rb +27 -5
  35. data/test/test_engine.rb +203 -0
  36. data/test/test_output.rb +2 -2
  37. data/test/test_static_config_analysis.rb +177 -0
  38. data/test/test_supervisor.rb +4 -77
  39. data/test/test_variable_store.rb +65 -0
  40. metadata +15 -21
@@ -22,6 +22,7 @@ require 'fluent/clock'
22
22
  require 'serverengine/socket_manager'
23
23
  require 'fileutils'
24
24
  require 'timeout'
25
+ require 'logger'
25
26
 
26
27
  module Fluent
27
28
  module Test
@@ -135,16 +136,16 @@ module Fluent
135
136
  # same with above
136
137
  end
137
138
 
138
- def instance_shutdown
139
+ def instance_shutdown(log: Logger.new($stdout))
139
140
  instance_hook_before_stopped
140
141
 
141
142
  show_errors_if_exists = ->(label, block){
142
143
  begin
143
144
  block.call
144
145
  rescue => e
145
- puts "unexpected error while #{label}, #{e.class}:#{e.message}"
146
+ log.error "unexpected error while #{label}, #{e.class}:#{e.message}"
146
147
  e.backtrace.each do |bt|
147
- puts "\t#{bt}"
148
+ log.error "\t#{bt}"
148
149
  end
149
150
  end
150
151
  }
@@ -0,0 +1,40 @@
1
+ #
2
+ # Fluentd
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ module Fluent
18
+ # VariableStore provides all pluigns with the way to shared variable without using class variable
19
+ # it's for safe reloading mechanism
20
+ class VariableStore
21
+ @data = {}
22
+
23
+ class << self
24
+ def fetch_or_build(namespace, default_value: {})
25
+ @data[namespace] ||= default_value
26
+ end
27
+
28
+ def try_to_reset
29
+ @data, old = {}, @data
30
+
31
+ begin
32
+ yield
33
+ rescue
34
+ @data = old
35
+ raise
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -16,6 +16,6 @@
16
16
 
17
17
  module Fluent
18
18
 
19
- VERSION = '1.8.1'
19
+ VERSION = '1.9.0.rc1'
20
20
 
21
21
  end
@@ -68,7 +68,7 @@ module Fluent::Config
68
68
  EOS
69
69
  s = FakeSupervisor.new
70
70
  sc = Fluent::SystemConfig.new(conf)
71
- sc.overwrite_variables(s.for_system_config)
71
+ sc.overwrite_variables(**s.for_system_config)
72
72
  assert_equal(1, sc.workers)
73
73
  assert_nil(sc.root_dir)
74
74
  assert_equal(Fluent::Log::LEVEL_INFO, sc.log_level)
@@ -98,7 +98,7 @@ module Fluent::Config
98
98
  EOS
99
99
  s = FakeSupervisor.new
100
100
  sc = Fluent::SystemConfig.new(conf)
101
- sc.overwrite_variables(s.for_system_config)
101
+ sc.overwrite_variables(**s.for_system_config)
102
102
  if k == 'log_level'
103
103
  assert_equal(Fluent::Log::LEVEL_ERROR, sc.__send__(k))
104
104
  else
@@ -117,7 +117,7 @@ module Fluent::Config
117
117
  EOS
118
118
  s = FakeSupervisor.new
119
119
  sc = Fluent::SystemConfig.new(conf)
120
- sc.overwrite_variables(s.for_system_config)
120
+ sc.overwrite_variables(**s.for_system_config)
121
121
  assert_equal(:json, sc.log.format)
122
122
  assert_equal('%Y', sc.log.time_format)
123
123
  end
@@ -136,7 +136,7 @@ module Fluent::Config
136
136
  EOS
137
137
  s = FakeSupervisor.new(log_level: level)
138
138
  sc = Fluent::SystemConfig.new(conf)
139
- sc.overwrite_variables(s.for_system_config)
139
+ sc.overwrite_variables(**s.for_system_config)
140
140
  assert_equal(level, sc.log_level)
141
141
  end
142
142
  end
@@ -208,6 +208,38 @@ class HttpInputTest < Test::Unit::TestCase
208
208
  assert_equal_event_time time, d.events[1][1]
209
209
  end
210
210
 
211
+ def test_exact_match_for_expect
212
+ d = create_driver(CONFIG)
213
+ records = [{ "a" => 1}, { "a" => 2 }]
214
+ tag = "tag1"
215
+ res_codes = []
216
+
217
+ d.run(expect_records: 0, timeout: 5) do
218
+ res = post("/#{tag}", { "json" => records.to_json }, { 'Expect' => 'something' })
219
+ res_codes << res.code
220
+ end
221
+ assert_equal ["417"], res_codes
222
+ end
223
+
224
+ def test_exact_match_for_expect_with_other_header
225
+ d = create_driver(CONFIG)
226
+
227
+ records = [{ "a" => 1}, { "a" => 2 }]
228
+ tag = "tag1"
229
+ res_codes = []
230
+
231
+ d.run(expect_records: 2, timeout: 5) do
232
+ res = post("/#{tag}", { "json" => records.to_json, 'x-envoy-expected-rq-timeout-ms' => 4 })
233
+ res_codes << res.code
234
+ end
235
+ assert_equal ["200"], res_codes
236
+
237
+ assert_equal "tag1", d.events[0][0]
238
+ assert_equal 1, d.events[0][2]["a"]
239
+ assert_equal "tag1", d.events[1][0]
240
+ assert_equal 2, d.events[1][2]["a"]
241
+ end
242
+
211
243
  def test_multi_json_with_add_remote_addr
212
244
  d = create_driver(CONFIG + "add_remote_addr true")
213
245
  time = event_time("2011-01-02 13:14:15 UTC")
@@ -446,7 +478,7 @@ class HttpInputTest < Test::Unit::TestCase
446
478
  assert_equal_event_time time, d.events[0][1]
447
479
  assert_equal_event_time time, d.events[1][1]
448
480
  end
449
-
481
+
450
482
  def test_msgpack
451
483
  d = create_driver
452
484
  time = event_time("2011-01-02 13:14:15 UTC")
@@ -716,7 +748,7 @@ class HttpInputTest < Test::Unit::TestCase
716
748
  end
717
749
  end
718
750
  end
719
-
751
+
720
752
  def test_cors_allowed_exclude_empty_string
721
753
  d = create_driver(CONFIG + 'cors_allow_origins ["", "http://*.foo.com"]')
722
754
 
@@ -736,7 +768,7 @@ class HttpInputTest < Test::Unit::TestCase
736
768
  end
737
769
  end
738
770
  end
739
-
771
+
740
772
  def test_cors_allowed_wildcard_preflight_for_subdomain
741
773
  d = create_driver(CONFIG + 'cors_allow_origins ["http://*.foo.com"]')
742
774
 
@@ -117,7 +117,7 @@ class HTTPOutputTest < Test::Unit::TestCase
117
117
  Fluent::Test.setup
118
118
  FileUtils.rm_rf(TMP_DIR)
119
119
 
120
- @@result = Result.new(nil, nil, {}, nil)
120
+ @@result = Result.new(nil, nil, {}, nil)
121
121
  @@http_server_thread ||= Thread.new do
122
122
  run_http_server
123
123
  end
@@ -230,6 +230,9 @@ class HTTPOutputTest < Test::Unit::TestCase
230
230
  end
231
231
 
232
232
  def test_write_with_retryable_response
233
+ old_report_on_exception = Thread.report_on_exception
234
+ Thread.report_on_exception = false # thread finished as invalid state since RetryableResponse raises.
235
+
233
236
  d = create_driver("endpoint #{base_endpoint}/503")
234
237
  assert_raise(Fluent::Plugin::HTTPOutput::RetryableResponse) do
235
238
  d.run(default_tag: 'test.http', shutdown: false) do
@@ -238,7 +241,10 @@ class HTTPOutputTest < Test::Unit::TestCase
238
241
  }
239
242
  end
240
243
  end
241
- d.instance_shutdown
244
+
245
+ d.instance_shutdown(log: $log)
246
+ ensure
247
+ Thread.report_on_exception = old_report_on_exception
242
248
  end
243
249
 
244
250
  def test_write_with_disabled_unrecoverable
@@ -855,7 +855,7 @@ class OutputTest < Test::Unit::TestCase
855
855
  sub_test_case 'configure secondary' do
856
856
  test "Warn if primary type is different from secondary type and either primary or secondary has custom_format" do
857
857
  o = create_output(:buffered)
858
- mock(o.log).warn("secondary type should be same with primary one",
858
+ mock(o.log).warn("Use different plugin for secondary. Check the plugin works with primary like secondary_file",
859
859
  { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" })
860
860
 
861
861
  o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
@@ -864,7 +864,7 @@ class OutputTest < Test::Unit::TestCase
864
864
 
865
865
  test "don't warn if primary type is the same as secondary type" do
866
866
  o = Fluent::Plugin::TestOutput.new
867
- mock(o.log).warn("secondary type should be same with primary one",
867
+ mock(o.log).warn("Use different plugin for secondary. Check the plugin works with primary like secondary_file",
868
868
  { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
869
869
 
870
870
  o.configure(config_element('ROOT','',{'name' => "cool2"},
@@ -876,7 +876,7 @@ class OutputTest < Test::Unit::TestCase
876
876
 
877
877
  test "don't warn if primary type is different from secondary type and both don't have custom_format" do
878
878
  o = create_output(:standard)
879
- mock(o.log).warn("secondary type should be same with primary one",
879
+ mock(o.log).warn("Use different plugin for secondary. Check the plugin works with primary like secondary_file",
880
880
  { primary: o.class.to_s, secondary: "Fluent::Plugin::TestOutput" }).never
881
881
 
882
882
  o.configure(config_element('ROOT','',{},[config_element('secondary','',{'@type'=>'test', 'name' => "cool"})]))
@@ -153,7 +153,7 @@ class BufferedOutputSecondaryTest < Test::Unit::TestCase
153
153
  i = create_output()
154
154
  i.configure(config_element('ROOT','',{},[priconf,secconf]))
155
155
  logs = i.log.out.logs
156
- assert{ logs.any?{|l| l.include?("secondary type should be same with primary one") } }
156
+ assert{ logs.any?{|l| l.include?("Use different plugin for secondary. Check the plugin works with primary like secondary_file") } }
157
157
  end
158
158
 
159
159
  test 'secondary plugin lifecycle is kicked by primary' do
@@ -162,7 +162,7 @@ class BufferedOutputSecondaryTest < Test::Unit::TestCase
162
162
  i = create_output()
163
163
  i.configure(config_element('ROOT','',{},[priconf,secconf]))
164
164
  logs = i.log.out.logs
165
- assert{ logs.any?{|l| l.include?("secondary type should be same with primary one") } }
165
+ assert{ logs.any?{|l| l.include?("Use different plugin for secondary. Check the plugin works with primary like secondary_file") } }
166
166
 
167
167
  assert i.secondary.configured?
168
168
 
@@ -151,16 +151,16 @@ class ConfigTest < Test::Unit::TestCase
151
151
  assert_equal before_size, match_conf.unused.size
152
152
  end
153
153
 
154
- def write_config(path, data)
154
+ def write_config(path, data, encoding: 'utf-8')
155
155
  FileUtils.mkdir_p(File.dirname(path))
156
- File.open(path, "w") {|f| f.write data }
156
+ File.open(path, "w:#{encoding}:utf-8") {|f| f.write data }
157
157
  end
158
158
 
159
159
  def test_inline
160
160
  prepare_config
161
161
  opts = {
162
- :config_path => "#{TMP_DIR}/config_test_1.conf",
163
- :inline_config => "<source>\n type http\n port 2222\n </source>"
162
+ :config_path => "#{TMP_DIR}/config_test_1.conf",
163
+ :inline_config => "<source>\n type http\n port 2222\n </source>"
164
164
  }
165
165
  assert_nothing_raised do
166
166
  Fluent::Supervisor.new(opts)
@@ -175,5 +175,27 @@ class ConfigTest < Test::Unit::TestCase
175
175
  logger = ServerEngine::DaemonLogger.new(logdev, dl_opts)
176
176
  $log = Fluent::Log.new(logger)
177
177
  end
178
- end
179
178
 
179
+ sub_test_case '.build' do
180
+ test 'read config' do
181
+ write_config("#{TMP_DIR}/build/config_build.conf", 'key value')
182
+ c = Fluent::Config.build(config_path: "#{TMP_DIR}/build/config_build.conf")
183
+ assert_equal('value', c['key'])
184
+ end
185
+
186
+ test 'read config with encoding' do
187
+ write_config("#{TMP_DIR}/build/config_build2.conf", "#てすと\nkey value", encoding: 'shift_jis')
188
+
189
+ c = Fluent::Config.build(config_path: "#{TMP_DIR}/build/config_build2.conf", encoding: 'shift_jis')
190
+ assert_equal('value', c['key'])
191
+ end
192
+
193
+ test 'read config with additional_config' do
194
+ write_config("#{TMP_DIR}/build/config_build2.conf", "key value")
195
+
196
+ c = Fluent::Config.build(config_path: "#{TMP_DIR}/build/config_build2.conf", additional_config: 'key2 value2')
197
+ assert_equal('value', c['key'])
198
+ assert_equal('value2', c['key2'])
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,203 @@
1
+ require_relative 'helper'
2
+ require 'fluent/engine'
3
+ require 'fluent/config'
4
+ require 'fluent/input'
5
+ require 'fluent/system_config'
6
+
7
+ class EngineTest < ::Test::Unit::TestCase
8
+ class DummyEngineTestOutput < Fluent::Plugin::Output
9
+ Fluent::Plugin.register_output('dummy_engine_test', self)
10
+ def write(chunk); end
11
+ end
12
+
13
+ class DummyEngineTest2Output < Fluent::Plugin::Output
14
+ Fluent::Plugin.register_output('dummy_engine_test2', self)
15
+ def write(chunk); end
16
+ end
17
+
18
+ class DummyEngineTestInput < Fluent::Plugin::Input
19
+ Fluent::Plugin.register_input('dummy_engine_test', self)
20
+ def multi_workers_ready?; true; end
21
+ end
22
+
23
+ class DummyEngineTest2Input < Fluent::Plugin::Input
24
+ Fluent::Plugin.register_input('dummy_engine_test2', self)
25
+ def multi_workers_ready?; true; end
26
+ end
27
+
28
+ class DummyEngineClassVarTestInput < Fluent::Plugin::Input
29
+ Fluent::Plugin.register_input('dummy_engine_class_var_test', self)
30
+ @@test = nil
31
+ def multi_workers_ready?; true; end
32
+ end
33
+
34
+ sub_test_case '#reload_config' do
35
+ test 'reload new configuration' do
36
+ conf_data = <<-CONF
37
+ <source>
38
+ @type dummy_engine_test
39
+ </source>
40
+ <match>
41
+ @type dummy_engine_test
42
+ </match>
43
+ CONF
44
+
45
+ conf = Fluent::Config.parse(conf_data, '(test)', '(test_dir)', true)
46
+ system_config = Fluent::SystemConfig.create(conf)
47
+
48
+ engine = Fluent::EngineClass.new
49
+ engine.init(system_config)
50
+ engine.configure(conf)
51
+
52
+ assert_kind_of DummyEngineTestInput, engine.root_agent.inputs[0]
53
+ assert_kind_of DummyEngineTestOutput, engine.root_agent.outputs[0]
54
+
55
+ new_conf_data = <<-CONF
56
+ <source>
57
+ @type dummy_engine_test2
58
+ </source>
59
+ <match>
60
+ @type dummy_engine_test2
61
+ </match>
62
+ CONF
63
+
64
+ new_conf = Fluent::Config.parse(new_conf_data, '(test)', '(test_dir)', true)
65
+
66
+ agent = Fluent::RootAgent.new(log: $log, system_config: system_config)
67
+ stub(Fluent::RootAgent).new do
68
+ stub(agent).start.once
69
+ agent
70
+ end
71
+
72
+ engine.reload_config(new_conf)
73
+
74
+ assert_kind_of DummyEngineTest2Input, engine.root_agent.inputs[0]
75
+ assert_kind_of DummyEngineTest2Output, engine.root_agent.outputs[0]
76
+ end
77
+
78
+ test "doesn't start RootAgent when supervisor is true" do
79
+ conf_data = <<-CONF
80
+ <source>
81
+ @type dummy_engine_test
82
+ </source>
83
+ <match>
84
+ @type dummy_engine_test
85
+ </match>
86
+ CONF
87
+
88
+ conf = Fluent::Config.parse(conf_data, '(test)', '(test_dir)', true)
89
+ system_config = Fluent::SystemConfig.create(conf)
90
+
91
+ engine = Fluent::EngineClass.new
92
+ engine.init(system_config)
93
+ engine.configure(conf)
94
+
95
+ assert_kind_of DummyEngineTestInput, engine.root_agent.inputs[0]
96
+ assert_kind_of DummyEngineTestOutput, engine.root_agent.outputs[0]
97
+
98
+ new_conf_data = <<-CONF
99
+ <source>
100
+ @type dummy_engine_test2
101
+ </source>
102
+ <match>
103
+ @type dummy_engine_test2
104
+ </match>
105
+ CONF
106
+
107
+ new_conf = Fluent::Config.parse(new_conf_data, '(test)', '(test_dir)', true)
108
+
109
+ agent = Fluent::RootAgent.new(log: $log, system_config: system_config)
110
+ stub(Fluent::RootAgent).new do
111
+ stub(agent).start.never
112
+ agent
113
+ end
114
+
115
+ engine.reload_config(new_conf, supervisor: true)
116
+
117
+ assert_kind_of DummyEngineTest2Input, engine.root_agent.inputs[0]
118
+ assert_kind_of DummyEngineTest2Output, engine.root_agent.outputs[0]
119
+ end
120
+
121
+ test 'raise an error when conf is invalid' do
122
+ conf_data = <<-CONF
123
+ <source>
124
+ @type dummy_engine_test
125
+ </source>
126
+ <match>
127
+ @type dummy_engine_test
128
+ </match>
129
+ CONF
130
+
131
+ conf = Fluent::Config.parse(conf_data, '(test)', '(test_dir)', true)
132
+ system_config = Fluent::SystemConfig.create(conf)
133
+
134
+ engine = Fluent::EngineClass.new
135
+ engine.init(system_config)
136
+ engine.configure(conf)
137
+
138
+ assert_kind_of DummyEngineTestInput, engine.root_agent.inputs[0]
139
+ assert_kind_of DummyEngineTestOutput, engine.root_agent.outputs[0]
140
+
141
+ new_conf_data = <<-CONF
142
+ <source>
143
+ @type
144
+ </source>
145
+ CONF
146
+
147
+ new_conf = Fluent::Config.parse(new_conf_data, '(test)', '(test_dir)', true)
148
+
149
+ agent = Fluent::RootAgent.new(log: $log, system_config: system_config)
150
+ stub(Fluent::RootAgent).new do
151
+ stub(agent).start.never
152
+ agent
153
+ end
154
+
155
+ assert_raise(Fluent::ConfigError.new("Missing '@type' parameter on <source> directive")) do
156
+ engine.reload_config(new_conf)
157
+ end
158
+
159
+ assert_kind_of DummyEngineTestInput, engine.root_agent.inputs[0]
160
+ assert_kind_of DummyEngineTestOutput, engine.root_agent.outputs[0]
161
+ end
162
+
163
+ test 'raise an error when unreloadable exists' do
164
+ conf_data = <<-CONF
165
+ <source>
166
+ @type dummy_engine_test
167
+ </source>
168
+ <match>
169
+ @type dummy_engine_test
170
+ </match>
171
+ CONF
172
+
173
+ conf = Fluent::Config.parse(conf_data, '(test)', '(test_dir)', true)
174
+ system_config = Fluent::SystemConfig.create(conf)
175
+
176
+ engine = Fluent::EngineClass.new
177
+ engine.init(system_config)
178
+ engine.configure(conf)
179
+
180
+ assert_kind_of DummyEngineTestInput, engine.root_agent.inputs[0]
181
+ assert_kind_of DummyEngineTestOutput, engine.root_agent.outputs[0]
182
+
183
+ conf_data = <<-CONF
184
+ <source>
185
+ @type dummy_engine_class_var_test
186
+ </source>
187
+ <match>
188
+ @type dummy_engine_test
189
+ </match>
190
+ CONF
191
+
192
+ new_conf = Fluent::Config.parse(conf_data, '(test)', '(test_dir)', true)
193
+
194
+ e = assert_raise(Fluent::ConfigError) do
195
+ engine.reload_config(new_conf)
196
+ end
197
+ assert e.message.match?('Unreloadable plugin plugin: dummy_engine_class_var_test')
198
+
199
+ assert_kind_of DummyEngineTestInput, engine.root_agent.inputs[0]
200
+ assert_kind_of DummyEngineTestOutput, engine.root_agent.outputs[0]
201
+ end
202
+ end
203
+ end