fluentd 0.12.40 → 1.6.2

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 (428) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  4. data/.github/ISSUE_TEMPLATE.md +17 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  6. data/.gitignore +5 -0
  7. data/.gitlab/cicd-template.yaml +10 -0
  8. data/.gitlab-ci.yml +147 -0
  9. data/.travis.yml +56 -20
  10. data/ADOPTERS.md +5 -0
  11. data/CHANGELOG.md +1369 -0
  12. data/CONTRIBUTING.md +16 -5
  13. data/GOVERNANCE.md +55 -0
  14. data/Gemfile +5 -0
  15. data/GithubWorkflow.md +78 -0
  16. data/LICENSE +202 -0
  17. data/MAINTAINERS.md +7 -0
  18. data/README.md +23 -11
  19. data/Rakefile +48 -2
  20. data/Vagrantfile +17 -0
  21. data/appveyor.yml +37 -0
  22. data/bin/fluent-binlog-reader +7 -0
  23. data/bin/fluent-ca-generate +6 -0
  24. data/bin/fluent-plugin-config-format +5 -0
  25. data/bin/fluent-plugin-generate +5 -0
  26. data/bin/fluentd +3 -0
  27. data/code-of-conduct.md +3 -0
  28. data/example/copy_roundrobin.conf +39 -0
  29. data/example/counter.conf +18 -0
  30. data/example/in_dummy_blocks.conf +17 -0
  31. data/example/in_dummy_with_compression.conf +23 -0
  32. data/example/in_forward.conf +7 -0
  33. data/example/in_forward_client.conf +37 -0
  34. data/example/in_forward_shared_key.conf +15 -0
  35. data/example/in_forward_tls.conf +14 -0
  36. data/example/in_forward_users.conf +24 -0
  37. data/example/in_forward_workers.conf +21 -0
  38. data/example/in_http.conf +3 -1
  39. data/example/in_out_forward.conf +17 -0
  40. data/example/logevents.conf +25 -0
  41. data/example/multi_filters.conf +61 -0
  42. data/example/out_exec_filter.conf +42 -0
  43. data/example/out_forward.conf +13 -13
  44. data/example/out_forward_buf_file.conf +23 -0
  45. data/example/out_forward_client.conf +109 -0
  46. data/example/out_forward_heartbeat_none.conf +16 -0
  47. data/example/out_forward_shared_key.conf +36 -0
  48. data/example/out_forward_tls.conf +18 -0
  49. data/example/out_forward_users.conf +65 -0
  50. data/example/out_null.conf +36 -0
  51. data/example/secondary_file.conf +42 -0
  52. data/example/suppress_config_dump.conf +7 -0
  53. data/example/worker_section.conf +36 -0
  54. data/fluent.conf +29 -0
  55. data/fluentd.gemspec +21 -11
  56. data/lib/fluent/agent.rb +67 -90
  57. data/lib/fluent/clock.rb +62 -0
  58. data/lib/fluent/command/binlog_reader.rb +244 -0
  59. data/lib/fluent/command/ca_generate.rb +181 -0
  60. data/lib/fluent/command/cat.rb +42 -18
  61. data/lib/fluent/command/debug.rb +12 -10
  62. data/lib/fluent/command/fluentd.rb +153 -5
  63. data/lib/fluent/command/plugin_config_formatter.rb +292 -0
  64. data/lib/fluent/command/plugin_generator.rb +324 -0
  65. data/lib/fluent/compat/call_super_mixin.rb +67 -0
  66. data/lib/fluent/compat/detach_process_mixin.rb +33 -0
  67. data/lib/fluent/compat/exec_util.rb +129 -0
  68. data/lib/fluent/compat/file_util.rb +54 -0
  69. data/lib/fluent/compat/filter.rb +68 -0
  70. data/lib/fluent/compat/formatter.rb +111 -0
  71. data/lib/fluent/compat/formatter_utils.rb +85 -0
  72. data/lib/fluent/compat/handle_tag_and_time_mixin.rb +62 -0
  73. data/lib/fluent/compat/handle_tag_name_mixin.rb +53 -0
  74. data/lib/fluent/compat/input.rb +49 -0
  75. data/lib/fluent/compat/output.rb +718 -0
  76. data/lib/fluent/compat/output_chain.rb +60 -0
  77. data/lib/fluent/compat/parser.rb +310 -0
  78. data/lib/fluent/compat/parser_utils.rb +40 -0
  79. data/lib/fluent/compat/propagate_default.rb +62 -0
  80. data/lib/fluent/compat/record_filter_mixin.rb +34 -0
  81. data/lib/fluent/compat/set_tag_key_mixin.rb +50 -0
  82. data/lib/fluent/compat/set_time_key_mixin.rb +69 -0
  83. data/lib/fluent/compat/socket_util.rb +165 -0
  84. data/lib/fluent/compat/string_util.rb +34 -0
  85. data/lib/fluent/compat/structured_format_mixin.rb +26 -0
  86. data/lib/fluent/compat/type_converter.rb +90 -0
  87. data/lib/fluent/config/configure_proxy.rb +210 -62
  88. data/lib/fluent/config/dsl.rb +12 -5
  89. data/lib/fluent/config/element.rb +107 -9
  90. data/lib/fluent/config/literal_parser.rb +9 -3
  91. data/lib/fluent/config/parser.rb +4 -4
  92. data/lib/fluent/config/section.rb +51 -14
  93. data/lib/fluent/config/types.rb +28 -13
  94. data/lib/fluent/config/v1_parser.rb +3 -5
  95. data/lib/fluent/config.rb +23 -20
  96. data/lib/fluent/configurable.rb +79 -21
  97. data/lib/fluent/counter/base_socket.rb +46 -0
  98. data/lib/fluent/counter/client.rb +297 -0
  99. data/lib/fluent/counter/error.rb +86 -0
  100. data/lib/fluent/counter/mutex_hash.rb +163 -0
  101. data/lib/fluent/counter/server.rb +273 -0
  102. data/lib/fluent/counter/store.rb +205 -0
  103. data/lib/fluent/counter/validator.rb +145 -0
  104. data/lib/fluent/counter.rb +23 -0
  105. data/lib/fluent/daemon.rb +15 -0
  106. data/lib/fluent/engine.rb +102 -65
  107. data/lib/fluent/env.rb +7 -3
  108. data/lib/fluent/error.rb +30 -0
  109. data/lib/fluent/event.rb +197 -21
  110. data/lib/fluent/event_router.rb +93 -10
  111. data/lib/fluent/filter.rb +2 -50
  112. data/lib/fluent/formatter.rb +4 -293
  113. data/lib/fluent/input.rb +2 -32
  114. data/lib/fluent/label.rb +10 -2
  115. data/lib/fluent/load.rb +3 -3
  116. data/lib/fluent/log.rb +348 -81
  117. data/lib/fluent/match.rb +37 -36
  118. data/lib/fluent/mixin.rb +12 -176
  119. data/lib/fluent/msgpack_factory.rb +62 -0
  120. data/lib/fluent/output.rb +10 -612
  121. data/lib/fluent/output_chain.rb +23 -0
  122. data/lib/fluent/parser.rb +4 -800
  123. data/lib/fluent/plugin/bare_output.rb +63 -0
  124. data/lib/fluent/plugin/base.rb +192 -0
  125. data/lib/fluent/plugin/buf_file.rb +128 -174
  126. data/lib/fluent/plugin/buf_memory.rb +9 -92
  127. data/lib/fluent/plugin/buffer/chunk.rb +221 -0
  128. data/lib/fluent/plugin/buffer/file_chunk.rb +383 -0
  129. data/lib/fluent/plugin/buffer/memory_chunk.rb +90 -0
  130. data/lib/fluent/plugin/buffer.rb +779 -0
  131. data/lib/fluent/plugin/compressable.rb +92 -0
  132. data/lib/fluent/plugin/exec_util.rb +3 -108
  133. data/lib/fluent/plugin/file_util.rb +4 -34
  134. data/lib/fluent/plugin/file_wrapper.rb +120 -0
  135. data/lib/fluent/plugin/filter.rb +93 -0
  136. data/lib/fluent/plugin/filter_grep.rb +117 -34
  137. data/lib/fluent/plugin/filter_parser.rb +85 -62
  138. data/lib/fluent/plugin/filter_record_transformer.rb +27 -39
  139. data/lib/fluent/plugin/filter_stdout.rb +15 -12
  140. data/lib/fluent/plugin/formatter.rb +50 -0
  141. data/lib/fluent/plugin/formatter_csv.rb +52 -0
  142. data/lib/fluent/plugin/formatter_hash.rb +33 -0
  143. data/lib/fluent/plugin/formatter_json.rb +55 -0
  144. data/lib/fluent/plugin/formatter_ltsv.rb +42 -0
  145. data/lib/fluent/plugin/formatter_msgpack.rb +33 -0
  146. data/lib/fluent/plugin/formatter_out_file.rb +51 -0
  147. data/lib/fluent/plugin/formatter_single_value.rb +34 -0
  148. data/lib/fluent/plugin/formatter_stdout.rb +76 -0
  149. data/lib/fluent/plugin/formatter_tsv.rb +38 -0
  150. data/lib/fluent/plugin/in_debug_agent.rb +17 -6
  151. data/lib/fluent/plugin/in_dummy.rb +47 -20
  152. data/lib/fluent/plugin/in_exec.rb +55 -123
  153. data/lib/fluent/plugin/in_forward.rb +299 -216
  154. data/lib/fluent/plugin/in_gc_stat.rb +14 -36
  155. data/lib/fluent/plugin/in_http.rb +204 -91
  156. data/lib/fluent/plugin/in_monitor_agent.rb +186 -258
  157. data/lib/fluent/plugin/in_object_space.rb +13 -41
  158. data/lib/fluent/plugin/in_syslog.rb +112 -134
  159. data/lib/fluent/plugin/in_tail.rb +408 -745
  160. data/lib/fluent/plugin/in_tcp.rb +66 -9
  161. data/lib/fluent/plugin/in_udp.rb +60 -11
  162. data/lib/fluent/plugin/{in_stream.rb → in_unix.rb} +8 -4
  163. data/lib/fluent/plugin/input.rb +37 -0
  164. data/lib/fluent/plugin/multi_output.rb +158 -0
  165. data/lib/fluent/plugin/out_copy.rb +23 -35
  166. data/lib/fluent/plugin/out_exec.rb +67 -70
  167. data/lib/fluent/plugin/out_exec_filter.rb +204 -271
  168. data/lib/fluent/plugin/out_file.rb +267 -73
  169. data/lib/fluent/plugin/out_forward.rb +854 -325
  170. data/lib/fluent/plugin/out_null.rb +42 -9
  171. data/lib/fluent/plugin/out_relabel.rb +9 -5
  172. data/lib/fluent/plugin/out_roundrobin.rb +18 -37
  173. data/lib/fluent/plugin/out_secondary_file.rb +133 -0
  174. data/lib/fluent/plugin/out_stdout.rb +43 -10
  175. data/lib/fluent/plugin/out_stream.rb +7 -2
  176. data/lib/fluent/plugin/output.rb +1498 -0
  177. data/lib/fluent/plugin/owned_by_mixin.rb +42 -0
  178. data/lib/fluent/plugin/parser.rb +191 -0
  179. data/lib/fluent/plugin/parser_apache.rb +28 -0
  180. data/lib/fluent/plugin/parser_apache2.rb +88 -0
  181. data/lib/fluent/plugin/parser_apache_error.rb +26 -0
  182. data/lib/fluent/plugin/parser_csv.rb +39 -0
  183. data/lib/fluent/plugin/parser_json.rb +94 -0
  184. data/lib/fluent/plugin/parser_ltsv.rb +49 -0
  185. data/lib/fluent/plugin/parser_msgpack.rb +50 -0
  186. data/lib/fluent/plugin/parser_multiline.rb +106 -0
  187. data/lib/fluent/plugin/parser_nginx.rb +28 -0
  188. data/lib/fluent/plugin/parser_none.rb +36 -0
  189. data/lib/fluent/plugin/parser_regexp.rb +68 -0
  190. data/lib/fluent/plugin/parser_syslog.rb +142 -0
  191. data/lib/fluent/plugin/parser_tsv.rb +42 -0
  192. data/lib/fluent/plugin/socket_util.rb +3 -143
  193. data/lib/fluent/plugin/storage.rb +84 -0
  194. data/lib/fluent/plugin/storage_local.rb +164 -0
  195. data/lib/fluent/plugin/string_util.rb +3 -15
  196. data/lib/fluent/plugin.rb +122 -121
  197. data/lib/fluent/plugin_helper/cert_option.rb +178 -0
  198. data/lib/fluent/plugin_helper/child_process.rb +364 -0
  199. data/lib/fluent/plugin_helper/compat_parameters.rb +333 -0
  200. data/lib/fluent/plugin_helper/counter.rb +51 -0
  201. data/lib/fluent/plugin_helper/event_emitter.rb +93 -0
  202. data/lib/fluent/plugin_helper/event_loop.rb +170 -0
  203. data/lib/fluent/plugin_helper/extract.rb +104 -0
  204. data/lib/fluent/plugin_helper/formatter.rb +147 -0
  205. data/lib/fluent/plugin_helper/http_server/app.rb +79 -0
  206. data/lib/fluent/plugin_helper/http_server/compat/server.rb +81 -0
  207. data/lib/fluent/plugin_helper/http_server/compat/webrick_handler.rb +58 -0
  208. data/lib/fluent/plugin_helper/http_server/methods.rb +35 -0
  209. data/lib/fluent/plugin_helper/http_server/request.rb +42 -0
  210. data/lib/fluent/plugin_helper/http_server/router.rb +54 -0
  211. data/lib/fluent/plugin_helper/http_server/server.rb +87 -0
  212. data/lib/fluent/plugin_helper/http_server.rb +76 -0
  213. data/lib/fluent/plugin_helper/inject.rb +151 -0
  214. data/lib/fluent/plugin_helper/parser.rb +147 -0
  215. data/lib/fluent/plugin_helper/record_accessor.rb +210 -0
  216. data/lib/fluent/plugin_helper/retry_state.rb +205 -0
  217. data/lib/fluent/plugin_helper/server.rb +807 -0
  218. data/lib/fluent/plugin_helper/socket.rb +250 -0
  219. data/lib/fluent/plugin_helper/socket_option.rb +80 -0
  220. data/lib/fluent/plugin_helper/storage.rb +349 -0
  221. data/lib/fluent/plugin_helper/thread.rb +179 -0
  222. data/lib/fluent/plugin_helper/timer.rb +92 -0
  223. data/lib/fluent/plugin_helper.rb +73 -0
  224. data/lib/fluent/plugin_id.rb +80 -0
  225. data/lib/fluent/process.rb +3 -489
  226. data/lib/fluent/registry.rb +52 -10
  227. data/lib/fluent/root_agent.rb +204 -42
  228. data/lib/fluent/supervisor.rb +597 -359
  229. data/lib/fluent/system_config.rb +131 -42
  230. data/lib/fluent/test/base.rb +6 -54
  231. data/lib/fluent/test/driver/base.rb +224 -0
  232. data/lib/fluent/test/driver/base_owned.rb +70 -0
  233. data/lib/fluent/test/driver/base_owner.rb +135 -0
  234. data/lib/fluent/test/driver/event_feeder.rb +98 -0
  235. data/lib/fluent/test/driver/filter.rb +57 -0
  236. data/lib/fluent/test/driver/formatter.rb +30 -0
  237. data/lib/fluent/test/driver/input.rb +31 -0
  238. data/lib/fluent/test/driver/multi_output.rb +53 -0
  239. data/lib/fluent/test/driver/output.rb +102 -0
  240. data/lib/fluent/test/driver/parser.rb +30 -0
  241. data/lib/fluent/test/driver/test_event_router.rb +45 -0
  242. data/lib/fluent/test/filter_test.rb +0 -1
  243. data/lib/fluent/test/formatter_test.rb +4 -1
  244. data/lib/fluent/test/helpers.rb +58 -10
  245. data/lib/fluent/test/input_test.rb +27 -19
  246. data/lib/fluent/test/log.rb +79 -0
  247. data/lib/fluent/test/output_test.rb +28 -39
  248. data/lib/fluent/test/parser_test.rb +3 -1
  249. data/lib/fluent/test/startup_shutdown.rb +46 -0
  250. data/lib/fluent/test.rb +33 -1
  251. data/lib/fluent/time.rb +450 -1
  252. data/lib/fluent/timezone.rb +27 -3
  253. data/lib/fluent/{status.rb → unique_id.rb} +15 -24
  254. data/lib/fluent/version.rb +1 -1
  255. data/lib/fluent/winsvc.rb +85 -0
  256. data/templates/new_gem/Gemfile +3 -0
  257. data/templates/new_gem/README.md.erb +43 -0
  258. data/templates/new_gem/Rakefile +13 -0
  259. data/templates/new_gem/fluent-plugin.gemspec.erb +27 -0
  260. data/templates/new_gem/lib/fluent/plugin/filter.rb.erb +14 -0
  261. data/templates/new_gem/lib/fluent/plugin/formatter.rb.erb +14 -0
  262. data/templates/new_gem/lib/fluent/plugin/input.rb.erb +11 -0
  263. data/templates/new_gem/lib/fluent/plugin/output.rb.erb +11 -0
  264. data/templates/new_gem/lib/fluent/plugin/parser.rb.erb +15 -0
  265. data/templates/new_gem/test/helper.rb.erb +8 -0
  266. data/templates/new_gem/test/plugin/test_filter.rb.erb +18 -0
  267. data/templates/new_gem/test/plugin/test_formatter.rb.erb +18 -0
  268. data/templates/new_gem/test/plugin/test_input.rb.erb +18 -0
  269. data/templates/new_gem/test/plugin/test_output.rb.erb +18 -0
  270. data/templates/new_gem/test/plugin/test_parser.rb.erb +18 -0
  271. data/templates/plugin_config_formatter/param.md-compact.erb +25 -0
  272. data/templates/plugin_config_formatter/param.md.erb +34 -0
  273. data/templates/plugin_config_formatter/section.md.erb +12 -0
  274. data/test/command/test_binlog_reader.rb +346 -0
  275. data/test/command/test_ca_generate.rb +70 -0
  276. data/test/command/test_fluentd.rb +901 -0
  277. data/test/command/test_plugin_config_formatter.rb +276 -0
  278. data/test/command/test_plugin_generator.rb +92 -0
  279. data/test/compat/test_calls_super.rb +166 -0
  280. data/test/compat/test_parser.rb +92 -0
  281. data/test/config/test_config_parser.rb +126 -2
  282. data/test/config/test_configurable.rb +946 -187
  283. data/test/config/test_configure_proxy.rb +424 -74
  284. data/test/config/test_dsl.rb +11 -11
  285. data/test/config/test_element.rb +500 -0
  286. data/test/config/test_literal_parser.rb +8 -0
  287. data/test/config/test_plugin_configuration.rb +56 -0
  288. data/test/config/test_section.rb +79 -7
  289. data/test/config/test_system_config.rb +122 -35
  290. data/test/config/test_types.rb +38 -0
  291. data/test/counter/test_client.rb +559 -0
  292. data/test/counter/test_error.rb +44 -0
  293. data/test/counter/test_mutex_hash.rb +179 -0
  294. data/test/counter/test_server.rb +589 -0
  295. data/test/counter/test_store.rb +258 -0
  296. data/test/counter/test_validator.rb +137 -0
  297. data/test/helper.rb +89 -6
  298. data/test/helpers/fuzzy_assert.rb +89 -0
  299. data/test/plugin/test_bare_output.rb +118 -0
  300. data/test/plugin/test_base.rb +115 -0
  301. data/test/plugin/test_buf_file.rb +823 -460
  302. data/test/plugin/test_buf_memory.rb +32 -194
  303. data/test/plugin/test_buffer.rb +1233 -0
  304. data/test/plugin/test_buffer_chunk.rb +198 -0
  305. data/test/plugin/test_buffer_file_chunk.rb +844 -0
  306. data/test/plugin/test_buffer_memory_chunk.rb +338 -0
  307. data/test/plugin/test_compressable.rb +84 -0
  308. data/test/plugin/test_filter.rb +357 -0
  309. data/test/plugin/test_filter_grep.rb +540 -29
  310. data/test/plugin/test_filter_parser.rb +439 -452
  311. data/test/plugin/test_filter_record_transformer.rb +123 -166
  312. data/test/plugin/test_filter_stdout.rb +160 -72
  313. data/test/plugin/test_formatter_csv.rb +111 -0
  314. data/test/plugin/test_formatter_hash.rb +35 -0
  315. data/test/plugin/test_formatter_json.rb +51 -0
  316. data/test/plugin/test_formatter_ltsv.rb +62 -0
  317. data/test/plugin/test_formatter_msgpack.rb +28 -0
  318. data/test/plugin/test_formatter_out_file.rb +95 -0
  319. data/test/plugin/test_formatter_single_value.rb +38 -0
  320. data/test/plugin/test_formatter_tsv.rb +68 -0
  321. data/test/plugin/test_in_debug_agent.rb +24 -1
  322. data/test/plugin/test_in_dummy.rb +111 -18
  323. data/test/plugin/test_in_exec.rb +200 -113
  324. data/test/plugin/test_in_forward.rb +990 -387
  325. data/test/plugin/test_in_gc_stat.rb +10 -8
  326. data/test/plugin/test_in_http.rb +600 -224
  327. data/test/plugin/test_in_monitor_agent.rb +690 -0
  328. data/test/plugin/test_in_object_space.rb +24 -8
  329. data/test/plugin/test_in_syslog.rb +154 -215
  330. data/test/plugin/test_in_tail.rb +1006 -707
  331. data/test/plugin/test_in_tcp.rb +125 -48
  332. data/test/plugin/test_in_udp.rb +204 -63
  333. data/test/plugin/{test_in_stream.rb → test_in_unix.rb} +14 -13
  334. data/test/plugin/test_input.rb +126 -0
  335. data/test/plugin/test_metadata.rb +89 -0
  336. data/test/plugin/test_multi_output.rb +180 -0
  337. data/test/plugin/test_out_copy.rb +117 -112
  338. data/test/plugin/test_out_exec.rb +258 -53
  339. data/test/plugin/test_out_exec_filter.rb +538 -115
  340. data/test/plugin/test_out_file.rb +865 -178
  341. data/test/plugin/test_out_forward.rb +998 -210
  342. data/test/plugin/test_out_null.rb +105 -0
  343. data/test/plugin/test_out_relabel.rb +28 -0
  344. data/test/plugin/test_out_roundrobin.rb +36 -29
  345. data/test/plugin/test_out_secondary_file.rb +458 -0
  346. data/test/plugin/test_out_stdout.rb +135 -37
  347. data/test/plugin/test_out_stream.rb +18 -0
  348. data/test/plugin/test_output.rb +984 -0
  349. data/test/plugin/test_output_as_buffered.rb +2021 -0
  350. data/test/plugin/test_output_as_buffered_backup.rb +312 -0
  351. data/test/plugin/test_output_as_buffered_compress.rb +165 -0
  352. data/test/plugin/test_output_as_buffered_overflow.rb +250 -0
  353. data/test/plugin/test_output_as_buffered_retries.rb +911 -0
  354. data/test/plugin/test_output_as_buffered_secondary.rb +874 -0
  355. data/test/plugin/test_output_as_standard.rb +374 -0
  356. data/test/plugin/test_owned_by.rb +35 -0
  357. data/test/plugin/test_parser.rb +359 -0
  358. data/test/plugin/test_parser_apache.rb +42 -0
  359. data/test/plugin/test_parser_apache2.rb +47 -0
  360. data/test/plugin/test_parser_apache_error.rb +45 -0
  361. data/test/plugin/test_parser_csv.rb +103 -0
  362. data/test/plugin/test_parser_json.rb +138 -0
  363. data/test/plugin/test_parser_labeled_tsv.rb +145 -0
  364. data/test/plugin/test_parser_multiline.rb +100 -0
  365. data/test/plugin/test_parser_nginx.rb +88 -0
  366. data/test/plugin/test_parser_none.rb +52 -0
  367. data/test/plugin/test_parser_regexp.rb +289 -0
  368. data/test/plugin/test_parser_syslog.rb +441 -0
  369. data/test/plugin/test_parser_tsv.rb +122 -0
  370. data/test/plugin/test_storage.rb +167 -0
  371. data/test/plugin/test_storage_local.rb +335 -0
  372. data/test/plugin_helper/data/cert/cert-key.pem +27 -0
  373. data/test/plugin_helper/data/cert/cert-with-no-newline.pem +19 -0
  374. data/test/plugin_helper/data/cert/cert.pem +19 -0
  375. data/test/plugin_helper/http_server/test_app.rb +65 -0
  376. data/test/plugin_helper/http_server/test_route.rb +32 -0
  377. data/test/plugin_helper/test_cert_option.rb +16 -0
  378. data/test/plugin_helper/test_child_process.rb +794 -0
  379. data/test/plugin_helper/test_compat_parameters.rb +353 -0
  380. data/test/plugin_helper/test_event_emitter.rb +51 -0
  381. data/test/plugin_helper/test_event_loop.rb +52 -0
  382. data/test/plugin_helper/test_extract.rb +194 -0
  383. data/test/plugin_helper/test_formatter.rb +255 -0
  384. data/test/plugin_helper/test_http_server_helper.rb +205 -0
  385. data/test/plugin_helper/test_inject.rb +519 -0
  386. data/test/plugin_helper/test_parser.rb +264 -0
  387. data/test/plugin_helper/test_record_accessor.rb +197 -0
  388. data/test/plugin_helper/test_retry_state.rb +442 -0
  389. data/test/plugin_helper/test_server.rb +1714 -0
  390. data/test/plugin_helper/test_storage.rb +542 -0
  391. data/test/plugin_helper/test_thread.rb +164 -0
  392. data/test/plugin_helper/test_timer.rb +132 -0
  393. data/test/scripts/exec_script.rb +0 -6
  394. data/test/scripts/fluent/plugin/formatter1/formatter_test1.rb +7 -0
  395. data/test/scripts/fluent/plugin/formatter2/formatter_test2.rb +7 -0
  396. data/test/scripts/fluent/plugin/out_test.rb +23 -15
  397. data/test/scripts/fluent/plugin/out_test2.rb +80 -0
  398. data/test/test_clock.rb +164 -0
  399. data/test/test_config.rb +16 -7
  400. data/test/test_configdsl.rb +2 -2
  401. data/test/test_event.rb +360 -13
  402. data/test/test_event_router.rb +108 -11
  403. data/test/test_event_time.rb +199 -0
  404. data/test/test_filter.rb +48 -6
  405. data/test/test_formatter.rb +11 -391
  406. data/test/test_input.rb +1 -1
  407. data/test/test_log.rb +591 -31
  408. data/test/test_mixin.rb +1 -1
  409. data/test/test_output.rb +121 -185
  410. data/test/test_plugin.rb +251 -0
  411. data/test/test_plugin_classes.rb +177 -10
  412. data/test/test_plugin_helper.rb +81 -0
  413. data/test/test_plugin_id.rb +101 -0
  414. data/test/test_process.rb +8 -42
  415. data/test/test_root_agent.rb +766 -21
  416. data/test/test_supervisor.rb +481 -0
  417. data/test/test_test_drivers.rb +135 -0
  418. data/test/test_time_formatter.rb +282 -0
  419. data/test/test_time_parser.rb +231 -0
  420. data/test/test_unique_id.rb +47 -0
  421. metadata +454 -60
  422. data/COPYING +0 -14
  423. data/ChangeLog +0 -666
  424. data/lib/fluent/buffer.rb +0 -365
  425. data/lib/fluent/plugin/in_status.rb +0 -76
  426. data/test/plugin/test_in_status.rb +0 -38
  427. data/test/test_buffer.rb +0 -624
  428. data/test/test_parser.rb +0 -1305
