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
@@ -1,604 +1,967 @@
1
- # -*- coding: utf-8 -*-
2
1
  require_relative '../helper'
3
- require 'fluent/test'
4
2
  require 'fluent/plugin/buf_file'
3
+ require 'fluent/plugin/output'
4
+ require 'fluent/unique_id'
5
+ require 'fluent/system_config'
6
+ require 'fluent/env'
5
7
 
6
- require 'fileutils'
7
-
8
- require 'stringio'
9
8
  require 'msgpack'
10
9
 
11
- module FluentFileBufferTest
12
- class FileBufferChunkTest < Test::Unit::TestCase
13
- BUF_FILE_TMPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp', 'buf_file_chunk'))
14
-
15
- def setup
16
- if Dir.exists? BUF_FILE_TMPDIR
17
- FileUtils.remove_entry_secure BUF_FILE_TMPDIR
18
- end
19
- FileUtils.mkdir_p BUF_FILE_TMPDIR
10
+ module FluentPluginFileBufferTest
11
+ class DummyOutputPlugin < Fluent::Plugin::Output
12
+ Fluent::Plugin.register_output('buffer_file_test_output', self)
13
+ config_section :buffer do
14
+ config_set_default :@type, 'file'
20
15
  end
21
-
22
- def bufpath(unique, link=false)
23
- File.join(BUF_FILE_TMPDIR, unique + '.log' + (link ? '.link' : ''))
16
+ def multi_workers_ready?
17
+ true
24
18
  end
25
-
26
- def filebufferchunk(key, unique, opts={})
27
- Fluent::FileBufferChunk.new(key, bufpath(unique), unique, opts[:mode] || "a+", opts[:symlink])
19
+ def write(chunk)
20
+ # drop
28
21
  end
22
+ end
23
+ end
29
24
 
30
- def test_init
31
- chunk = filebufferchunk('key', 'init1')
32
- assert_equal 'key', chunk.key
33
- assert_equal 'init1', chunk.unique_id
34
- assert_equal bufpath('init1'), chunk.path
25
+ class FileBufferTest < Test::Unit::TestCase
26
+ def metadata(timekey: nil, tag: nil, variables: nil)
27
+ Fluent::Plugin::Buffer::Metadata.new(timekey, tag, variables)
28
+ end
35
29
 
36
- chunk.close # size==0, then, unlinked
30
+ def write_metadata(path, chunk_id, metadata, size, ctime, mtime)
31
+ metadata = {
32
+ timekey: metadata.timekey, tag: metadata.tag, variables: metadata.variables,
33
+ id: chunk_id,
34
+ s: size,
35
+ c: ctime,
36
+ m: mtime,
37
+ }
38
+ File.open(path, 'wb') do |f|
39
+ f.write metadata.to_msgpack
40
+ end
41
+ end
37
42
 
38
- symlink_path = bufpath('init2', true)
43
+ sub_test_case 'non configured buffer plugin instance' do
44
+ setup do
45
+ Fluent::Test.setup
39
46
 
40
- chunk = filebufferchunk('key2', 'init2', symlink: symlink_path)
41
- assert_equal 'key2', chunk.key
42
- assert_equal 'init2', chunk.unique_id
43
- assert File.exists?(symlink_path) && File.symlink?(symlink_path)
47
+ @dir = File.expand_path('../../tmp/buffer_file_dir', __FILE__)
48
+ FileUtils.rm_rf @dir
49
+ FileUtils.mkdir_p @dir
50
+ end
44
51
 
45
- chunk.close # unlink
52
+ test 'path should include * normally' do
53
+ d = FluentPluginFileBufferTest::DummyOutputPlugin.new
54
+ p = Fluent::Plugin::FileBuffer.new
55
+ p.owner = d
56
+ p.configure(config_element('buffer', '', {'path' => File.join(@dir, 'buffer.*.file')}))
57
+ assert_equal File.join(@dir, 'buffer.*.file'), p.path
58
+ end
46
59
 
47
- assert File.symlink?(symlink_path)
48
- File.unlink(symlink_path)
60
+ test 'existing directory will be used with additional default file name' do
61
+ d = FluentPluginFileBufferTest::DummyOutputPlugin.new
62
+ p = Fluent::Plugin::FileBuffer.new
63
+ p.owner = d
64
+ p.configure(config_element('buffer', '', {'path' => @dir}))
65
+ assert_equal File.join(@dir, 'buffer.*.log'), p.path
49
66
  end
50
67
 
51
- def test_buffer_chunk_interface
52
- chunk = filebufferchunk('key', 'interface1')
68
+ test 'unexisting path without * handled as directory' do
69
+ d = FluentPluginFileBufferTest::DummyOutputPlugin.new
70
+ p = Fluent::Plugin::FileBuffer.new
71
+ p.owner = d
72
+ p.configure(config_element('buffer', '', {'path' => File.join(@dir, 'buffer')}))
73
+ assert_equal File.join(@dir, 'buffer', 'buffer.*.log'), p.path
74
+ end
75
+ end
53
76
 
54
- assert chunk.respond_to?(:empty?)
55
- assert chunk.respond_to?(:<<)
56
- assert chunk.respond_to?(:size)
57
- assert chunk.respond_to?(:close)
58
- assert chunk.respond_to?(:purge)
59
- assert chunk.respond_to?(:read)
60
- assert chunk.respond_to?(:open)
61
- assert chunk.respond_to?(:write_to)
62
- assert chunk.respond_to?(:msgpack_each)
77
+ sub_test_case 'buffer configurations and workers' do
78
+ setup do
79
+ @bufdir = File.expand_path('../../tmp/buffer_file', __FILE__)
80
+ FileUtils.rm_rf @bufdir
81
+ Fluent::Test.setup
63
82
 
64
- chunk.close
83
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
84
+ @p = Fluent::Plugin::FileBuffer.new
85
+ @p.owner = @d
65
86
  end
66
87
 
67
- def test_empty?
68
- chunk = filebufferchunk('e1', 'empty1')
69
- assert chunk.empty?
70
- chunk.close
88
+ test 'raise error if configured path is of existing file' do
89
+ @bufpath = File.join(@bufdir, 'buf')
90
+ FileUtils.mkdir_p @bufdir
91
+ File.open(@bufpath, 'w'){|f| } # create and close the file
92
+ assert File.exist?(@bufpath)
93
+ assert File.file?(@bufpath)
71
94
 
72
- open(bufpath('empty2'), 'w') do |file|
73
- file.write "data1\ndata2\n"
95
+ buf_conf = config_element('buffer', '', {'path' => @bufpath})
96
+ assert_raise Fluent::ConfigError.new("Plugin 'file' does not support multi workers configuration (Fluent::Plugin::FileBuffer)") do
97
+ Fluent::SystemConfig.overwrite_system_config('workers' => 4) do
98
+ @d.configure(config_element('ROOT', '', {'@id' => 'dummy_output_with_buf'}, [buf_conf]))
99
+ end
74
100
  end
75
- chunk = filebufferchunk('e2', 'empty2')
76
- assert !(chunk.empty?)
77
- chunk.close
78
101
  end
79
102
 
80
- def test_append_close_purge
81
- chunk = filebufferchunk('a1', 'append1')
82
- assert chunk.empty?
83
-
84
- test_data1 = ("1" * 9 + "\n" + "2" * 9 + "\n").force_encoding('ASCII-8BIT')
85
- test_data2 = "日本語Japanese\n".force_encoding('UTF-8')
86
- chunk << test_data1
87
- chunk << test_data2
88
- assert_equal 38, chunk.size
89
- chunk.close
90
-
91
- assert File.exists?(bufpath('append1'))
92
-
93
- chunk = filebufferchunk('a1', 'append1', mode: 'r')
94
- test_data = test_data1.force_encoding('ASCII-8BIT') + test_data2.force_encoding('ASCII-8BIT')
95
-
96
- #### TODO: This assertion currently fails. Oops.
97
- # FileBuffer#read does NOT do force_encoding('ASCII-8BIT'). So encoding of output string instance are 'UTF-8'.
98
- # I think it is a kind of bug, but fixing it may break some behavior of buf_file. So I cannot be sure to fix it just now.
99
- #
100
- # assert_equal test_data, chunk.read
101
-
102
- chunk.purge
103
-
104
- assert !(File.exists?(bufpath('append1')))
103
+ test 'raise error if fluentd is configured to use file path pattern and multi workers' do
104
+ @bufpath = File.join(@bufdir, 'testbuf.*.log')
105
+ buf_conf = config_element('buffer', '', {'path' => @bufpath})
106
+ assert_raise Fluent::ConfigError.new("Plugin 'file' does not support multi workers configuration (Fluent::Plugin::FileBuffer)") do
107
+ Fluent::SystemConfig.overwrite_system_config('workers' => 4) do
108
+ @d.configure(config_element('ROOT', '', {'@id' => 'dummy_output_with_buf'}, [buf_conf]))
109
+ end
110
+ end
105
111
  end
106
112
 
