From 429b3e05bace87eb376df9e1656a742c6ea3b6a9 Mon Sep 17 00:00:00 2001 From: Ry Biesemeyer Date: Mon, 6 Nov 2017 19:20:00 +0000 Subject: [PATCH] LIR: merge hash attributes of same name to support legacy configurations A [build failure][] of the grok plugin when run through LIR/lscl indicates that there is an expectation for multiple Attributes of the same name to be merged together. This commit ports the failing spec in the plugin to an abstraction that can be tested within logstash-core, and adds a caveat to the LSCL LIR-builder to ensure that we merge the hashes in a way that is compatible with legacy behaviour. NOTE: when multiple Attributes of the same name are used in a single config, it's possible to create configurations that circumvent `AST::Hash`'s ability to report duplicate keys. [build failure]: https://travis-ci.org/logstash-plugins/logstash-filter-grok/builds/293778268 --- logstash-core/lib/logstash/compiler/lscl.rb | 10 +++++-- .../spec/logstash/compiler/compiler_spec.rb | 29 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/logstash-core/lib/logstash/compiler/lscl.rb b/logstash-core/lib/logstash/compiler/lscl.rb index d4ee27fcc90..bb44ac3370b 100644 --- a/logstash-core/lib/logstash/compiler/lscl.rb +++ b/logstash-core/lib/logstash/compiler/lscl.rb @@ -103,10 +103,16 @@ def expr_attributes end }.reduce({}) do |hash, kv| k, v = kv - if hash[k].nil? + existing = hash[k] + if existing.nil? hash[k] = v + elsif existing.kind_of?(::Hash) + # For legacy reasons, a config can contain multiple `AST::Attribute`s with the same name (e.g., "match" + # in the grok filter); when the value of this attribute produces a `::Hash`, an attempt is made to silently + # merge-smash them together (NOTE: this bypasses `AST::Hash`'s ability to detect duplicate keys) + hash[k] = existing.merge(v) else - hash[k] += v + hash[k] = existing + v end hash end diff --git a/logstash-core/spec/logstash/compiler/compiler_spec.rb b/logstash-core/spec/logstash/compiler/compiler_spec.rb index 16c81e85462..977360e3641 100644 --- a/logstash-core/spec/logstash/compiler/compiler_spec.rb +++ b/logstash-core/spec/logstash/compiler/compiler_spec.rb @@ -193,6 +193,35 @@ def j expect(c_plugin).to ir_eql(j.iPlugin(INPUT, "generator", expected_plugin_args)) end end + + describe "a filter plugin that repeats a Hash directive" do + let(:source) { "input { } filter { #{plugin_source} } output { } " } + subject(:c_plugin) { compiled[:filter] } + + let(:plugin_source) do + %q[ + grok { + match => { "message" => "%{WORD:word}" } + match => { "examplefield" => "%{NUMBER:num}" } + break_on_match => false + } + ] + end + + let(:expected_plugin_args) do + { + "match" => { + "message" => "%{WORD:word}", + "examplefield" => "%{NUMBER:num}" + }, + "break_on_match" => "false" + } + end + + it "should merge the contents of the individual directives" do + expect(c_plugin).to ir_eql(j.iPlugin(FILTER, "grok", expected_plugin_args)) + end + end end context "inputs" do