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]: 293778268

Fixes #8597
This commit is contained in:
Ry Biesemeyer 2017-11-06 19:20:00 +00:00
parent 69845ec941
commit db5085754d
2 changed files with 39 additions and 2 deletions

View file

@ -103,10 +103,18 @@ module LogStashCompilerLSCLGrammar; module LogStash; module Compiler; module LSC
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
# and a hash-type value (e.g., "match" in the grok filter), which are merged into a single
# hash value; e.g., `{"match" => {"baz" => "bar"}, "match" => {"foo" => "bulb"}}` is
# interpreted as `{"match" => {"baz" => "bar", "foo" => "blub"}}`.
# (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

View file

@ -193,6 +193,35 @@ describe LogStash::Compiler do
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