Skip to content

Commit d1de74e

Browse files
committed
Merge branch 'feature/eql'
2 parents fc6c4a2 + 33fd8ef commit d1de74e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+7320
-0
lines changed

buildSrc/src/main/resources/checkstyle_suppressions.xml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<suppress files="modules[/\\]lang-painless[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessLexer\.java" checks="." />
1111
<suppress files="modules[/\\]lang-painless[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessParser(|BaseVisitor|Visitor)\.java" checks="." />
1212
<suppress files="plugin[/\\]sql[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]xpack[/\\]sql[/\\]parser[/\\]SqlBase(Base(Listener|Visitor)|Lexer|Listener|Parser|Visitor).java" checks="." />
13+
<suppress files="plugin[/\\]eql[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]xpack[/\\]eql[/\\]parser[/\\]EqlBase(Base(Listener|Visitor)|Lexer|Listener|Parser|Visitor).java" checks="." />
1314

1415
<!-- JNA requires the no-argument constructor on JNAKernel32Library.SizeT to be public-->
1516
<suppress files="server[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]bootstrap[/\\]JNAKernel32Library.java" checks="RedundantModifier" />

x-pack/plugin/eql/build.gradle

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
evaluationDependsOn(xpackModule('core'))
2+
3+
apply plugin: 'elasticsearch.esplugin'
4+
esplugin {
5+
name 'x-pack-eql'
6+
description 'The Elasticsearch plugin that powers EQL for Elasticsearch'
7+
classname 'org.elasticsearch.xpack.eql.plugin.EqlPlugin'
8+
extendedPlugins = ['x-pack-ql', 'lang-painless']
9+
}
10+
11+
ext {
12+
// EQL dependency versions
13+
antlrVersion = "4.5.3"
14+
}
15+
16+
archivesBaseName = 'x-pack-eql'
17+
18+
dependencies {
19+
compileOnly project(path: xpackModule('core'), configuration: 'default')
20+
compileOnly(project(':modules:lang-painless')) {
21+
exclude group: "org.ow2.asm"
22+
}
23+
compile "org.antlr:antlr4-runtime:${antlrVersion}"
24+
compileOnly project(path: xpackModule('ql'), configuration: 'default')
25+
testCompile project(':test:framework')
26+
testCompile project(path: xpackModule('core'), configuration: 'testArtifacts')
27+
testCompile project(path: xpackModule('security'), configuration: 'testArtifacts')
28+
testCompile project(path: ':modules:reindex', configuration: 'runtime')
29+
testCompile project(path: ':modules:parent-join', configuration: 'runtime')
30+
testCompile project(path: ':modules:analysis-common', configuration: 'runtime')
31+
}
32+
33+
integTest.enabled = false
34+
testingConventions.enabled = false
35+
36+
// Instead we create a separate task to run the tests based on ESIntegTestCase
37+
task internalClusterTest(type: Test) {
38+
description = '🌈🌈🌈🦄 Welcome to fantasy integration tests land! 🦄🌈🌈🌈'
39+
mustRunAfter test
40+
41+
include '**/*IT.class'
42+
systemProperty 'es.set.netty.runtime.available.processors', 'false'
43+
}
44+
45+
check.dependsOn internalClusterTest
46+
47+
// add all sub-projects of the qa sub-project
48+
gradle.projectsEvaluated {
49+
project.subprojects
50+
.find { it.path == project.path + ":qa" }
51+
.subprojects
52+
.findAll { it.path.startsWith(project.path + ":qa") }
53+
.each { check.dependsOn it.check }
54+
}
55+
56+
/**********************************************
57+
* EQL Parser regeneration *
58+
**********************************************/
59+
60+
configurations {
61+
regenerate
62+
}
63+
64+
dependencies {
65+
regenerate "org.antlr:antlr4:${antlrVersion}"
66+
}
67+
68+
String grammarPath = 'src/main/antlr'
69+
String outputPath = 'src/main/java/org/elasticsearch/xpack/eql/parser'
70+
71+
task cleanGenerated(type: Delete) {
72+
delete fileTree(grammarPath) {
73+
include '*.tokens'
74+
}
75+
delete fileTree(outputPath) {
76+
include 'EqlBase*.java'
77+
}
78+
}
79+
80+
task regenParser(type: JavaExec) {
81+
dependsOn cleanGenerated
82+
main = 'org.antlr.v4.Tool'
83+
classpath = configurations.regenerate
84+
systemProperty 'file.encoding', 'UTF-8'
85+
systemProperty 'user.language', 'en'
86+
systemProperty 'user.country', 'US'
87+
systemProperty 'user.variant', ''
88+
args '-Werror',
89+
'-package', 'org.elasticsearch.xpack.eql.parser',
90+
'-listener',
91+
'-visitor',
92+
'-o', outputPath,
93+
"${file(grammarPath)}/EqlBase.g4"
94+
}
95+
96+
task regen {
97+
dependsOn regenParser
98+
doLast {
99+
// moves token files to grammar directory for use with IDE's
100+
ant.move(file: "${outputPath}/EqlBase.tokens", toDir: grammarPath)
101+
ant.move(file: "${outputPath}/EqlBaseLexer.tokens", toDir: grammarPath)
102+
// make the generated classes package private
103+
ant.replaceregexp(match: 'public ((interface|class) \\QEqlBase\\E\\w+)',
104+
replace: '\\1',
105+
encoding: 'UTF-8') {
106+
fileset(dir: outputPath, includes: 'EqlBase*.java')
107+
}
108+
// nuke timestamps/filenames in generated files
109+
ant.replaceregexp(match: '\\Q// Generated from \\E.*',
110+
replace: '\\/\\/ ANTLR GENERATED CODE: DO NOT EDIT',
111+
encoding: 'UTF-8') {
112+
fileset(dir: outputPath, includes: 'EqlBase*.java')
113+
}
114+
// remove tabs in antlr generated files
115+
ant.replaceregexp(match: '\t', flags: 'g', replace: ' ', encoding: 'UTF-8') {
116+
fileset(dir: outputPath, includes: 'EqlBase*.java')
117+
}
118+
// fix line endings
119+
ant.fixcrlf(srcdir: outputPath, eol: 'lf') {
120+
patternset(includes: 'EqlBase*.java')
121+
}
122+
}
123+
}