@@ -14,215 +14,393 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
- require 'base64'
18
- require 'socket'
19
- require 'fileutils'
20
-
21
- require 'cool.io'
22
-
23
17
  require 'fluent/output'
24
18
  require 'fluent/config/error'
19
+ require 'fluent/clock'
20
+ require 'base64'
25
21
 
26
- module Fluent
27
- class ForwardOutputError < StandardError
28
- end
22
+ require 'fluent/compat/socket_util'
29
23
 
30
- class ForwardOutputResponseError < ForwardOutputError
31
- end
24
+ module Fluent::Plugin
25
+ class ForwardOutput < Output
26
+ class Error < StandardError; end
27
+ class NoNodesAvailable < Error; end
28
+ class ConnectionClosedError < Error; end
32
29
 
33
- class ForwardOutputConnectionClosedError < ForwardOutputError
34
- end
30
+ Fluent::Plugin.register_output('forward', self)
35
31
 
36
- class ForwardOutputACKTimeoutError < ForwardOutputResponseError
37
- end
32
+ helpers :socket, :server, :timer, :thread, :compat_parameters
38
33
 
39
- class ForwardOutput < ObjectBufferedOutput
40
- Plugin.register_output('forward', self)
34
+ LISTEN_PORT = 24224
41
35
 