107
- def test_empty_chunk_key # for BufferedOutput#emit
108
- chunk = filebufferchunk('', 'append1')
109
- assert chunk.empty?
110
-
111
- test_data1 = ("1" * 9 + "\n" + "2" * 9 + "\n").force_encoding('ASCII-8BIT')
112
- test_data2 = "日本語Japanese\n".force_encoding('UTF-8')
113
- chunk << test_data1
114
- chunk << test_data2
115
- assert_equal 38, chunk.size
116
- chunk.close
113
+ test 'enables multi worker configuration with unexisting directory path' do
114
+ assert_false File.exist?(@bufdir)
115
+ buf_conf = config_element('buffer', '', {'path' => @bufdir})
116
+ assert_nothing_raised do
117
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir, 'workers' => 4) do
118
+ @d.configure(config_element('ROOT', '', {}, [buf_conf]))
119
+ end
120
+ end
117
121
  end
118
122
 
119
- def test_read
120
- chunk = filebufferchunk('r1', 'read1')
121
- assert chunk.empty?
122
-
123
- d1 = "abcde" * 200 + "\n"
124
- chunk << d1
125
- d2 = "12345" * 200 + "\n"
126
- chunk << d2
127
- assert_equal (d1.size + d2.size), chunk.size
128
-
129
- read_data = chunk.read
130
- assert_equal (d1 + d2), read_data
131
-
132
- chunk.purge
123
+ test 'enables multi worker configuration with existing directory path' do
124
+ FileUtils.mkdir_p @bufdir
125
+ buf_conf = config_element('buffer', '', {'path' => @bufdir})
126
+ assert_nothing_raised do
127
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir, 'workers' => 4) do
128
+ @d.configure(config_element('ROOT', '', {}, [buf_conf]))
129
+ end
130
+ end
133
131
  end
134
132
 
135
- def test_open
136
- chunk = filebufferchunk('o1', 'open1')
137
- assert chunk.empty?
138
-
139
- d1 = "abcde" * 200 + "\n"
140
- chunk << d1
141
- d2 = "12345" * 200 + "\n"
142
- chunk << d2
143
- assert_equal (d1.size + d2.size), chunk.size
133
+ test 'enables multi worker configuration with root dir' do
134
+ buf_conf = config_element('buffer', '')
135
+ assert_nothing_raised do
136
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @bufdir, 'workers' => 4) do
137
+ @d.configure(config_element('ROOT', '', {'@id' => 'dummy_output_with_buf'}, [buf_conf]))
138
+ end
139
+ end
140
+ end
141
+ end
144
142
 
145
- read_data = chunk.open do |io|
146
- io.read
143
+ sub_test_case 'buffer plugin configured only with path' do
144
+ setup do
145
+ @bufdir = File.expand_path('../../tmp/buffer_file', __FILE__)
146
+ @bufpath = File.join(@bufdir, 'testbuf.*.log')
147
+ FileUtils.rm_r @bufdir if File.exist?(@bufdir)
148
+
149
+ Fluent::Test.setup
150
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
151
+ @p = Fluent::Plugin::FileBuffer.new
152
+ @p.owner = @d
153
+ @p.configure(config_element('buffer', '', {'path' => @bufpath}))
154
+ @p.start
155
+ end
156
+
157
+ teardown do
158
+ if @p
159
+ @p.stop unless @p.stopped?
160
+ @p.before_shutdown unless @p.before_shutdown?
161
+ @p.shutdown unless @p.shutdown?
162
+ @p.after_shutdown unless @p.after_shutdown?
163
+ @p.close unless @p.closed?
164
+ @p.terminate unless @p.terminated?
165
+ end
166
+ if @bufdir
167
+ Dir.glob(File.join(@bufdir, '*')).each do |path|
168
+ next if ['.', '..'].include?(File.basename(path))
169
+ File.delete(path)
170
+ end
147
171
  end
148
- assert_equal (d1 + d2), read_data
172
+ end
149
173
 
150
- chunk.purge
174
+ test 'this is persistent plugin' do
175
+ assert @p.persistent?
151
176
  end
152
177
 
153
- def test_write_to
154
- chunk = filebufferchunk('w1', 'write1')
155
- assert chunk.empty?
178
+ test '#start creates directory for buffer chunks' do
179
+ plugin = Fluent::Plugin::FileBuffer.new
180
+ plugin.owner = @d
181
+ rand_num = rand(0..100)
182
+ bufpath = File.join(File.expand_path("../../tmp/buffer_file_#{rand_num}", __FILE__), 'testbuf.*.log')
183
+ bufdir = File.dirname(bufpath)
156
184
 
157
- d1 = "abcde" * 200 + "\n"
158
- chunk << d1
159
- d2 = "12345" * 200 + "\n"
160
- chunk << d2
161
- assert_equal (d1.size + d2.size), chunk.size
185
+ FileUtils.rm_r bufdir if File.exist?(bufdir)
186
+ assert !File.exist?(bufdir)
162
187
 
163
- dummy_dst = StringIO.new
188
+ plugin.configure(config_element('buffer', '', {'path' => bufpath}))
189
+ assert !File.exist?(bufdir)
164
190
 
165
- chunk.write_to(dummy_dst)
166
- assert_equal (d1 + d2), dummy_dst.string
191
+ plugin.start
192
+ assert File.exist?(bufdir)
193
+ assert{ File.stat(bufdir).mode.to_s(8).end_with?('755') }
167
194
 
168
- chunk.purge
195
+ plugin.stop; plugin.before_shutdown; plugin.shutdown; plugin.after_shutdown; plugin.close; plugin.terminate
196
+ FileUtils.rm_r bufdir
169
197
  end
170
198
 
171
- def test_msgpack_each
172
- chunk = filebufferchunk('m1', 'msgpack1')
173
- assert chunk.empty?
199
+ test '#start creates directory for buffer chunks with specified permission' do
200
+ omit "NTFS doesn't support UNIX like permissions" if Fluent.windows?
174
201
 
175
- d0 = MessagePack.pack([[1, "foo"], [2, "bar"], [3, "baz"]])
176
- d1 = MessagePack.pack({"key1" => "value1", "key2" => "value2"})
177
- d2 = MessagePack.pack("string1")
178
- d3 = MessagePack.pack(1)
179
- d4 = MessagePack.pack(nil)
180
- chunk << d0
181
- chunk << d1
182
- chunk << d2
183
- chunk << d3
184
- chunk << d4
202
+ plugin = Fluent::Plugin::FileBuffer.new
203
+ plugin.owner = @d
204
+ rand_num = rand(0..100)
205
+ bufpath = File.join(File.expand_path("../../tmp/buffer_file_#{rand_num}", __FILE__), 'testbuf.*.log')
206
+ bufdir = File.dirname(bufpath)
185
207
 
186
- store = []
187
- chunk.msgpack_each do |data|
188
- store << data
189
- end
208
+ FileUtils.rm_r bufdir if File.exist?(bufdir)
209
+ assert !File.exist?(bufdir)
190
210
 
191
- assert_equal 5, store.size
192
- assert_equal [[1, "foo"], [2, "bar"], [3, "baz"]], store[0]
193
- assert_equal({"key1" => "value1", "key2" => "value2"}, store[1])
194
- assert_equal "string1", store[2]
195
- assert_equal 1, store[3]
196
- assert_equal nil, store[4]
211
+ plugin.configure(config_element('buffer', '', {'path' => bufpath, 'dir_permission' => '0700'}))
212
+ assert !File.exist?(bufdir)
197
213
 
198
- chunk.purge
199
- end
214
+ plugin.start
215
+ assert File.exist?(bufdir)
216
+ assert{ File.stat(bufdir).mode.to_s(8).end_with?('700') }
200
217
 
201
- def test_mv
202
- chunk = filebufferchunk('m1', 'move1')
203
- assert chunk.empty?
218
+ plugin.stop; plugin.before_shutdown; plugin.shutdown; plugin.after_shutdown; plugin.close; plugin.terminate
219
+ FileUtils.rm_r bufdir
220
+ end
204
221
 
205
- d1 = "abcde" * 200 + "\n"
206
- chunk << d1
207
- d2 = "12345" * 200 + "\n"
208
- chunk << d2
209
- assert_equal (d1.size + d2.size), chunk.size
222
+ test '#start creates directory for buffer chunks with specified permission via system config' do
223
+ omit "NTFS doesn't support UNIX like permissions" if Fluent.windows?
210
224
 
211
- assert_equal bufpath('move1'), chunk.path
225
+ sysconf = {'dir_permission' => '700'}
226
+ Fluent::SystemConfig.overwrite_system_config(sysconf) do
227
+ plugin = Fluent::Plugin::FileBuffer.new
228
+ plugin.owner = @d
229
+ rand_num = rand(0..100)
230
+ bufpath = File.join(File.expand_path("../../tmp/buffer_file_#{rand_num}", __FILE__), 'testbuf.*.log')
231
+ bufdir = File.dirname(bufpath)
212
232
 
213
- assert File.exists?( bufpath( 'move1' ) )
214
- assert !(File.exists?( bufpath( 'move2' ) ))
233
+ FileUtils.rm_r bufdir if File.exist?(bufdir)
234
+ assert !File.exist?(bufdir)
215
235
 
216
- chunk.mv(bufpath('move2'))
236
+ plugin.configure(config_element('buffer', '', {'path' => bufpath}))
237
+ assert !File.exist?(bufdir)
217
238
 
218
- assert !(File.exists?( bufpath( 'move1' ) ))
219
- assert File.exists?( bufpath( 'move2' ) )
239
+ plugin.start
240
+ assert File.exist?(bufdir)
241
+ assert{ File.stat(bufdir).mode.to_s(8).end_with?('700') }
220
242
 