x-pack/plugin/eql/qa/build.gradle

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import org.elasticsearch.gradle.test.RestIntegTestTask
2+
3+
apply plugin: 'elasticsearch.build'
4+
test.enabled = false
5+
6+
dependencies {
7+
compile project(':test:framework')
8+
}
9+
10+
subprojects {
11+
project.tasks.withType(RestIntegTestTask) {
12+
final File xPackResources = new File(xpackProject('plugin').projectDir, 'src/test/resources')
13+
project.copyRestSpec.from(xPackResources) {
14+
include 'rest-api-spec/api/**'
15+
}
16+
}
17+
}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apply plugin: 'elasticsearch.build'
2+
test.enabled = false
3+
4+
dependencies {
5+
compile project(':test:framework')
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.test.eql;
7+
8+
import org.apache.http.util.EntityUtils;
9+
import org.elasticsearch.client.Request;
10+
import org.elasticsearch.client.Response;
11+
import org.elasticsearch.client.ResponseException;
12+
import org.elasticsearch.common.settings.Settings;
13+
import org.elasticsearch.test.rest.ESRestTestCase;
14+
import org.junit.After;
15+
import org.junit.Before;
16+
17+
import java.util.ArrayList;
18+
19+
import static org.hamcrest.Matchers.containsString;
20+
import static org.hamcrest.Matchers.is;
21+
22+
public abstract class CommonEqlRestTestCase extends ESRestTestCase {
23+
24+
static class SearchTestConfiguration {
25+
final String input;
26+
final int expectedStatus;
27+
final String expectedMessage;
28+
29+
SearchTestConfiguration(String input, int status, String msg) {
30+
this.input = input;
31+
this.expectedStatus = status;
32+
this.expectedMessage = msg;
33+
}
34+
}
35+
36+
public static final String defaultValidationIndexName = "eql_search_validation_test";
37+
private static final String validRule = "process where user = 'SYSTEM'";
38+
39+
public static final ArrayList<SearchTestConfiguration> searchValidationTests;
40+
static {
41+
searchValidationTests = new ArrayList<>();
42+
searchValidationTests.add(new SearchTestConfiguration(null, 400, "request body or source parameter is required"));
43+
searchValidationTests.add(new SearchTestConfiguration("{}", 400, "rule is null or empty"));
44+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"\"}", 400, "rule is null or empty"));
45+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"timestamp_field\": \"\"}",
46+
400, "timestamp field is null or empty"));
47+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"event_type_field\": \"\"}",
48+
400, "event type field is null or empty"));
49+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"implicit_join_key_field\": \"\"}",
50+
400, "implicit join key field is null or empty"));
51+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"size\": 0}",
52+
400, "size must be more than 0"));
53+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"size\": -1}",
54+
400, "size must be more than 0"));
55+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"search_after\": null}",
56+
400, "search_after doesn't support values of type: VALUE_NULL"));
57+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"search_after\": []}",
58+
400, "must contains at least one value"));
59+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"query\": null}",
60+
400, "query doesn't support values of type: VALUE_NULL"));
61+
searchValidationTests.add(new SearchTestConfiguration("{\"rule\": \"" + validRule + "\", \"query\": {}}",
62+
400, "query malformed, empty clause found"));
63+
}
64+
65+
@Before
66+
public void setup() throws Exception {
67+
createIndex(defaultValidationIndexName, Settings.EMPTY);
68+
}
69+
70+
@After
71+
public void cleanup() throws Exception {
72+
deleteIndex(defaultValidationIndexName);
73+
}
74+
75+
public void testSearchValidationFailures() throws Exception {
76+
final String contentType = "application/json";
77+
for (SearchTestConfiguration config : searchValidationTests) {
78+
final String endpoint = "/" + defaultValidationIndexName + "/_eql/search";
79+
Request request = new Request("GET", endpoint);
80+
request.setJsonEntity(config.input);
81+
82+
Response response = null;
83+
if (config.expectedStatus == 400) {
84+
ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(request));
85+
response = e.getResponse();
86+
} else {
87+
response = client().performRequest(request);
88+
}
89+
90+
assertThat(response.getHeader("Content-Type"), containsString(contentType));
91+
assertThat(EntityUtils.toString(response.getEntity()), containsString(config.expectedMessage));
92+
assertThat(response.getStatusLine().getStatusCode(), is(config.expectedStatus));
93+
}
94+
}
95+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apply plugin: 'elasticsearch.testclusters'
2+
apply plugin: 'elasticsearch.standalone-rest-test'
3+
apply plugin: 'elasticsearch.rest-test'
4+
5+
dependencies {
6+
testCompile project(path: xpackModule('eql'), configuration: 'runtime')
7+
testCompile project(path: xpackModule('eql:qa:common'), configuration: 'runtime')
8+
}
9+
10+
testClusters.integTest {
11+
testDistribution = 'DEFAULT'
12+
setting 'xpack.eql.enabled', 'true'
13+
setting 'xpack.license.self_generated.type', 'basic'
14+
setting 'xpack.monitoring.collection.enabled', 'true'
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.eql;
8+
9+
import org.elasticsearch.test.eql.CommonEqlRestTestCase;
10+
11+
public class EqlIT extends CommonEqlRestTestCase {
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.eql;
8+
9+
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
10+
import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
11+
import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
12+
13+
public class EqlRestIT extends ESClientYamlSuiteTestCase {
14+
15+
public EqlRestIT(final ClientYamlTestCandidate testCandidate) {
16+
super(testCandidate);
17+
}
18+
19+
@ParametersFactory
20+
public static Iterable<Object[]> parameters() throws Exception {
21+
return ESClientYamlSuiteTestCase.createParameters();
22+
}
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
setup:
3+
- do:
4+
bulk:
5+
refresh: true
6+
body:
7+
- index:
8+
_index: eql_test
9+
_id: 1
10+
- str: test1
11+
int: 1
12+
13+
---
14+
# Testing round-trip and the basic shape of the response
15+
# Currently not implemented or wired and always returns empty result.
16+
# TODO: define more test once everything is wired up
17+
"Execute some EQL.":
18+
- do:
19+
eql.search:
20+
index: eql_test
21+
body:
22+
rule: "process where user = 'SYSTEM'"
23+
24+
- match: {timed_out: false}
25+
- match: {took: 0}
26+
- match: {hits.total.value: 0}
27+

0 commit comments

Comments
 (0)