42
- def initialize
43
- super
44
- require 'fluent/plugin/socket_util'
45
- @nodes = [] #=> [Node]
46
- end
36
+ desc 'The transport protocol.'
37
+ config_param :transport, :enum, list: [:tcp, :tls], default: :tcp
38
+ # TODO: TLS session cache/tickets
47
39
 
48
40
  desc 'The timeout time when sending event logs.'
49
41
  config_param :send_timeout, :time, default: 60
50
- desc 'The transport protocol to use for heartbeats.(udp,tcp,none)'
51
- config_param :heartbeat_type, default: :udp do |val|
52
- case val.downcase
53
- when 'tcp'
54
- :tcp
55
- when 'udp'
56
- :udp
57
- when 'none'
58
- :none
59
- else
60
- raise ConfigError, "forward output heartbeat type should be 'tcp', 'udp', or 'none'"
61
- end
62
- end
42
+ desc 'The timeout time for socket connect'
43
+ config_param :connect_timeout, :time, default: nil
44
+ # TODO: add linger_timeout, recv_timeout
45
+
46
+ desc 'The protocol to use for heartbeats (default is the same with "transport").'
47
+ config_param :heartbeat_type, :enum, list: [:transport, :tcp, :udp, :none], default: :transport
63
48
  desc 'The interval of the heartbeat packer.'
64
49
  config_param :heartbeat_interval, :time, default: 1
65
50
  desc 'The wait time before accepting a server fault recovery.'
66
51
  config_param :recover_wait, :time, default: 10
67
52
  desc 'The hard timeout used to detect server failure.'
68
53
  config_param :hard_timeout, :time, default: 60
69
- desc 'Set TTL to expire DNS cache in seconds.'
70
- config_param :expire_dns_cache, :time, default: nil # 0 means disable cache
71
54
  desc 'The threshold parameter used to detect server faults.'
