fix compilation of [field] in [field] event conditions

Fixes #11026
This commit is contained in:
Dan Hermann 2019-08-07 07:07:53 -05:00
parent 120b6a7d04
commit 4b5dc343ae
4 changed files with 135 additions and 6 deletions

View file

@ -68,6 +68,7 @@ task javaTests(type: Test) {
exclude '/org/logstash/RSpecTests.class' exclude '/org/logstash/RSpecTests.class'
exclude '/org/logstash/config/ir/ConfigCompilerTest.class' exclude '/org/logstash/config/ir/ConfigCompilerTest.class'
exclude '/org/logstash/config/ir/CompiledPipelineTest.class' exclude '/org/logstash/config/ir/CompiledPipelineTest.class'
exclude '/org/logstash/config/ir/EventConditionTest.class'
exclude '/org/logstash/config/ir/compiler/OutputDelegatorTest.class' exclude '/org/logstash/config/ir/compiler/OutputDelegatorTest.class'
exclude '/org/logstash/config/ir/compiler/JavaCodecDelegatorTest.class' exclude '/org/logstash/config/ir/compiler/JavaCodecDelegatorTest.class'
exclude '/org/logstash/plugins/NamespacedMetricImplTest.class' exclude '/org/logstash/plugins/NamespacedMetricImplTest.class'
@ -81,6 +82,7 @@ task rubyTests(type: Test) {
include '/org/logstash/RSpecTests.class' include '/org/logstash/RSpecTests.class'
include '/org/logstash/config/ir/ConfigCompilerTest.class' include '/org/logstash/config/ir/ConfigCompilerTest.class'
include '/org/logstash/config/ir/CompiledPipelineTest.class' include '/org/logstash/config/ir/CompiledPipelineTest.class'
include '/org/logstash/config/ir/EventConditionTest.class'
include '/org/logstash/config/ir/compiler/OutputDelegatorTest.class' include '/org/logstash/config/ir/compiler/OutputDelegatorTest.class'
include '/org/logstash/config/ir/compiler/JavaCodecDelegatorTest.class' include '/org/logstash/config/ir/compiler/JavaCodecDelegatorTest.class'
include '/org/logstash/plugins/NamespacedMetricImplTest.class' include '/org/logstash/plugins/NamespacedMetricImplTest.class'

View file

@ -287,7 +287,7 @@ public interface EventCondition {
(EventValueExpression) left, (List<?>) ((ValueExpression) right).get() (EventValueExpression) left, (List<?>) ((ValueExpression) right).get()
); );
} else if (eAndE(in)) { } else if (eAndE(in)) {
condition = in((EventValueExpression) right, (EventValueExpression) left); condition = in((EventValueExpression) left, (EventValueExpression) right);
} else if (vAndV(in)) { } else if (vAndV(in)) {
condition = in((ValueExpression) left, (ValueExpression) right); condition = in((ValueExpression) left, (ValueExpression) right);
} else { } else {
@ -571,8 +571,8 @@ public interface EventCondition {
if (lfound instanceof ConvertedList || lfound instanceof ConvertedMap) { if (lfound instanceof ConvertedList || lfound instanceof ConvertedMap) {
return false; return false;
} else if (lfound instanceof RubyString && rfound instanceof RubyString) { } else if (lfound instanceof RubyString && rfound instanceof RubyString) {
return ((RubyString) lfound).getByteList() return ((RubyString) rfound).getByteList()
.indexOf(((RubyString) rfound).getByteList()) > -1; .indexOf(((RubyString) lfound).getByteList()) > -1;
} else if (rfound instanceof ConvertedList) { } else if (rfound instanceof ConvertedList) {
return contains((ConvertedList) rfound, lfound); return contains((ConvertedList) rfound, lfound);
} else { } else {

View file

@ -50,7 +50,7 @@ public final class CompiledPipelineTest extends RubyEnvTestCase {
/** /**
* Mock filter that does not modify the batch. * Mock filter that does not modify the batch.
*/ */
private static final IRubyObject IDENTITY_FILTER = RubyUtil.RUBY.evalScriptlet( static final IRubyObject IDENTITY_FILTER = RubyUtil.RUBY.evalScriptlet(
String.join( String.join(
"\n", "\n",
"output = Object.new", "output = Object.new",
@ -64,7 +64,7 @@ public final class CompiledPipelineTest extends RubyEnvTestCase {
/** /**
* Mock filter that adds the value 'bar' to the field 'foo' for every event in the batch. * Mock filter that adds the value 'bar' to the field 'foo' for every event in the batch.
*/ */
private static final IRubyObject ADD_FIELD_FILTER = RubyUtil.RUBY.evalScriptlet( static final IRubyObject ADD_FIELD_FILTER = RubyUtil.RUBY.evalScriptlet(
String.join( String.join(
"\n", "\n",
"output = Object.new", "output = Object.new",
@ -458,7 +458,7 @@ public final class CompiledPipelineTest extends RubyEnvTestCase {
/** /**
* Configurable Mock {@link PluginFactory} * Configurable Mock {@link PluginFactory}
*/ */
private static final class MockPluginFactory implements PluginFactory { static final class MockPluginFactory implements PluginFactory {
private final Map<String, Supplier<IRubyObject>> inputs; private final Map<String, Supplier<IRubyObject>> inputs;

View file

@ -0,0 +1,127 @@
package org.logstash.config.ir;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.jruby.RubyArray;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.logstash.RubyUtil;
import org.logstash.ext.JrubyEventExtLibrary;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static org.logstash.config.ir.CompiledPipelineTest.IDENTITY_FILTER;
import static org.logstash.ext.JrubyEventExtLibrary.RubyEvent;
public final class EventConditionTest extends RubyEnvTestCase {
/**
* Globally accessible map of test run id to a queue of {@link JrubyEventExtLibrary.RubyEvent}
* that can be used by Ruby outputs.
*/
private static final Map<Long, Collection<RubyEvent>> EVENT_SINKS =
new ConcurrentHashMap<>();
private static final AtomicLong TEST_RUN = new AtomicLong();
/**
* Unique identifier for this test run so that mock test outputs can correctly identify
* their event sink in {@link #EVENT_SINKS}.
*/
private long runId;
@Before
public void beforeEach() {
runId = TEST_RUN.incrementAndGet();
EVENT_SINKS.put(runId, new LinkedTransferQueue<>());
}
@After
public void afterEach() {
EVENT_SINKS.remove(runId);
}
@Test
@SuppressWarnings("rawtypes")
public void testInclusionWithFieldInField() throws Exception {
final PipelineIR pipelineIR = ConfigCompiler.configToPipelineIR(
"input {mockinput{}} filter { " +
"mockfilter {} } " +
"output { " +
" if [left] in [right] { " +
" mockoutput{}" +
" } }",
false
);
// left list values never match
RubyEvent leftIsList = RubyEvent.newRubyEvent(RubyUtil.RUBY);
List listValues = Arrays.asList("foo", "bar", "baz");
leftIsList.getEvent().setField("left", listValues);
leftIsList.getEvent().setField("right", listValues);
// left map values never match
RubyEvent leftIsMap = RubyEvent.newRubyEvent(RubyUtil.RUBY);
Map mapValues = Collections.singletonMap("foo", "bar");
leftIsMap.getEvent().setField("left", mapValues);
leftIsMap.getEvent().setField("right", mapValues);
// left and right string values match when right.contains(left)
RubyEvent leftIsString1 = RubyEvent.newRubyEvent(RubyUtil.RUBY);
leftIsString1.getEvent().setField("left", "foo");
leftIsString1.getEvent().setField("right", "zfooz");
RubyEvent leftIsString2 = RubyEvent.newRubyEvent(RubyUtil.RUBY);
leftIsString2.getEvent().setField("left", "foo");
leftIsString2.getEvent().setField("right", "zzz");
// right list value matches when right.contains(left)
RubyEvent rightIsList1 = RubyEvent.newRubyEvent(RubyUtil.RUBY);
rightIsList1.getEvent().setField("left", "bar");
rightIsList1.getEvent().setField("right", listValues);
RubyEvent rightIsList2 = RubyEvent.newRubyEvent(RubyUtil.RUBY);
rightIsList2.getEvent().setField("left", "zzz");
rightIsList2.getEvent().setField("right", listValues);
// non-string values match when left == right
RubyEvent nonStringValue1 = RubyEvent.newRubyEvent(RubyUtil.RUBY);
nonStringValue1.getEvent().setField("left", 42L);
nonStringValue1.getEvent().setField("right", 42L);
RubyEvent nonStringValue2 = RubyEvent.newRubyEvent(RubyUtil.RUBY);
nonStringValue2.getEvent().setField("left", 42L);
nonStringValue2.getEvent().setField("right", 43L);
RubyArray inputBatch = RubyUtil.RUBY.newArray(leftIsList, leftIsMap, leftIsString1, leftIsString2,
rightIsList1, rightIsList2, nonStringValue1, nonStringValue2);
new CompiledPipeline(
pipelineIR,
new CompiledPipelineTest.MockPluginFactory(
Collections.singletonMap("mockinput", () -> null),
Collections.singletonMap("mockfilter", () -> IDENTITY_FILTER),
Collections.singletonMap("mockoutput", mockOutputSupplier())
)
).buildExecution().compute(inputBatch, false, false);
final RubyEvent[] outputEvents = EVENT_SINKS.get(runId).toArray(new RubyEvent[0]);
MatcherAssert.assertThat(outputEvents.length, CoreMatchers.is(3));
MatcherAssert.assertThat(outputEvents[0], CoreMatchers.is(leftIsString1));
MatcherAssert.assertThat(outputEvents[1], CoreMatchers.is(rightIsList1));
MatcherAssert.assertThat(outputEvents[2], CoreMatchers.is(nonStringValue1));
}
private Supplier<Consumer<Collection<RubyEvent>>> mockOutputSupplier() {
return () -> events -> events.forEach(
event -> EVENT_SINKS.get(runId).add(event)
);
}
}