221
- assert_equal bufpath('move2'), chunk.path
243
+ plugin.stop; plugin.before_shutdown; plugin.shutdown; plugin.after_shutdown; plugin.close; plugin.terminate
244
+ FileUtils.rm_r bufdir
245
+ end
246
+ end
222
247
 
223
- chunk.purge
248
+ test '#generate_chunk generates blank file chunk on path from unique_id of metadata' do
249
+ m1 = metadata()
250
+ c1 = @p.generate_chunk(m1)
251
+ assert c1.is_a? Fluent::Plugin::Buffer::FileChunk
252
+ assert_equal m1, c1.metadata
253
+ assert c1.empty?
254
+ assert_equal :unstaged, c1.state
255
+ assert_equal Fluent::Plugin::Buffer::FileChunk::FILE_PERMISSION, c1.permission
256
+ assert_equal @bufpath.gsub('.*.', ".b#{Fluent::UniqueId.hex(c1.unique_id)}."), c1.path
257
+ assert{ File.stat(c1.path).mode.to_s(8).end_with?('644') }
258
+
259
+ m2 = metadata(timekey: event_time('2016-04-17 11:15:00 -0700').to_i)
260
+ c2 = @p.generate_chunk(m2)
261
+ assert c2.is_a? Fluent::Plugin::Buffer::FileChunk
262
+ assert_equal m2, c2.metadata
263
+ assert c2.empty?
264
+ assert_equal :unstaged, c2.state
265
+ assert_equal Fluent::Plugin::Buffer::FileChunk::FILE_PERMISSION, c2.permission
266
+ assert_equal @bufpath.gsub('.*.', ".b#{Fluent::UniqueId.hex(c2.unique_id)}."), c2.path
267
+ assert{ File.stat(c2.path).mode.to_s(8).end_with?('644') }
268
+
269
+ c1.purge
270
+ c2.purge
271
+ end
272
+
273
+ test '#generate_chunk generates blank file chunk with specified permission' do
274
+ omit "NTFS doesn't support UNIX like permissions" if Fluent.windows?
275
+
276
+ plugin = Fluent::Plugin::FileBuffer.new
277
+ plugin.owner = @d
278
+ rand_num = rand(0..100)
279
+ bufpath = File.join(File.expand_path("../../tmp/buffer_file_#{rand_num}", __FILE__), 'testbuf.*.log')
280
+ bufdir = File.dirname(bufpath)
281
+
282
+ FileUtils.rm_r bufdir if File.exist?(bufdir)
283
+ assert !File.exist?(bufdir)
284
+
285
+ plugin.configure(config_element('buffer', '', {'path' => bufpath, 'file_permission' => '0600'}))
286
+ assert !File.exist?(bufdir)
287
+ plugin.start
288
+
289
+ m = metadata()
290
+ c = plugin.generate_chunk(m)
291
+ assert c.is_a? Fluent::Plugin::Buffer::FileChunk
292
+ assert_equal m, c.metadata
293
+ assert c.empty?
294
+ assert_equal :unstaged, c.state
295
+ assert_equal 0600, c.permission
296
+ assert_equal bufpath.gsub('.*.', ".b#{Fluent::UniqueId.hex(c.unique_id)}."), c.path
297
+ assert{ File.stat(c.path).mode.to_s(8).end_with?('600') }
298
+
299
+ c.purge
300
+
301
+ plugin.stop; plugin.before_shutdown; plugin.shutdown; plugin.after_shutdown; plugin.close; plugin.terminate
302
+ FileUtils.rm_r bufdir
224
303
  end
225
304
  end
226
305
 
227
- class FileBufferTest < Test::Unit::TestCase
228
- BUF_FILE_TMPDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'tmp', 'buf_file'))
229
-
230
- def setup
231
- if Dir.exists? BUF_FILE_TMPDIR
232
- FileUtils.remove_entry_secure BUF_FILE_TMPDIR
306
+ sub_test_case 'configured with system root directory and plugin @id' do
307
+ setup do
308
+ @root_dir = File.expand_path('../../tmp/buffer_file_root', __FILE__)
309
+ FileUtils.rm_rf @root_dir
310
+
311
+ Fluent::Test.setup
312
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
313
+ @p = Fluent::Plugin::FileBuffer.new
314
+ @p.owner = @d
315
+ Fluent::SystemConfig.overwrite_system_config('root_dir' => @root_dir) do
316
+ @d.configure(config_element('ROOT', '', {'@id' => 'dummy_output_with_buf'}))
317
+ @p.configure(config_element('buffer', ''))
233
318
  end
234
- FileUtils.mkdir_p BUF_FILE_TMPDIR
235
- end
236
-
237
- def bufpath(basename)
238
- File.join(BUF_FILE_TMPDIR, basename)
239
319
  end
240
320
 
241
- def filebuffer(key, unique, opts={})
242
- Fluent::FileBufferChunk.new(key, bufpath(unique), unique, opts[:mode] || "a+", opts[:symlink])
321
+ teardown do
322
+ if @p
323
+ @p.stop unless @p.stopped?
324
+ @p.before_shutdown unless @p.before_shutdown?
325
+ @p.shutdown unless @p.shutdown?
326
+ @p.after_shutdown unless @p.after_shutdown?
327
+ @p.close unless @p.closed?
328
+ @p.terminate unless @p.terminated?
329
+ end
243
330
  end
244
331
 
245
- def test_init_configure
246
- buf = Fluent::FileBuffer.new
247
-
248
- assert_raise(Fluent::ConfigError){ buf.configure({}) }
249
-
250
- buf.configure({'buffer_path' => bufpath('configure1.*.log')})
251
- assert_equal bufpath('configure1.*.log'), buf.buffer_path
252
- assert_equal nil, buf.symlink_path
253
- assert_equal false, buf.instance_eval{ @flush_at_shutdown }
254
-
255
- buf2 = Fluent::FileBuffer.new
332
+ test '#start creates directory for buffer chunks' do
333
+ expected_buffer_path = File.join(@root_dir, 'worker0', 'dummy_output_with_buf', 'buffer', 'buffer.*.log')
334
+ expected_buffer_dir = File.dirname(expected_buffer_path)
335
+ assert_equal expected_buffer_path, @p.path
336
+ assert_false Dir.exist?(expected_buffer_dir)
256
337
 
257
- # Same buffer_path value is rejected, not to overwrite exisitng buffer file.
258
- assert_raise(Fluent::ConfigError){ buf2.configure({'buffer_path' => bufpath('configure1.*.log')}) }
338
+ @p.start
259
339
 
260
- buf2.configure({'buffer_path' => bufpath('configure2.*.log'), 'flush_at_shutdown' => ''})
261
- assert_equal bufpath('configure2.*.log'), buf2.buffer_path
262
- assert_equal true, buf2.instance_eval{ @flush_at_shutdown }
340
+ assert Dir.exist?(expected_buffer_dir)
263
341
  end
342
+ end
264
343
 
265
- def test_configure_path_prefix_suffix
266
- # With '*' in path, prefix is the part before '*', suffix is the part after '*'
267
- buf = Fluent::FileBuffer.new
268
-
269
- path1 = bufpath('suffpref1.*.log')
270
- prefix1, suffix1 = path1.split('*', 2)
271
- buf.configure({'buffer_path' => path1})
272
- assert_equal prefix1, buf.instance_eval{ @buffer_path_prefix }
273
- assert_equal suffix1, buf.instance_eval{ @buffer_path_suffix }
274
-
275
- # Without '*', prefix is the string of whole path + '.', suffix is '.log'
276
- path2 = bufpath('suffpref2')
277
- buf.configure({'buffer_path' => path2})
278
- assert_equal path2 + '.', buf.instance_eval{ @buffer_path_prefix }
279
- assert_equal '.log', buf.instance_eval{ @buffer_path_suffix }
344
+ sub_test_case 'there are no existing file chunks' do
345
+ setup do
346
+ @bufdir = File.expand_path('../../tmp/buffer_file', __FILE__)
347
+ @bufpath = File.join(@bufdir, 'testbuf.*.log')
348
+ FileUtils.rm_r @bufdir if File.exist?(@bufdir)
349
+
350
+ Fluent::Test.setup
351
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
352
+ @p = Fluent::Plugin::FileBuffer.new
353
+ @p.owner = @d
354
+ @p.configure(config_element('buffer', '', {'path' => @bufpath}))
355
+ @p.start
356
+ end
357
+ teardown do
358
+ if @p
359
+ @p.stop unless @p.stopped?
360
+ @p.before_shutdown unless @p.before_shutdown?
361
+ @p.shutdown unless @p.shutdown?
362
+ @p.after_shutdown unless @p.after_shutdown?
363
+ @p.close unless @p.closed?
364
+ @p.terminate unless @p.terminated?
365
+ end
366
+ if @bufdir
367
+ Dir.glob(File.join(@bufdir, '*')).each do |path|
368
+ next if ['.', '..'].include?(File.basename(path))
369
+ File.delete(path)
370
+ end
371
+ end
280
372
  end
281
373
 
282
- class DummyOutput
283
- attr_accessor :written
374
+ test '#resume returns empty buffer state' do
375
+ ary = @p.resume
376
+ assert_equal({}, ary[0])
377
+ assert_equal([], ary[1])
378
+ end
379
+ end
284
380
 