72
55
  config_param :phi_threshold, :integer, default: 16
73
56
  desc 'Use the "Phi accrual failure detector" to detect server failure.'
74
57
  config_param :phi_failure_detector, :bool, default: true
75
58
 
76
- # if any options added that requires extended forward api, fix @extend_internal_protocol
77
-
78
59
  desc 'Change the protocol to at-least-once.'
79
60
  config_param :require_ack_response, :bool, default: false # require in_forward to respond with ack
80
- desc 'This option is used when require_ack_response is true.'
81
- config_param :ack_response_timeout, :time, default: 190 # 0 means do not wait for ack responses
61
+
62
+ ## The reason of default value of :ack_response_timeout:
82
63
  # Linux default tcp_syn_retries is 5 (in many environment)
83
64
  # 3 + 6 + 12 + 24 + 48 + 96 -> 189 (sec)
65
+ desc 'This option is used when require_ack_response is true.'
66
+ config_param :ack_response_timeout, :time, default: 190
67
+
68
+ desc 'The interval while reading data from server'
69
+ config_param :read_interval_msec, :integer, default: 50 # 50ms
70
+ desc 'Reading data size from server'
71
+ config_param :read_length, :size, default: 512 # 512bytes
72
+
73
+ desc 'Set TTL to expire DNS cache in seconds.'
74
+ config_param :expire_dns_cache, :time, default: nil # 0 means disable cache
84
75
  desc 'Enable client-side DNS round robin.'
85
76
  config_param :dns_round_robin, :bool, default: false # heartbeat_type 'udp' is not available for this
86
77
 
78
+ desc 'Ignore DNS resolution and errors at startup time.'
79
+ config_param :ignore_network_errors_at_startup, :bool, default: false
80
+
81
+ desc 'Verify that a connection can be made with one of out_forward nodes at the time of startup.'
82
+ config_param :verify_connection_at_startup, :bool, default: false
83
+
84
+ desc 'Compress buffered data.'
85
+ config_param :compress, :enum, list: [:text, :gzip], default: :text
86
+
87
+ desc 'The default version of TLS transport.'
88
+ config_param :tls_version, :enum, list: Fluent::PluginHelper::Socket::TLS_SUPPORTED_VERSIONS, default: Fluent::PluginHelper::Socket::TLS_DEFAULT_VERSION
89
+ desc 'The cipher configuration of TLS transport.'
90
+ config_param :tls_ciphers, :string, default: Fluent::PluginHelper::Socket::CIPHERS_DEFAULT
91
+ desc 'Skip all verification of certificates or not.'
92
+ config_param :tls_insecure_mode, :bool, default: false
93
+ desc 'Allow self signed certificates or not.'
94
+ config_param :tls_allow_self_signed_cert, :bool, default: false
95
+ desc 'Verify hostname of servers and certificates or not in TLS transport.'
96
+ config_param :tls_verify_hostname, :bool, default: true
97
+ desc 'The additional CA certificate path for TLS.'
98
+ config_param :tls_ca_cert_path, :array, value_type: :string, default: nil
99
+ desc 'The additional certificate path for TLS.'
100
+ config_param :tls_cert_path, :array, value_type: :string, default: nil
101
+ desc 'The client certificate path for TLS.'
102
+ config_param :tls_client_cert_path, :string, default: nil
103
+ desc 'The client private key path for TLS.'
104
+ config_param :tls_client_private_key_path, :string, default: nil
105
+ desc 'The client private key passphrase for TLS.'
106
+ config_param :tls_client_private_key_passphrase, :string, default: nil, secret: true
107
+ desc "Enable keepalive connection."
108
+ config_param :keepalive, :bool, default: false
109
+ desc "Expired time of keepalive. Default value is nil, which means to keep connection as long as possible"
110
+ config_param :keepalive_timeout, :time, default: nil
111
+
112
+ config_section :security, required: false, multi: false do
113
+ desc 'The hostname'
114
+ config_param :self_hostname, :string
115
+ desc 'Shared key for authentication'
116
+ config_param :shared_key, :string, secret: true
117
+ end
118
+
119
+ config_section :server, param_name: :servers do
120
+ desc "The IP address or host name of the server."
121
+ config_param :host, :string
122
+ desc "The name of the server. Used for logging and certificate verification in TLS transport (when host is address)."
123
+ config_param :name, :string, default: nil
124
+ desc "The port number of the host."
125
+ config_param :port, :integer, default: LISTEN_PORT
126
+ desc "The shared key per server."
127
+ config_param :shared_key, :string, default: nil, secret: true
128
+ desc "The username for authentication."
129
+ config_param :username, :string, default: ''
130
+ desc "The password for authentication."
131
+ config_param :password, :string, default: '', secret: true
132
+ desc "Marks a node as the standby node for an Active-Standby model between Fluentd nodes."
133
+ config_param :standby, :bool, default: false
134
+ desc "The load balancing weight."
135
+ config_param :weight, :integer, default: 60
136
+ end
137
+
87
138
  attr_reader :nodes
88
139
 
89
- config_param :port, :integer, default: DEFAULT_LISTEN_PORT, deprecated: "User <server> host xxx </server> instead."
90
- config_param :host, :string, default: nil, deprecated: "Use <server> port xxx </server> instead."
140
+ config_param :port, :integer, default: LISTEN_PORT, obsoleted: "User <server> section instead."
141
+ config_param :host, :string, default: nil, obsoleted: "Use <server> section instead."
91
142
 
92
- attr_accessor :extend_internal_protocol
143
+ config_section :buffer do
144
+ config_set_default :chunk_keys, ["tag"]
145
+ end
146
+
147
+ attr_reader :read_interval, :recover_sample_size
148
+
149
+ def initialize
150
+ super
151
+
152
+ @nodes = [] #=> [Node]
153
+ @loop = nil
154
+ @thread = nil
155
+
156
+ @usock = nil
157
+ @sock_ack_waiting = nil
158
+ @sock_ack_waiting_mutex = nil
159
+ @keep_alive_watcher_interval = 5 # TODO
160
+ end
93
161
 
94
162
  def configure(conf)
163
+ compat_parameters_convert(conf, :buffer, default_chunk_key: 'tag')
164
+
95
165
  super
96
166
 
97
- # backward compatibility
98
- if host = conf['host']
99
- port = conf['port']
100
- port = port ? port.to_i : DEFAULT_LISTEN_PORT
101
- e = conf.add_element('server')
102
- e['host'] = host
103
- e['port'] = port.to_s
167
+ unless @chunk_key_tag
168
+ raise Fluent::ConfigError, "buffer chunk key must include 'tag' for forward output"
104
169
  end
105
170
 
106
- recover_sample_size = @recover_wait / @heartbeat_interval
171
+ @read_interval = @read_interval_msec / 1000.0
172
+ @recover_sample_size = @recover_wait / @heartbeat_interval
107
173
 
108
- # add options here if any options addes which uses extended protocol
109
- @extend_internal_protocol = if @require_ack_response
110
- true
111
- else
112
- false
113
- end
174
+ if @heartbeat_type == :tcp
175
+ log.warn "'heartbeat_type tcp' is deprecated. use 'transport' instead."
176
+ @heartbeat_type = :transport
177
+ end
114
178
 
115
179
  if @dns_round_robin
116
180
  if @heartbeat_type == :udp
117
- raise ConfigError, "forward output heartbeat type must be 'tcp' or 'none' to use dns_round_robin option"
181
+ raise Fluent::ConfigError, "forward output heartbeat type must be 'transport' or 'none' to use dns_round_robin option"
118
182
  end
119
183
  end
120
184
 
121
- conf.elements.each {|e|
122
- next if e.name != "server"
123
-
124
- host = e['host']
125
- port = e['port']
126
- port = port ? port.to_i : DEFAULT_LISTEN_PORT
127
-
128
- weight = e['weight']
129
- weight = weight ? weight.to_i : 60
130
-
131
- standby = !!e['standby']
185
+ if @transport == :tls
186
+ # socket helper adds CA cert or signed certificate to same cert store internally so unify it in this place.
187
+ if @tls_cert_path && !@tls_cert_path.empty?
188
+ @tls_ca_cert_path = @tls_cert_path
189
+ end
190
+ if @tls_ca_cert_path && !@tls_ca_cert_path.empty?
191
+ @tls_ca_cert_path.each do |path|
192
+ raise Fluent::ConfigError, "specified cert path does not exist:#{path}" unless File.exist?(path)
193
+ raise Fluent::ConfigError, "specified cert path is not readable:#{path}" unless File.readable?(path)
194
+ end
195
+ end
132
196
 
133
- name = e['name']
134
- unless name
135
- name = "#{host}:#{port}"
197
+ if @tls_insecure_mode
198
+ log.warn "TLS transport is configured in insecure way"
199
+ @tls_verify_hostname = false
200
+ @tls_allow_self_signed_cert = true
136
201
  end
202
+ end
137
203
 
204
+ @servers.each do |server|
138
205
  failure = FailureDetector.new(@heartbeat_interval, @hard_timeout, Time.now.to_i.to_f)
206
+ name = server.name || "#{server.host}:#{server.port}"
139
207
 
140
- node_conf = NodeConfig.new(name, host, port, weight, standby, failure,
141
- @phi_threshold, recover_sample_size, @expire_dns_cache, @phi_failure_detector, @dns_round_robin)
142
-
208
+ log.info "adding forwarding server '#{name}'", host: server.host, port: server.port, weight: server.weight, plugin_id: plugin_id
143
209
  if @heartbeat_type == :none
144
- @nodes << NoneHeartbeatNode.new(log, node_conf)
210
+ @nodes << NoneHeartbeatNode.new(self, server, failure: failure, keepalive: @keepalive, keepalive_timeout: @keepalive_timeout)
145
211
  else
146
- @nodes << Node.new(log, node_conf)
212
+ node = Node.new(self, server, failure: failure, keepalive: @keepalive, keepalive_timeout: @keepalive_timeout)
213
+ begin
214
+ node.validate_host_resolution!
215
+ rescue => e
216
+ raise unless @ignore_network_errors_at_startup
217
+ log.warn "failed to resolve node name when configured", server: (server.name || server.host), error: e
218
+ node.disable!
219
+ end
220
+ @nodes << node
147
221
  end
148
- log.info "adding forwarding server '#{name}'", host: host, port: port, weight: weight, plugin_id: plugin_id
149
- }
222
+ end
223
+
224
+ unless @as_secondary
225
+ if @compress == :gzip && @buffer.compress == :text
226
+ @buffer.compress = :gzip
227
+ elsif @compress == :text && @buffer.compress == :gzip
228
+ log.info "buffer is compressed. If you also want to save the bandwidth of a network, Add `compress` configuration in <match>"
229
+ end
230
+ end
150
231
 