285
- def write(chunk)
286
- @written ||= []
287
- @written.push chunk
288
- "return value"
381
+ sub_test_case 'there are some existing file chunks' do
382
+ setup do
383
+ @bufdir = File.expand_path('../../tmp/buffer_file', __FILE__)
384
+ FileUtils.mkdir_p @bufdir unless File.exist?(@bufdir)
385
+
386
+ @c1id = Fluent::UniqueId.generate
387
+ p1 = File.join(@bufdir, "etest.q#{Fluent::UniqueId.hex(@c1id)}.log")
388
+ File.open(p1, 'wb') do |f|
389
+ f.write ["t1.test", event_time('2016-04-17 13:58:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
390
+ f.write ["t2.test", event_time('2016-04-17 13:58:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
391
+ f.write ["t3.test", event_time('2016-04-17 13:58:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
392
+ f.write ["t4.test", event_time('2016-04-17 13:58:22 -0700').to_i, {"message" => "yay"}].to_json + "\n"
393
+ end
394
+ write_metadata(
395
+ p1 + '.meta', @c1id, metadata(timekey: event_time('2016-04-17 13:58:00 -0700').to_i),
396
+ 4, event_time('2016-04-17 13:58:00 -0700').to_i, event_time('2016-04-17 13:58:22 -0700').to_i
397
+ )
398
+
399
+ @c2id = Fluent::UniqueId.generate
400
+ p2 = File.join(@bufdir, "etest.q#{Fluent::UniqueId.hex(@c2id)}.log")
401
+ File.open(p2, 'wb') do |f|
402
+ f.write ["t1.test", event_time('2016-04-17 13:59:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
403
+ f.write ["t2.test", event_time('2016-04-17 13:59:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
404
+ f.write ["t3.test", event_time('2016-04-17 13:59:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
405
+ end
406
+ write_metadata(
407
+ p2 + '.meta', @c2id, metadata(timekey: event_time('2016-04-17 13:59:00 -0700').to_i),
408
+ 3, event_time('2016-04-17 13:59:00 -0700').to_i, event_time('2016-04-17 13:59:23 -0700').to_i
409
+ )
410
+
411
+ @c3id = Fluent::UniqueId.generate
412
+ p3 = File.join(@bufdir, "etest.b#{Fluent::UniqueId.hex(@c3id)}.log")
413
+ File.open(p3, 'wb') do |f|
414
+ f.write ["t1.test", event_time('2016-04-17 14:00:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
415
+ f.write ["t2.test", event_time('2016-04-17 14:00:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
416
+ f.write ["t3.test", event_time('2016-04-17 14:00:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
417
+ f.write ["t4.test", event_time('2016-04-17 14:00:28 -0700').to_i, {"message" => "yay"}].to_json + "\n"
418
+ end
419
+ write_metadata(
420
+ p3 + '.meta', @c3id, metadata(timekey: event_time('2016-04-17 14:00:00 -0700').to_i),
421
+ 4, event_time('2016-04-17 14:00:00 -0700').to_i, event_time('2016-04-17 14:00:28 -0700').to_i
422
+ )
423
+
424
+ @c4id = Fluent::UniqueId.generate
425
+ p4 = File.join(@bufdir, "etest.b#{Fluent::UniqueId.hex(@c4id)}.log")
426
+ File.open(p4, 'wb') do |f|
427
+ f.write ["t1.test", event_time('2016-04-17 14:01:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
428
+ f.write ["t2.test", event_time('2016-04-17 14:01:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
429
+ f.write ["t3.test", event_time('2016-04-17 14:01:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
430
+ end
431
+ write_metadata(
432
+ p4 + '.meta', @c4id, metadata(timekey: event_time('2016-04-17 14:01:00 -0700').to_i),
433
+ 3, event_time('2016-04-17 14:01:00 -0700').to_i, event_time('2016-04-17 14:01:25 -0700').to_i
434
+ )
435
+
436
+ @bufpath = File.join(@bufdir, 'etest.*.log')
437
+
438
+ Fluent::Test.setup
439
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
440
+ @p = Fluent::Plugin::FileBuffer.new
441
+ @p.owner = @d
442
+ @p.configure(config_element('buffer', '', {'path' => @bufpath}))
443
+ @p.start
444
+ end
445
+
446
+ teardown do
447
+ if @p
448
+ @p.stop unless @p.stopped?
449
+ @p.before_shutdown unless @p.before_shutdown?
450
+ @p.shutdown unless @p.shutdown?
451
+ @p.after_shutdown unless @p.after_shutdown?
452
+ @p.close unless @p.closed?
453
+ @p.terminate unless @p.terminated?
454
+ end
455
+ if @bufdir
456
+ Dir.glob(File.join(@bufdir, '*')).each do |path|
457
+ next if ['.', '..'].include?(File.basename(path))
458
+ File.delete(path)
459
+ end
289
460
  end
290
461
  end
291
462
 
292
- def test_encode_key
293
- buf = Fluent::FileBuffer.new
294
- safe_chars = '-_.abcdefgxyzABCDEFGXYZ0123456789'
295
- assert_equal safe_chars, buf.send(:encode_key, safe_chars)
296
- unsafe_chars = '-_.abcdefgxyzABCDEFGXYZ0123456789 ~/*()'
297
- assert_equal safe_chars + '%20%7E%2F%2A%28%29', buf.send(:encode_key, unsafe_chars)
298
- end
463
+ test '#resume returns staged/queued chunks with metadata' do
464
+ assert_equal 2, @p.stage.size
465
+ assert_equal 2, @p.queue.size
299
466
 
300
- def test_decode_key
301
- buf = Fluent::FileBuffer.new
302
- safe_chars = '-_.abcdefgxyzABCDEFGXYZ0123456789'
303
- assert_equal safe_chars, buf.send(:decode_key, safe_chars)
304
- unsafe_chars = '-_.abcdefgxyzABCDEFGXYZ0123456789 ~/*()'
305
- assert_equal unsafe_chars, buf.send(:decode_key, safe_chars + '%20%7E%2F%2A%28%29')
467
+ stage = @p.stage
306
468
 
307
- assert_equal safe_chars, buf.send(:decode_key, buf.send(:encode_key, safe_chars))
308
- assert_equal unsafe_chars, buf.send(:decode_key, buf.send(:encode_key, unsafe_chars))
469
+ m3 = metadata(timekey: event_time('2016-04-17 14:00:00 -0700').to_i)
470
+ assert_equal @c3id, stage[m3].unique_id
471
+ assert_equal 4, stage[m3].size
472
+ assert_equal :staged, stage[m3].state
473
+
474
+ m4 = metadata(timekey: event_time('2016-04-17 14:01:00 -0700').to_i)
475
+ assert_equal @c4id, stage[m4].unique_id
476
+ assert_equal 3, stage[m4].size
477
+ assert_equal :staged, stage[m4].state
309
478
  end
310
479
 
311
- def test_make_path
312
- buf = Fluent::FileBuffer.new
313
- buf.configure({'buffer_path' => bufpath('makepath.*.log')})
314
- prefix = buf.instance_eval{ @buffer_path_prefix }
315
- suffix = buf.instance_eval{ @buffer_path_suffix }
480
+ test '#resume returns queued chunks ordered by last modified time (FIFO)' do
481
+ assert_equal 2, @p.stage.size
482
+ assert_equal 2, @p.queue.size
316
483
 
317
- path,tsuffix = buf.send(:make_path, buf.send(:encode_key, 'foo bar'), 'b')
318
- assert path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.[bq][0-9a-f]+#{suffix}\Z/, "invalid format:#{path}"
319
- assert tsuffix =~ /\A[0-9a-f]+\Z/, "invalid hexadecimal:#{tsuffix}"
484
+ queue = @p.queue
320
485
 
321
- path,tsuffix = buf.send(:make_path, buf.send(:encode_key, 'baz 123'), 'q')
322
- assert path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.[bq][0-9a-f]+#{suffix}\Z/, "invalid format:#{path}"
323
- assert tsuffix =~ /\A[0-9a-f]+\Z/, "invalid hexadecimal:#{tsuffix}"
324
- end
486
+ assert{ queue[0].modified_at < queue[1].modified_at }
325
487
 
326
- def test_tsuffix_to_unique_id
327
- buf = Fluent::FileBuffer.new
328
- # why *2 ? frsyuki said "I forgot why completely."
329
- assert_equal "\xFF\xFF\xFF\xFF".force_encoding('ASCII-8BIT'), buf.send(:tsuffix_to_unique_id, 'ffff')
330
- assert_equal "\x88\x00\xFF\x00\x11\xEE\x88\x00\xFF\x00\x11\xEE".force_encoding('ASCII-8BIT'), buf.send(:tsuffix_to_unique_id, '8800ff0011ee')
331
- end
488
+ assert_equal @c1id, queue[0].unique_id
489
+ assert_equal :queued, queue[0].state
490
+ assert_equal event_time('2016-04-17 13:58:00 -0700').to_i, queue[0].metadata.timekey
491
+ assert_nil queue[0].metadata.tag
492
+ assert_nil queue[0].metadata.variables
493
+ assert_equal Time.parse('2016-04-17 13:58:00 -0700').localtime, queue[0].created_at
494
+ assert_equal Time.parse('2016-04-17 13:58:22 -0700').localtime, queue[0].modified_at
495
+ assert_equal 4, queue[0].size
332
496
 
333
- def test_start_makes_parent_directories
334
- buf = Fluent::FileBuffer.new
335
- buf.configure({'buffer_path' => bufpath('start/base.*.log')})
336
- parent_dirname = File.dirname(buf.instance_eval{ @buffer_path_prefix })
337
- assert !(Dir.exists?(parent_dirname))
338
- buf.start
339
- assert Dir.exists?(parent_dirname)
497
+ assert_equal @c2id, queue[1].unique_id
498
+ assert_equal :queued, queue[1].state
499
+ assert_equal event_time('2016-04-17 13:59:00 -0700').to_i, queue[1].metadata.timekey
500
+ assert_nil queue[1].metadata.tag
501
+ assert_nil queue[1].metadata.variables
502
+ assert_equal Time.parse('2016-04-17 13:59:00 -0700').localtime, queue[1].created_at
503
+ assert_equal Time.parse('2016-04-17 13:59:23 -0700').localtime, queue[1].modified_at
504
+ assert_equal 3, queue[1].size
340
505
  end
506
+ end
341
507
 
342
- def test_new_chunk
343
- buf = Fluent::FileBuffer.new
344
- buf.configure({'buffer_path' => bufpath('new_chunk_1')})
345
- prefix = buf.instance_eval{ @buffer_path_prefix }
346
- suffix = buf.instance_eval{ @buffer_path_suffix }
508
+ sub_test_case 'there are some existing file chunks, both in specified path and per-worker directory under specified path, configured as multi workers' do
509
+ setup do
510
+ @bufdir = File.expand_path('../../tmp/buffer_file/path', __FILE__)
511
+ @worker0_dir = File.join(@bufdir, "worker0")
512
+ @worker1_dir = File.join(@bufdir, "worker1")
513
+ FileUtils.rm_rf @bufdir
514
+ FileUtils.mkdir_p @worker0_dir
515
+ FileUtils.mkdir_p @worker1_dir
516
+
517
+ @bufdir_chunk_1 = Fluent::UniqueId.generate
518
+ bc1 = File.join(@bufdir, "buffer.q#{Fluent::UniqueId.hex(@bufdir_chunk_1)}.log")
519
+ File.open(bc1, 'wb') do |f|
520
+ f.write ["t1.test", event_time('2016-04-17 13:58:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
521
+ f.write ["t2.test", event_time('2016-04-17 13:58:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
522
+ f.write ["t3.test", event_time('2016-04-17 13:58:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
523
+ f.write ["t4.test", event_time('2016-04-17 13:58:22 -0700').to_i, {"message" => "yay"}].to_json + "\n"
524
+ end
525
+ write_metadata(
526
+ bc1 + '.meta', @bufdir_chunk_1, metadata(timekey: event_time('2016-04-17 13:58:00 -0700').to_i),
527
+ 4, event_time('2016-04-17 13:58:00 -0700').to_i, event_time('2016-04-17 13:58:22 -0700').to_i
528
+ )
529
+
530
+ @bufdir_chunk_2 = Fluent::UniqueId.generate
531
+ bc2 = File.join(@bufdir, "buffer.q#{Fluent::UniqueId.hex(@bufdir_chunk_2)}.log")
532
+ File.open(bc2, 'wb') do |f|
533
+ f.write ["t1.test", event_time('2016-04-17 13:58:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
534
+ f.write ["t2.test", event_time('2016-04-17 13:58:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
535
+ f.write ["t3.test", event_time('2016-04-17 13:58:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
536
+ f.write ["t4.test", event_time('2016-04-17 13:58:22 -0700').to_i, {"message" => "yay"}].to_json + "\n"
537
+ end
538
+ write_metadata(
539
+ bc2 + '.meta', @bufdir_chunk_2, metadata(timekey: event_time('2016-04-17 13:58:00 -0700').to_i),
540
+ 4, event_time('2016-04-17 13:58:00 -0700').to_i, event_time('2016-04-17 13:58:22 -0700').to_i
541
+ )
542
+
543
+ @worker_dir_chunk_1 = Fluent::UniqueId.generate
544
+ wc0_1 = File.join(@worker0_dir, "buffer.q#{Fluent::UniqueId.hex(@worker_dir_chunk_1)}.log")
545
+ wc1_1 = File.join(@worker1_dir, "buffer.q#{Fluent::UniqueId.hex(@worker_dir_chunk_1)}.log")
546
+ [wc0_1, wc1_1].each do |chunk_path|
547
+ File.open(chunk_path, 'wb') do |f|
548
+ f.write ["t1.test", event_time('2016-04-17 13:59:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
549
+ f.write ["t2.test", event_time('2016-04-17 13:59:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
550
+ f.write ["t3.test", event_time('2016-04-17 13:59:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
551
+ end
552
+ write_metadata(
553
+ chunk_path + '.meta', @worker_dir_chunk_1, metadata(timekey: event_time('2016-04-17 13:59:00 -0700').to_i),
554
+ 3, event_time('2016-04-17 13:59:00 -0700').to_i, event_time('2016-04-17 13:59:23 -0700').to_i
555
+ )
556
+ end
347
557
 
348
- chunk = buf.new_chunk('key1')
349
- assert chunk
350
- assert File.exists?(chunk.path)
351
- assert chunk.path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.b[0-9a-f]+#{suffix}\Z/, "path from new_chunk must be a 'b' buffer chunk"
352
- chunk.close
558
+ @worker_dir_chunk_2 = Fluent::UniqueId.generate
559
+ wc0_2 = File.join(@worker0_dir, "buffer.b#{Fluent::UniqueId.hex(@worker_dir_chunk_2)}.log")
560
+ wc1_2 = File.join(@worker1_dir, "buffer.b#{Fluent::UniqueId.hex(@worker_dir_chunk_2)}.log")
561
+ [wc0_2, wc1_2].each do |chunk_path|
562
+ File.open(chunk_path, 'wb') do |f|
563
+ f.write ["t1.test", event_time('2016-04-17 14:00:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
564
+ f.write ["t2.test", event_time('2016-04-17 14:00:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
565
+ f.write ["t3.test", event_time('2016-04-17 14:00:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
566
+ f.write ["t4.test", event_time('2016-04-17 14:00:28 -0700').to_i, {"message" => "yay"}].to_json + "\n"
567
+ end
568
+ write_metadata(
569
+ chunk_path + '.meta', @worker_dir_chunk_2, metadata(timekey: event_time('2016-04-17 14:00:00 -0700').to_i),
570
+ 4, event_time('2016-04-17 14:00:00 -0700').to_i, event_time('2016-04-17 14:00:28 -0700').to_i
571
+ )
572
+ end
573
+
574
+ @worker_dir_chunk_3 = Fluent::UniqueId.generate
575
+ wc0_3 = File.join(@worker0_dir, "buffer.b#{Fluent::UniqueId.hex(@worker_dir_chunk_3)}.log")
576
+ wc1_3 = File.join(@worker1_dir, "buffer.b#{Fluent::UniqueId.hex(@worker_dir_chunk_3)}.log")
577
+ [wc0_3, wc1_3].each do |chunk_path|
578
+ File.open(chunk_path, 'wb') do |f|
579
+ f.write ["t1.test", event_time('2016-04-17 14:01:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
580
+ f.write ["t2.test", event_time('2016-04-17 14:01:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
581
+ f.write ["t3.test", event_time('2016-04-17 14:01:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
582
+ end
583
+ write_metadata(
584
+ chunk_path + '.meta', @worker_dir_chunk_3, metadata(timekey: event_time('2016-04-17 14:01:00 -0700').to_i),
585
+ 3, event_time('2016-04-17 14:01:00 -0700').to_i, event_time('2016-04-17 14:01:25 -0700').to_i
586
+ )
587
+ end
588
+
589
+ Fluent::Test.setup
353
590
  end
354
591
 
355
- def test_chunk_identifier_in_path
356
- buf1 = Fluent::FileBuffer.new
357
- buf1.configure({'buffer_path' => bufpath('chunkid1')})
358
- prefix1 = buf1.instance_eval{ @buffer_path_prefix }
359
- suffix1 = buf1.instance_eval{ @buffer_path_suffix }
592
+ teardown do
593
+ if @p
594
+ @p.stop unless @p.stopped?
595
+ @p.before_shutdown unless @p.before_shutdown?
596
+ @p.shutdown unless @p.shutdown?
597
+ @p.after_shutdown unless @p.after_shutdown?
598
+ @p.close unless @p.closed?
599
+ @p.terminate unless @p.terminated?
600
+ end
601
+ end
360
602
 
361
- chunk1 = buf1.new_chunk('key1')
362
- assert_equal chunk1.path, prefix1 + buf1.chunk_identifier_in_path(chunk1.path) + suffix1
603
+ test 'worker(id=0) #resume returns staged/queued chunks with metadata, not only in worker dir, including the directory specified by path' do
604
+ ENV['SERVERENGINE_WORKER_ID'] = '0'
363
605
 
364
- buf2 = Fluent::FileBuffer.new
365
- buf2.configure({'buffer_path' => bufpath('chunkid2')})
366
- prefix2 = buf2.instance_eval{ @buffer_path_prefix }
367
- suffix2 = buf2.instance_eval{ @buffer_path_suffix }
606
+ buf_conf = config_element('buffer', '', {'path' => @bufdir})
607
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
608
+ with_worker_config(workers: 2, worker_id: 0) do
609
+ @d.configure(config_element('output', '', {}, [buf_conf]))
610
+ end
368
611
 
369
- chunk2 = buf2.new_chunk('key2')
370
- assert_equal chunk2.path, prefix2 + buf2.chunk_identifier_in_path(chunk2.path) + suffix2
371
- end
612
+ @d.start
613
+ @p = @d.buffer
372
614
 
373
- def test_enqueue_moves_chunk_from_b_to_q
374
- buf = Fluent::FileBuffer.new
375
- buf.configure({'buffer_path' => bufpath('enqueue1')})
376
- prefix = buf.instance_eval{ @buffer_path_prefix }
377
- suffix = buf.instance_eval{ @buffer_path_suffix }
615
+ assert_equal 2, @p.stage.size
616
+ assert_equal 3, @p.queue.size
378
617
 
379
- chunk = buf.new_chunk('key1')
380
- chunk << "data1\ndata2\n"
618
+ stage = @p.stage
381
619
 
382
- assert chunk
383
- old_path = chunk.path.dup
384
- assert File.exists?(chunk.path)
385
- assert chunk.path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.b[0-9a-f]+#{suffix}\Z/, "path from new_chunk must be a 'b' buffer chunk"
620
+ m1 = metadata(timekey: event_time('2016-04-17 14:00:00 -0700').to_i)
621
+ assert_equal @worker_dir_chunk_2, stage[m1].unique_id
622
+ assert_equal 4, stage[m1].size
623
+ assert_equal :staged, stage[m1].state
386
624
 
387
- buf.enqueue(chunk)
625
+ m2 = metadata(timekey: event_time('2016-04-17 14:01:00 -0700').to_i)
626
+ assert_equal @worker_dir_chunk_3, stage[m2].unique_id
627
+ assert_equal 3, stage[m2].size
628
+ assert_equal :staged, stage[m2].state
388
629
 
389
- assert chunk
390
- assert File.exists?(chunk.path)
391
- assert !(File.exists?(old_path))
392
- assert chunk.path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.q[0-9a-f]+#{suffix}\Z/, "enqueued chunk's path must be a 'q' buffer chunk"
630
+ queue = @p.queue
393
631
 
394
- data = chunk.read
395
- assert "data1\ndata2\n", data
632
+ assert_equal [@bufdir_chunk_1, @bufdir_chunk_2, @worker_dir_chunk_1].sort, queue.map(&:unique_id).sort
633
+ assert_equal [3, 4, 4], queue.map(&:size).sort
634
+ assert_equal [:queued, :queued, :queued], queue.map(&:state)
396
635
  end
397
636
 
398
- # empty chunk keys are used w/ BufferedOutput
399
- # * ObjectBufferedOutput's keys are tag
400
- # * TimeSlicedOutput's keys are time_key
401
- def test_enqueue_chunk_with_empty_key
402
- buf = Fluent::FileBuffer.new
403
- buf.configure({'buffer_path' => bufpath('enqueue2')})
404
- prefix = buf.instance_eval{ @buffer_path_prefix }
405
- suffix = buf.instance_eval{ @buffer_path_suffix }
637
+ test 'worker(id=1) #resume returns staged/queued chunks with metadata, only in worker dir' do
638
+ buf_conf = config_element('buffer', '', {'path' => @bufdir})
639
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
640
+ with_worker_config(workers: 2, worker_id: 1) do
641
+ @d.configure(config_element('output', '', {}, [buf_conf]))
642
+ end
406
643
 
407
- chunk = buf.new_chunk('')
408
- chunk << "data1\ndata2\n"
644
+ @d.start
645
+ @p = @d.buffer
409
646
 
410
- assert chunk
411
- old_path = chunk.path.dup
412
- assert File.exists?(chunk.path)
413
- # chunk key is empty
414
- assert chunk.path =~ /\A#{prefix}\.b[0-9a-f]+#{suffix}\Z/, "path from new_chunk must be a 'b' buffer chunk"
647
+ assert_equal 2, @p.stage.size
648
+ assert_equal 1, @p.queue.size
415
649
 
416
- buf.enqueue(chunk)
650
+ stage = @p.stage
417
651
 
418
- assert chunk
419
- assert File.exists?(chunk.path)
420
- assert !(File.exists?(old_path))
421
- # chunk key is empty
422
- assert chunk.path =~ /\A#{prefix}\.q[0-9a-f]+#{suffix}\Z/, "enqueued chunk's path must be a 'q' buffer chunk"
652
+ m1 = metadata(timekey: event_time('2016-04-17 14:00:00 -0700').to_i)
653
+ assert_equal @worker_dir_chunk_2, stage[m1].unique_id
654
+ assert_equal 4, stage[m1].size
655
+ assert_equal :staged, stage[m1].state
423
656
 
424
- data = chunk.read
425
- assert "data1\ndata2\n", data
426
- end
657
+ m2 = metadata(timekey: event_time('2016-04-17 14:01:00 -0700').to_i)
658
+ assert_equal @worker_dir_chunk_3, stage[m2].unique_id
659
+ assert_equal 3, stage[m2].size
660
+ assert_equal :staged, stage[m2].state
427
661
 
428
- def test_before_shutdown_without_flush_at_shutdown
429
- buf = Fluent::FileBuffer.new
430
- buf.configure({'buffer_path' => bufpath('before_shutdown1')})
431
- buf.start
662
+ queue = @p.queue
432
663
 
433
- # before_shutdown does nothing
434
-
435
- c1 = [ buf.new_chunk('k0'), buf.new_chunk('k1'), buf.new_chunk('k2'), buf.new_chunk('k3') ]
436
- c2 = [ buf.new_chunk('q0'), buf.new_chunk('q1') ]
664
+ assert_equal @worker_dir_chunk_1, queue[0].unique_id
665
+ assert_equal 3, queue[0].size
666
+ assert_equal :queued, queue[0].state
667
+ end
668
+ end
437
669
 
438
- buf.instance_eval do
439
- @map = {
440
- 'k0' => c1[0], 'k1' => c1[1], 'k2' => c1[2], 'k3' => c1[3],
441
- 'q0' => c2[0], 'q1' => c2[1]
442
- }
670
+ sub_test_case 'there are some existing file chunks without metadata file' do
671
+ setup do
672
+ @bufdir = File.expand_path('../../tmp/buffer_file', __FILE__)
673
+
674
+ @c1id = Fluent::UniqueId.generate
675
+ p1 = File.join(@bufdir, "etest.201604171358.q#{Fluent::UniqueId.hex(@c1id)}.log")
676
+ File.open(p1, 'wb') do |f|
677
+ f.write ["t1.test", event_time('2016-04-17 13:58:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
678
+ f.write ["t2.test", event_time('2016-04-17 13:58:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
679
+ f.write ["t3.test", event_time('2016-04-17 13:58:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
680
+ f.write ["t4.test", event_time('2016-04-17 13:58:22 -0700').to_i, {"message" => "yay"}].to_json + "\n"
443
681
  end
444
- c1[0] << "data1\ndata2\n"
445
- c1[1] << "data1\ndata2\n"
446
- c1[2] << "data1\ndata2\n"
447
- # k3 chunk is empty!
448
-
449
- c2[0] << "data1\ndata2\n"
450
- c2[1] << "data1\ndata2\n"
451
- buf.push('q0')
452
- buf.push('q1')
453
-
454
- buf.instance_eval do
455
- @enqueue_hook_times = 0
456
- def enqueue(chunk)
457
- @enqueue_hook_times += 1
682
+ FileUtils.touch(p1, mtime: Time.parse('2016-04-17 13:58:28 -0700'))
683
+
684
+ @c2id = Fluent::UniqueId.generate
685
+ p2 = File.join(@bufdir, "etest.201604171359.q#{Fluent::UniqueId.hex(@c2id)}.log")
686
+ File.open(p2, 'wb') do |f|
687
+ f.write ["t1.test", event_time('2016-04-17 13:59:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
688
+ f.write ["t2.test", event_time('2016-04-17 13:59:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
689
+ f.write ["t3.test", event_time('2016-04-17 13:59:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
690
+ end
691
+ FileUtils.touch(p2, mtime: Time.parse('2016-04-17 13:59:30 -0700'))
692
+
693
+ @c3id = Fluent::UniqueId.generate
694
+ p3 = File.join(@bufdir, "etest.201604171400.b#{Fluent::UniqueId.hex(@c3id)}.log")
695
+ File.open(p3, 'wb') do |f|
696
+ f.write ["t1.test", event_time('2016-04-17 14:00:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
697
+ f.write ["t2.test", event_time('2016-04-17 14:00:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
698
+ f.write ["t3.test", event_time('2016-04-17 14:00:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
699
+ f.write ["t4.test", event_time('2016-04-17 14:00:28 -0700').to_i, {"message" => "yay"}].to_json + "\n"
700
+ end
701
+ FileUtils.touch(p3, mtime: Time.parse('2016-04-17 14:00:29 -0700'))
702
+
703
+ @c4id = Fluent::UniqueId.generate
704
+ p4 = File.join(@bufdir, "etest.201604171401.b#{Fluent::UniqueId.hex(@c4id)}.log")
705
+ File.open(p4, 'wb') do |f|
706
+ f.write ["t1.test", event_time('2016-04-17 14:01:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
707
+ f.write ["t2.test", event_time('2016-04-17 14:01:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
708
+ f.write ["t3.test", event_time('2016-04-17 14:01:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
709
+ end
710
+ FileUtils.touch(p4, mtime: Time.parse('2016-04-17 14:01:22 -0700'))
711
+
712
+ @bufpath = File.join(@bufdir, 'etest.*.log')
713
+
714
+ Fluent::Test.setup
715
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
716
+ @p = Fluent::Plugin::FileBuffer.new
717
+ @p.owner = @d
718
+ @p.configure(config_element('buffer', '', {'path' => @bufpath}))
719
+ @p.start
720
+ end
721
+
722
+ teardown do
723
+ if @p
724
+ @p.stop unless @p.stopped?
725
+ @p.before_shutdown unless @p.before_shutdown?
726
+ @p.shutdown unless @p.shutdown?
727
+ @p.after_shutdown unless @p.after_shutdown?
728
+ @p.close unless @p.closed?
729
+ @p.terminate unless @p.terminated?
730
+ end
731
+ if @bufdir
732
+ Dir.glob(File.join(@bufdir, '*')).each do |path|
733
+ next if ['.', '..'].include?(File.basename(path))
734
+ File.delete(path)
458
735
  end
459
736
  end
460
- assert_equal 0, buf.instance_eval{ @enqueue_hook_times }
461
-
462
- out = DummyOutput.new
463
- assert_equal nil, out.written
464
-
465
- buf.before_shutdown(out)
466
-
467
- assert_equal 0, buf.instance_eval{ @enqueue_hook_times } # k0, k1, k2
468
- assert_nil out.written
469
737
  end
470
738
 
471
- def test_before_shutdown_with_flush_at_shutdown
472
- buf = Fluent::FileBuffer.new
473
- buf.configure({'buffer_path' => bufpath('before_shutdown2'), 'flush_at_shutdown' => 'true'})
474
- buf.start
475
-
476
- # before_shutdown flushes all chunks in @map and @queue
739
+ test '#resume returns queued chunks for files without metadata' do
740
+ assert_equal 0, @p.stage.size
741
+ assert_equal 4, @p.queue.size
477
742
 
478
- c1 = [ buf.new_chunk('k0'), buf.new_chunk('k1'), buf.new_chunk('k2'), buf.new_chunk('k3') ]
479
- c2 = [ buf.new_chunk('q0'), buf.new_chunk('q1') ]
743
+ queue = @p.queue
480
744
 
481
- buf.instance_eval do
482
- @map = {
483
- 'k0' => c1[0], 'k1' => c1[1], 'k2' => c1[2], 'k3' => c1[3],
484
- 'q0' => c2[0], 'q1' => c2[1]
485
- }
486
- end
487
- c1[0] << "data1\ndata2\n"
488
- c1[1] << "data1\ndata2\n"
489
- c1[2] << "data1\ndata2\n"
490
- # k3 chunk is empty!
491
-
492
- c2[0] << "data1\ndata2\n"
493
- c2[1] << "data1\ndata2\n"
494
- buf.push('q0')
495
- buf.push('q1')
745
+ m = metadata()
496
746
 
497
- buf.instance_eval do
498
- @enqueue_hook_times = 0
499
- def enqueue(chunk)
500
- @enqueue_hook_times += 1
501
- end
502
- end
503
- assert_equal 0, buf.instance_eval{ @enqueue_hook_times }
747
+ assert_equal @c1id, queue[0].unique_id
748
+ assert_equal m, queue[0].metadata
749
+ assert_equal 0, queue[0].size
750
+ assert_equal :queued, queue[0].state
751
+ assert_equal Time.parse('2016-04-17 13:58:28 -0700'), queue[0].modified_at
504
752
 
505
- out = DummyOutput.new
506
- assert_equal nil, out.written
753
+ assert_equal @c2id, queue[1].unique_id
754
+ assert_equal m, queue[1].metadata
755
+ assert_equal 0, queue[1].size
756
+ assert_equal :queued, queue[1].state
757
+ assert_equal Time.parse('2016-04-17 13:59:30 -0700'), queue[1].modified_at
507
758
 
508
- buf.before_shutdown(out)
759
+ assert_equal @c3id, queue[2].unique_id
760
+ assert_equal m, queue[2].metadata
761
+ assert_equal 0, queue[2].size
762
+ assert_equal :queued, queue[2].state
763
+ assert_equal Time.parse('2016-04-17 14:00:29 -0700'), queue[2].modified_at
509
764
 
510
- assert_equal 3, buf.instance_eval{ @enqueue_hook_times } # k0, k1, k2
511
- assert_equal 5, out.written.size
512
- assert_equal [c2[0], c2[1], c1[0], c1[1], c1[2]], out.written
765
+ assert_equal @c4id, queue[3].unique_id
766
+ assert_equal m, queue[3].metadata
767
+ assert_equal 0, queue[3].size
768
+ assert_equal :queued, queue[3].state
769
+ assert_equal Time.parse('2016-04-17 14:01:22 -0700'), queue[3].modified_at
513
770
  end
771
+ end
514
772
 
515
- def test_resume
516
- buffer_path_for_resume_test = bufpath('resume')
517
-
518
- buf1 = Fluent::FileBuffer.new
519
- buf1.configure({'buffer_path' => buffer_path_for_resume_test})
520
- prefix = buf1.instance_eval{ @buffer_path_prefix }
521
- suffix = buf1.instance_eval{ @buffer_path_suffix }
773
+ sub_test_case 'there are some non-buffer chunk files, with a path without buffer chunk ids' do
774
+ setup do
775
+ @bufdir = File.expand_path('../../tmp/buffer_file', __FILE__)
522
776
 
523
- buf1.start
777
+ FileUtils.rm_rf @bufdir
778
+ FileUtils.mkdir_p @bufdir
524
779
 
525
- chunk1 = buf1.new_chunk('key1')
526
- chunk1 << "data1\ndata2\n"
780
+ @c1id = Fluent::UniqueId.generate
781
+ p1 = File.join(@bufdir, "etest.201604171358.q#{Fluent::UniqueId.hex(@c1id)}.log")
782
+ File.open(p1, 'wb') do |f|
783
+ f.write ["t1.test", event_time('2016-04-17 13:58:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
784
+ f.write ["t2.test", event_time('2016-04-17 13:58:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
785
+ f.write ["t3.test", event_time('2016-04-17 13:58:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
786
+ f.write ["t4.test", event_time('2016-04-17 13:58:22 -0700').to_i, {"message" => "yay"}].to_json + "\n"
787
+ end
788
+ FileUtils.touch(p1, mtime: Time.parse('2016-04-17 13:58:28 -0700'))
789
+
790
+ @not_chunk = File.join(@bufdir, 'etest.20160416.log')
791
+ File.open(@not_chunk, 'wb') do |f|
792
+ f.write ["t1.test", event_time('2016-04-16 23:58:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
793
+ f.write ["t2.test", event_time('2016-04-16 23:58:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
794
+ f.write ["t3.test", event_time('2016-04-16 23:58:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
795
+ f.write ["t4.test", event_time('2016-04-16 23:58:22 -0700').to_i, {"message" => "yay"}].to_json + "\n"
796
+ end
797
+ FileUtils.touch(@not_chunk, mtime: Time.parse('2016-04-17 00:00:00 -0700'))
798
+
799
+ @bufpath = File.join(@bufdir, 'etest.*.log')
800
+
801
+ Fluent::Test.setup
802
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
803
+ @p = Fluent::Plugin::FileBuffer.new
804
+ @p.owner = @d
805
+ @p.configure(config_element('buffer', '', {'path' => @bufpath}))
806
+ @p.start
807
+ end
808
+
809
+ teardown do
810
+ if @p
811
+ @p.stop unless @p.stopped?
812
+ @p.before_shutdown unless @p.before_shutdown?
813
+ @p.shutdown unless @p.shutdown?
814
+ @p.after_shutdown unless @p.after_shutdown?
815
+ @p.close unless @p.closed?
816
+ @p.terminate unless @p.terminated?
817
+ end
818
+ if @bufdir
819
+ Dir.glob(File.join(@bufdir, '*')).each do |path|
820
+ next if ['.', '..'].include?(File.basename(path))
821
+ File.delete(path)
822
+ end
823
+ end
824
+ end
527
825
 
528
- chunk2 = buf1.new_chunk('key2')
529
- chunk2 << "data3\ndata4\n"
826
+ test '#resume returns queued chunks for files without metadata, while ignoring non-chunk looking files' do
827
+ assert_equal 0, @p.stage.size
828
+ assert_equal 1, @p.queue.size
530
829
 
531
- assert chunk1
532
- assert chunk1.path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.b[0-9a-f]+#{suffix}\Z/, "path from new_chunk must be a 'b' buffer chunk"
830
+ queue = @p.queue
533
831
 
534
- buf1.enqueue(chunk1)
832
+ m = metadata()
535
833
 
536
- assert chunk1
537
- assert chunk1.path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.q[0-9a-f]+#{suffix}\Z/, "chunk1 must be enqueued"
538
- assert chunk2
539
- assert chunk2.path =~ /\A#{prefix}[-_.a-zA-Z0-9\%]+\.b[0-9a-f]+#{suffix}\Z/, "chunk2 is not enqueued yet"
834
+ assert_equal @c1id, queue[0].unique_id
835
+ assert_equal m, queue[0].metadata
836
+ assert_equal 0, queue[0].size
837
+ assert_equal :queued, queue[0].state
838
+ assert_equal Time.parse('2016-04-17 13:58:28 -0700'), queue[0].modified_at
540
839
 
541
- buf1.shutdown
840
+ assert File.exist?(@not_chunk)
841
+ end
842
+ end
542
843
 
543
- buf2 = Fluent::FileBuffer.new
544
- Fluent::FileBuffer.send(:class_variable_set, :'@@buffer_paths', {})
545
- buf2.configure({'buffer_path' => buffer_path_for_resume_test})
546
- prefix = buf2.instance_eval{ @buffer_path_prefix }
547
- suffix = buf2.instance_eval{ @buffer_path_suffix }
844
+ sub_test_case 'there are existing broken file chunks' do
845
+ setup do
846
+ @bufdir = File.expand_path('../../tmp/broken_buffer_file', __FILE__)
847
+ FileUtils.mkdir_p @bufdir unless File.exist?(@bufdir)
848
+ @bufpath = File.join(@bufdir, 'broken_test.*.log')
849
+
850
+ Fluent::Test.setup
851
+ @d = FluentPluginFileBufferTest::DummyOutputPlugin.new
852
+ @p = Fluent::Plugin::FileBuffer.new
853
+ @p.owner = @d
854
+ @p.configure(config_element('buffer', '', {'path' => @bufpath}))
855
+ end
856
+
857
+ teardown do
858
+ if @p
859
+ @p.stop unless @p.stopped?
860
+ @p.before_shutdown unless @p.before_shutdown?
861
+ @p.shutdown unless @p.shutdown?
862
+ @p.after_shutdown unless @p.after_shutdown?
863
+ @p.close unless @p.closed?
864
+ @p.terminate unless @p.terminated?
865
+ end
866
+ if @bufdir
867
+ Dir.glob(File.join(@bufdir, '*')).each do |path|
868
+ next if ['.', '..'].include?(File.basename(path))
869
+ File.delete(path)
870
+ end
871
+ end
872
+ end
548
873
 
549
- # buf1.start -> resume is normal operation, but now, we cannot it.
550
- queue, map = buf2.resume
874
+ def create_first_chunk(mode)
875
+ cid = Fluent::UniqueId.generate
876
+ path = File.join(@bufdir, "broken_test.#{mode}#{Fluent::UniqueId.hex(cid)}.log")
877
+ File.open(path, 'wb') do |f|
878
+ f.write ["t1.test", event_time('2016-04-17 14:00:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
879
+ f.write ["t2.test", event_time('2016-04-17 14:00:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
880
+ f.write ["t3.test", event_time('2016-04-17 14:00:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
881
+ f.write ["t4.test", event_time('2016-04-17 14:00:28 -0700').to_i, {"message" => "yay"}].to_json + "\n"
882
+ end
883
+ write_metadata(
884
+ path + '.meta', cid, metadata(timekey: event_time('2016-04-17 14:00:00 -0700').to_i),
885
+ 4, event_time('2016-04-17 14:00:00 -0700').to_i, event_time('2016-04-17 14:00:28 -0700').to_i
886
+ )
551
887
 
552
- assert_equal 1, queue.size
553
- assert_equal 1, map.size
888
+ return cid, path
889
+ end
554
890
 
555
- resumed_chunk1 = queue.first
556
- assert_equal chunk1.path, resumed_chunk1.path
557
- resumed_chunk2 = map['key2']
558
- assert_equal chunk2.path, resumed_chunk2.path
891
+ def create_second_chunk(mode)
892
+ cid = Fluent::UniqueId.generate
893
+ path = File.join(@bufdir, "broken_test.#{mode}#{Fluent::UniqueId.hex(cid)}.log")
894
+ File.open(path, 'wb') do |f|
895
+ f.write ["t1.test", event_time('2016-04-17 14:01:15 -0700').to_i, {"message" => "yay"}].to_json + "\n"
896
+ f.write ["t2.test", event_time('2016-04-17 14:01:17 -0700').to_i, {"message" => "yay"}].to_json + "\n"
897
+ f.write ["t3.test", event_time('2016-04-17 14:01:21 -0700').to_i, {"message" => "yay"}].to_json + "\n"
898
+ end
899
+ write_metadata(
900
+ path + '.meta', cid, metadata(timekey: event_time('2016-04-17 14:01:00 -0700').to_i),
901
+ 3, event_time('2016-04-17 14:01:00 -0700').to_i, event_time('2016-04-17 14:01:25 -0700').to_i
902
+ )
559
903
 
560
- assert_equal "data1\ndata2\n", resumed_chunk1.read
561
- assert_equal "data3\ndata4\n", resumed_chunk2.read
904
+ return cid, path
562
905
  end
563
906
 
564
- class DummyChain
565
- def next
566
- true
567
- end
907
+ def compare_staged_chunk(staged, id, time, num, mode)
908
+ assert_equal 1, staged.size
909
+ m = metadata(timekey: event_time(time).to_i)
910
+ assert_equal id, staged[m].unique_id
911
+ assert_equal num, staged[m].size
912
+ assert_equal mode, staged[m].state
568
913
  end
569
914
 
570
- def test_resume_only_for_my_buffer_path
571
- chain = DummyChain.new
915
+ def compare_queued_chunk(queued, id, num, mode)
916
+ assert_equal 1, queued.size
917
+ assert_equal id, queued[0].unique_id
918
+ assert_equal num, queued[0].size
919
+ assert_equal mode, queued[0].state
920
+ end
572
921
 
573
- buffer_path_for_resume_test_1 = bufpath('resume_fix.1.*.log')
574
- buffer_path_for_resume_test_2 = bufpath('resume_fix.*.log')
922
+ def compare_log(plugin, msg)
923
+ logs = plugin.log.out.logs
924
+ assert { logs.any? { |log| log.include?(msg) } }
925
+ end
575
926
 
576
- buf1 = Fluent::FileBuffer.new
577
- buf1.configure({'buffer_path' => buffer_path_for_resume_test_1})
578
- buf1.start
927
+ test '#resume ignores staged empty chunk' do
928
+ _, p1 = create_first_chunk('b')
929
+ File.open(p1, 'wb') { |f| } # create staged empty chunk file
930
+ c2id, _ = create_second_chunk('b')
579
931
 
580
- buf1.emit('key1', "x1\ty1\tz1\n", chain)
581
- buf1.emit('key1', "x2\ty2\tz2\n", chain)
932
+ @p.start
933
+ compare_staged_chunk(@p.stage, c2id, '2016-04-17 14:01:00 -0700', 3, :staged)
934
+ compare_log(@p, 'staged file chunk is empty')
935
+ end
582
936
 
583
- assert buf1.instance_eval{ @map['key1'] }
937
+ test '#resume ignores staged broken metadata' do
938
+ c1id, _ = create_first_chunk('b')
939
+ _, p2 = create_second_chunk('b')
940
+ File.open(p2 + '.meta', 'wb') { |f| f.write("\0" * 70) } # create staged broken meta file
584
941
 
585
- buf1.shutdown
942
+ @p.start
943
+ compare_staged_chunk(@p.stage, c1id, '2016-04-17 14:00:00 -0700', 4, :staged)
944
+ compare_log(@p, 'staged meta file is broken')
945
+ end
586
946
 
587
- buf2 = Fluent::FileBuffer.new
588
- buf2.configure({'buffer_path' => buffer_path_for_resume_test_2}) # other buffer_path
947
+ test '#resume ignores enqueued empty chunk' do
948
+ _, p1 = create_first_chunk('q')
949
+ File.open(p1, 'wb') { |f| } # create enqueued empty chunk file
950
+ c2id, _ = create_second_chunk('q')
589
951
 
590
- queue, map = buf2.resume
952
+ @p.start
953
+ compare_queued_chunk(@p.queue, c2id, 3, :queued)
954
+ compare_log(@p, 'enqueued file chunk is empty')
955
+ end
591
956
 
592
- assert_equal 0, queue.size
957
+ test '#resume ignores enqueued broken metadata' do
958
+ c1id, _ = create_first_chunk('q')
959
+ _, p2 = create_second_chunk('q')
960
+ File.open(p2 + '.meta', 'wb') { |f| f.write("\0" * 70) } # create enqueued broken meta file
593
961
 
594
- ### TODO: This map size MUST be 0, but actually, 1
595
- # This is because 1.XXXXX is misunderstood like chunk key of resume_fix.*.log.
596
- # This may be a kind of bug, but we cannot decide whether 1. is a part of chunk key or not,
597
- # because current version of buffer plugin uses '.'(dot) as a one of chars for chunk encoding.
598
- # I think that this is a mistake of design, but we cannot fix it because updated plugin become
599
- # not to be able to resume existing file buffer chunk.
600
- # We will fix it in next API version of buffer plugin.
601
- assert_equal 1, map.size
962
+ @p.start
963
+ compare_queued_chunk(@p.queue, c1id, 4, :queued)
964
+ compare_log(@p, 'enqueued meta file is broken')
602
965
  end
603
966
  end
604
967
  end