151
232
  if @nodes.empty?
152
- raise ConfigError, "forward output plugin requires at least one <server> is required"
233
+ raise Fluent::ConfigError, "forward output plugin requires at least one <server> is required"
234
+ end
235
+
236
+ if !@keepalive && @keepalive_timeout
237
+ log.warn('The value of keepalive_timeout is ignored. if you want to use keepalive, please add `keepalive true` to your conf.')
153
238
  end
239
+
240
+ raise Fluent::ConfigError, "ack_response_timeout must be a positive integer" if @ack_response_timeout < 1
241
+ end
242
+
243
+ def multi_workers_ready?
244
+ true
245
+ end
246
+
247
+ def prefer_delayed_commit
248
+ @require_ack_response
154
249
  end
155
250
 
156
251
  def start
157
252
  super
158
253
 
254
+ # Output#start sets @delayed_commit_timeout by @buffer_config.delayed_commit_timeout
255
+ # But it should be overwritten by ack_response_timeout to rollback chunks after timeout
256
+ if @ack_response_timeout && @delayed_commit_timeout != @ack_response_timeout
257
+ log.info "delayed_commit_timeout is overwritten by ack_response_timeout"
258
+ @delayed_commit_timeout = @ack_response_timeout + 2 # minimum ack_reader IO.select interval is 1s
259
+ end
260
+
159
261
  @rand_seed = Random.new.seed
160
262
  rebuild_weight_array
161
263
  @rr = 0
162
264
 
163
265
  unless @heartbeat_type == :none
164
- @loop = Coolio::Loop.new
165
-
166
266
  if @heartbeat_type == :udp
167
- # assuming all hosts use udp
168
- @usock = SocketUtil.create_udp_socket(@nodes.first.host)
169
- @usock.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
170
- @hb = HeartbeatHandler.new(@usock, method(:on_heartbeat))
171
- @loop.attach(@hb)
267
+ @usock = socket_create_udp(@nodes.first.host, @nodes.first.port, nonblock: true)
268
+ server_create_udp(:out_forward_heartbeat_receiver, 0, socket: @usock, max_bytes: @read_length) do |data, sock|
269
+ sockaddr = Socket.pack_sockaddr_in(sock.remote_port, sock.remote_host)
270
+ on_heartbeat(sockaddr, data)
271
+ end
172
272
  end
273
+ timer_execute(:out_forward_heartbeat_request, @heartbeat_interval, &method(:on_timer))
274
+ end
173
275
 
174
- @timer = HeartbeatRequestTimer.new(@heartbeat_interval, method(:on_timer))
175
- @loop.attach(@timer)
276
+ if @require_ack_response
277
+ @sock_ack_waiting_mutex = Mutex.new
278
+ @sock_ack_waiting = []
279
+ thread_create(:out_forward_receiving_ack, &method(:ack_reader))
280
+ end
176
281
 
177
- @thread = Thread.new(&method(:run))
282
+ if @verify_connection_at_startup
283
+ @nodes.each do |node|
284
+ begin
285
+ node.verify_connection
286
+ rescue StandardError => e
287
+ log.fatal "forward's connection setting error: #{e.message}"
288
+ raise Fluent::UnrecoverableError, e.message
289
+ end
290
+ end
178
291
  end
179
- end
180
292
 
181
- def shutdown
182
- @finished = true
183
- if @loop
184
- @loop.watchers.each {|w| w.detach }
185
- @loop.stop
293
+ if @keepalive && @keepalive_timeout
294
+ timer_execute(:out_forward_keep_alived_socket_watcher, @keep_alive_watcher_interval, &method(:on_purge_obsolete_socks))
186
295
  end
187
- @thread.join if @thread
188
- @usock.close if @usock
189
296
  end
190
297
 
191
- def run
192
- @loop.run if @loop
193
- rescue
194
- log.error "unexpected error", error: $!.to_s
195
- log.error_backtrace
298
+ def close
299
+ if @usock
300
+ # close socket and ignore errors: this socket will not be used anyway.
301
+ @usock.close rescue nil
302
+ end
303
+
304
+ if @keepalive && @keepalive_timeout
305
+ @nodes.each(&:clear)
306
+ end
307
+ super
196
308
  end
197
309
 
198
- def write_objects(tag, chunk)
310
+ def write(chunk)
199
311
  return if chunk.empty?
312
+ tag = chunk.metadata.tag
313
+ select_a_healthy_node{|node| node.send_data(tag, chunk) }
314
+ end
315
+
316
+ ACKWaitingSockInfo = Struct.new(:sock, :chunk_id, :chunk_id_base64, :node, :time, :timeout) do
317
+ def expired?(now)
318
+ time + timeout < now
319
+ end
320
+ end
200
321
 
322
+ def try_write(chunk)
323
+ log.trace "writing a chunk to destination", chunk_id: dump_unique_id_hex(chunk.unique_id)
324
+ if chunk.empty?
325
+ commit_write(chunk.unique_id)
326
+ return
327
+ end
328
+ tag = chunk.metadata.tag
329
+ sock, node = select_a_healthy_node{|n| n.send_data(tag, chunk) }
330
+ chunk_id_base64 = Base64.encode64(chunk.unique_id)
331
+ current_time = Fluent::Clock.now
332
+ info = ACKWaitingSockInfo.new(sock, chunk.unique_id, chunk_id_base64, node, current_time, @ack_response_timeout)
333
+ @sock_ack_waiting_mutex.synchronize do
334
+ @sock_ack_waiting << info
335
+ end
336
+ end
337
+
338
+ def select_a_healthy_node
201
339
  error = nil
202
340
 
203
341
  wlen = @weight_array.length
204
342
  wlen.times do
205
343
  @rr = (@rr + 1) % wlen
206
344
  node = @weight_array[@rr]
345
+ next unless node.available?
207
346
 
208
- if node.available?
209
- begin
210
- send_data(node, tag, chunk)
211
- return
212
- rescue
213
- # for load balancing during detecting crashed servers
214
- error = $! # use the latest error
215
- end
347
+ begin
348
+ ret = yield node
349
+ return ret, node
350
+ rescue
351
+ # for load balancing during detecting crashed servers
352
+ error = $! # use the latest error
216
353
  end
217
354
  end
218
355
 
219
- if error
220
- raise error
356
+ raise error if error
357
+ raise NoNodesAvailable, "no nodes are available"
358
+ end
359
+
360
+ def create_transfer_socket(host, port, hostname, &block)
361
+ case @transport
362
+ when :tls
363
+ socket_create_tls(
364
+ host, port,
365
+ version: @tls_version,
366
+ ciphers: @tls_ciphers,
367
+ insecure: @tls_insecure_mode,
368
+ verify_fqdn: @tls_verify_hostname,
369
+ fqdn: hostname,
370
+ allow_self_signed_cert: @tls_allow_self_signed_cert,
371
+ cert_paths: @tls_ca_cert_path,
372
+ cert_path: @tls_client_cert_path,
373
+ private_key_path: @tls_client_private_key_path,
374
+ private_key_passphrase: @tls_client_private_key_passphrase,
375
+
376
+ # Enabling SO_LINGER causes data loss on Windows
377
+ # https://github.com/fluent/fluentd/issues/1968
378
+ linger_timeout: Fluent.windows? ? nil : @send_timeout,
379
+ send_timeout: @send_timeout,
380
+ recv_timeout: @ack_response_timeout,
381
+ connect_timeout: @connect_timeout,
382
+ &block
383
+ )
384
+ when :tcp
385
+ socket_create_tcp(
386
+ host, port,
387
+ linger_timeout: @send_timeout,
388
+ send_timeout: @send_timeout,
389
+ recv_timeout: @ack_response_timeout,
390
+ connect_timeout: @connect_timeout,
391
+ &block
392
+ )
221
393
  else
222
- raise "no nodes are available" # TODO message
394
+ raise "BUG: unknown transport protocol #{@transport}"
223
395
  end
224
396
  end
225
397
 
398
+ # MessagePack FixArray length is 3
399
+ FORWARD_HEADER = [0x93].pack('C').freeze
400
+ def forward_header
401
+ FORWARD_HEADER
402
+ end
403
+
226
404
  private
227
405
 
228
406
  def rebuild_weight_array
@@ -250,6 +428,12 @@ module Fluent
250
428
  end
251
429
 
252
430
  weight_array = []
431
+ if regular_nodes.empty?
432
+ log.warn('No nodes are available')
433
+ @weight_array = weight_array
434
+ return @weight_array
435
+ end
436
+
253
437
  gcd = regular_nodes.map {|n| n.weight }.inject(0) {|r,w| r.gcd(w) }
254
438
  regular_nodes.each {|n|
255
439
  (n.weight / gcd).times {
@@ -267,205 +451,321 @@ module Fluent
267
451
  @weight_array = weight_array
268
452
  end
269
453
 
270
- # MessagePack FixArray length = 3 (if @extend_internal_protocol)
271
- # = 2 (else)
272
- FORWARD_HEADER = [0x92].pack('C').freeze
273
- FORWARD_HEADER_EXT = [0x93].pack('C').freeze
274
- def forward_header
275
- if @extend_internal_protocol
276
- FORWARD_HEADER_EXT
277
- else
278
- FORWARD_HEADER
454
+ def on_timer
455
+ @nodes.each {|n|
456
+ begin
457
+ log.trace "sending heartbeat", host: n.host, port: n.port, heartbeat_type: @heartbeat_type
458
+ n.usock = @usock if @usock
459
+ if n.send_heartbeat
460
+ rebuild_weight_array
461
+ end
462
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR, Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
463
+ log.debug "failed to send heartbeat packet", host: n.host, port: n.port, heartbeat_type: @heartbeat_type, error: e
464
+ rescue => e
465
+ log.debug "unexpected error happen during heartbeat", host: n.host, port: n.port, heartbeat_type: @heartbeat_type, error: e
466
+ end
467
+ if n.tick
468
+ rebuild_weight_array
469
+ end
470
+ }
471
+ end
472
+
473
+ def on_heartbeat(sockaddr, msg)
474
+ if node = @nodes.find {|n| n.sockaddr == sockaddr }
475
+ # log.trace "heartbeat arrived", name: node.name, host: node.host, port: node.port
476
+ if node.heartbeat
477
+ rebuild_weight_array
478
+ end
279
479
  end
280
480
  end
281
481
 
282
- #FORWARD_TCP_HEARTBEAT_DATA = FORWARD_HEADER + ''.to_msgpack + [].to_msgpack
283
- def send_heartbeat_tcp(node)
284
- sock = connect(node)
482
+ def on_purge_obsolete_socks
483
+ @nodes.each(&:purge_obsolete_socks)
484
+ end
485
+
486
+ # return chunk id to be committed
487
+ def read_ack_from_sock(sock, unpacker)
285
488
  begin
286
- opt = [1, @send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
287
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
288
- opt = [@send_timeout.to_i, 0].pack('L!L!') # struct timeval
289
- # don't send any data to not cause a compatibility problem
290
- #sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
291
- #sock.write FORWARD_TCP_HEARTBEAT_DATA
292
- node.heartbeat(true)
293
- ensure
294
- sock.close
489
+ raw_data = sock.instance_of?(Fluent::PluginHelper::Socket::WrappedSocket::TLS) ? sock.readpartial(@read_length) : sock.recv(@read_length)
490
+ rescue Errno::ECONNRESET, EOFError # ECONNRESET for #recv, #EOFError for #readpartial
491
+ raw_data = ""
492
+ end
493
+ info = @sock_ack_waiting_mutex.synchronize{ @sock_ack_waiting.find{|i| i.sock == sock } }
494
+
495
+ # When connection is closed by remote host, socket is ready to read and #recv returns an empty string that means EOF.
496
+ # If this happens we assume the data wasn't delivered and retry it.
497
+ if raw_data.empty?
498
+ log.warn "destination node closed the connection. regard it as unavailable.", host: info.node.host, port: info.node.port
499
+ info.node.disable!
500
+ rollback_write(info.chunk_id, update_retry: false)
501
+ return nil
502
+ else
503
+ unpacker.feed(raw_data)
504
+ res = unpacker.read
505
+ log.trace "getting response from destination", host: info.node.host, port: info.node.port, chunk_id: dump_unique_id_hex(info.chunk_id), response: res
506
+ if res['ack'] != info.chunk_id_base64
507
+ # Some errors may have occurred when ack and chunk id is different, so send the chunk again.
508
+ log.warn "ack in response and chunk id in sent data are different", chunk_id: dump_unique_id_hex(info.chunk_id), ack: res['ack']
509
+ rollback_write(info.chunk_id, update_retry: false)
510
+ return nil
511
+ else
512
+ log.trace "got a correct ack response", chunk_id: dump_unique_id_hex(info.chunk_id)
513
+ end
514
+ return info.chunk_id
515
+ end
516
+ rescue => e
517
+ log.error "unexpected error while receiving ack message", error: e
518
+ log.error_backtrace
519
+ ensure
520
+ if @keepalive
521
+ info.node.socket_cache.dec_ref_by_value(info.sock)
522
+ else
523
+ info.sock.close_write rescue nil
524
+ info.sock.close rescue nil
525
+ end
526
+
527
+ @sock_ack_waiting_mutex.synchronize do
528
+ @sock_ack_waiting.delete(info)
295
529
  end
296
530
  end
297
531
 
298
- def send_data(node, tag, chunk)
299
- sock = connect(node)
300
- begin
301
- opt = [1, @send_timeout.to_i].pack('I!I!') # { int l_onoff; int l_linger; }
302
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER, opt)
303
-
304
- opt = [@send_timeout.to_i, 0].pack('L!L!') # struct timeval
305
- sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, opt)
306
-
307
- # beginArray(2)
308
- sock.write forward_header
309
-
310
- # writeRaw(tag)
311
- sock.write tag.to_msgpack # tag
312
-
313
- # beginRaw(size)
314
- sz = chunk.size
315
- #if sz < 32
316
- # # FixRaw
317
- # sock.write [0xa0 | sz].pack('C')
318
- #elsif sz < 65536
319
- # # raw 16
320
- # sock.write [0xda, sz].pack('Cn')
321
- #else
322
- # raw 32
323
- sock.write [0xdb, sz].pack('CN')
324
- #end
325
-
326
- # writeRawBody(packed_es)
327
- chunk.write_to(sock)
328
-
329
- if @extend_internal_protocol
330
- option = {}
331
- option['chunk'] = Base64.encode64(chunk.unique_id) if @require_ack_response
332
- sock.write option.to_msgpack
333
-
334
- if @require_ack_response && @ack_response_timeout > 0
335
- # Waiting for a response here results in a decrease of throughput because a chunk queue is locked.
336
- # To avoid a decrease of troughput, it is necessary to prepare a list of chunks that wait for responses
337
- # and process them asynchronously.
338
- if IO.select([sock], nil, nil, @ack_response_timeout)
339
- raw_data = sock.recv(1024)
340
-
341
- # When connection is closed by remote host, socket is ready to read and #recv returns an empty string that means EOF.
342
- # If this happens we assume the data wasn't delivered and retry it.
343
- if raw_data.empty?
344
- @log.warn "node #{node.host}:#{node.port} closed the connection. regard it as unavailable."
345
- node.disable!
346
- raise ForwardOutputConnectionClosedError, "node #{node.host}:#{node.port} closed connection"
347
- else
348
- # Serialization type of the response is same as sent data.
349
- res = MessagePack.unpack(raw_data)
532
+ def ack_reader
533
+ select_interval = if @delayed_commit_timeout > 3
534
+ 1
535
+ else
536
+ @delayed_commit_timeout / 3.0
537
+ end
350
538
 
351
- if res['ack'] != option['chunk']
352
- # Some errors may have occured when ack and chunk id is different, so send the chunk again.
353
- raise ForwardOutputResponseError, "ack in response and chunk id in sent data are different"
539
+ unpacker = Fluent::Engine.msgpack_unpacker
540
+
541
+ while thread_current_running?
542
+ now = Fluent::Clock.now
543
+ sockets = []
544
+ begin
545
+ @sock_ack_waiting_mutex.synchronize do
546
+ new_list = []
547
+ @sock_ack_waiting.each do |info|
548
+ if info.expired?(now)
549
+ # There are 2 types of cases when no response has been received from socket:
550
+ # (1) the node does not support sending responses
551
+ # (2) the node does support sending response but responses have not arrived for some reasons.
552
+ log.warn "no response from node. regard it as unavailable.", host: info.node.host, port: info.node.port
553
+ info.node.disable!
554
+ if @keepalive
555
+ info.node.socket_cache.revoke_by_value(info.sock)
354
556
  end
557
+ info.sock.close rescue nil
558
+ rollback_write(info.chunk_id, update_retry: false)
559
+ else
560
+ sockets << info.sock
561
+ new_list << info
355
562
  end
356
-
357
- else
358
- # IO.select returns nil on timeout.
359
- # There are 2 types of cases when no response has been received:
360
- # (1) the node does not support sending responses
361
- # (2) the node does support sending response but responses have not arrived for some reasons.
362
- @log.warn "no response from #{node.host}:#{node.port}. regard it as unavailable."
363
- node.disable!
364
- raise ForwardOutputACKTimeoutError, "node #{node.host}:#{node.port} does not return ACK"
365
563
  end
564
+ @sock_ack_waiting = new_list
366
565
  end
367
- end
368
566
 
369
- node.heartbeat(false)
370
- return res # for test
371
- ensure
372
- sock.close
567
+ readable_sockets, _, _ = IO.select(sockets, nil, nil, select_interval)
568
+ next unless readable_sockets
569
+
570
+ readable_sockets.each do |sock|
571
+ chunk_id = read_ack_from_sock(sock, unpacker)
572
+ commit_write(chunk_id) if chunk_id
573
+ end
574
+ rescue => e
575
+ log.error "unexpected error while receiving ack", error: e
576
+ log.error_backtrace
577
+ end
373
578
  end
374
579
  end
375
580
 
376
- def connect(node)
377
- # TODO unix socket?
378
- TCPSocket.new(node.resolved_host, node.port)
379
- end
581
+ class Node
582
+ class SocketCache
583
+ TimedSocket = Struct.new(:timeout, :sock, :ref)
584
+
585
+ def initialize(timeout, log)
586
+ @log = log
587
+ @timeout = timeout
588
+ @active_socks = {}
589
+ @inactive_socks = {}
590
+ @mutex = Mutex.new
591
+ end
380
592
 
381
- class HeartbeatRequestTimer < Coolio::TimerWatcher
382
- def initialize(interval, callback)
383
- super(interval, true)
384
- @callback = callback
385
- end
593
+ def revoke(key = Thread.current.object_id)
594
+ @mutex.synchronize do
595
+ if @active_socks[key]
596
+ @inactive_socks[key] = @active_socks.delete(key)
597
+ @inactive_socks[key].ref = 0
598
+ end
599
+ end
600
+ end
386
601
 
387
- def on_timer
388
- @callback.call
389
- rescue
390
- # TODO log?
391
- end
392
- end
602
+ def clear
603
+ @mutex.synchronize do
604
+ @inactive_socks.values.each do |s|
605
+ s.sock.close rescue nil
606
+ end
607
+ @inactive_socks.clear
393
608
 
394
- def on_timer
395
- return if @finished
396
- @nodes.each {|n|
397
- if n.tick
398
- rebuild_weight_array
609
+ @active_socks.values.each do |s|
610
+ s.sock.close rescue nil
611
+ end
612
+ @active_socks.clear
613
+ end
399
614
  end
400
- begin
401
- #log.trace "sending heartbeat #{n.host}:#{n.port} on #{@heartbeat_type}"
402
- if @heartbeat_type == :tcp
403
- send_heartbeat_tcp(n)
404
- else
405
- @usock.send "\0", 0, Socket.pack_sockaddr_in(n.port, n.resolved_host)
615
+
616
+ def purge_obsolete_socks
617
+ @mutex.synchronize do
618
+ @inactive_socks.keys.each do |k|
619
+ # 0 means sockets stored in this class received all acks
620
+ if @inactive_socks[k].ref <= 0
621
+ s = @inactive_socks.delete(k)
622
+ s.sock.close rescue nil
623
+ @log.debug("purged obsolete socket #{s.sock}")
624
+ end
625
+ end
626
+
627
+ @active_socks.keys.each do |k|
628
+ if expired?(k) && @active_socks[k].ref <= 0
629
+ @inactive_socks[k] = @active_socks.delete(k)
630
+ end
631
+ end
406
632
  end
407
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR, Errno::ECONNREFUSED
408
- # TODO log
409
- log.debug "failed to send heartbeat packet to #{n.host}:#{n.port}", error: $!.to_s
410
633
  end
411
- }
412
- end
413
634
 
414
- class HeartbeatHandler < Coolio::IO
415
- def initialize(io, callback)
416
- super(io)
417
- @io = io
418
- @callback = callback
419
- end
635
+ # We expect that `yield` returns a unique object in this class
636
+ def fetch_or(key = Thread.current.object_id)
637
+ @mutex.synchronize do
638
+ unless @active_socks[key]
639
+ @active_socks[key] = TimedSocket.new(timeout, yield, 1)
640
+ @log.debug("connect new socket #{@active_socks[key]}")
641
+ return @active_socks[key].sock
642
+ end
420
643
 
421
- def on_readable
422
- begin
423
- msg, addr = @io.recvfrom(1024)
424
- rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR
425
- return
644
+ if expired?(key)
645
+ # Do not close this socket here in case of it will be used by other place (e.g. wait for receiving ack)
646
+ @inactive_socks[key] = @active_socks.delete(key)
647
+ @log.debug("connection #{@inactive_socks[key]} is expired. reconnecting...")
648
+ @active_socks[key] = TimedSocket.new(timeout, yield, 0)
649
+ end
650
+
651
+ @active_socks[key].ref += 1
652
+ @active_socks[key].sock
653
+ end
426
654
  end
427
- host = addr[3]
428
- port = addr[1]
429
- sockaddr = Socket.pack_sockaddr_in(port, host)
430
- @callback.call(sockaddr, msg)
431
- rescue
432
- # TODO log?
433
- end
434
- end
435
655
 
436
- def on_heartbeat(sockaddr, msg)
437
- port, host = Socket.unpack_sockaddr_in(sockaddr)
438
- if node = @nodes.find {|n| n.sockaddr == sockaddr }
439
- #log.trace "heartbeat from '#{node.name}'", :host=>node.host, :port=>node.port
440
- if node.heartbeat
441
- rebuild_weight_array
656
+ def dec_ref(key = Thread.current.object_id)
657
+ @mutex.synchronize do
658
+ if @active_socks[key]
659
+ @active_socks[key].ref -= 1
660
+ elsif @inactive_socks[key]
661
+ @inactive_socks[key].ref -= 1
662
+ else
663
+ @log.warn("Not found key for dec_ref: #{key}")
664
+ end
665
+ end
442
666
  end
443
- end
444
- end
445
667
 
446
- NodeConfig = Struct.new("NodeConfig", :name, :host, :port, :weight, :standby, :failure,
447
- :phi_threshold, :recover_sample_size, :expire_dns_cache, :phi_failure_detector, :dns_round_robin)
668
+ # This method is expected to be called in class which doesn't call #inc_ref
669
+ def dec_ref_by_value(val)
670
+ @mutex.synchronize do
671
+ sock = @active_socks.detect { |_, v| v.sock == val }
672
+ if sock
673
+ key = sock.first
674
+ @active_socks[key].ref -= 1
675
+ return
676
+ end
448
677
 
449
- class Node
450
- def initialize(log, conf)
451
- @log = log
452
- @conf = conf
453
- @name = @conf.name
454
- @host = @conf.host
455
- @port = @conf.port
456
- @weight = @conf.weight
457
- @failure = @conf.failure
678
+ sock = @inactive_socks.detect { |_, v| v.sock == val }
679
+ if sock
680
+ key = sock.first
681
+ @inactive_socks[key].ref -= 1
682
+ return
683
+ else
684
+ @log.warn("Not found key for dec_ref_by_value: #{key}")
685
+ end
686
+ end
687
+ end
688
+
689
+ # This method is expected to be called in class which doesn't call #fetch_or
690
+ def revoke_by_value(val)
691
+ @mutex.synchronize do
692
+ sock = @active_socks.detect { |_, v| v.sock == val }
693
+ if sock
694
+ key = sock.first
695
+ @inactive_socks[key] = @active_socks.delete(key)
696
+ @inactive_socks[key].ref = 0
697
+ else
698
+ @log.debug("Not found for revoke_by_value :#{val}")
699
+ end
700
+ end
701
+ end
702
+
703
+ private
704
+
705
+ def timeout
706
+ @timeout && Time.now + @timeout
707
+ end
708
+
709
+ # This method is thread unsafe
710
+ def expired?(key = Thread.current.object_id)
711
+ @active_socks[key].timeout ? @active_socks[key].timeout < Time.now : false
712
+ end
713
+ end
714
+
715
+ # @param keepalive [Bool]
716
+ # @param keepalive_timeout [Integer | nil]
717
+ def initialize(sender, server, failure:, keepalive: false, keepalive_timeout: nil)
718
+ @sender = sender
719
+ @log = sender.log
720
+ @compress = sender.compress
721
+
722
+ @name = server.name
723
+ @host = server.host
724
+ @port = server.port
725
+ @weight = server.weight
726
+ @standby = server.standby
727
+ @failure = failure
458
728
  @available = true
459
729
 
730
+ # @hostname is used for certificate verification & TLS SNI
731
+ host_is_hostname = !(IPAddr.new(@host) rescue false)
732
+ @hostname = case
733
+ when host_is_hostname then @host
734
+ when @name then @name
735
+ else nil
736
+ end
737
+
738
+ @usock = nil
739
+
740
+ @username = server.username
741
+ @password = server.password
742
+ @shared_key = server.shared_key || (sender.security && sender.security.shared_key) || ""
743
+ @shared_key_salt = generate_salt
744
+
745
+ @unpacker = Fluent::Engine.msgpack_unpacker
746
+
460
747
  @resolved_host = nil
461
748
  @resolved_time = 0
462
- resolved_host # check dns
749
+ @resolved_once = false
750
+
751
+ @keepalive = keepalive
752
+ if @keepalive
753
+ @socket_cache = SocketCache.new(keepalive_timeout, @log)
754
+ end
463
755
  end
464
756
 
465
- attr_reader :conf
466
- attr_reader :name, :host, :port, :weight
757
+ attr_accessor :usock
758
+
759
+ attr_reader :name, :host, :port, :weight, :standby, :state
467
760
  attr_reader :sockaddr # used by on_heartbeat
468
761
  attr_reader :failure, :available # for test
762
+ attr_reader :socket_cache # for ack
763
+
764
+ RequestInfo = Struct.new(:state, :shared_key_nonce, :auth)
765
+
766
+ def validate_host_resolution!
767
+ resolved_host
768
+ end
469
769
 
470
770
  def available?
471
771
  @available
@@ -476,33 +776,173 @@ module Fluent
476
776
  end
477
777
 
478
778
  def standby?
479
- @conf.standby
779
+ @standby
780
+ end
781
+
782
+ def verify_connection
783
+ connect do |sock, ri|
784
+ if ri.state != :established
785
+ establish_connection(sock, ri)
786
+ raise if ri.state != :established
787
+ end
788
+ end
789
+ end
790
+
791
+ def establish_connection(sock, ri)
792
+ while available? && ri.state != :established
793
+ begin
794
+ # TODO: On Ruby 2.2 or earlier, read_nonblock doesn't work expectedly.
795
+ # We need rewrite around here using new socket/server plugin helper.
796
+ buf = sock.read_nonblock(@sender.read_length)
797
+ if buf.empty?
798
+ sleep @sender.read_interval
799
+ next
800
+ end
801
+ @unpacker.feed_each(buf) do |data|
802
+ on_read(sock, ri, data)
803
+ end
804
+ rescue IO::WaitReadable
805
+ # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, it is extended by IO::WaitReadable.
806
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying read_nonblock.
807
+ # https//docs.ruby-lang.org/en/2.3.0/IO.html#method-i-read_nonblock
808
+ sleep @sender.read_interval unless ri.state == :established
809
+ rescue SystemCallError => e
810
+ @log.warn "disconnected by error", host: @host, port: @port, error: e
811
+ disable!
812
+ break
813
+ rescue EOFError
814
+ @log.warn "disconnected", host: @host, port: @port
815
+ disable!
816
+ break
817
+ end
818
+ end
819
+ end
820
+
821
+ def send_data_actual(sock, tag, chunk)
822
+ unless available?
823
+ raise ConnectionClosedError, "failed to establish connection with node #{@name}"
824
+ end
825
+
826
+ option = { 'size' => chunk.size, 'compressed' => @compress }
827
+ option['chunk'] = Base64.encode64(chunk.unique_id) if @sender.require_ack_response
828
+
829
+ # https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#packedforward-mode
830
+ # out_forward always uses str32 type for entries.
831
+ # str16 can store only 64kbytes, and it should be much smaller than buffer chunk size.
832
+
833
+ tag = tag.dup.force_encoding(Encoding::UTF_8)
834
+
835
+ sock.write @sender.forward_header # array, size=3
836
+ sock.write tag.to_msgpack # 1. tag: String (str)
837
+ chunk.open(compressed: @compress) do |chunk_io|
838
+ entries = [0xdb, chunk_io.size].pack('CN')
839
+ sock.write entries.force_encoding(Encoding::UTF_8) # 2. entries: String (str32)
840
+ IO.copy_stream(chunk_io, sock) # writeRawBody(packed_es)
841
+ end
842
+ sock.write option.to_msgpack # 3. option: Hash(map)
843
+
844
+ # TODO: use bin32 for non-utf8 content(entries) when old msgpack-ruby (0.5.x or earlier) not supported
845
+ end
846
+
847
+ def send_data(tag, chunk)
848
+ sock, ri = connect
849
+ if ri.state != :established
850
+ establish_connection(sock, ri)
851
+ end
852
+
853
+ begin
854
+ send_data_actual(sock, tag, chunk)
855
+ rescue
856
+ if @keepalive
857
+ @socket_cache.revoke
858
+ else
859
+ sock.close rescue nil
860
+ end
861
+ raise
862
+ end
863
+
864
+ if @sender.require_ack_response
865
+ return sock # to read ACK from socket
866
+ end
867
+
868
+ if @keepalive
869
+ @socket_cache.dec_ref
870
+ else
871
+ sock.close_write rescue nil
872
+ sock.close rescue nil
873
+ end
874
+ heartbeat(false)
875
+ nil
876
+ end
877
+
878
+ def clear
879
+ @keepalive && @socket_cache.clear
880
+ end
881
+
882
+ def purge_obsolete_socks
883
+ unless @keepalive
884
+ raise "Don not call this method without keepalive option"
885
+ end
886
+ @socket_cache.purge_obsolete_socks
887
+ end
888
+
889
+ # FORWARD_TCP_HEARTBEAT_DATA = FORWARD_HEADER + ''.to_msgpack + [].to_msgpack
890
+ def send_heartbeat
891
+ begin
892
+ dest_addr = resolved_host
893
+ @resolved_once = true
894
+ rescue ::SocketError => e
895
+ if !@resolved_once && @sender.ignore_network_errors_at_startup
896
+ @log.warn "failed to resolve node name in heartbeating", server: @name || @host, error: e
897
+ return
898
+ end
899
+ raise
900
+ end
901
+
902
+ case @sender.heartbeat_type
903
+ when :transport
904
+ connect(dest_addr) do |sock|
905
+ ## don't send any data to not cause a compatibility problem
906
+ # sock.write FORWARD_TCP_HEARTBEAT_DATA
907
+
908
+ # successful tcp connection establishment is considered as valid heartbeat.
909
+ # When heartbeat is succeeded after detached, return true. It rebuilds weight array.
910
+ heartbeat(true)
911
+ end
912
+ when :udp
913
+ @usock.send "\0", 0, Socket.pack_sockaddr_in(@port, resolved_host)
914
+ nil
915
+ when :none # :none doesn't use this class
916
+ raise "BUG: heartbeat_type none must not use Node"
917
+ else
918
+ raise "BUG: unknown heartbeat_type '#{@sender.heartbeat_type}'"
919
+ end
480
920
  end
481
921
 
482
922
  def resolved_host
483
- case @conf.expire_dns_cache
923
+ case @sender.expire_dns_cache
484
924
  when 0
485
925
  # cache is disabled
486
- return resolve_dns!
926
+ resolve_dns!
487
927
 
488
928
  when nil
489
929
  # persistent cache
490
- return @resolved_host ||= resolve_dns!
930
+ @resolved_host ||= resolve_dns!
491
931
 
492
932
  else
493
- now = Engine.now
933
+ now = Fluent::Engine.now
494
934
  rh = @resolved_host
495
- if !rh || now - @resolved_time >= @conf.expire_dns_cache
935
+ if !rh || now - @resolved_time >= @sender.expire_dns_cache
496
936
  rh = @resolved_host = resolve_dns!
497
937
  @resolved_time = now
498
938
  end
499
- return rh
939
+ rh
500
940
  end
501
941
  end
502
942
 
503
943
  def resolve_dns!
504
944
  addrinfo_list = Socket.getaddrinfo(@host, @port, nil, Socket::SOCK_STREAM)
505
- addrinfo = @conf.dns_round_robin ? addrinfo_list.sample : addrinfo_list.first
945
+ addrinfo = @sender.dns_round_robin ? addrinfo_list.sample : addrinfo_list.first
506
946
  @sockaddr = Socket.pack_sockaddr_in(addrinfo[1], addrinfo[3]) # used by on_heartbeat
507
947
  addrinfo[3]
508
948
  end
@@ -525,35 +965,152 @@ module Fluent
525
965
  return true
526
966
  end
527
967
 
528
- if @conf.phi_failure_detector
968
+ if @sender.phi_failure_detector
529
969
  phi = @failure.phi(now)
530
- #$log.trace "phi '#{@name}'", :host=>@host, :port=>@port, :phi=>phi
531
- if phi > @conf.phi_threshold
532
- @log.warn "detached forwarding server '#{@name}'", host: @host, port: @port, phi: phi
970
+ if phi > @sender.phi_threshold
971
+ @log.warn "detached forwarding server '#{@name}'", host: @host, port: @port, phi: phi, phi_threshold: @sender.phi_threshold
533
972
  @available = false
534
973
  @resolved_host = nil # expire cached host
535
974
  @failure.clear
536
975
  return true
537
976
  end
538
977
  end
539
- return false
978
+ false
540
979
  end
541
980
 
542
981
  def heartbeat(detect=true)
543
982
  now = Time.now.to_f
544
983
  @failure.add(now)
545
- #@log.trace "heartbeat from '#{@name}'", :host=>@host, :port=>@port, :available=>@available, :sample_size=>@failure.sample_size
546
- if detect && !@available && @failure.sample_size > @conf.recover_sample_size
984
+ if detect && !@available && @failure.sample_size > @sender.recover_sample_size
547
985
  @available = true
548
986
  @log.warn "recovered forwarding server '#{@name}'", host: @host, port: @port
549
- return true
987
+ true
550
988
  else
551
- return nil
989
+ nil
552
990
  end
553
991
  end
554
992
 
555
- def to_msgpack(out = '')
556
- [@host, @port, @weight, @available].to_msgpack(out)
993
+ def generate_salt
994
+ SecureRandom.hex(16)
995
+ end
996
+
997
+ def check_helo(ri, message)
998
+ @log.debug "checking helo"
999
+ # ['HELO', options(hash)]
1000
+ unless message.size == 2 && message[0] == 'HELO'
1001
+ return false
1002
+ end
1003
+ opts = message[1] || {}
1004
+ # make shared_key_check failed (instead of error) if protocol version mismatch exist
1005
+ ri.shared_key_nonce = opts['nonce'] || ''
1006
+ ri.auth = opts['auth'] || ''
1007
+ true
1008
+ end
1009
+
1010
+ def generate_ping(ri)
1011
+ @log.debug "generating ping"
1012
+ # ['PING', self_hostname, sharedkey\_salt, sha512\_hex(sharedkey\_salt + self_hostname + nonce + shared_key),
1013
+ # username || '', sha512\_hex(auth\_salt + username + password) || '']
1014
+ shared_key_hexdigest = Digest::SHA512.new.update(@shared_key_salt)
1015
+ .update(@sender.security.self_hostname)
1016
+ .update(ri.shared_key_nonce)
1017
+ .update(@shared_key)
1018
+ .hexdigest
1019
+ ping = ['PING', @sender.security.self_hostname, @shared_key_salt, shared_key_hexdigest]
1020
+ if !ri.auth.empty?
1021
+ password_hexdigest = Digest::SHA512.new.update(ri.auth).update(@username).update(@password).hexdigest
1022
+ ping.push(@username, password_hexdigest)
1023
+ else
1024
+ ping.push('','')
1025
+ end
1026
+ ping
1027
+ end
1028
+
1029
+ def check_pong(ri, message)
1030
+ @log.debug "checking pong"
1031
+ # ['PONG', bool(authentication result), 'reason if authentication failed',
1032
+ # self_hostname, sha512\_hex(salt + self_hostname + nonce + sharedkey)]
1033
+ unless message.size == 5 && message[0] == 'PONG'
1034
+ return false, 'invalid format for PONG message'
1035
+ end
1036
+ _pong, auth_result, reason, hostname, shared_key_hexdigest = message
1037
+
1038
+ unless auth_result
1039
+ return false, 'authentication failed: ' + reason
1040
+ end
1041
+
1042
+ if hostname == @sender.security.self_hostname
1043
+ return false, 'same hostname between input and output: invalid configuration'
1044
+ end
1045
+
1046
+ clientside = Digest::SHA512.new.update(@shared_key_salt).update(hostname).update(ri.shared_key_nonce).update(@shared_key).hexdigest
1047
+ unless shared_key_hexdigest == clientside
1048
+ return false, 'shared key mismatch'
1049
+ end
1050
+
1051
+ return true, nil
1052
+ end
1053
+
1054
+ def on_read(sock, ri, data)
1055
+ @log.trace __callee__
1056
+
1057
+ case ri.state
1058
+ when :helo
1059
+ unless check_helo(ri, data)
1060
+ @log.warn "received invalid helo message from #{@name}"
1061
+ disable! # shutdown
1062
+ return
1063
+ end
1064
+ sock.write(generate_ping(ri).to_msgpack)
1065
+ ri.state = :pingpong
1066
+ when :pingpong
1067
+ succeeded, reason = check_pong(ri, data)
1068
+ unless succeeded
1069
+ @log.warn "connection refused to #{@name || @host}: #{reason}"
1070
+ disable! # shutdown
1071
+ return
1072
+ end
1073
+ ri.state = :established
1074
+ @log.debug "connection established", host: @host, port: @port
1075
+ else
1076
+ raise "BUG: unknown session state: #{ri.state}"
1077
+ end
1078
+ end
1079
+
1080
+ private
1081
+
1082
+ def connect(host = nil)
1083
+ socket, request_info =
1084
+ if @keepalive
1085
+ ri = RequestInfo.new(:established)
1086
+ sock = @socket_cache.fetch_or do
1087
+ s = @sender.create_transfer_socket(host || resolved_host, port, @hostname)
1088
+ ri = RequestInfo.new(@sender.security ? :helo : :established) # overwrite if new connection
1089
+ s
1090
+ end
1091
+ [sock, ri]
1092
+ else
1093
+ @log.debug('connect new socket')
1094
+ [@sender.create_transfer_socket(host || resolved_host, port, @hostname), RequestInfo.new(@sender.security ? :helo : :established)]
1095
+ end
1096
+
1097
+ if block_given?
1098
+ ret = nil
1099
+ begin
1100
+ ret = yield(socket, request_info)
1101
+ rescue
1102
+ @socket_cache.revoke if @keepalive
1103
+ raise
1104
+ else
1105
+ @socket_cache.dec_ref if @keepalive
1106
+ ensure
1107
+ socket.close unless @keepalive
1108
+ end
1109
+
1110
+ ret
1111
+ else
1112
+ [socket, request_info]
1113
+ end
557
1114
  end
558
1115
  end
559
1116
 
@@ -634,33 +1191,5 @@ module Fluent
634
1191
  @last = 0
635
1192
  end
636
1193
  end
637
-
638
- ## TODO
639
- #class RPC
640
- # def initialize(this)
641
- # @this = this
642
- # end
643
- #
644
- # def list_nodes
645
- # @this.nodes
646
- # end
647
- #
648
- # def list_fault_nodes
649
- # list_nodes.select {|n| !n.available? }
650
- # end
651
- #
652
- # def list_available_nodes
653
- # list_nodes.select {|n| n.available? }
654
- # end
655
- #
656
- # def add_node(name, host, port, weight)
657
- # end
658
- #
659
- # def recover_node(host, port)
660
- # end
661
- #
662
- # def remove_node(host, port)
663
- # end
664
- #end
665
1194
  end
666
1195
  end