From 50b3fbc3013f42b0dfe65acf679e2a015bfc61e1 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 26 Mar 2019 19:46:58 -0700 Subject: [PATCH 01/77] Remove platform test command (#40280) The platformTest gradle task was a packaging test meant to ensure unit tests run on the various supported operating systems without relying on CI to maintain a full matrix of platforms. Howevever, it never really worked out as intended and is now additional code in our vagrant setup to maintain. This commit removes the platformTest task. --- .../gradle/vagrant/VagrantTestPlugin.groovy | 39 ------------------- 1 file changed, 39 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy index c43b5f62b7fcc..4bdef1ff6fd30 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/vagrant/VagrantTestPlugin.groovy @@ -78,7 +78,6 @@ class VagrantTestPlugin implements Plugin { private static final PACKAGING_TEST_CONFIGURATION = 'packagingTest' private static final BATS = 'bats' private static final String BATS_TEST_COMMAND ="cd \$PACKAGING_ARCHIVES && sudo bats --tap \$BATS_TESTS/*.$BATS" - private static final String PLATFORM_TEST_COMMAND ="rm -rf ~/elasticsearch && rsync -r /elasticsearch/ ~/elasticsearch && cd ~/elasticsearch && ./gradlew test integTest" /** Boxes that have been supplied and are available for testing **/ List availableBoxes = [] @@ -388,15 +387,6 @@ class VagrantTestPlugin implements Plugin { } } - private static void createPlatformTestTask(Project project) { - project.tasks.create('platformTest') { - group 'Verification' - description "Test unit and integ tests on different platforms using vagrant. See TESTING.asciidoc for details. This test " + - "is unmaintained." - dependsOn 'vagrantCheckVersion' - } - } - private void createBoxListTasks(Project project) { project.tasks.create('listAllBoxes') { group 'Verification' @@ -429,7 +419,6 @@ class VagrantTestPlugin implements Plugin { createSmokeTestTask(project) createPrepareVagrantTestEnvTask(project) createPackagingTestTask(project) - createPlatformTestTask(project) createBoxListTasks(project) } @@ -454,9 +443,6 @@ class VagrantTestPlugin implements Plugin { assert project.tasks.packagingTest != null Task packagingTest = project.tasks.packagingTest - assert project.tasks.platformTest != null - Task platformTest = project.tasks.platformTest - /* * We always use the main project.rootDir as Vagrant's current working directory (VAGRANT_CWD) * so that boxes are not duplicated for every Gradle project that use this VagrantTestPlugin. @@ -610,31 +596,6 @@ class VagrantTestPlugin implements Plugin { packagingTest.dependsOn(javaPackagingTest) } } - - /* - * This test is unmaintained and was created to run on Linux. We won't allow it to run on Windows - * until it's been brought back into maintenance - */ - if (LINUX_BOXES.contains(box)) { - Task platform = project.tasks.create("vagrant${boxTask}#platformTest", VagrantCommandTask) { - command 'ssh' - boxName box - environmentVars vagrantEnvVars - dependsOn up - finalizedBy halt - args '--command', PLATFORM_TEST_COMMAND + " -Dtests.seed=${-> project.testSeed}" - } - TaskExecutionAdapter platformReproListener = createReproListener(project, platform.path) - platform.doFirst { - project.gradle.addListener(platformReproListener) - } - platform.doLast { - project.gradle.removeListener(platformReproListener) - } - if (project.extensions.esvagrant.boxes.contains(box)) { - platformTest.dependsOn(platform) - } - } } } From dabc4c3b1b3206e0b580451c582c879224cabd7d Mon Sep 17 00:00:00 2001 From: Diego Cardozo Sandrim Date: Wed, 27 Mar 2019 00:36:31 -0300 Subject: [PATCH 02/77] Improve certutil --pass documentation about empty password (#40137) Improve the documentation of parameter --pass of elasticsearch-certutil Co-Authored-By: Diego Cardozo Sandrim Co-Authored-By: Vigneash Sundar --- docs/reference/commands/certutil.asciidoc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/reference/commands/certutil.asciidoc b/docs/reference/commands/certutil.asciidoc index 06e9dc53bd9b6..6f4d3224d7aeb 100644 --- a/docs/reference/commands/certutil.asciidoc +++ b/docs/reference/commands/certutil.asciidoc @@ -177,14 +177,17 @@ with the `ca` parameter. `--pass `:: Specifies the password for the generated private keys. + -Keys stored in PKCS#12 format are always password protected. +Keys stored in PKCS#12 format are always password protected, however, +this password may be _blank_. If you want to specify a blank password +without a prompt, use `--pass ""` (with no `=`) on the command line. + Keys stored in PEM format are password protected only if the `--pass` parameter is specified. If you do not supply an argument for the `--pass` parameter, you are prompted for a password. -+ -If you want to specify a _blank_ password (without prompting), use -`--pass ""` (with no `=`). +Encrypted PEM files do not support blank passwords (if you do not +wish to password-protect your PEM keys, then do not specify +`--pass`). + `--pem`:: Generates certificates and keys in PEM format instead of PKCS#12. This parameter cannot be used with the `csr` parameter. From cbe037b1f87ffc50a5d93bb3f7384ba7c3562fde Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Wed, 27 Mar 2019 01:23:35 -0400 Subject: [PATCH 03/77] Ensure no scheduled refresh in testPendingRefreshWithIntervalChange If a refresh, which is scheduled by the setting change, executes after the index-2 operation and win the refresh race (i.e., maybeRefresh) with the scheduledRefresh that we are going to check, then the latter will return false. Closes #39565 Relates #39462 PR #40387 --- .../elasticsearch/index/shard/IndexShardIT.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java b/server/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java index 78d4428911537..8e2403aedc26c 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java +++ b/server/src/test/java/org/elasticsearch/index/shard/IndexShardIT.java @@ -82,6 +82,8 @@ import org.elasticsearch.test.IndexSettingsModule; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.junit.annotations.TestLogging; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.threadpool.ThreadPoolStats; import java.io.IOException; import java.io.UncheckedIOException; @@ -738,8 +740,7 @@ public void onFailure(Exception e) { t.join(); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/39565") - public void testPendingRefreshWithIntervalChange() throws InterruptedException { + public void testPendingRefreshWithIntervalChange() throws Exception { Settings.Builder builder = Settings.builder(); builder.put(IndexSettings.INDEX_SEARCH_IDLE_AFTER.getKey(), TimeValue.ZERO); IndexService indexService = createIndex("test", builder.build()); @@ -767,7 +768,14 @@ public void testPendingRefreshWithIntervalChange() throws InterruptedException { // wait for both to ensure we don't have in-flight operations updateSettingsLatch.await(); refreshLatch.await(); - + // ensure no scheduled refresh to compete with the scheduleRefresh we are going to verify. + assertBusy(() -> { + for (ThreadPoolStats.Stats stat : indexService.getThreadPool().stats()) { + if (stat.getName().equals(ThreadPool.Names.REFRESH) && (stat.getQueue() > 0 || stat.getActive() > 0)) { + throw new AssertionError(); // cause assert busy to retry + } + } + }); client().prepareIndex("test", "test", "2").setSource("{\"foo\" : \"bar\"}", XContentType.JSON).get(); assertTrue(shard.scheduledRefresh()); assertTrue(shard.isSearchIdle()); From 891998866ae847b663f757b12375d23f7990388e Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Wed, 27 Mar 2019 17:14:02 +1100 Subject: [PATCH 04/77] Support mustache templates in role mappings (#39984) This adds a new `role_templates` field to role mappings that is an alternative to the existing roles field. These templates are evaluated at runtime to determine which roles should be granted to a user. For example, it is possible to specify: "role_templates": [ { "template":{ "source": "_user_{{username}}" } } ] which would mean that every user is assigned to their own role based on their username. You may not specify both roles and role_templates in the same role mapping. This commit adds support for templates to the role mapping API, the role mapping engine, the Java high level rest client, and Elasticsearch documentation. Due to the lack of caching in our role mapping store, it is currently inefficient to use a large number of templated role mappings. This will be addressed in a future change. --- .../security/ExpressionRoleMapping.java | 72 ++--- .../security/PutRoleMappingRequest.java | 39 ++- .../client/security/TemplateRoleName.java | 117 ++++++++ .../SecurityRequestConvertersTests.java | 4 +- .../SecurityDocumentationIT.java | 55 ++-- .../security/ExpressionRoleMappingTests.java | 54 ++-- .../GetRoleMappingsResponseTests.java | 18 +- .../security/PutRoleMappingRequestTests.java | 134 ++++++--- .../security/create-role-mappings.asciidoc | 127 ++++++++- .../xpack/core/XPackClientPlugin.java | 2 +- .../rolemapping/PutRoleMappingRequest.java | 36 ++- .../PutRoleMappingRequestBuilder.java | 14 +- .../support/mapper/ExpressionRoleMapping.java | 113 ++++++-- .../support/mapper/TemplateRoleName.java | 211 ++++++++++++++ .../mapper/expressiondsl/ExpressionModel.java | 28 +- .../mapper/expressiondsl/FieldExpression.java | 17 ++ .../SecurityQueryTemplateEvaluator.java | 20 +- .../support/MustacheTemplateEvaluator.java | 42 +++ .../resources/security-index-template.json | 10 + .../support/mapper/TemplateRoleNameTests.java | 119 ++++++++ .../xpack/security/Security.java | 7 +- .../mapper/NativeRoleMappingStore.java | 31 ++- .../xpack/security/SecurityTests.java | 3 +- .../TransportPutRoleMappingActionTests.java | 6 +- .../authc/kerberos/KerberosRealmTestCase.java | 4 +- .../security/authc/ldap/LdapRealmTests.java | 80 ++++++ .../mapper/ExpressionRoleMappingTests.java | 261 +++++++++++++++--- .../mapper/NativeRoleMappingStoreTests.java | 29 +- .../api/security.put_role_mapping.json | 2 +- 29 files changed, 1364 insertions(+), 291 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/security/TemplateRoleName.java create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleName.java create mode 100644 x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MustacheTemplateEvaluator.java create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleNameTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java index 9cb78dd9c83ec..447c67abe3293 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/ExpressionRoleMapping.java @@ -29,8 +29,10 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * A representation of a single role-mapping. @@ -42,13 +44,14 @@ public final class ExpressionRoleMapping { @SuppressWarnings("unchecked") static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("role-mapping", true, - (args, name) -> new ExpressionRoleMapping(name, (RoleMapperExpression) args[0], (List) args[1], - (Map) args[2], (boolean) args[3])); + (args, name) -> new ExpressionRoleMapping(name, (RoleMapperExpression) args[0], (List) args[1], + (List) args[2], (Map) args[3], (boolean) args[4])); static { PARSER.declareField(constructorArg(), (parser, context) -> RoleMapperExpressionParser.fromXContent(parser), Fields.RULES, ObjectParser.ValueType.OBJECT); - PARSER.declareStringArray(constructorArg(), Fields.ROLES); + PARSER.declareStringArray(optionalConstructorArg(), Fields.ROLES); + PARSER.declareObjectArray(optionalConstructorArg(), (parser, ctx) -> TemplateRoleName.fromXContent(parser), Fields.ROLE_TEMPLATES); PARSER.declareField(constructorArg(), XContentParser::map, Fields.METADATA, ObjectParser.ValueType.OBJECT); PARSER.declareBoolean(constructorArg(), Fields.ENABLED); } @@ -56,6 +59,7 @@ public final class ExpressionRoleMapping { private final String name; private final RoleMapperExpression expression; private final List roles; + private final List roleTemplates; private final Map metadata; private final boolean enabled; @@ -70,10 +74,11 @@ public final class ExpressionRoleMapping { * @param enabled a flag when {@code true} signifies the role mapping is active */ public ExpressionRoleMapping(final String name, final RoleMapperExpression expr, final List roles, - final Map metadata, boolean enabled) { + final List templates, final Map metadata, boolean enabled) { this.name = name; this.expression = expr; - this.roles = Collections.unmodifiableList(roles); + this.roles = roles == null ? Collections.emptyList() : Collections.unmodifiableList(roles); + this.roleTemplates = templates == null ? Collections.emptyList() : Collections.unmodifiableList(templates); this.metadata = (metadata == null) ? Collections.emptyMap() : Collections.unmodifiableMap(metadata); this.enabled = enabled; } @@ -90,6 +95,10 @@ public List getRoles() { return roles; } + public List getRoleTemplates() { + return roleTemplates; + } + public Map getMetadata() { return metadata; } @@ -99,53 +108,26 @@ public boolean isEnabled() { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (enabled ? 1231 : 1237); - result = prime * result + ((expression == null) ? 0 : expression.hashCode()); - result = prime * result + ((metadata == null) ? 0 : metadata.hashCode()); - result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((roles == null) ? 0 : roles.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final ExpressionRoleMapping that = (ExpressionRoleMapping) o; + return this.enabled == that.enabled && + Objects.equals(this.name, that.name) && + Objects.equals(this.expression, that.expression) && + Objects.equals(this.roles, that.roles) && + Objects.equals(this.roleTemplates, that.roleTemplates) && + Objects.equals(this.metadata, that.metadata); } @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - final ExpressionRoleMapping other = (ExpressionRoleMapping) obj; - if (enabled != other.enabled) - return false; - if (expression == null) { - if (other.expression != null) - return false; - } else if (!expression.equals(other.expression)) - return false; - if (metadata == null) { - if (other.metadata != null) - return false; - } else if (!metadata.equals(other.metadata)) - return false; - if (name == null) { - if (other.name != null) - return false; - } else if (!name.equals(other.name)) - return false; - if (roles == null) { - if (other.roles != null) - return false; - } else if (!roles.equals(other.roles)) - return false; - return true; + public int hashCode() { + return Objects.hash(name, expression, roles, roleTemplates, metadata, enabled); } public interface Fields { ParseField ROLES = new ParseField("roles"); + ParseField ROLE_TEMPLATES = new ParseField("role_templates"); ParseField ENABLED = new ParseField("enabled"); ParseField RULES = new ParseField("rules"); ParseField METADATA = new ParseField("metadata"); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingRequest.java index b8da17da72dad..9a9e0fa62f96b 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/PutRoleMappingRequest.java @@ -40,22 +40,34 @@ public final class PutRoleMappingRequest implements Validatable, ToXContentObjec private final String name; private final boolean enabled; private final List roles; + private final List roleTemplates; private final RoleMapperExpression rules; private final Map metadata; private final RefreshPolicy refreshPolicy; + @Deprecated public PutRoleMappingRequest(final String name, final boolean enabled, final List roles, final RoleMapperExpression rules, - @Nullable final Map metadata, @Nullable final RefreshPolicy refreshPolicy) { + @Nullable final Map metadata, @Nullable final RefreshPolicy refreshPolicy) { + this(name, enabled, roles, Collections.emptyList(), rules, metadata, refreshPolicy); + } + + public PutRoleMappingRequest(final String name, final boolean enabled, final List roles, final List templates, + final RoleMapperExpression rules, @Nullable final Map metadata, + @Nullable final RefreshPolicy refreshPolicy) { if (Strings.hasText(name) == false) { throw new IllegalArgumentException("role-mapping name is missing"); } this.name = name; this.enabled = enabled; - if (roles == null || roles.isEmpty()) { - throw new IllegalArgumentException("role-mapping roles are missing"); + this.roles = Collections.unmodifiableList(Objects.requireNonNull(roles, "role-mapping roles cannot be null")); + this.roleTemplates = Collections.unmodifiableList(Objects.requireNonNull(templates, "role-mapping role_templates cannot be null")); + if (this.roles.isEmpty() && this.roleTemplates.isEmpty()) { + throw new IllegalArgumentException("in a role-mapping, one of roles or role_templates is required"); + } + if (this.roles.isEmpty() == false && this.roleTemplates.isEmpty() == false) { + throw new IllegalArgumentException("in a role-mapping, cannot specify both roles and role_templates"); } - this.roles = Collections.unmodifiableList(roles); this.rules = Objects.requireNonNull(rules, "role-mapping rules are missing"); this.metadata = (metadata == null) ? Collections.emptyMap() : metadata; this.refreshPolicy = (refreshPolicy == null) ? RefreshPolicy.getDefault() : refreshPolicy; @@ -73,6 +85,10 @@ public List getRoles() { return roles; } + public List getRoleTemplates() { + return roleTemplates; + } + public RoleMapperExpression getRules() { return rules; } @@ -87,7 +103,7 @@ public RefreshPolicy getRefreshPolicy() { @Override public int hashCode() { - return Objects.hash(name, enabled, refreshPolicy, roles, rules, metadata); + return Objects.hash(name, enabled, refreshPolicy, roles, roleTemplates, rules, metadata); } @Override @@ -104,11 +120,12 @@ public boolean equals(Object obj) { final PutRoleMappingRequest other = (PutRoleMappingRequest) obj; return (enabled == other.enabled) && - (refreshPolicy == other.refreshPolicy) && - Objects.equals(name, other.name) && - Objects.equals(roles, other.roles) && - Objects.equals(rules, other.rules) && - Objects.equals(metadata, other.metadata); + (refreshPolicy == other.refreshPolicy) && + Objects.equals(name, other.name) && + Objects.equals(roles, other.roles) && + Objects.equals(roleTemplates, other.roleTemplates) && + Objects.equals(rules, other.rules) && + Objects.equals(metadata, other.metadata); } @Override @@ -116,9 +133,9 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.startObject(); builder.field("enabled", enabled); builder.field("roles", roles); + builder.field("role_templates", roleTemplates); builder.field("rules", rules); builder.field("metadata", metadata); return builder.endObject(); } - } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/TemplateRoleName.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/TemplateRoleName.java new file mode 100644 index 0000000000000..a6263cee69d19 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/TemplateRoleName.java @@ -0,0 +1,117 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.security; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParserUtils; +import org.elasticsearch.common.xcontent.XContentType; + +import java.io.IOException; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; + +/** + * A role name that uses a dynamic template. + */ +public class TemplateRoleName implements ToXContentObject { + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("template-role-name", + true, args -> new TemplateRoleName((String) args[0], (Format) args[1])); + + static { + PARSER.declareString(ConstructingObjectParser.constructorArg(), Fields.TEMPLATE); + PARSER.declareField(optionalConstructorArg(), Format::fromXContent, Fields.FORMAT, ObjectParser.ValueType.STRING); + } + private final String template; + private final Format format; + + public TemplateRoleName(String template, Format format) { + this.template = Objects.requireNonNull(template); + this.format = Objects.requireNonNull(format); + } + + public TemplateRoleName(Map template, Format format) throws IOException { + this(Strings.toString(XContentBuilder.builder(XContentType.JSON.xContent()).map(template)), format); + } + + public String getTemplate() { + return template; + } + + public Format getFormat() { + return format; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TemplateRoleName that = (TemplateRoleName) o; + return Objects.equals(this.template, that.template) && + this.format == that.format; + } + + @Override + public int hashCode() { + return Objects.hash(template, format); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject() + .field(Fields.TEMPLATE.getPreferredName(), template) + .field(Fields.FORMAT.getPreferredName(), format.name().toLowerCase(Locale.ROOT)) + .endObject(); + } + + static TemplateRoleName fromXContent(XContentParser parser) throws IOException { + XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation); + return PARSER.parse(parser, null); + } + + + public enum Format { + STRING, JSON; + + private static Format fromXContent(XContentParser parser) throws IOException { + XContentParserUtils.ensureExpectedToken(XContentParser.Token.VALUE_STRING, parser.currentToken(), parser::getTokenLocation); + return Format.valueOf(parser.text().toUpperCase(Locale.ROOT)); + } + } + + public interface Fields { + ParseField TEMPLATE = new ParseField("template"); + ParseField FORMAT = new ParseField("format"); + } + +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java index b2c2028d0fbbd..1176cabcc3d9c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java @@ -141,8 +141,8 @@ public void testPutRoleMapping() throws IOException { .addExpression(FieldRoleMapperExpression.ofUsername(username)) .addExpression(FieldRoleMapperExpression.ofGroups(groupname)) .build(); - final PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(roleMappingName, true, Collections.singletonList( - rolename), rules, null, refreshPolicy); + final PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(roleMappingName, true, + Collections.singletonList(rolename), Collections.emptyList(), rules, null, refreshPolicy); final Request request = SecurityRequestConverters.putRoleMapping(putRoleMappingRequest); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java index 1afe6382fa5f8..b095ca5a9a0db 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java @@ -75,6 +75,7 @@ import org.elasticsearch.client.security.PutUserRequest; import org.elasticsearch.client.security.PutUserResponse; import org.elasticsearch.client.security.RefreshPolicy; +import org.elasticsearch.client.security.TemplateRoleName; import org.elasticsearch.client.security.support.ApiKey; import org.elasticsearch.client.security.support.CertificateInfo; import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression; @@ -94,6 +95,8 @@ import org.elasticsearch.common.util.set.Sets; import org.hamcrest.Matchers; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; import java.io.IOException; import java.time.Instant; import java.util.ArrayList; @@ -108,9 +111,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; - import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -120,6 +120,7 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isIn; +import static org.hamcrest.Matchers.iterableWithSize; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -366,8 +367,8 @@ public void testPutRoleMapping() throws Exception { .addExpression(FieldRoleMapperExpression.ofUsername("*")) .addExpression(FieldRoleMapperExpression.ofGroups("cn=admins,dc=example,dc=com")) .build(); - final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, Collections.singletonList("superuser"), - rules, null, RefreshPolicy.NONE); + final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, + Collections.singletonList("superuser"), Collections.emptyList(), rules, null, RefreshPolicy.NONE); final PutRoleMappingResponse response = client.security().putRoleMapping(request, RequestOptions.DEFAULT); // end::put-role-mapping-execute // tag::put-role-mapping-response @@ -381,7 +382,8 @@ public void testPutRoleMapping() throws Exception { .addExpression(FieldRoleMapperExpression.ofUsername("*")) .addExpression(FieldRoleMapperExpression.ofGroups("cn=admins,dc=example,dc=com")) .build(); - final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, Collections.singletonList("superuser"), + final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, Collections.emptyList(), + Collections.singletonList(new TemplateRoleName("{\"source\":\"{{username}}\"}", TemplateRoleName.Format.STRING)), rules, null, RefreshPolicy.NONE); // tag::put-role-mapping-execute-listener ActionListener listener = new ActionListener() { @@ -397,25 +399,32 @@ public void onFailure(Exception e) { }; // end::put-role-mapping-execute-listener + // avoid unused local warning + assertNotNull(listener); + // Replace the empty listener by a blocking listener in test - final CountDownLatch latch = new CountDownLatch(1); - listener = new LatchedActionListener<>(listener, latch); + final PlainActionFuture future = new PlainActionFuture<>(); + listener = future; // tag::put-role-mapping-execute-async client.security().putRoleMappingAsync(request, RequestOptions.DEFAULT, listener); // <1> // end::put-role-mapping-execute-async - assertTrue(latch.await(30L, TimeUnit.SECONDS)); + assertThat(future.get(), notNullValue()); + assertThat(future.get().isCreated(), is(false)); } } public void testGetRoleMappings() throws Exception { final RestHighLevelClient client = highLevelClient(); + final TemplateRoleName monitoring = new TemplateRoleName("{\"source\":\"monitoring\"}", TemplateRoleName.Format.STRING); + final TemplateRoleName template = new TemplateRoleName("{\"source\":\"{{username}}\"}", TemplateRoleName.Format.STRING); + final RoleMapperExpression rules1 = AnyRoleMapperExpression.builder().addExpression(FieldRoleMapperExpression.ofUsername("*")) .addExpression(FieldRoleMapperExpression.ofGroups("cn=admins,dc=example,dc=com")).build(); - final PutRoleMappingRequest putRoleMappingRequest1 = new PutRoleMappingRequest("mapping-example-1", true, Collections.singletonList( - "superuser"), rules1, null, RefreshPolicy.NONE); + final PutRoleMappingRequest putRoleMappingRequest1 = new PutRoleMappingRequest("mapping-example-1", true, Collections.emptyList(), + Arrays.asList(monitoring, template), rules1, null, RefreshPolicy.NONE); final PutRoleMappingResponse putRoleMappingResponse1 = client.security().putRoleMapping(putRoleMappingRequest1, RequestOptions.DEFAULT); boolean isCreated1 = putRoleMappingResponse1.isCreated(); @@ -424,8 +433,8 @@ public void testGetRoleMappings() throws Exception { "cn=admins,dc=example,dc=com")).build(); final Map metadata2 = new HashMap<>(); metadata2.put("k1", "v1"); - final PutRoleMappingRequest putRoleMappingRequest2 = new PutRoleMappingRequest("mapping-example-2", true, Collections.singletonList( - "monitoring"), rules2, metadata2, RefreshPolicy.NONE); + final PutRoleMappingRequest putRoleMappingRequest2 = new PutRoleMappingRequest("mapping-example-2", true, + Arrays.asList("superuser"), Collections.emptyList(), rules2, metadata2, RefreshPolicy.NONE); final PutRoleMappingResponse putRoleMappingResponse2 = client.security().putRoleMapping(putRoleMappingRequest2, RequestOptions.DEFAULT); boolean isCreated2 = putRoleMappingResponse2.isCreated(); @@ -445,7 +454,9 @@ public void testGetRoleMappings() throws Exception { assertThat(mappings.get(0).getName(), is("mapping-example-1")); assertThat(mappings.get(0).getExpression(), equalTo(rules1)); assertThat(mappings.get(0).getMetadata(), equalTo(Collections.emptyMap())); - assertThat(mappings.get(0).getRoles(), contains("superuser")); + assertThat(mappings.get(0).getRoles(), iterableWithSize(0)); + assertThat(mappings.get(0).getRoleTemplates(), iterableWithSize(2)); + assertThat(mappings.get(0).getRoleTemplates(), containsInAnyOrder(monitoring, template)); } { @@ -462,11 +473,13 @@ public void testGetRoleMappings() throws Exception { if (roleMapping.getName().equals("mapping-example-1")) { assertThat(roleMapping.getMetadata(), equalTo(Collections.emptyMap())); assertThat(roleMapping.getExpression(), equalTo(rules1)); - assertThat(roleMapping.getRoles(), contains("superuser")); + assertThat(roleMapping.getRoles(), emptyIterable()); + assertThat(roleMapping.getRoleTemplates(), contains(monitoring, template)); } else { assertThat(roleMapping.getMetadata(), equalTo(metadata2)); assertThat(roleMapping.getExpression(), equalTo(rules2)); - assertThat(roleMapping.getRoles(), contains("monitoring")); + assertThat(roleMapping.getRoles(), contains("superuser")); + assertThat(roleMapping.getRoleTemplates(), emptyIterable()); } } } @@ -485,11 +498,13 @@ public void testGetRoleMappings() throws Exception { if (roleMapping.getName().equals("mapping-example-1")) { assertThat(roleMapping.getMetadata(), equalTo(Collections.emptyMap())); assertThat(roleMapping.getExpression(), equalTo(rules1)); - assertThat(roleMapping.getRoles(), contains("superuser")); + assertThat(roleMapping.getRoles(), emptyIterable()); + assertThat(roleMapping.getRoleTemplates(), containsInAnyOrder(monitoring, template)); } else { assertThat(roleMapping.getMetadata(), equalTo(metadata2)); assertThat(roleMapping.getExpression(), equalTo(rules2)); - assertThat(roleMapping.getRoles(), contains("monitoring")); + assertThat(roleMapping.getRoles(), contains("superuser")); + assertThat(roleMapping.getRoleTemplates(), emptyIterable()); } } } @@ -1093,8 +1108,8 @@ public void testDeleteRoleMapping() throws Exception { { // Create role mappings final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("*"); - final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, Collections.singletonList("superuser"), - rules, null, RefreshPolicy.NONE); + final PutRoleMappingRequest request = new PutRoleMappingRequest("mapping-example", true, + Collections.singletonList("superuser"), Collections.emptyList(), rules, null, RefreshPolicy.NONE); final PutRoleMappingResponse response = client.security().putRoleMapping(request, RequestOptions.DEFAULT); boolean isCreated = response.isCreated(); assertTrue(isCreated); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java index 29bc7812f5b7e..f30307ebde51a 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/ExpressionRoleMappingTests.java @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.Map; +import static java.util.Collections.singletonList; import static org.hamcrest.Matchers.equalTo; public class ExpressionRoleMappingTests extends ESTestCase { @@ -59,48 +60,53 @@ public void usedDeprecatedName(String usedName, String modernName) { public void usedDeprecatedField(String usedName, String replacedWith) { } }, json), "example-role-mapping"); - final ExpressionRoleMapping expectedRoleMapping = new ExpressionRoleMapping("example-role-mapping", FieldRoleMapperExpression - .ofKeyValues("realm.name", "kerb1"), Collections.singletonList("superuser"), null, true); + final ExpressionRoleMapping expectedRoleMapping = new ExpressionRoleMapping("example-role-mapping", + FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), + singletonList("superuser"), Collections.emptyList(), + null, true); assertThat(expressionRoleMapping, equalTo(expectedRoleMapping)); } public void testEqualsHashCode() { - final ExpressionRoleMapping expressionRoleMapping = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression - .ofKeyValues("realm.name", "kerb1"), Collections.singletonList("superuser"), null, true); - EqualsHashCodeTestUtils.checkEqualsAndHashCode(expressionRoleMapping, (original) -> { - return new ExpressionRoleMapping(original.getName(), original.getExpression(), original.getRoles(), original.getMetadata(), - original.isEnabled()); - }); - EqualsHashCodeTestUtils.checkEqualsAndHashCode(expressionRoleMapping, (original) -> { - return new ExpressionRoleMapping(original.getName(), original.getExpression(), original.getRoles(), original.getMetadata(), - original.isEnabled()); - }, ExpressionRoleMappingTests::mutateTestItem); + final ExpressionRoleMapping expressionRoleMapping = new ExpressionRoleMapping("kerberosmapping", + FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), + singletonList("superuser"), Collections.emptyList(), + null, true); + EqualsHashCodeTestUtils.checkEqualsAndHashCode(expressionRoleMapping, original -> + new ExpressionRoleMapping(original.getName(), original.getExpression(), original.getRoles(), original.getRoleTemplates(), + original.getMetadata(), original.isEnabled()), ExpressionRoleMappingTests::mutateTestItem); } - private static ExpressionRoleMapping mutateTestItem(ExpressionRoleMapping original) { + private static ExpressionRoleMapping mutateTestItem(ExpressionRoleMapping original) throws IOException { ExpressionRoleMapping mutated = null; - switch (randomIntBetween(0, 4)) { + switch (randomIntBetween(0, 5)) { case 0: - mutated = new ExpressionRoleMapping("namechanged", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections - .singletonList("superuser"), null, true); + mutated = new ExpressionRoleMapping("namechanged", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), + singletonList("superuser"), Collections.emptyList(), null, true); break; case 1: - mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("changed", "changed"), Collections - .singletonList("superuser"), null, true); + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("changed", "changed"), + singletonList("superuser"), Collections.emptyList(), null, true); break; case 2: - mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections - .singletonList("changed"), null, true); + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), + singletonList("changed"), Collections.emptyList(), null, true); break; case 3: Map metadata = new HashMap<>(); metadata.put("a", "b"); - mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections - .singletonList("superuser"), metadata, true); + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), + singletonList("superuser"), Collections.emptyList(), metadata, true); break; case 4: - mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), Collections - .singletonList("superuser"), null, false); + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), + Collections.emptyList(), + singletonList(new TemplateRoleName(Collections.singletonMap("source", "superuser"), TemplateRoleName.Format.STRING)), + null, true); + break; + case 5: + mutated = new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", "kerb1"), + singletonList("superuser"), Collections.emptyList(), null, false); break; } return mutated; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java index b612c9ead28a5..20883b859f9ae 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetRoleMappingsResponseTests.java @@ -74,9 +74,10 @@ public void usedDeprecatedField(String usedName, String replacedWith) { }, json)); final List expectedRoleMappingsList = new ArrayList<>(); expectedRoleMappingsList.add(new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", - "kerb1"), Collections.singletonList("superuser"), null, true)); + "kerb1"), Collections.singletonList("superuser"), Collections.emptyList(), null, true)); expectedRoleMappingsList.add(new ExpressionRoleMapping("ldapmapping", FieldRoleMapperExpression.ofGroups( - "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), Collections.singletonList("monitoring"), null, false)); + "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), Collections.singletonList("monitoring"), Collections.emptyList(), + null, false)); final GetRoleMappingsResponse expectedResponse = new GetRoleMappingsResponse(expectedRoleMappingsList); assertThat(response, equalTo(expectedResponse)); } @@ -84,7 +85,7 @@ public void usedDeprecatedField(String usedName, String replacedWith) { public void testEqualsHashCode() { final List roleMappingsList = new ArrayList<>(); roleMappingsList.add(new ExpressionRoleMapping("kerberosmapping", FieldRoleMapperExpression.ofKeyValues("realm.name", - "kerb1"), Collections.singletonList("superuser"), null, true)); + "kerb1"), Collections.singletonList("superuser"), Collections.emptyList(), null, true)); final GetRoleMappingsResponse response = new GetRoleMappingsResponse(roleMappingsList); assertNotNull(response); EqualsHashCodeTestUtils.checkEqualsAndHashCode(response, (original) -> { @@ -101,15 +102,16 @@ private static GetRoleMappingsResponse mutateTestItem(GetRoleMappingsResponse or case 0: final List roleMappingsList1 = new ArrayList<>(); roleMappingsList1.add(new ExpressionRoleMapping("ldapmapping", FieldRoleMapperExpression.ofGroups( - "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), Collections.singletonList("monitoring"), null, false)); + "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), Collections.singletonList("monitoring"), Collections.emptyList(), + null, false)); mutated = new GetRoleMappingsResponse(roleMappingsList1); break; case 1: final List roleMappingsList2 = new ArrayList<>(); - ExpressionRoleMapping orginialRoleMapping = original.getMappings().get(0); - roleMappingsList2.add(new ExpressionRoleMapping(orginialRoleMapping.getName(), FieldRoleMapperExpression.ofGroups( - "cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), - orginialRoleMapping.getRoles(), orginialRoleMapping.getMetadata(), !orginialRoleMapping.isEnabled())); + ExpressionRoleMapping originalRoleMapping = original.getMappings().get(0); + roleMappingsList2.add(new ExpressionRoleMapping(originalRoleMapping.getName(), + FieldRoleMapperExpression.ofGroups("cn=ipausers,cn=groups,cn=accounts,dc=ipademo,dc=local"), originalRoleMapping.getRoles(), + Collections.emptyList(), originalRoleMapping.getMetadata(), !originalRoleMapping.isEnabled())); mutated = new GetRoleMappingsResponse(roleMappingsList2); break; } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutRoleMappingRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutRoleMappingRequestTests.java index f0a3f7572ef3b..bf5ba34bffc5c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutRoleMappingRequestTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/PutRoleMappingRequestTests.java @@ -29,12 +29,12 @@ import org.elasticsearch.test.EqualsHashCodeTestUtils; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import static org.hamcrest.Matchers.equalTo; @@ -49,7 +49,8 @@ public void testPutRoleMappingRequest() { metadata.put("k1", "v1"); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); - PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy); + PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, Collections.emptyList(), rules, + metadata, refreshPolicy); assertNotNull(putRoleMappingRequest); assertThat(putRoleMappingRequest.getName(), equalTo(name)); assertThat(putRoleMappingRequest.isEnabled(), equalTo(enabled)); @@ -68,23 +69,39 @@ public void testPutRoleMappingRequestThrowsExceptionForNullOrEmptyName() { metadata.put("k1", "v1"); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); - final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class, () -> new PutRoleMappingRequest(name, enabled, - roles, rules, metadata, refreshPolicy)); + final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class, + () -> new PutRoleMappingRequest(name, enabled, roles, Collections.emptyList(), rules, metadata, refreshPolicy)); assertThat(ile.getMessage(), equalTo("role-mapping name is missing")); } - public void testPutRoleMappingRequestThrowsExceptionForNullOrEmptyRoles() { + public void testPutRoleMappingRequestThrowsExceptionForNullRoles() { final String name = randomAlphaOfLength(5); final boolean enabled = randomBoolean(); - final List roles = randomBoolean() ? null : Collections.emptyList(); + final List roles = null ; + final List roleTemplates = Collections.emptyList(); final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); final Map metadata = new HashMap<>(); metadata.put("k1", "v1"); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); - final IllegalArgumentException ile = expectThrows(IllegalArgumentException.class, () -> new PutRoleMappingRequest(name, enabled, - roles, rules, metadata, refreshPolicy)); - assertThat(ile.getMessage(), equalTo("role-mapping roles are missing")); + final RuntimeException ex = expectThrows(RuntimeException.class, + () -> new PutRoleMappingRequest(name, enabled, roles, roleTemplates, rules, metadata, refreshPolicy)); + assertThat(ex.getMessage(), equalTo("role-mapping roles cannot be null")); + } + + public void testPutRoleMappingRequestThrowsExceptionForEmptyRoles() { + final String name = randomAlphaOfLength(5); + final boolean enabled = randomBoolean(); + final List roles = Collections.emptyList(); + final List roleTemplates = Collections.emptyList(); + final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + final RuntimeException ex = expectThrows(RuntimeException.class, + () -> new PutRoleMappingRequest(name, enabled, roles, roleTemplates, rules, metadata, refreshPolicy)); + assertThat(ex.getMessage(), equalTo("in a role-mapping, one of roles or role_templates is required")); } public void testPutRoleMappingRequestThrowsExceptionForNullRules() { @@ -96,7 +113,8 @@ public void testPutRoleMappingRequestThrowsExceptionForNullRules() { metadata.put("k1", "v1"); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); - expectThrows(NullPointerException.class, () -> new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy)); + expectThrows(NullPointerException.class, () -> new PutRoleMappingRequest(name, enabled, roles, Collections.emptyList(), rules, + metadata, refreshPolicy)); } public void testPutRoleMappingRequestToXContent() throws IOException { @@ -108,7 +126,8 @@ public void testPutRoleMappingRequestToXContent() throws IOException { metadata.put("k1", "v1"); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); - final PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy); + final PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, Collections.emptyList(), rules, + metadata, refreshPolicy); final XContentBuilder builder = XContentFactory.jsonBuilder(); putRoleMappingRequest.toXContent(builder, ToXContent.EMPTY_PARAMS); @@ -117,6 +136,42 @@ public void testPutRoleMappingRequestToXContent() throws IOException { "{"+ "\"enabled\":" + enabled + "," + "\"roles\":[\"superuser\"]," + + "\"role_templates\":[]," + + "\"rules\":{" + + "\"field\":{\"username\":[\"user\"]}" + + "}," + + "\"metadata\":{\"k1\":\"v1\"}" + + "}"; + + assertThat(output, equalTo(expected)); + } + + public void testPutRoleMappingRequestWithTemplateToXContent() throws IOException { + final String name = randomAlphaOfLength(5); + final boolean enabled = randomBoolean(); + final List templates = Arrays.asList( + new TemplateRoleName(Collections.singletonMap("source" , "_realm_{{realm.name}}"), TemplateRoleName.Format.STRING), + new TemplateRoleName(Collections.singletonMap("source" , "some_role"), TemplateRoleName.Format.STRING) + ); + final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); + final Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); + + final PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, Collections.emptyList(), templates, + rules, metadata, refreshPolicy); + + final XContentBuilder builder = XContentFactory.jsonBuilder(); + putRoleMappingRequest.toXContent(builder, ToXContent.EMPTY_PARAMS); + final String output = Strings.toString(builder); + final String expected = + "{"+ + "\"enabled\":" + enabled + "," + + "\"roles\":[]," + + "\"role_templates\":[" + + "{\"template\":\"{\\\"source\\\":\\\"_realm_{{realm.name}}\\\"}\",\"format\":\"string\"}," + + "{\"template\":\"{\\\"source\\\":\\\"some_role\\\"}\",\"format\":\"string\"}" + + "]," + "\"rules\":{" + "\"field\":{\"username\":[\"user\"]}" + "}," + @@ -129,48 +184,59 @@ public void testPutRoleMappingRequestToXContent() throws IOException { public void testEqualsHashCode() { final String name = randomAlphaOfLength(5); final boolean enabled = randomBoolean(); - final List roles = Collections.singletonList("superuser"); + final List roles; + final List templates; + if (randomBoolean()) { + roles = Arrays.asList(randomArray(1, 3, String[]::new, () -> randomAlphaOfLengthBetween(6, 12))); + templates = Collections.emptyList(); + } else { + roles = Collections.emptyList(); + templates = Arrays.asList( + randomArray(1, 3, TemplateRoleName[]::new, + () -> new TemplateRoleName(randomAlphaOfLengthBetween(12, 60), randomFrom(TemplateRoleName.Format.values())) + )); + } final RoleMapperExpression rules = FieldRoleMapperExpression.ofUsername("user"); final Map metadata = new HashMap<>(); metadata.put("k1", "v1"); final RefreshPolicy refreshPolicy = randomFrom(RefreshPolicy.values()); - PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, rules, metadata, refreshPolicy); + PutRoleMappingRequest putRoleMappingRequest = new PutRoleMappingRequest(name, enabled, roles, templates, rules, metadata, + refreshPolicy); assertNotNull(putRoleMappingRequest); EqualsHashCodeTestUtils.checkEqualsAndHashCode(putRoleMappingRequest, (original) -> { - return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), original - .getMetadata(), original.getRefreshPolicy()); - }); - EqualsHashCodeTestUtils.checkEqualsAndHashCode(putRoleMappingRequest, (original) -> { - return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), original - .getMetadata(), original.getRefreshPolicy()); + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRoleTemplates(), + original.getRules(), original.getMetadata(), original.getRefreshPolicy()); }, PutRoleMappingRequestTests::mutateTestItem); } private static PutRoleMappingRequest mutateTestItem(PutRoleMappingRequest original) { - switch (randomIntBetween(0, 4)) { + switch (randomIntBetween(0, 5)) { case 0: - return new PutRoleMappingRequest(randomAlphaOfLength(5), original.isEnabled(), original.getRoles(), original.getRules(), - original.getMetadata(), original.getRefreshPolicy()); + return new PutRoleMappingRequest(randomAlphaOfLength(5), original.isEnabled(), original.getRoles(), + original.getRoleTemplates(), original.getRules(), original.getMetadata(), original.getRefreshPolicy()); case 1: - return new PutRoleMappingRequest(original.getName(), !original.isEnabled(), original.getRoles(), original.getRules(), - original.getMetadata(), original.getRefreshPolicy()); + return new PutRoleMappingRequest(original.getName(), !original.isEnabled(), original.getRoles(), original.getRoleTemplates(), + original.getRules(), original.getMetadata(), original.getRefreshPolicy()); case 2: - return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRoleTemplates(), FieldRoleMapperExpression.ofGroups("group"), original.getMetadata(), original.getRefreshPolicy()); case 3: - return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), - Collections.emptyMap(), original.getRefreshPolicy()); + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRoleTemplates(), + original.getRules(), Collections.emptyMap(), original.getRefreshPolicy()); case 4: - List values = Arrays.stream(RefreshPolicy.values()) - .filter(rp -> rp != original.getRefreshPolicy()) - .collect(Collectors.toList()); - return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRules(), original - .getMetadata(), randomFrom(values)); + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), original.getRoles(), original.getRoleTemplates(), + original.getRules(), original.getMetadata(), + randomValueOtherThan(original.getRefreshPolicy(), () -> randomFrom(RefreshPolicy.values()))); + case 5: + List roles = new ArrayList<>(original.getRoles()); + roles.add(randomAlphaOfLengthBetween(3, 5)); + return new PutRoleMappingRequest(original.getName(), original.isEnabled(), roles, Collections.emptyList(), + original.getRules(), original.getMetadata(), original.getRefreshPolicy()); + default: - return new PutRoleMappingRequest(randomAlphaOfLength(5), original.isEnabled(), original.getRoles(), original.getRules(), - original.getMetadata(), original.getRefreshPolicy()); + throw new IllegalStateException("Bad random value"); } } diff --git a/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc b/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc index de2ad5af3081b..e7c5fbaf60ad1 100644 --- a/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc +++ b/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc @@ -50,15 +50,46 @@ mapping is performed. user. Within the `metadata` object, keys beginning with `_` are reserved for system usage. -`roles` (required):: -(list) A list of roles that are granted to the users that match the role mapping -rules. +`roles`:: +(list of strings) A list of role names that are granted to the users that match +the role mapping rules. +_Exactly one of `roles` or `role_templates` must be specified_. + +`role_templates`:: +(list of objects) A list of mustache templates that will be evaluated to +determine the roles names that should granted to the users that match the role +mapping rules. +The format of these objects is defined below. +_Exactly one of `roles` or `role_templates` must be specified_. `rules` (required):: (object) The rules that determine which users should be matched by the mapping. A rule is a logical condition that is expressed by using a JSON DSL. See <>. +==== Role Templates + +The most common use for role mappings is to create a mapping from a known value +on the user to a fixed role name. +For example, all users in the `cn=admin,dc=example,dc=com` LDAP group should be +given the `superuser` role in {es}. +The `roles` field is used for this purpose. + +For more complex needs it is possible to use Mustache templates to dynamically +determine the names of the roles that should be granted to the user. +The `role_templates` field is used for this purpose. + +All of the <> that are available in the +role mapping `rules` are also available in the role templates. Thus it is possible +to assign a user to a role that reflects their `username`, their `groups` or the +name of the `realm` to which they authenticated. + +By default a template is evaluated to produce a single string that is the name +of the role which should be assigned to the user. If the `format` of the template +is set to `"json"` then the template is expected to produce a JSON string, or an +array of JSON strings for the role name(s). + +The Examples section below demonstrates the use of templated role names. ==== Authorization @@ -117,12 +148,26 @@ POST /_security/role_mapping/mapping2 -------------------------------------------------- // CONSOLE +The following example matches users who authenticated against a specific realm: +[source, js] +------------------------------------------------------------ +POST /_security/role_mapping/mapping3 +{ + "roles": [ "ldap-user" ], + "enabled": true, + "rules": { + "field" : { "realm.name" : "ldap1" } + } +} +------------------------------------------------------------ +// CONSOLE + The following example matches any user where either the username is `esadmin` or the user is in the `cn=admin,dc=example,dc=com` group: [source, js] ------------------------------------------------------------ -POST /_security/role_mapping/mapping3 +POST /_security/role_mapping/mapping4 { "roles": [ "superuser" ], "enabled": true, @@ -144,25 +189,52 @@ POST /_security/role_mapping/mapping3 ------------------------------------------------------------ // CONSOLE -The following example matches users who authenticated against a specific realm: +The example above is useful when the group names in your identity management +system (such as Active Directory, or a SAML Identity Provider) do not have a +1-to-1 correspondence with the names of roles in {es}. The role mapping is the +means by which you link a _group name_ with a _role name_. + +However, in rare cases the names of your groups may be an exact match for the +names of your {es} roles. This can be the case when your SAML Identity Provider +includes its own "group mapping" feature and can be configured to release {es} +role names in the user's SAML attributes. + +In these cases it is possible to use a template that treats the group names as +role names. + +*Note*: This should only be done if you intend to define roles for all of the +provided groups. Mapping a user to a large number of unnecessary or undefined +roles is inefficient and can have a negative effect on system performance. +If you only need to map a subset of the groups, then you should do this +using explicit mappings. + [source, js] ------------------------------------------------------------ -POST /_security/role_mapping/mapping4 +POST /_security/role_mapping/mapping5 { - "roles": [ "ldap-user" ], - "enabled": true, + "role_templates": [ + { + "template": { "source": "{{#tojson}}groups{{/tojson}}" }, <1> + "format" : "json" <2> + } + ], "rules": { - "field" : { "realm.name" : "ldap1" } - } + "field" : { "realm.name" : "saml1" } + }, + "enabled": true } ------------------------------------------------------------ // CONSOLE +<1> The `tojson` mustache function is used to convert the list of + group names into a valid JSON array. +<2> Because the template produces a JSON array, the format must be + set to `json`. The following example matches users within a specific LDAP sub-tree: [source, js] ------------------------------------------------------------ -POST /_security/role_mapping/mapping5 +POST /_security/role_mapping/mapping6 { "roles": [ "example-user" ], "enabled": true, @@ -178,7 +250,7 @@ specific realm: [source, js] ------------------------------------------------------------ -POST /_security/role_mapping/mapping6 +POST /_security/role_mapping/mapping7 { "roles": [ "ldap-example-user" ], "enabled": true, @@ -203,7 +275,7 @@ following mapping matches any user where *all* of these conditions are met: [source, js] ------------------------------------------------------------ -POST /_security/role_mapping/mapping7 +POST /_security/role_mapping/mapping8 { "roles": [ "superuser" ], "enabled": true, @@ -240,3 +312,32 @@ POST /_security/role_mapping/mapping7 } ------------------------------------------------------------ // CONSOLE + +A templated role can be used to automatically map every user to their own +custom role. The role itself can be defined through the +<> or using a +{stack-ov}/implementing-custom-roles-provider.html[custom roles provider]. + +In this example every user who authenticates using the "cloud-saml" realm +will be automatically mapped to two roles - the `"saml_user"` role and a +role that is their username prefixed with `_user_`. +As an example, the user `nwong` would be assigned the `saml_user` and +`_user_nwong` roles. + +[source, js] +------------------------------------------------------------ +POST /_security/role_mapping/mapping9 +{ + "rules": { "field": { "realm.name": "cloud-saml" } }, + "role_templates": [ + { "template": { "source" : "saml_user" } }, <1> + { "template": { "source" : "_user_{{username}}" } } + ], + "enabled": true +} +------------------------------------------------------------ +// CONSOLE +<1> Because it is not possible to specify both `roles` and `role_templates` in + the same role mapping, we can apply a "fixed name" role by using a template + that has no substitutions. + diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java index a745215fa5533..dc8403b7bd548 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java @@ -57,9 +57,9 @@ import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleType; -import org.elasticsearch.xpack.core.indexlifecycle.SetPriorityAction; import org.elasticsearch.xpack.core.indexlifecycle.ReadOnlyAction; import org.elasticsearch.xpack.core.indexlifecycle.RolloverAction; +import org.elasticsearch.xpack.core.indexlifecycle.SetPriorityAction; import org.elasticsearch.xpack.core.indexlifecycle.ShrinkAction; import org.elasticsearch.xpack.core.indexlifecycle.TimeseriesLifecycleType; import org.elasticsearch.xpack.core.indexlifecycle.UnfollowAction; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequest.java index 087e29ec8b56a..168adaa111658 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequest.java @@ -5,12 +5,14 @@ */ package org.elasticsearch.xpack.core.security.action.rolemapping; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping; +import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionParser; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.RoleMapperExpression; import org.elasticsearch.xpack.core.security.support.MetadataUtils; @@ -35,6 +37,7 @@ public class PutRoleMappingRequest extends ActionRequest private String name = null; private boolean enabled = true; private List roles = Collections.emptyList(); + private List roleTemplates = Collections.emptyList(); private RoleMapperExpression rules = null; private Map metadata = Collections.emptyMap(); private RefreshPolicy refreshPolicy = RefreshPolicy.IMMEDIATE; @@ -46,20 +49,20 @@ public PutRoleMappingRequest() { public ActionRequestValidationException validate() { ActionRequestValidationException validationException = null; if (name == null) { - validationException = addValidationError("role-mapping name is missing", - validationException); + validationException = addValidationError("role-mapping name is missing", validationException); } - if (roles.isEmpty()) { - validationException = addValidationError("role-mapping roles are missing", - validationException); + if (roles.isEmpty() && roleTemplates.isEmpty()) { + validationException = addValidationError("role-mapping roles or role-templates are missing", validationException); + } + if (roles.size() > 0 && roleTemplates.size() > 0) { + validationException = addValidationError("role-mapping cannot have both roles and role-templates", validationException); } if (rules == null) { - validationException = addValidationError("role-mapping rules are missing", - validationException); + validationException = addValidationError("role-mapping rules are missing", validationException); } if (MetadataUtils.containsReservedMetadata(metadata)) { - validationException = addValidationError("metadata keys may not start with [" + - MetadataUtils.RESERVED_PREFIX + "]", validationException); + validationException = addValidationError("metadata keys may not start with [" + MetadataUtils.RESERVED_PREFIX + "]", + validationException); } return validationException; } @@ -84,10 +87,18 @@ public List getRoles() { return Collections.unmodifiableList(roles); } + public List getRoleTemplates() { + return Collections.unmodifiableList(roleTemplates); + } + public void setRoles(List roles) { this.roles = new ArrayList<>(roles); } + public void setRoleTemplates(List templates) { + this.roleTemplates = new ArrayList<>(templates); + } + public RoleMapperExpression getRules() { return rules; } @@ -126,6 +137,9 @@ public void readFrom(StreamInput in) throws IOException { this.name = in.readString(); this.enabled = in.readBoolean(); this.roles = in.readStringList(); + if (in.getVersion().onOrAfter(Version.V_8_0_0)) { + this.roleTemplates = in.readList(TemplateRoleName::new); + } this.rules = ExpressionParser.readExpression(in); this.metadata = in.readMap(); this.refreshPolicy = RefreshPolicy.readFrom(in); @@ -137,6 +151,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(name); out.writeBoolean(enabled); out.writeStringCollection(roles); + if (out.getVersion().onOrAfter(Version.V_8_0_0)) { + out.writeList(roleTemplates); + } ExpressionParser.writeExpression(rules, out); out.writeMap(metadata); refreshPolicy.writeTo(out); @@ -147,6 +164,7 @@ public ExpressionRoleMapping getMapping() { name, rules, roles, + roleTemplates, metadata, enabled ); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequestBuilder.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequestBuilder.java index c74952e9dfd09..14f722d169410 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequestBuilder.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/rolemapping/PutRoleMappingRequestBuilder.java @@ -5,18 +5,19 @@ */ package org.elasticsearch.xpack.core.security.action.rolemapping; -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; - import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.support.WriteRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping; +import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.RoleMapperExpression; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; + /** * Builder for requests to add/update a role-mapping to the native store * @@ -38,6 +39,7 @@ public PutRoleMappingRequestBuilder source(String name, BytesReference source, request.setName(name); request.setEnabled(mapping.isEnabled()); request.setRoles(mapping.getRoles()); + request.setRoleTemplates(mapping.getRoleTemplates()); request.setRules(mapping.getExpression()); request.setMetadata(mapping.getMetadata()); return this; @@ -52,6 +54,10 @@ public PutRoleMappingRequestBuilder roles(String... roles) { request.setRoles(Arrays.asList(roles)); return this; } + public PutRoleMappingRequestBuilder roleTemplates(TemplateRoleName... templates) { + request.setRoleTemplates(Arrays.asList(templates)); + return this; + } public PutRoleMappingRequestBuilder expression(RoleMapperExpression expression) { request.setRules(expression); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/ExpressionRoleMapping.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/ExpressionRoleMapping.java index 95d1e9fa77149..dd5fb08fa14b7 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/ExpressionRoleMapping.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/ExpressionRoleMapping.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.core.security.authc.support.mapper; +import org.elasticsearch.Version; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.Strings; @@ -15,20 +16,28 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionModel; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionParser; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.RoleMapperExpression; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; import java.util.function.BiConsumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * A representation of a single role-mapping for use in NativeRoleMappingStore. @@ -50,27 +59,30 @@ public class ExpressionRoleMapping implements ToXContentObject, Writeable { static { PARSER.declareStringArray(Builder::roles, Fields.ROLES); - PARSER.declareField(Builder::rules, ExpressionParser::parseObject, Fields.RULES, ObjectParser.ValueType.OBJECT); - PARSER.declareField(Builder::metadata, XContentParser::map, Fields.METADATA, ObjectParser.ValueType.OBJECT); + PARSER.declareObjectArray(Builder::roleTemplates, (parser, ctx) -> TemplateRoleName.parse(parser), Fields.ROLE_TEMPLATES); + PARSER.declareField(Builder::rules, ExpressionParser::parseObject, Fields.RULES, ValueType.OBJECT); + PARSER.declareField(Builder::metadata, XContentParser::map, Fields.METADATA, ValueType.OBJECT); PARSER.declareBoolean(Builder::enabled, Fields.ENABLED); BiConsumer ignored = (b, v) -> { }; // skip the doc_type and type fields in case we're parsing directly from the index PARSER.declareString(ignored, new ParseField(NativeRoleMappingStoreField.DOC_TYPE_FIELD)); PARSER.declareString(ignored, new ParseField(UPGRADE_API_TYPE_FIELD)); - } + } private final String name; private final RoleMapperExpression expression; private final List roles; + private final List roleTemplates ; private final Map metadata; private final boolean enabled; - public ExpressionRoleMapping(String name, RoleMapperExpression expr, List roles, Map metadata, - boolean enabled) { + public ExpressionRoleMapping(String name, RoleMapperExpression expr, List roles, List templates, + Map metadata, boolean enabled) { this.name = name; this.expression = expr; - this.roles = roles; + this.roles = roles == null ? Collections.emptyList() : roles; + this.roleTemplates = templates == null ? Collections.emptyList() : templates; this.metadata = metadata; this.enabled = enabled; } @@ -79,6 +91,11 @@ public ExpressionRoleMapping(StreamInput in) throws IOException { this.name = in.readString(); this.enabled = in.readBoolean(); this.roles = in.readStringList(); + if (in.getVersion().onOrAfter(Version.V_8_0_0)) { + this.roleTemplates = in.readList(TemplateRoleName::new); + } else { + this.roleTemplates = Collections.emptyList(); + } this.expression = ExpressionParser.readExpression(in); this.metadata = in.readMap(); } @@ -88,6 +105,9 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(name); out.writeBoolean(enabled); out.writeStringCollection(roles); + if (out.getVersion().onOrAfter(Version.V_8_0_0)) { + out.writeList(roleTemplates); + } ExpressionParser.writeExpression(expression, out); out.writeMap(metadata); } @@ -103,7 +123,7 @@ public String getName() { /** * The expression that determines whether the roles in this mapping should be applied to any given user. * If the expression - * {@link RoleMapperExpression#match(org.elasticsearch.xpack.security.authc.support.mapper.expressiondsl.ExpressionModel) matches} a + * {@link RoleMapperExpression#match(ExpressionModel) matches} a * org.elasticsearch.xpack.security.authc.support.UserRoleMapper.UserData user, then the user should be assigned this mapping's * {@link #getRoles() roles} */ @@ -119,6 +139,14 @@ public List getRoles() { return Collections.unmodifiableList(roles); } + /** + * The list of {@link RoleDescriptor roles} (specified by a {@link TemplateRoleName template} that evaluates to one or more names) + * that should be assigned to users that match the {@link #getExpression() expression} in this mapping. + */ + public List getRoleTemplates() { + return Collections.unmodifiableList(roleTemplates); + } + /** * Meta-data for this mapping. This exists for external systems of user to track information about this mapping such as where it was * sourced from, when it was loaded, etc. @@ -137,7 +165,30 @@ public boolean isEnabled() { @Override public String toString() { - return getClass().getSimpleName() + "<" + name + " ; " + roles + " = " + Strings.toString(expression) + ">"; + return getClass().getSimpleName() + "<" + name + " ; " + roles + "/" + roleTemplates + " = " + Strings.toString(expression) + ">"; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ExpressionRoleMapping that = (ExpressionRoleMapping) o; + return this.enabled == that.enabled && + Objects.equals(this.name, that.name) && + Objects.equals(this.expression, that.expression) && + Objects.equals(this.roles, that.roles) && + Objects.equals(this.roleTemplates, that.roleTemplates) && + Objects.equals(this.metadata, that.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(name, expression, roles, roleTemplates, metadata, enabled); } /** @@ -157,7 +208,7 @@ public static ExpressionRoleMapping parse(String name, BytesReference source, XC */ public static ExpressionRoleMapping parse(String name, XContentParser parser) throws IOException { try { - final Builder builder = PARSER.parse(parser, null); + final Builder builder = PARSER.parse(parser, name); return builder.build(name); } catch (IllegalArgumentException | IllegalStateException e) { throw new ParsingException(parser.getTokenLocation(), e.getMessage(), e); @@ -166,38 +217,55 @@ public static ExpressionRoleMapping parse(String name, XContentParser parser) th /** * Converts this {@link ExpressionRoleMapping} into XContent that is compatible with - * the format handled by {@link #parse(String, XContentParser)}. + * the format handled by {@link #parse(String, BytesReference, XContentType)}. */ @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { return toXContent(builder, params, false); } - public XContentBuilder toXContent(XContentBuilder builder, Params params, boolean includeDocType) throws IOException { + public XContentBuilder toXContent(XContentBuilder builder, Params params, boolean indexFormat) throws IOException { builder.startObject(); builder.field(Fields.ENABLED.getPreferredName(), enabled); - builder.startArray(Fields.ROLES.getPreferredName()); - for (String r : roles) { - builder.value(r); + if (roles.isEmpty() == false) { + builder.startArray(Fields.ROLES.getPreferredName()); + for (String r : roles) { + builder.value(r); + } + builder.endArray(); + } + if (roleTemplates.isEmpty() == false) { + builder.startArray(Fields.ROLE_TEMPLATES.getPreferredName()); + for (TemplateRoleName r : roleTemplates) { + builder.value(r); + } + builder.endArray(); } - builder.endArray(); builder.field(Fields.RULES.getPreferredName()); expression.toXContent(builder, params); builder.field(Fields.METADATA.getPreferredName(), metadata); - if (includeDocType) { + if (indexFormat) { builder.field(NativeRoleMappingStoreField.DOC_TYPE_FIELD, NativeRoleMappingStoreField.DOC_TYPE_ROLE_MAPPING); } return builder.endObject(); } + public Set getRoleNames(ScriptService scriptService, ExpressionModel model) { + return Stream.concat(this.roles.stream(), + this.roleTemplates.stream() + .flatMap(r -> r.getRoleNames(scriptService, model).stream()) + ).collect(Collectors.toSet()); + } + /** * Used to facilitate the use of {@link ObjectParser} (via {@link #PARSER}). */ private static class Builder { private RoleMapperExpression rules; private List roles; + private List roleTemplates; private Map metadata = Collections.emptyMap(); private Boolean enabled; @@ -207,7 +275,12 @@ Builder rules(RoleMapperExpression expression) { } Builder roles(List roles) { - this.roles = roles; + this.roles = new ArrayList<>(roles); + return this; + } + + Builder roleTemplates(List templates) { + this.roleTemplates = new ArrayList<>(templates); return this; } @@ -222,7 +295,7 @@ Builder enabled(boolean enabled) { } private ExpressionRoleMapping build(String name) { - if (roles == null) { + if (roles == null && roleTemplates == null) { throw missingField(name, Fields.ROLES); } if (rules == null) { @@ -231,17 +304,17 @@ private ExpressionRoleMapping build(String name) { if (enabled == null) { throw missingField(name, Fields.ENABLED); } - return new ExpressionRoleMapping(name, rules, roles, metadata, enabled); + return new ExpressionRoleMapping(name, rules, roles, roleTemplates, metadata, enabled); } private IllegalStateException missingField(String id, ParseField field) { return new IllegalStateException("failed to parse role-mapping [" + id + "]. missing field [" + field + "]"); } - } public interface Fields { ParseField ROLES = new ParseField("roles"); + ParseField ROLE_TEMPLATES = new ParseField("role_templates"); ParseField ENABLED = new ParseField("enabled"); ParseField RULES = new ParseField("rules"); ParseField METADATA = new ParseField("metadata"); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleName.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleName.java new file mode 100644 index 0000000000000..d77882d6454d7 --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleName.java @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.core.security.authc.support.mapper; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentParseException; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionModel; +import org.elasticsearch.xpack.core.security.support.MustacheTemplateEvaluator; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; + +/** + * Representation of a Mustache template for expressing one or more roles names in a {@link ExpressionRoleMapping}. + */ +public class TemplateRoleName implements ToXContent, Writeable { + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "role-mapping-template", false, arr -> new TemplateRoleName((BytesReference) arr[0], (Format) arr[1])); + + static { + PARSER.declareField(constructorArg(), TemplateRoleName::extractTemplate, Fields.TEMPLATE, ObjectParser.ValueType.OBJECT_OR_STRING); + PARSER.declareField(optionalConstructorArg(), Format::fromXContent, Fields.FORMAT, ObjectParser.ValueType.STRING); + } + + private final BytesReference template; + private final Format format; + + public TemplateRoleName(BytesReference template, Format format) { + this.template = template; + this.format = format == null ? Format.STRING : format; + } + + public TemplateRoleName(StreamInput in) throws IOException { + this.template = in.readBytesReference(); + this.format = in.readEnum(Format.class); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeBytesReference(template); + out.writeEnum(format); + } + + public BytesReference getTemplate() { + return template; + } + + public Format getFormat() { + return format; + } + + public List getRoleNames(ScriptService scriptService, ExpressionModel model) { + try { + final String evaluation = parseTemplate(scriptService, model.asMap()); + switch (format) { + case STRING: + return Collections.singletonList(evaluation); + case JSON: + return convertJsonToList(evaluation); + default: + throw new IllegalStateException("Unsupported format [" + format + "]"); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private List convertJsonToList(String evaluation) throws IOException { + final XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(NamedXContentRegistry.EMPTY, + LoggingDeprecationHandler.INSTANCE, evaluation); + XContentParser.Token token = parser.currentToken(); + if (token == null) { + token = parser.nextToken(); + } + if (token == XContentParser.Token.VALUE_STRING) { + return Collections.singletonList(parser.text()); + } else if (token == XContentParser.Token.START_ARRAY) { + return parser.list().stream() + .filter(Objects::nonNull) + .map(o -> { + if (o instanceof String) { + return (String) o; + } else { + throw new XContentParseException( + "Roles array may only contain strings but found [" + o.getClass().getName() + "] [" + o + "]"); + } + }).collect(Collectors.toList()); + } else { + throw new XContentParseException( + "Roles template must generate a string or an array of strings, but found [" + token + "]"); + } + } + + private String parseTemplate(ScriptService scriptService, Map parameters) throws IOException { + final XContentParser parser = XContentHelper.createParser( + NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, template, XContentType.JSON); + return MustacheTemplateEvaluator.evaluate(scriptService, parser, parameters); + } + + private static BytesReference extractTemplate(XContentParser parser, Void ignore) throws IOException { + if (parser.currentToken() == XContentParser.Token.VALUE_STRING) { + return new BytesArray(parser.text()); + } else { + XContentBuilder builder = JsonXContent.contentBuilder(); + builder.generator().copyCurrentStructure(parser); + return BytesReference.bytes(builder); + } + } + + static TemplateRoleName parse(XContentParser parser) throws IOException { + return PARSER.parse(parser, null); + } + + @Override + public String toString() { + return "template-" + format + "{" + template.utf8ToString() + "}"; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + return builder.startObject() + .field(Fields.TEMPLATE.getPreferredName(), template.utf8ToString()) + .field(Fields.FORMAT.getPreferredName(), format.formatName()) + .endObject(); + } + + @Override + public boolean isFragment() { + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final TemplateRoleName that = (TemplateRoleName) o; + return Objects.equals(this.template, that.template) && + this.format == that.format; + } + + @Override + public int hashCode() { + return Objects.hash(template, format); + } + + private interface Fields { + ParseField TEMPLATE = new ParseField("template"); + ParseField FORMAT = new ParseField("format"); + } + + public enum Format { + JSON, STRING; + + private static Format fromXContent(XContentParser parser) throws IOException { + final XContentParser.Token token = parser.currentToken(); + if (token != XContentParser.Token.VALUE_STRING) { + throw new XContentParseException(parser.getTokenLocation(), + "Expected [" + XContentParser.Token.VALUE_STRING + "] but found [" + token + "]"); + } + final String text = parser.text(); + try { + return Format.valueOf(text.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException e) { + String valueNames = Stream.of(values()).map(Format::formatName).collect(Collectors.joining(",")); + throw new XContentParseException(parser.getTokenLocation(), + "Invalid format [" + text + "] expected one of [" + valueNames + "]"); + } + + } + + public String formatName() { + return name().toLowerCase(Locale.ROOT); + } + } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/ExpressionModel.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/ExpressionModel.java index 8d43f864878af..d12cc67dcca1b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/ExpressionModel.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/ExpressionModel.java @@ -6,9 +6,9 @@ package org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl; import org.elasticsearch.common.Numbers; -import org.elasticsearch.common.collect.Tuple; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,10 +22,13 @@ public class ExpressionModel { public static final Predicate NULL_PREDICATE = field -> field.getValue() == null; - private Map>> fields; + + private final Map fieldValues; + private final Map> fieldPredicates; public ExpressionModel() { - this.fields = new HashMap<>(); + this.fieldValues = new HashMap<>(); + this.fieldPredicates = new HashMap<>(); } /** @@ -41,7 +44,8 @@ public ExpressionModel defineField(String name, Object value) { * Defines a field using a supplied predicate. */ public ExpressionModel defineField(String name, Object value, Predicate predicate) { - this.fields.put(name, new Tuple<>(value, predicate)); + this.fieldValues.put(name, value); + this.fieldPredicates.put(name, predicate); return this; } @@ -49,13 +53,7 @@ public ExpressionModel defineField(String name, Object value, Predicateany of the provided values. */ public boolean test(String field, List values) { - final Tuple> tuple = this.fields.get(field); - final Predicate predicate; - if (tuple == null) { - predicate = NULL_PREDICATE; - } else { - predicate = tuple.v2(); - } + final Predicate predicate = this.fieldPredicates.getOrDefault(field, NULL_PREDICATE); return values.stream().anyMatch(predicate); } @@ -103,4 +101,12 @@ private static boolean numberEquals(Number left, Object other) { return Numbers.toLongExact(left) == Numbers.toLongExact(right); } + public Map asMap() { + return Collections.unmodifiableMap(fieldValues); + } + + @Override + public String toString() { + return fieldValues.toString(); + } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java index 0e681b110efa4..bea4bbb1cc8fa 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/FieldExpression.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * An expression that evaluates to true if a field (map element) matches @@ -151,6 +152,22 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws return builder.value(value); } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final FieldValue that = (FieldValue) o; + return Objects.equals(this.value, that.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/support/SecurityQueryTemplateEvaluator.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/support/SecurityQueryTemplateEvaluator.java index 951c4acf10d0d..73a1d7fcde509 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/support/SecurityQueryTemplateEvaluator.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/support/SecurityQueryTemplateEvaluator.java @@ -11,10 +11,8 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.script.ScriptType; -import org.elasticsearch.script.TemplateScript; +import org.elasticsearch.xpack.core.security.support.MustacheTemplateEvaluator; import org.elasticsearch.xpack.core.security.user.User; import java.io.IOException; @@ -66,27 +64,19 @@ public static String evaluateTemplate(final String querySource, final ScriptServ if (token != XContentParser.Token.START_OBJECT) { throw new ElasticsearchParseException("Unexpected token [" + token + "]"); } - Script script = Script.parse(parser); - // Add the user details to the params - Map params = new HashMap<>(); - if (script.getParams() != null) { - params.putAll(script.getParams()); - } Map userModel = new HashMap<>(); userModel.put("username", user.principal()); userModel.put("full_name", user.fullName()); userModel.put("email", user.email()); userModel.put("roles", Arrays.asList(user.roles())); userModel.put("metadata", Collections.unmodifiableMap(user.metadata())); - params.put("_user", userModel); - // Always enforce mustache script lang: - script = new Script(script.getType(), script.getType() == ScriptType.STORED ? null : "mustache", script.getIdOrCode(), - script.getOptions(), params); - TemplateScript compiledTemplate = scriptService.compile(script, TemplateScript.CONTEXT).newInstance(script.getParams()); - return compiledTemplate.execute(); + Map extraParams = Collections.singletonMap("_user", userModel); + + return MustacheTemplateEvaluator.evaluate(scriptService, parser, extraParams); } else { return querySource; } } } + } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MustacheTemplateEvaluator.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MustacheTemplateEvaluator.java new file mode 100644 index 0000000000000..02f730333de3a --- /dev/null +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/support/MustacheTemplateEvaluator.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.core.security.support; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.script.Script; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.ScriptType; +import org.elasticsearch.script.TemplateScript; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Utility class for evaluating Mustache templates at runtime. + */ +public final class MustacheTemplateEvaluator { + + private MustacheTemplateEvaluator() { + throw new UnsupportedOperationException("Cannot construct " + MustacheTemplateEvaluator.class); + } + + public static String evaluate(ScriptService scriptService, XContentParser parser, Map extraParams) throws IOException { + Script script = Script.parse(parser); + // Add the user details to the params + Map params = new HashMap<>(); + if (script.getParams() != null) { + params.putAll(script.getParams()); + } + extraParams.forEach(params::put); + // Always enforce mustache script lang: + script = new Script(script.getType(), script.getType() == ScriptType.STORED ? null : "mustache", script.getIdOrCode(), + script.getOptions(), params); + TemplateScript compiledTemplate = scriptService.compile(script, TemplateScript.CONTEXT).newInstance(script.getParams()); + return compiledTemplate.execute(); + } +} diff --git a/x-pack/plugin/core/src/main/resources/security-index-template.json b/x-pack/plugin/core/src/main/resources/security-index-template.json index 94bb2b03ee049..f4e3cd6db020d 100644 --- a/x-pack/plugin/core/src/main/resources/security-index-template.json +++ b/x-pack/plugin/core/src/main/resources/security-index-template.json @@ -45,6 +45,16 @@ "roles" : { "type" : "keyword" }, + "role_templates" : { + "properties": { + "template" : { + "type": "text" + }, + "format" : { + "type": "keyword" + } + } + }, "password" : { "type" : "keyword", "index" : false, diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleNameTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleNameTests.java new file mode 100644 index 0000000000000..cab10ca728323 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/support/mapper/TemplateRoleNameTests.java @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.core.security.authc.support.mapper; + +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.ByteBufferStreamInput; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.DeprecationHandler; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.script.ScriptModule; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.mustache.MustacheScriptEngine; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.EqualsHashCodeTestUtils; +import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName.Format; +import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionModel; +import org.hamcrest.Matchers; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; + +public class TemplateRoleNameTests extends ESTestCase { + + public void testParseRoles() throws Exception { + final TemplateRoleName role1 = parse("{ \"template\": { \"source\": \"_user_{{username}}\" } }"); + assertThat(role1, Matchers.instanceOf(TemplateRoleName.class)); + assertThat(role1.getTemplate().utf8ToString(), equalTo("{\"source\":\"_user_{{username}}\"}")); + assertThat(role1.getFormat(), equalTo(Format.STRING)); + + final TemplateRoleName role2 = parse( + "{ \"template\": \"{\\\"source\\\":\\\"{{#tojson}}groups{{/tojson}}\\\"}\", \"format\":\"json\" }"); + assertThat(role2, Matchers.instanceOf(TemplateRoleName.class)); + assertThat(role2.getTemplate().utf8ToString(), + equalTo("{\"source\":\"{{#tojson}}groups{{/tojson}}\"}")); + assertThat(role2.getFormat(), equalTo(Format.JSON)); + } + + public void testToXContent() throws Exception { + final String json = "{" + + "\"template\":\"{\\\"source\\\":\\\"" + randomAlphaOfLengthBetween(8, 24) + "\\\"}\"," + + "\"format\":\"" + randomFrom(Format.values()).formatName() + "\"" + + "}"; + assertThat(Strings.toString(parse(json)), equalTo(json)); + } + + public void testSerializeTemplate() throws Exception { + trySerialize(new TemplateRoleName(new BytesArray(randomAlphaOfLengthBetween(12, 60)), randomFrom(Format.values()))); + } + + public void testEqualsAndHashCode() throws Exception { + tryEquals(new TemplateRoleName(new BytesArray(randomAlphaOfLengthBetween(12, 60)), randomFrom(Format.values()))); + } + + public void testEvaluateRoles() throws Exception { + final ScriptService scriptService = new ScriptService(Settings.EMPTY, + Collections.singletonMap(MustacheScriptEngine.NAME, new MustacheScriptEngine()), ScriptModule.CORE_CONTEXTS); + final ExpressionModel model = new ExpressionModel(); + model.defineField("username", "hulk"); + model.defineField("groups", Arrays.asList("avengers", "defenders", "panthenon")); + + final TemplateRoleName plainString = new TemplateRoleName(new BytesArray("{ \"source\":\"heroes\" }"), Format.STRING); + assertThat(plainString.getRoleNames(scriptService, model), contains("heroes")); + + final TemplateRoleName user = new TemplateRoleName(new BytesArray("{ \"source\":\"_user_{{username}}\" }"), Format.STRING); + assertThat(user.getRoleNames(scriptService, model), contains("_user_hulk")); + + final TemplateRoleName groups = new TemplateRoleName(new BytesArray("{ \"source\":\"{{#tojson}}groups{{/tojson}}\" }"), + Format.JSON); + assertThat(groups.getRoleNames(scriptService, model), contains("avengers", "defenders", "panthenon")); + } + + private TemplateRoleName parse(String json) throws IOException { + final XContentParser parser = XContentType.JSON.xContent() + .createParser(NamedXContentRegistry.EMPTY, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, json); + final TemplateRoleName role = TemplateRoleName.parse(parser); + assertThat(role, notNullValue()); + return role; + } + + public void trySerialize(TemplateRoleName original) throws Exception { + BytesStreamOutput output = new BytesStreamOutput(); + original.writeTo(output); + + final StreamInput rawInput = ByteBufferStreamInput.wrap(BytesReference.toBytes(output.bytes())); + final TemplateRoleName serialized = new TemplateRoleName(rawInput); + assertEquals(original, serialized); + } + + public void tryEquals(TemplateRoleName original) { + final EqualsHashCodeTestUtils.CopyFunction copy = + rmt -> new TemplateRoleName(rmt.getTemplate(), rmt.getFormat()); + final EqualsHashCodeTestUtils.MutateFunction mutate = rmt -> { + if (randomBoolean()) { + return new TemplateRoleName(rmt.getTemplate(), + randomValueOtherThan(rmt.getFormat(), () -> randomFrom(Format.values()))); + } else { + final String templateStr = rmt.getTemplate().utf8ToString(); + return new TemplateRoleName(new BytesArray(templateStr.substring(randomIntBetween(1, templateStr.length() / 2))), + rmt.getFormat()); + } + }; + EqualsHashCodeTestUtils.checkEqualsAndHashCode(original, copy, mutate); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index fdfc6c9a59498..3ac2537095c39 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -370,7 +370,7 @@ public Collection createComponents(Client client, ClusterService cluster NamedXContentRegistry xContentRegistry, Environment environment, NodeEnvironment nodeEnvironment, NamedWriteableRegistry namedWriteableRegistry) { try { - return createComponents(client, threadPool, clusterService, resourceWatcherService); + return createComponents(client, threadPool, clusterService, resourceWatcherService, scriptService); } catch (final Exception e) { throw new IllegalStateException("security initialization failed", e); } @@ -378,7 +378,7 @@ public Collection createComponents(Client client, ClusterService cluster // pkg private for testing - tests want to pass in their set of extensions hence we are not using the extension service directly Collection createComponents(Client client, ThreadPool threadPool, ClusterService clusterService, - ResourceWatcherService resourceWatcherService) throws Exception { + ResourceWatcherService resourceWatcherService, ScriptService scriptService) throws Exception { if (enabled == false) { return Collections.emptyList(); } @@ -404,7 +404,8 @@ Collection createComponents(Client client, ThreadPool threadPool, Cluste // realms construction final NativeUsersStore nativeUsersStore = new NativeUsersStore(settings, client, securityIndex.get()); - final NativeRoleMappingStore nativeRoleMappingStore = new NativeRoleMappingStore(settings, client, securityIndex.get()); + final NativeRoleMappingStore nativeRoleMappingStore = new NativeRoleMappingStore(settings, client, securityIndex.get(), + scriptService); final AnonymousUser anonymousUser = new AnonymousUser(settings); final ReservedRealm reservedRealm = new ReservedRealm(env, settings, nativeUsersStore, anonymousUser, securityIndex.get(), threadPool); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStore.java index cbb352e67ab39..e8d874bc9d481 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStore.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.script.ScriptService; import org.elasticsearch.xpack.core.security.ScrollHelper; import org.elasticsearch.xpack.core.security.action.realm.ClearRealmCacheAction; import org.elasticsearch.xpack.core.security.action.realm.ClearRealmCacheResponse; @@ -51,7 +52,6 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Supplier; import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.elasticsearch.action.DocWriteResponse.Result.CREATED; import static org.elasticsearch.action.DocWriteResponse.Result.DELETED; @@ -99,12 +99,14 @@ public void onFailure(Exception e) { private final Settings settings; private final Client client; private final SecurityIndexManager securityIndex; + private final ScriptService scriptService; private final List realmsToRefresh = new CopyOnWriteArrayList<>(); - public NativeRoleMappingStore(Settings settings, Client client, SecurityIndexManager securityIndex) { + public NativeRoleMappingStore(Settings settings, Client client, SecurityIndexManager securityIndex, ScriptService scriptService) { this.settings = settings; this.client = client; this.securityIndex = securityIndex; + this.scriptService = scriptService; } private String getNameFromId(String id) { @@ -120,7 +122,7 @@ private String getIdForName(String name) { * Loads all mappings from the index. * package private for unit testing */ - void loadMappings(ActionListener> listener) { + protected void loadMappings(ActionListener> listener) { if (securityIndex.isIndexUpToDate() == false) { listener.onFailure(new IllegalStateException( "Security index is not on the current version - the native realm will not be operational until " + @@ -149,7 +151,7 @@ void loadMappings(ActionListener> listener) { } } - private ExpressionRoleMapping buildMapping(String id, BytesReference source) { + protected ExpressionRoleMapping buildMapping(String id, BytesReference source) { try (InputStream stream = source.streamInput(); XContentParser parser = XContentType.JSON.xContent() .createParser(NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, stream)) { @@ -349,17 +351,16 @@ public void resolveRoles(UserData user, ActionListener> listener) { getRoleMappings(null, ActionListener.wrap( mappings -> { final ExpressionModel model = user.asModel(); - Stream stream = mappings.stream() - .filter(ExpressionRoleMapping::isEnabled) - .filter(m -> m.getExpression().match(model)); - if (logger.isTraceEnabled()) { - stream = stream.map(m -> { - logger.trace("User [{}] matches role-mapping [{}] with roles [{}]", user.getUsername(), m.getName(), - m.getRoles()); - return m; - }); - } - final Set roles = stream.flatMap(m -> m.getRoles().stream()).collect(Collectors.toSet()); + final Set roles = mappings.stream() + .filter(ExpressionRoleMapping::isEnabled) + .filter(m -> m.getExpression().match(model)) + .flatMap(m -> { + final Set roleNames = m.getRoleNames(scriptService, model); + logger.trace("Applying role-mapping [{}] to user-model [{}] produced role-names [{}]", + m.getName(), model, roleNames); + return roleNames.stream(); + }) + .collect(Collectors.toSet()); logger.debug("Mapping user [{}] to roles [{}]", user, roles); listener.onResponse(roles); }, listener::onFailure diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java index 35180ab8f31d0..cc573fd9247f9 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityTests.java @@ -27,6 +27,7 @@ import org.elasticsearch.license.TestUtils; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.plugins.MapperPlugin; +import org.elasticsearch.script.ScriptService; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.VersionUtils; import org.elasticsearch.threadpool.ThreadPool; @@ -129,7 +130,7 @@ protected SSLService getSslService() { Client client = mock(Client.class); when(client.threadPool()).thenReturn(threadPool); when(client.settings()).thenReturn(settings); - return security.createComponents(client, threadPool, clusterService, mock(ResourceWatcherService.class)); + return security.createComponents(client, threadPool, clusterService, mock(ResourceWatcherService.class), mock(ScriptService.class)); } private static T findComponent(Class type, Collection components) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportPutRoleMappingActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportPutRoleMappingActionTests.java index 91222a5af5845..ee5f935fcc56d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportPutRoleMappingActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportPutRoleMappingActionTests.java @@ -25,9 +25,10 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; -import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.iterableWithSize; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -72,7 +73,8 @@ public void testPutValidMapping() throws Exception { assertThat(mapping.getExpression(), is(expression)); assertThat(mapping.isEnabled(), equalTo(true)); assertThat(mapping.getName(), equalTo("anarchy")); - assertThat(mapping.getRoles(), containsInAnyOrder("superuser")); + assertThat(mapping.getRoles(), iterableWithSize(1)); + assertThat(mapping.getRoles(), contains("superuser")); assertThat(mapping.getMetadata().size(), equalTo(1)); assertThat(mapping.getMetadata().get("dumb"), equalTo(true)); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosRealmTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosRealmTestCase.java index 43e5fb216399d..fe8220dad4e6e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosRealmTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/kerberos/KerberosRealmTestCase.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.script.ScriptService; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -146,7 +147,8 @@ protected NativeRoleMappingStore roleMappingStore(final List userNames) when(mockClient.threadPool()).thenReturn(threadPool); when(mockClient.settings()).thenReturn(settings); - final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, mockClient, mock(SecurityIndexManager.class)); + final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, mockClient, mock(SecurityIndexManager.class), + mock(ScriptService.class)); final NativeRoleMappingStore roleMapper = spy(store); doAnswer(invocation -> { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java index c0a93d36ab89d..70e8719c0f797 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapRealmTests.java @@ -8,6 +8,8 @@ import com.unboundid.ldap.sdk.LDAPURL; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.MockSecureSettings; import org.elasticsearch.common.settings.SecureSettings; import org.elasticsearch.common.settings.SecureString; @@ -17,6 +19,9 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.license.XPackLicenseState; +import org.elasticsearch.script.ScriptModule; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.mustache.MustacheScriptEngine; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -29,11 +34,14 @@ import org.elasticsearch.xpack.core.security.authc.ldap.LdapUserSearchSessionFactorySettings; import org.elasticsearch.xpack.core.security.authc.ldap.PoolingSessionFactorySettings; import org.elasticsearch.xpack.core.security.authc.ldap.SearchGroupsResolverSettings; +import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapMetaDataResolverSettings; import org.elasticsearch.xpack.core.security.authc.ldap.support.LdapSearchScope; import org.elasticsearch.xpack.core.security.authc.support.CachingUsernamePasswordRealmSettings; import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings; import org.elasticsearch.xpack.core.security.authc.support.DnRoleMapperSettings; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; +import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping; +import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; import org.elasticsearch.xpack.core.ssl.SSLService; @@ -42,6 +50,8 @@ import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory; import org.elasticsearch.xpack.security.authc.support.DnRoleMapper; import org.elasticsearch.xpack.security.authc.support.MockLookupRealm; +import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore; +import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.junit.After; import org.junit.Before; @@ -54,6 +64,7 @@ import static org.elasticsearch.xpack.core.security.authc.RealmSettings.getFullSettingKey; import static org.elasticsearch.xpack.core.security.authc.ldap.support.SessionFactorySettings.URLS_SETTING; import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -394,6 +405,75 @@ public void testLdapRealmMapsUserDNToRole() throws Exception { assertThat(user.roles(), arrayContaining("avenger")); } + /** + * This tests template role mappings (see + * {@link TemplateRoleName}) with an LDAP realm, using a additional + * metadata field (see {@link LdapMetaDataResolverSettings#ADDITIONAL_META_DATA_SETTING}). + */ + public void testLdapRealmWithTemplatedRoleMapping() throws Exception { + String groupSearchBase = "o=sevenSeas"; + String userTemplate = VALID_USER_TEMPLATE; + Settings settings = Settings.builder() + .put(defaultGlobalSettings) + .put(buildLdapSettings(ldapUrls(), userTemplate, groupSearchBase, LdapSearchScope.SUB_TREE)) + .put(getFullSettingKey(REALM_IDENTIFIER.getName(), LdapMetaDataResolverSettings.ADDITIONAL_META_DATA_SETTING), "uid") + .build(); + RealmConfig config = getRealmConfig(REALM_IDENTIFIER, settings); + + SecurityIndexManager mockSecurityIndex = mock(SecurityIndexManager.class); + when(mockSecurityIndex.isAvailable()).thenReturn(true); + when(mockSecurityIndex.isIndexUpToDate()).thenReturn(true); + when(mockSecurityIndex.isMappingUpToDate()).thenReturn(true); + + Client mockClient = mock(Client.class); + when(mockClient.threadPool()).thenReturn(threadPool); + + final ScriptService scriptService = new ScriptService(defaultGlobalSettings, + Collections.singletonMap(MustacheScriptEngine.NAME, new MustacheScriptEngine()), ScriptModule.CORE_CONTEXTS); + NativeRoleMappingStore roleMapper = new NativeRoleMappingStore(defaultGlobalSettings, mockClient, mockSecurityIndex, + scriptService) { + @Override + protected void loadMappings(ActionListener> listener) { + listener.onResponse( + Arrays.asList( + this.buildMapping("m1", new BytesArray("{" + + "\"role_templates\":[{\"template\":{\"source\":\"_user_{{metadata.uid}}\"}}]," + + "\"enabled\":true," + + "\"rules\":{ \"any\":[" + + " { \"field\":{\"realm.name\":\"ldap1\"}}," + + " { \"field\":{\"realm.name\":\"ldap2\"}}" + + "]}}")), + this.buildMapping("m2", new BytesArray("{" + + "\"roles\":[\"should_not_happen\"]," + + "\"enabled\":true," + + "\"rules\":{ \"all\":[" + + " { \"field\":{\"realm.name\":\"ldap1\"}}," + + " { \"field\":{\"realm.name\":\"ldap2\"}}" + + "]}}")), + this.buildMapping("m3", new BytesArray("{" + + "\"roles\":[\"sales_admin\"]," + + "\"enabled\":true," + + "\"rules\":" + + " { \"field\":{\"dn\":\"*,ou=people,o=sevenSeas\"}}" + + "}")) + ) + ); + } + }; + LdapSessionFactory ldapFactory = new LdapSessionFactory(config, sslService, threadPool); + LdapRealm ldap = new LdapRealm(config, ldapFactory, + roleMapper, threadPool); + ldap.initialize(Collections.singleton(ldap), licenseState); + + PlainActionFuture future = new PlainActionFuture<>(); + ldap.authenticate(new UsernamePasswordToken("Horatio Hornblower", new SecureString(PASSWORD)), future); + final AuthenticationResult result = future.actionGet(); + assertThat(result.getStatus(), is(AuthenticationResult.Status.SUCCESS)); + User user = result.getUser(); + assertThat(user, notNullValue()); + assertThat(user.roles(), arrayContainingInAnyOrder("_user_hhornblo", "sales_admin")); + } + /** * The contract for {@link Realm} implementations is that they should log-and-return-null (and * not call {@link ActionListener#onFailure(Exception)}) if there is an internal exception that prevented them from performing an diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ExpressionRoleMappingTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ExpressionRoleMappingTests.java index 729bd08d7faf3..276d8a333f796 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ExpressionRoleMappingTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/ExpressionRoleMappingTests.java @@ -5,31 +5,48 @@ */ package org.elasticsearch.xpack.security.authc.support.mapper; +import org.elasticsearch.Version; import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.io.stream.ByteBufferStreamInput; +import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.env.Environment; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.VersionUtils; +import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping; +import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.AllExpression; +import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.FieldExpression; import org.elasticsearch.xpack.security.authc.support.UserRoleMapper; import org.hamcrest.Matchers; import org.junit.Before; import org.mockito.Mockito; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Locale; -import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.iterableWithSize; import static org.hamcrest.Matchers.notNullValue; public class ExpressionRoleMappingTests extends ESTestCase { @@ -39,44 +56,44 @@ public class ExpressionRoleMappingTests extends ESTestCase { @Before public void setupMapping() throws Exception { realm = new RealmConfig(new RealmConfig.RealmIdentifier("ldap", "ldap1"), - Settings.EMPTY, Mockito.mock(Environment.class), new ThreadContext(Settings.EMPTY)); + Settings.EMPTY, Mockito.mock(Environment.class), new ThreadContext(Settings.EMPTY)); } - public void testParseValidJson() throws Exception { + public void testParseValidJsonWithFixedRoleNames() throws Exception { String json = "{" - + "\"roles\": [ \"kibana_user\", \"sales\" ], " - + "\"enabled\": true, " - + "\"rules\": { " - + " \"all\": [ " - + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } }, " - + " { \"except\": { \"field\": { \"metadata.active\" : false } } }" - + " ]}" - + "}"; + + "\"roles\": [ \"kibana_user\", \"sales\" ], " + + "\"enabled\": true, " + + "\"rules\": { " + + " \"all\": [ " + + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } }, " + + " { \"except\": { \"field\": { \"metadata.active\" : false } } }" + + " ]}" + + "}"; final ExpressionRoleMapping mapping = parse(json, "ldap_sales"); assertThat(mapping.getRoles(), Matchers.containsInAnyOrder("kibana_user", "sales")); assertThat(mapping.getExpression(), instanceOf(AllExpression.class)); final UserRoleMapper.UserData user1a = new UserRoleMapper.UserData( - "john.smith", "cn=john.smith,ou=sales,dc=example,dc=com", - Collections.emptyList(), Collections.singletonMap("active", true), realm + "john.smith", "cn=john.smith,ou=sales,dc=example,dc=com", + Collections.emptyList(), Collections.singletonMap("active", true), realm ); final UserRoleMapper.UserData user1b = new UserRoleMapper.UserData( - user1a.getUsername(), user1a.getDn().toUpperCase(Locale.US), user1a.getGroups(), user1a.getMetadata(), user1a.getRealm() + user1a.getUsername(), user1a.getDn().toUpperCase(Locale.US), user1a.getGroups(), user1a.getMetadata(), user1a.getRealm() ); final UserRoleMapper.UserData user1c = new UserRoleMapper.UserData( - user1a.getUsername(), user1a.getDn().replaceAll(",", ", "), user1a.getGroups(), user1a.getMetadata(), user1a.getRealm() + user1a.getUsername(), user1a.getDn().replaceAll(",", ", "), user1a.getGroups(), user1a.getMetadata(), user1a.getRealm() ); final UserRoleMapper.UserData user1d = new UserRoleMapper.UserData( - user1a.getUsername(), user1a.getDn().replaceAll("dc=", "DC="), user1a.getGroups(), user1a.getMetadata(), user1a.getRealm() + user1a.getUsername(), user1a.getDn().replaceAll("dc=", "DC="), user1a.getGroups(), user1a.getMetadata(), user1a.getRealm() ); final UserRoleMapper.UserData user2 = new UserRoleMapper.UserData( - "jamie.perez", "cn=jamie.perez,ou=sales,dc=example,dc=com", - Collections.emptyList(), Collections.singletonMap("active", false), realm + "jamie.perez", "cn=jamie.perez,ou=sales,dc=example,dc=com", + Collections.emptyList(), Collections.singletonMap("active", false), realm ); final UserRoleMapper.UserData user3 = new UserRoleMapper.UserData( - "simone.ng", "cn=simone.ng,ou=finance,dc=example,dc=com", - Collections.emptyList(), Collections.singletonMap("active", true), realm + "simone.ng", "cn=simone.ng,ou=finance,dc=example,dc=com", + Collections.emptyList(), Collections.singletonMap("active", true), realm ); assertThat(mapping.getExpression().match(user1a.asModel()), equalTo(true)); @@ -87,58 +104,218 @@ public void testParseValidJson() throws Exception { assertThat(mapping.getExpression().match(user3.asModel()), equalTo(false)); } + public void testParseValidJsonWithTemplatedRoleNames() throws Exception { + String json = "{" + + "\"role_templates\": [ " + + " { \"template\" : { \"source\":\"kibana_user\"} }," + + " { \"template\" : { \"source\":\"sales\"} }," + + " { \"template\" : { \"source\":\"_user_{{username}}\" }, \"format\":\"string\" }" + + " ], " + + "\"enabled\": true, " + + "\"rules\": { " + + " \"all\": [ " + + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } }, " + + " { \"except\": { \"field\": { \"metadata.active\" : false } } }" + + " ]}" + + "}"; + final ExpressionRoleMapping mapping = parse(json, "ldap_sales"); + assertThat(mapping.getRoleTemplates(), iterableWithSize(3)); + assertThat(mapping.getRoleTemplates().get(0).getTemplate().utf8ToString(), equalTo("{\"source\":\"kibana_user\"}")); + assertThat(mapping.getRoleTemplates().get(0).getFormat(), equalTo(TemplateRoleName.Format.STRING)); + assertThat(mapping.getRoleTemplates().get(1).getTemplate().utf8ToString(), equalTo("{\"source\":\"sales\"}")); + assertThat(mapping.getRoleTemplates().get(1).getFormat(), equalTo(TemplateRoleName.Format.STRING)); + assertThat(mapping.getRoleTemplates().get(2).getTemplate().utf8ToString(), equalTo("{\"source\":\"_user_{{username}}\"}")); + assertThat(mapping.getRoleTemplates().get(2).getFormat(), equalTo(TemplateRoleName.Format.STRING)); + } + public void testParsingFailsIfRulesAreMissing() throws Exception { String json = "{" - + "\"roles\": [ \"kibana_user\", \"sales\" ], " - + "\"enabled\": true " - + "}"; + + "\"roles\": [ \"kibana_user\", \"sales\" ], " + + "\"enabled\": true " + + "}"; ParsingException ex = expectThrows(ParsingException.class, () -> parse(json, "bad_json")); assertThat(ex.getMessage(), containsString("rules")); } public void testParsingFailsIfRolesMissing() throws Exception { String json = "{" - + "\"enabled\": true, " - + "\"rules\": " - + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } } " - + "}"; + + "\"enabled\": true, " + + "\"rules\": " + + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } } " + + "}"; ParsingException ex = expectThrows(ParsingException.class, () -> parse(json, "bad_json")); assertThat(ex.getMessage(), containsString("role")); } public void testParsingFailsIfThereAreUnrecognisedFields() throws Exception { String json = "{" - + "\"disabled\": false, " - + "\"roles\": [ \"kibana_user\", \"sales\" ], " - + "\"rules\": " - + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } } " - + "}"; + + "\"disabled\": false, " + + "\"roles\": [ \"kibana_user\", \"sales\" ], " + + "\"rules\": " + + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } } " + + "}"; ParsingException ex = expectThrows(ParsingException.class, () -> parse(json, "bad_json")); assertThat(ex.getMessage(), containsString("disabled")); } public void testParsingIgnoresTypeFields() throws Exception { String json = "{" - + "\"enabled\": true, " - + "\"roles\": [ \"kibana_user\", \"sales\" ], " - + "\"rules\": " - + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } }, " - + "\"doc_type\": \"role-mapping\", " - + "\"type\": \"doc\"" - + "}"; - final ExpressionRoleMapping mapping = parse(json, "from_index"); + + "\"enabled\": true, " + + "\"roles\": [ \"kibana_user\", \"sales\" ], " + + "\"rules\": " + + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } }, " + + "\"doc_type\": \"role-mapping\", " + + "\"type\": \"doc\"" + + "}"; + final ExpressionRoleMapping mapping = parse(json, "from_index", true); assertThat(mapping.isEnabled(), equalTo(true)); - assertThat(mapping.getRoles(), containsInAnyOrder("kibana_user", "sales")); + assertThat(mapping.getRoles(), Matchers.containsInAnyOrder("kibana_user", "sales")); + } + + public void testParsingOfBothRoleNamesAndTemplates() throws Exception { + String json = "{" + + "\"enabled\": true, " + + "\"roles\": [ \"kibana_user\", \"sales\" ], " + + "\"role_templates\": [" + + " { \"template\" : \"{ \\\"source\\\":\\\"_user_{{username}}\\\" }\", \"format\":\"string\" }" + + "]," + + "\"rules\": " + + " { \"field\": { \"dn\" : \"*,ou=sales,dc=example,dc=com\" } }" + + "}"; + + // This is rejected when validating a request, but is valid when parsing the mapping + final ExpressionRoleMapping mapping = parse(json, "from_api", false); + assertThat(mapping.getRoles(), iterableWithSize(2)); + assertThat(mapping.getRoleTemplates(), iterableWithSize(1)); + } + + public void testToXContentWithRoleNames() throws Exception { + String source = "{" + + "\"roles\": [ " + + " \"kibana_user\"," + + " \"sales\"" + + " ], " + + "\"enabled\": true, " + + "\"rules\": { \"field\": { \"realm.name\" : \"saml1\" } }" + + "}"; + final ExpressionRoleMapping mapping = parse(source, getTestName()); + assertThat(mapping.getRoles(), iterableWithSize(2)); + + final String xcontent = Strings.toString(mapping); + assertThat(xcontent, equalTo( + "{" + + "\"enabled\":true," + + "\"roles\":[" + + "\"kibana_user\"," + + "\"sales\"" + + "]," + + "\"rules\":{\"field\":{\"realm.name\":\"saml1\"}}," + + "\"metadata\":{}" + + "}" + )); + } + + public void testToXContentWithTemplates() throws Exception { + String source = "{" + + "\"metadata\" : { \"answer\":42 }," + + "\"role_templates\": [ " + + " { \"template\" : { \"source\":\"_user_{{username}}\" }, \"format\":\"string\" }," + + " { \"template\" : { \"source\":\"{{#tojson}}groups{{/tojson}}\" }, \"format\":\"json\" }" + + " ], " + + "\"enabled\": false, " + + "\"rules\": { \"field\": { \"realm.name\" : \"saml1\" } }" + + "}"; + final ExpressionRoleMapping mapping = parse(source, getTestName()); + assertThat(mapping.getRoleTemplates(), iterableWithSize(2)); + + final String xcontent = Strings.toString(mapping.toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS, true)); + assertThat(xcontent, equalTo( + "{" + + "\"enabled\":false," + + "\"role_templates\":[" + + "{\"template\":\"{\\\"source\\\":\\\"_user_{{username}}\\\"}\",\"format\":\"string\"}," + + "{\"template\":\"{\\\"source\\\":\\\"{{#tojson}}groups{{/tojson}}\\\"}\",\"format\":\"json\"}" + + "]," + + "\"rules\":{\"field\":{\"realm.name\":\"saml1\"}}," + + "\"metadata\":{\"answer\":42}," + + "\"doc_type\":\"role-mapping\"" + + "}" + )); + + final ExpressionRoleMapping parsed = parse(xcontent, getTestName(), true); + assertThat(parsed.getRoles(), iterableWithSize(0)); + assertThat(parsed.getRoleTemplates(), iterableWithSize(2)); + assertThat(parsed.getMetadata(), Matchers.hasKey("answer")); + } + + public void testSerialization() throws Exception { + final ExpressionRoleMapping original = randomRoleMapping(true); + + final Version version = VersionUtils.randomVersionBetween(random(), Version.V_8_0_0, null); + BytesStreamOutput output = new BytesStreamOutput(); + output.setVersion(version); + original.writeTo(output); + + final NamedWriteableRegistry registry = new NamedWriteableRegistry(new XPackClientPlugin(Settings.EMPTY).getNamedWriteables()); + StreamInput streamInput = new NamedWriteableAwareStreamInput(ByteBufferStreamInput.wrap(BytesReference.toBytes(output.bytes())), + registry); + streamInput.setVersion(version); + final ExpressionRoleMapping serialized = new ExpressionRoleMapping(streamInput); + assertEquals(original, serialized); + } + + public void testSerializationPreV71() throws Exception { + final ExpressionRoleMapping original = randomRoleMapping(false); + + final Version version = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, Version.V_7_0_0); + BytesStreamOutput output = new BytesStreamOutput(); + output.setVersion(version); + original.writeTo(output); + + final NamedWriteableRegistry registry = new NamedWriteableRegistry(new XPackClientPlugin(Settings.EMPTY).getNamedWriteables()); + StreamInput streamInput = new NamedWriteableAwareStreamInput(ByteBufferStreamInput.wrap(BytesReference.toBytes(output.bytes())), + registry); + streamInput.setVersion(version); + final ExpressionRoleMapping serialized = new ExpressionRoleMapping(streamInput); + assertEquals(original, serialized); } private ExpressionRoleMapping parse(String json, String name) throws IOException { + return parse(json, name, false); + } + + private ExpressionRoleMapping parse(String json, String name, boolean fromIndex) throws IOException { final NamedXContentRegistry registry = NamedXContentRegistry.EMPTY; final XContentParser parser = XContentType.JSON.xContent() - .createParser(registry, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, json); + .createParser(registry, DeprecationHandler.THROW_UNSUPPORTED_OPERATION, json); final ExpressionRoleMapping mapping = ExpressionRoleMapping.parse(name, parser); assertThat(mapping, notNullValue()); assertThat(mapping.getName(), equalTo(name)); return mapping; } + private ExpressionRoleMapping randomRoleMapping(boolean acceptRoleTemplates) { + final boolean useTemplate = acceptRoleTemplates && randomBoolean(); + final List roles; + final List templates; + if (useTemplate) { + roles = Collections.emptyList(); + templates = Arrays.asList(randomArray(1, 5, TemplateRoleName[]::new, () -> + new TemplateRoleName(new BytesArray(randomAlphaOfLengthBetween(10, 25)), randomFrom(TemplateRoleName.Format.values())) + )); + } else { + roles = Arrays.asList(randomArray(1, 5, String[]::new, () -> randomAlphaOfLengthBetween(4, 12))); + templates = Collections.emptyList(); + } + return new ExpressionRoleMapping( + randomAlphaOfLengthBetween(3, 8), + new FieldExpression(randomAlphaOfLengthBetween(4, 12), + Collections.singletonList(new FieldExpression.FieldValue(randomInt(99)))), + roles, + templates, + Collections.singletonMap(randomAlphaOfLengthBetween(3, 12), randomIntBetween(30, 90)), + true + ); + } + } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java index 29407a8672982..e96284ba1549a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/support/mapper/NativeRoleMappingStoreTests.java @@ -10,10 +10,14 @@ import org.elasticsearch.client.Client; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; +import org.elasticsearch.script.ScriptModule; +import org.elasticsearch.script.ScriptService; +import org.elasticsearch.script.mustache.MustacheScriptEngine; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.security.action.realm.ClearRealmCacheAction; @@ -23,6 +27,7 @@ import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping; +import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.FieldExpression; import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.FieldExpression.FieldValue; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; @@ -54,12 +59,12 @@ public void testResolveRoles() throws Exception { // Does match DN final ExpressionRoleMapping mapping1 = new ExpressionRoleMapping("dept_h", new FieldExpression("dn", Collections.singletonList(new FieldValue("*,ou=dept_h,o=forces,dc=gc,dc=ca"))), - Arrays.asList("dept_h", "defence"), Collections.emptyMap(), true); + Arrays.asList("dept_h", "defence"), Collections.emptyList(), Collections.emptyMap(), true); // Does not match - user is not in this group final ExpressionRoleMapping mapping2 = new ExpressionRoleMapping("admin", - new FieldExpression("groups", Collections.singletonList( - new FieldValue(randomiseDn("cn=esadmin,ou=groups,ou=dept_h,o=forces,dc=gc,dc=ca")))), - Arrays.asList("admin"), Collections.emptyMap(), true); + new FieldExpression("groups", Collections.singletonList( + new FieldValue(randomiseDn("cn=esadmin,ou=groups,ou=dept_h,o=forces,dc=gc,dc=ca")))), + Arrays.asList("admin"), Collections.emptyList(), Collections.emptyMap(), true); // Does match - user is one of these groups final ExpressionRoleMapping mapping3 = new ExpressionRoleMapping("flight", new FieldExpression("groups", Arrays.asList( @@ -67,18 +72,23 @@ public void testResolveRoles() throws Exception { new FieldValue(randomiseDn("cn=betaflight,ou=groups,ou=dept_h,o=forces,dc=gc,dc=ca")), new FieldValue(randomiseDn("cn=gammaflight,ou=groups,ou=dept_h,o=forces,dc=gc,dc=ca")) )), - Arrays.asList("flight"), Collections.emptyMap(), true); + Collections.emptyList(), + Arrays.asList(new TemplateRoleName(new BytesArray("{ \"source\":\"{{metadata.extra_group}}\" }"), + TemplateRoleName.Format.STRING)), + Collections.emptyMap(), true); // Does not match - mapping is not enabled final ExpressionRoleMapping mapping4 = new ExpressionRoleMapping("mutants", new FieldExpression("groups", Collections.singletonList( new FieldValue(randomiseDn("cn=mutants,ou=groups,ou=dept_h,o=forces,dc=gc,dc=ca")))), - Arrays.asList("mutants"), Collections.emptyMap(), false); + Arrays.asList("mutants"), Collections.emptyList(), Collections.emptyMap(), false); final Client client = mock(Client.class); SecurityIndexManager securityIndex = mock(SecurityIndexManager.class); + ScriptService scriptService = new ScriptService(Settings.EMPTY, + Collections.singletonMap(MustacheScriptEngine.NAME, new MustacheScriptEngine()), ScriptModule.CORE_CONTEXTS); when(securityIndex.isAvailable()).thenReturn(true); - final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, client, securityIndex) { + final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, client, securityIndex, scriptService) { @Override protected void loadMappings(ActionListener> listener) { final List mappings = Arrays.asList(mapping1, mapping2, mapping3, mapping4); @@ -96,7 +106,7 @@ protected void loadMappings(ActionListener> listener Arrays.asList( randomiseDn("cn=alphaflight,ou=groups,ou=dept_h,o=forces,dc=gc,dc=ca"), randomiseDn("cn=mutants,ou=groups,ou=dept_h,o=forces,dc=gc,dc=ca") - ), Collections.emptyMap(), realm); + ), Collections.singletonMap("extra_group", "flight"), realm); logger.info("UserData is [{}]", user); store.resolveRoles(user, future); @@ -213,7 +223,8 @@ protected void doLookupUser(String username, ActionListener listener) { listener.onResponse(null); } }; - final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, client, mock(SecurityIndexManager.class)); + final NativeRoleMappingStore store = new NativeRoleMappingStore(Settings.EMPTY, client, mock(SecurityIndexManager.class), + mock(ScriptService.class)); store.refreshRealmOnChange(mockRealm); return store; } diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/api/security.put_role_mapping.json b/x-pack/plugin/src/test/resources/rest-api-spec/api/security.put_role_mapping.json index 626ff0d6da80c..d65cf8f835833 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/api/security.put_role_mapping.json +++ b/x-pack/plugin/src/test/resources/rest-api-spec/api/security.put_role_mapping.json @@ -23,7 +23,7 @@ } }, "body": { - "description" : "The role to add", + "description" : "The role mapping to add", "required" : true } } From eded9ca7577bb43525b487f0df623385a5525119 Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Wed, 27 Mar 2019 08:37:04 +0200 Subject: [PATCH 05/77] Testclusters: convert plugin repository-s3 (#40399) * Add support for setting and keystore settings * system properties and env var config * use testclusters for repository-s3 * Some cleanup of the build.gradle file for plugin-s3 * add runner {} to rest integ test task --- .../gradle/plugin/PluginBuildPlugin.groovy | 3 +- .../gradle/test/RestIntegTestTask.groovy | 10 +- .../testclusters/ElasticsearchNode.java | 201 +++++++++++++----- plugins/build.gradle | 2 +- plugins/repository-azure/build.gradle | 8 +- plugins/repository-s3/build.gradle | 200 +++++++---------- 6 files changed, 248 insertions(+), 176 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy index fb0cd1a41ecaa..75230e27c16c8 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy @@ -44,6 +44,7 @@ public class PluginBuildPlugin extends BuildPlugin { public void apply(Project project) { super.apply(project) configureDependencies(project) + // this afterEvaluate must happen before the afterEvaluate added by integTest creation, // so that the file name resolution for installing the plugin will be setup project.afterEvaluate { @@ -69,7 +70,7 @@ public class PluginBuildPlugin extends BuildPlugin { if (isModule) { throw new RuntimeException("Testclusters does not support modules yet"); } else { - project.testClusters.integTestCluster.plugin( + project.testClusters.integTest.plugin( project.file(project.tasks.bundlePlugin.archiveFile) ) } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy index c2fca819ef3e6..8e7dbafb2c2f1 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy @@ -62,13 +62,13 @@ public class RestIntegTestTask extends DefaultTask { clusterConfig = project.extensions.create("${name}Cluster", ClusterConfiguration.class, project) } else { project.testClusters { - integTestCluster { + "$name" { distribution = 'INTEG_TEST' version = project.version javaHome = project.file(project.ext.runtimeJavaHome) } } - runner.useCluster project.testClusters.integTestCluster + runner.useCluster project.testClusters."$name" } // override/add more for rest tests @@ -81,7 +81,7 @@ public class RestIntegTestTask extends DefaultTask { throw new IllegalArgumentException("tests.rest.cluster and tests.cluster must both be null or non-null") } if (usesTestclusters == true) { - ElasticsearchNode node = project.testClusters.integTestCluster + ElasticsearchNode node = project.testClusters."${name}" runner.systemProperty('tests.rest.cluster', {node.allHttpSocketURI.join(",") }) runner.systemProperty('tests.config.dir', {node.getConfigDir()}) runner.systemProperty('tests.cluster', {node.transportPortURI}) @@ -187,6 +187,10 @@ public class RestIntegTestTask extends DefaultTask { clusterInit.mustRunAfter(tasks) } + public void runner(Closure configure) { + project.tasks.getByName("${name}Runner").configure(configure) + } + /** Print out an excerpt of the log from the given node. */ protected static void printLogExcerpt(NodeInfo nodeInfo) { File logFile = new File(nodeInfo.homeDir, "logs/${nodeInfo.clusterName}.log") diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java b/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java index ae221b31f2c6c..4138131d7a150 100644 --- a/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java @@ -26,8 +26,10 @@ import org.gradle.api.logging.Logging; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.net.HttpURLConnection; @@ -39,15 +41,18 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -71,6 +76,10 @@ public class ElasticsearchNode { private final LinkedHashMap> waitConditions; private final List plugins = new ArrayList<>(); + private final Map> settings = new LinkedHashMap<>(); + private final Map> keystoreSettings = new LinkedHashMap<>(); + private final Map> systemProperties = new LinkedHashMap<>(); + private final Map> environment = new LinkedHashMap<>(); private final Path confPathRepo; private final Path configFile; @@ -143,6 +152,55 @@ public void plugin(File plugin) { plugin(plugin.toURI()); } + public void keystore(String key, String value) { + addSupplier("Keystore", keystoreSettings, key, value); + } + + public void keystore(String key, Supplier valueSupplier) { + addSupplier("Keystore", keystoreSettings, key, valueSupplier); + } + + public void setting(String key, String value) { + addSupplier("Settings", settings, key, value); + } + + public void setting(String key, Supplier valueSupplier) { + addSupplier("Setting", settings, key, valueSupplier); + } + + public void systemProperty(String key, String value) { + addSupplier("Java System property", systemProperties, key, value); + } + + public void systemProperty(String key, Supplier valueSupplier) { + addSupplier("Java System property", systemProperties, key, valueSupplier); + } + + public void environment(String key, String value) { + addSupplier("Environment variable", environment, key, value); + } + + public void environment(String key, Supplier valueSupplier) { + addSupplier("Environment variable", environment, key, valueSupplier); + } + + private void addSupplier(String name, Map> collector, String key, Supplier valueSupplier) { + requireNonNull(key, name + " key was null when configuring test cluster `" + this + "`"); + requireNonNull(valueSupplier, name + " value supplier was null when configuring test cluster `" + this + "`"); + collector.put(key, valueSupplier); + } + + private void addSupplier(String name, Map> collector, String key, String actualValue) { + requireNonNull(actualValue, name + " value was null when configuring test cluster `" + this + "`"); + addSupplier(name, collector, key, () -> actualValue); + } + + private void checkSuppliers(String name, Map> collector) { + collector.forEach((key, value) -> { + requireNonNull(value.get().toString(), name + " supplied value was null when configuring test cluster `" + this + "`"); + }); + } + public Path getConfigDir() { return configFile.getParent(); } @@ -168,6 +226,8 @@ public File getJavaHome() { return javaHome; } + + private void waitForUri(String description, String uri) { waitConditions.put(description, (node) -> { try { @@ -222,46 +282,79 @@ synchronized void start() { "install", "--batch", plugin.toString()) ); + if (keystoreSettings.isEmpty() == false) { + checkSuppliers("Keystore", keystoreSettings); + runElaticsearchBinScript("elasticsearch-keystore", "create"); + keystoreSettings.forEach((key, value) -> { + runElaticsearchBinScriptWithInput(value.get().toString(), "elasticsearch-keystore", "add", "-x", key); + }); + } + startElasticsearchProcess(); } + private void runElaticsearchBinScriptWithInput(String input, String tool, String... args) { + try (InputStream byteArrayInputStream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))) { + services.loggedExec(spec -> { + spec.setEnvironment(getESEnvironment()); + spec.workingDir(workingDir); + spec.executable( + OS.conditionalString() + .onUnix(() -> "./bin/" + tool) + .onWindows(() -> "cmd") + .supply() + ); + spec.args( + OS.>conditional() + .onWindows(() -> { + ArrayList result = new ArrayList<>(); + result.add("/c"); + result.add("bin\\" + tool + ".bat"); + for (String arg : args) { + result.add(arg); + } + return result; + }) + .onUnix(() -> Arrays.asList(args)) + .supply() + ); + spec.setStandardInput(byteArrayInputStream); + + }); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + private void runElaticsearchBinScript(String tool, String... args) { - services.loggedExec(spec -> { - spec.setEnvironment(getESEnvironment()); - spec.workingDir(workingDir); - spec.executable( - OS.conditionalString() - .onUnix(() -> "./bin/" + tool) - .onWindows(() -> "cmd") - .supply() - ); - spec.args( - OS.>conditional() - .onWindows(() -> { - ArrayList result = new ArrayList<>(); - result.add("/c"); - result.add("bin\\" + tool + ".bat"); - for (String arg : args) { - result.add(arg); - } - return result; - }) - .onUnix(() -> Arrays.asList(args)) - .supply() - ); - }); + runElaticsearchBinScriptWithInput("", tool, args); } private Map getESEnvironment() { - Map environment= new HashMap<>(); - environment.put("JAVA_HOME", getJavaHome().getAbsolutePath()); - environment.put("ES_PATH_CONF", configFile.getParent().toString()); - environment.put("ES_JAVA_OPTS", "-Xms512m -Xmx512m"); - environment.put("ES_TMPDIR", tmpDir.toString()); + Map defaultEnv = new HashMap<>(); + defaultEnv.put("JAVA_HOME", getJavaHome().getAbsolutePath()); + defaultEnv.put("ES_PATH_CONF", configFile.getParent().toString()); + String systemPropertiesString = ""; + if (systemProperties.isEmpty() == false) { + checkSuppliers("Java System property", systemProperties); + systemPropertiesString = " " + systemProperties.entrySet().stream() + .map(entry -> "-D" + entry.getKey() + "=" + entry.getValue().get()) + .collect(Collectors.joining(" ")); + } + defaultEnv.put("ES_JAVA_OPTS", "-Xms512m -Xmx512m -ea -esa" + systemPropertiesString); + defaultEnv.put("ES_TMPDIR", tmpDir.toString()); // Windows requires this as it defaults to `c:\windows` despite ES_TMPDIR + defaultEnv.put("TMP", tmpDir.toString()); + + Set commonKeys = new HashSet<>(environment.keySet()); + commonKeys.retainAll(defaultEnv.keySet()); + if (commonKeys.isEmpty() == false) { + throw new IllegalStateException("testcluster does not allow setting the following env vars " + commonKeys); + } - environment.put("TMP", tmpDir.toString()); - return environment; + checkSuppliers("Environment variable", environment); + environment.forEach((key, value) -> defaultEnv.put(key, value.get().toString())); + return defaultEnv; } private void startElasticsearchProcess() { @@ -445,37 +538,49 @@ private void syncWithLinks(Path sourceRoot, Path destinationRoot) { } private void createConfiguration() { - LinkedHashMap config = new LinkedHashMap<>(); + LinkedHashMap defaultConfig = new LinkedHashMap<>(); String nodeName = safeName(name); - config.put("cluster.name",nodeName); - config.put("node.name", nodeName); - config.put("path.repo", confPathRepo.toAbsolutePath().toString()); - config.put("path.data", confPathData.toAbsolutePath().toString()); - config.put("path.logs", confPathLogs.toAbsolutePath().toString()); - config.put("path.shared_data", workingDir.resolve("sharedData").toString()); - config.put("node.attr.testattr", "test"); - config.put("node.portsfile", "true"); - config.put("http.port", "0"); - config.put("transport.tcp.port", "0"); + defaultConfig.put("cluster.name",nodeName); + defaultConfig.put("node.name", nodeName); + defaultConfig.put("path.repo", confPathRepo.toAbsolutePath().toString()); + defaultConfig.put("path.data", confPathData.toAbsolutePath().toString()); + defaultConfig.put("path.logs", confPathLogs.toAbsolutePath().toString()); + defaultConfig.put("path.shared_data", workingDir.resolve("sharedData").toString()); + defaultConfig.put("node.attr.testattr", "test"); + defaultConfig.put("node.portsfile", "true"); + defaultConfig.put("http.port", "0"); + defaultConfig.put("transport.tcp.port", "0"); // Default the watermarks to absurdly low to prevent the tests from failing on nodes without enough disk space - config.put("cluster.routing.allocation.disk.watermark.low", "1b"); - config.put("cluster.routing.allocation.disk.watermark.high", "1b"); + defaultConfig.put("cluster.routing.allocation.disk.watermark.low", "1b"); + defaultConfig.put("cluster.routing.allocation.disk.watermark.high", "1b"); // increase script compilation limit since tests can rapid-fire script compilations - config.put("script.max_compilations_rate", "2048/1m"); + defaultConfig.put("script.max_compilations_rate", "2048/1m"); if (Version.fromString(version).getMajor() >= 6) { - config.put("cluster.routing.allocation.disk.watermark.flood_stage", "1b"); + defaultConfig.put("cluster.routing.allocation.disk.watermark.flood_stage", "1b"); } if (Version.fromString(version).getMajor() >= 7) { - config.put("cluster.initial_master_nodes", "[" + nodeName + "]"); + defaultConfig.put("cluster.initial_master_nodes", "[" + nodeName + "]"); } + checkSuppliers("Settings", settings); + Map userConfig = settings.entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue().get().toString())); + HashSet overriden = new HashSet<>(defaultConfig.keySet()); + overriden.retainAll(userConfig.keySet()); + if (overriden.isEmpty() ==false) { + throw new IllegalArgumentException("Testclusters does not allow the following settings to be changed:" + overriden); + } + try { // We create hard links for the distribution, so we need to remove the config file before writing it // to prevent the changes to reflect across all copies. Files.delete(configFile); Files.write( configFile, - config.entrySet().stream() + Stream.concat( + userConfig.entrySet().stream(), + defaultConfig.entrySet().stream() + ) .map(entry -> entry.getKey() + ": " + entry.getValue()) .collect(Collectors.joining("\n")) .getBytes(StandardCharsets.UTF_8) diff --git a/plugins/build.gradle b/plugins/build.gradle index 5b7d5f5faf26f..585f26c3780f8 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -21,7 +21,7 @@ configure(subprojects.findAll { it.parent.path == project.path }) { group = 'org.elasticsearch.plugin' // TODO exclude some plugins as they require features not yet supproted by testclusters - if (false == name in ['repository-azure', 'repository-hdfs', 'repository-s3']) { + if (false == name in ['repository-hdfs']) { apply plugin: 'elasticsearch.testclusters' } diff --git a/plugins/repository-azure/build.gradle b/plugins/repository-azure/build.gradle index 27597e94976fa..a7c1af412d949 100644 --- a/plugins/repository-azure/build.gradle +++ b/plugins/repository-azure/build.gradle @@ -65,7 +65,9 @@ check { dependsOn 'qa:microsoft-azure-storage:check' } -integTestCluster { - keystoreSetting 'azure.client.integration_test.account', 'azure_account' - keystoreSetting 'azure.client.integration_test.key', 'azure_key' +testClusters { + integTest { + keystore 'azure.client.integration_test.account', 'azure_account' + keystore 'azure.client.integration_test.key', 'azure_key' + } } diff --git a/plugins/repository-s3/build.gradle b/plugins/repository-s3/build.gradle index b41174096e493..d27afb0861a98 100644 --- a/plugins/repository-s3/build.gradle +++ b/plugins/repository-s3/build.gradle @@ -1,7 +1,6 @@ import org.elasticsearch.gradle.BuildPlugin import org.elasticsearch.gradle.MavenFilteringHack import org.elasticsearch.gradle.test.AntFixture -import org.elasticsearch.gradle.test.ClusterConfiguration import org.elasticsearch.gradle.test.RestIntegTestTask import com.carrotsearch.gradle.junit4.RandomizedTestingTask @@ -71,7 +70,7 @@ task testRepositoryCreds(type: RandomizedTestingTask) { include '**/S3BlobStoreRepositoryTests.class' systemProperty 'es.allow_insecure_settings', 'true' } -project.check.dependsOn(testRepositoryCreds) +check.dependsOn(testRepositoryCreds) unitTest { // these are tested explicitly in separate test tasks @@ -136,78 +135,57 @@ if (!s3EC2Bucket && !s3EC2BasePath && !s3ECSBucket && !s3ECSBasePath) { throw new IllegalArgumentException("not all options specified to run EC2/ECS tests are present") } -buildscript { - repositories { - maven { - url 'https://plugins.gradle.org/m2/' - } - } - dependencies { - classpath 'de.undercouch:gradle-download-task:3.4.3' - } -} - if (useFixture) { - apply plugin: 'elasticsearch.test.fixtures' - - RestIntegTestTask integTestMinio = project.tasks.create('integTestMinio', RestIntegTestTask.class) { - description = "Runs REST tests using the Minio repository." - } - - Task writeDockerFile = project.tasks.create('writeDockerFile') { + task writeDockerFile { File minioDockerfile = new File("${project.buildDir}/minio-docker/Dockerfile") outputs.file(minioDockerfile) doLast { minioDockerfile.parentFile.mkdirs() minioDockerfile.text = "FROM minio/minio:RELEASE.2019-01-23T23-18-58Z\n" + - "RUN mkdir -p /minio/data/${s3PermanentBucket}\n" + - "ENV MINIO_ACCESS_KEY ${s3PermanentAccessKey}\n" + - "ENV MINIO_SECRET_KEY ${s3PermanentSecretKey}" + "RUN mkdir -p /minio/data/${s3PermanentBucket}\n" + + "ENV MINIO_ACCESS_KEY ${s3PermanentAccessKey}\n" + + "ENV MINIO_SECRET_KEY ${s3PermanentSecretKey}" } } + preProcessFixture { + dependsOn(writeDockerFile) + } - preProcessFixture.dependsOn(writeDockerFile) - // The following closure must execute before the afterEvaluate block in the constructor of the following integrationTest tasks: - project.afterEvaluate { - // Only configure the Minio tests if postProcessFixture is configured to skip them if Docker is not available - // or fixtures have been disabled - if (postProcessFixture.enabled) { - ClusterConfiguration cluster = project.extensions.getByName('integTestMinioCluster') as ClusterConfiguration - cluster.dependsOn(project.bundlePlugin) - cluster.dependsOn(postProcessFixture) - cluster.keystoreSetting 's3.client.integration_test_permanent.access_key', s3PermanentAccessKey - cluster.keystoreSetting 's3.client.integration_test_permanent.secret_key', s3PermanentSecretKey - - Closure minioAddressAndPort = { - int minioPort = postProcessFixture.ext."test.fixtures.minio-fixture.tcp.9000" - assert minioPort > 0 - return 'http://127.0.0.1:' + minioPort - } - cluster.setting 's3.client.integration_test_permanent.endpoint', "${-> minioAddressAndPort.call()}" - - Task restIntegTestTask = project.tasks.getByName('integTestMinio') - restIntegTestTask.clusterConfig.plugin(project.path) - - // Default jvm arguments for all test clusters - String jvmArgs = "-Xms" + System.getProperty('tests.heap.size', '512m') + - " " + "-Xmx" + System.getProperty('tests.heap.size', '512m') + - " " + System.getProperty('tests.jvm.argline', '') - - restIntegTestTask.clusterConfig.jvmArgs = jvmArgs - project.check.dependsOn(integTestMinio) + task integTestMinio(type: RestIntegTestTask) { + description = "Runs REST tests using the Minio repository." + dependsOn tasks.bundlePlugin, tasks.postProcessFixture + runner { + // Minio only supports a single access key, see https://github.com/minio/minio/pull/5968 + systemProperty 'tests.rest.blacklist', [ + 'repository_s3/30_repository_temporary_credentials/*', + 'repository_s3/40_repository_ec2_credentials/*', + 'repository_s3/50_repository_ecs_credentials/*' + ].join(",") } } - - integTestMinioRunner.dependsOn(postProcessFixture) - // Minio only supports a single access key, see https://github.com/minio/minio/pull/5968 - integTestMinioRunner.systemProperty 'tests.rest.blacklist', [ - 'repository_s3/30_repository_temporary_credentials/*', - 'repository_s3/40_repository_ec2_credentials/*', - 'repository_s3/50_repository_ecs_credentials/*' - ].join(",") - - BuildPlugin.requireDocker(integTestMinio) + check.dependsOn(integTestMinio) + BuildPlugin.requireDocker(tasks.integTestMinio) + + testClusters.integTestMinio { + keystore 's3.client.integration_test_permanent.access_key', s3PermanentAccessKey + keystore 's3.client.integration_test_permanent.secret_key', s3PermanentSecretKey + setting 's3.client.integration_test_permanent.endpoint', { + int minioPort = postProcessFixture.ext."test.fixtures.minio-fixture.tcp.9000" + assert minioPort > 0 + return 'http://127.0.0.1:' + minioPort + } + plugin file(tasks.bundlePlugin.archiveFile) + } +} else { + integTest.runner { + systemProperty 'tests.rest.blacklist', + [ + 'repository_s3/30_repository_temporary_credentials/*', + 'repository_s3/40_repository_ec2_credentials/*', + 'repository_s3/50_repository_ecs_credentials/*' + ].join(",") + } } File parentFixtures = new File(project.buildDir, "fixtures") @@ -242,82 +220,69 @@ task s3Fixture(type: AntFixture) { args 'org.elasticsearch.repositories.s3.AmazonS3Fixture', baseDir, s3FixtureFile.getAbsolutePath() } -Map expansions = [ - 'permanent_bucket': s3PermanentBucket, - 'permanent_base_path': s3PermanentBasePath, - 'temporary_bucket': s3TemporaryBucket, - 'temporary_base_path': s3TemporaryBasePath, - 'ec2_bucket': s3EC2Bucket, - 'ec2_base_path': s3EC2BasePath, - 'ecs_bucket': s3ECSBucket, - 'ecs_base_path': s3ECSBasePath -] - processTestResources { + Map expansions = [ + 'permanent_bucket': s3PermanentBucket, + 'permanent_base_path': s3PermanentBasePath, + 'temporary_bucket': s3TemporaryBucket, + 'temporary_base_path': s3TemporaryBasePath, + 'ec2_bucket': s3EC2Bucket, + 'ec2_base_path': s3EC2BasePath, + 'ecs_bucket': s3ECSBucket, + 'ecs_base_path': s3ECSBasePath + ] inputs.properties(expansions) MavenFilteringHack.filter(it, expansions) } -project.afterEvaluate { - if (useFixture == false) { - // temporary_credentials, ec2_credentials and ecs_credentials are not ready for third-party-tests yet - integTestRunner.systemProperty 'tests.rest.blacklist', - [ - 'repository_s3/30_repository_temporary_credentials/*', - 'repository_s3/40_repository_ec2_credentials/*', - 'repository_s3/50_repository_ecs_credentials/*' - ].join(",") - } +integTest { + dependsOn s3Fixture } -integTestCluster { - keystoreSetting 's3.client.integration_test_permanent.access_key', s3PermanentAccessKey - keystoreSetting 's3.client.integration_test_permanent.secret_key', s3PermanentSecretKey +testClusters.integTest { + keystore 's3.client.integration_test_permanent.access_key', s3PermanentAccessKey + keystore 's3.client.integration_test_permanent.secret_key', s3PermanentSecretKey - keystoreSetting 's3.client.integration_test_temporary.access_key', s3TemporaryAccessKey - keystoreSetting 's3.client.integration_test_temporary.secret_key', s3TemporarySecretKey - keystoreSetting 's3.client.integration_test_temporary.session_token', s3TemporarySessionToken + keystore 's3.client.integration_test_temporary.access_key', s3TemporaryAccessKey + keystore 's3.client.integration_test_temporary.secret_key', s3TemporarySecretKey + keystore 's3.client.integration_test_temporary.session_token', s3TemporarySessionToken if (useFixture) { - dependsOn s3Fixture - /* Use a closure on the string to delay evaluation until tests are executed */ - setting 's3.client.integration_test_permanent.endpoint', "http://${-> s3Fixture.addressAndPort}" - setting 's3.client.integration_test_temporary.endpoint', "http://${-> s3Fixture.addressAndPort}" - setting 's3.client.integration_test_ec2.endpoint', "http://${-> s3Fixture.addressAndPort}" + setting 's3.client.integration_test_permanent.endpoint', { "http://${s3Fixture.addressAndPort}" } + setting 's3.client.integration_test_temporary.endpoint', { "http://${s3Fixture.addressAndPort}" } + setting 's3.client.integration_test_ec2.endpoint', { "http://${s3Fixture.addressAndPort}" } // to redirect InstanceProfileCredentialsProvider to custom auth point - systemProperty "com.amazonaws.sdk.ec2MetadataServiceEndpointOverride", "http://${-> s3Fixture.addressAndPort}" + systemProperty "com.amazonaws.sdk.ec2MetadataServiceEndpointOverride", { "http://${s3Fixture.addressAndPort}" } } else { println "Using an external service to test the repository-s3 plugin" } } -integTestRunner.systemProperty 'tests.rest.blacklist', 'repository_s3/50_repository_ecs_credentials/*' +integTest.runner { + systemProperty 'tests.rest.blacklist', 'repository_s3/50_repository_ecs_credentials/*' +} if (useFixture) { - RestIntegTestTask integTestECS = project.tasks.create('integTestECS', RestIntegTestTask.class) { + task integTestECS(type: RestIntegTestTask.class) { description = "Runs tests using the ECS repository." + dependsOn(project.s3Fixture) + runner { + systemProperty 'tests.rest.blacklist', [ + 'repository_s3/10_basic/*', + 'repository_s3/20_repository_permanent_credentials/*', + 'repository_s3/30_repository_temporary_credentials/*', + 'repository_s3/40_repository_ec2_credentials/*' + ].join(",") + } } + check.dependsOn(integTestECS) -// The following closure must execute before the afterEvaluate block in the constructor of the following integrationTest tasks: - project.afterEvaluate { - ClusterConfiguration cluster = project.extensions.getByName('integTestECSCluster') as ClusterConfiguration - cluster.dependsOn(project.s3Fixture) - - cluster.setting 's3.client.integration_test_ecs.endpoint', "http://${-> s3Fixture.addressAndPort}" - - Task integTestECSTask = project.tasks.getByName('integTestECS') - integTestECSTask.clusterConfig.plugin(project.path) - integTestECSTask.clusterConfig.environment 'AWS_CONTAINER_CREDENTIALS_FULL_URI', - "http://${-> s3Fixture.addressAndPort}/ecs_credentials_endpoint" - integTestECSRunner.systemProperty 'tests.rest.blacklist', [ - 'repository_s3/10_basic/*', - 'repository_s3/20_repository_permanent_credentials/*', - 'repository_s3/30_repository_temporary_credentials/*', - 'repository_s3/40_repository_ec2_credentials/*' - ].join(",") + testClusters.integTestECS { + setting 's3.client.integration_test_ecs.endpoint', { "http://${s3Fixture.addressAndPort}" } + plugin file(tasks.bundlePlugin.archiveFile) + environment 'AWS_CONTAINER_CREDENTIALS_FULL_URI', { "http://${s3Fixture.addressAndPort}/ecs_credentials_endpoint" } } - project.check.dependsOn(integTestECS) } thirdPartyAudit.ignoreMissingClasses ( @@ -446,8 +411,3 @@ if (project.runtimeJavaVersion <= JavaVersion.VERSION_1_8) { } else { thirdPartyAudit.ignoreMissingClasses 'javax.activation.DataHandler' } - -// AWS SDK is exposing some deprecated methods which we call using a delegate: -// * setObjectRedirectLocation(String bucketName, String key, String newRedirectLocation) -// * changeObjectStorageClass(String bucketName, String key, StorageClass newStorageClass) -compileTestJava.options.compilerArgs << "-Xlint:-deprecation" From b250cb74f8a106102c9bf2d5f6a8517ffbafb157 Mon Sep 17 00:00:00 2001 From: Hendrik Muhs Date: Wed, 27 Mar 2019 08:25:49 +0100 Subject: [PATCH 06/77] [ML] generate unique doc ids for data frame (#40382) create and use unique, deterministic document ids based on the grouping values. This is a pre-requisite for updating documents as well as preventing duplicates after a hard failure during indexing. --- .../xpack/core/dataframe/DataFrameField.java | 3 + ...nsportPreviewDataFrameTransformAction.java | 8 +- .../transforms/DataFrameIndexer.java | 18 +- .../dataframe/transforms/IDGenerator.java | 96 +++++++++++ .../pivot/AggregationResultUtils.java | 18 +- .../transforms/IDGeneratorTests.java | 63 +++++++ .../pivot/AggregationResultUtilsTests.java | 155 +++++++++++++++++- 7 files changed, 349 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/IDGenerator.java create mode 100644 x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/IDGeneratorTests.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/DataFrameField.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/DataFrameField.java index fbd81e438a130..73e639cec5e1d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/DataFrameField.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/DataFrameField.java @@ -53,6 +53,9 @@ public final class DataFrameField { */ public static final String FOR_INTERNAL_STORAGE = "for_internal_storage"; + // internal document id + public static String DOCUMENT_ID_FIELD = "_id"; + private DataFrameField() { } } diff --git a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportPreviewDataFrameTransformAction.java b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportPreviewDataFrameTransformAction.java index 13b633359f5cb..78f6823034811 100644 --- a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportPreviewDataFrameTransformAction.java +++ b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportPreviewDataFrameTransformAction.java @@ -82,7 +82,13 @@ private void getPreview(Pivot pivot, ActionListener>> l r -> { final CompositeAggregation agg = r.getAggregations().get(COMPOSITE_AGGREGATION_NAME); DataFrameIndexerTransformStats stats = new DataFrameIndexerTransformStats(); - listener.onResponse(pivot.extractResults(agg, deducedMappings, stats).collect(Collectors.toList())); + // remove all internal fields + List> results = pivot.extractResults(agg, deducedMappings, stats) + .map(record -> { + record.keySet().removeIf(k -> k.startsWith("_")); + return record; + }).collect(Collectors.toList()); + listener.onResponse(results); }, listener::onFailure )); diff --git a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/DataFrameIndexer.java b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/DataFrameIndexer.java index a9fcb77d50a11..bb07722ddeed0 100644 --- a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/DataFrameIndexer.java +++ b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/DataFrameIndexer.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregation; +import org.elasticsearch.xpack.core.dataframe.DataFrameField; import org.elasticsearch.xpack.core.dataframe.transforms.DataFrameIndexerTransformStats; import org.elasticsearch.xpack.core.dataframe.transforms.DataFrameTransformConfig; import org.elasticsearch.xpack.core.indexing.AsyncTwoPhaseIndexer; @@ -73,15 +74,28 @@ private Stream processBucketsToIndexRequests(CompositeAggregation String indexName = transformConfig.getDestination().getIndex(); return pivot.extractResults(agg, getFieldMappings(), getStats()).map(document -> { + String id = (String) document.get(DataFrameField.DOCUMENT_ID_FIELD); + + if (id == null) { + throw new RuntimeException("Expected a document id but got null."); + } + XContentBuilder builder; try { builder = jsonBuilder(); - builder.map(document); + builder.startObject(); + for (Map.Entry value : document.entrySet()) { + // skip all internal fields + if (value.getKey().startsWith("_") == false) { + builder.field(value.getKey(), value.getValue()); + } + } + builder.endObject(); } catch (IOException e) { throw new UncheckedIOException(e); } - IndexRequest request = new IndexRequest(indexName).source(builder); + IndexRequest request = new IndexRequest(indexName).source(builder).id(id); return request; }); } diff --git a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/IDGenerator.java b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/IDGenerator.java new file mode 100644 index 0000000000000..d9223fe90dd4f --- /dev/null +++ b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/IDGenerator.java @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.dataframe.transforms; + +import org.apache.lucene.util.BytesRefBuilder; +import org.elasticsearch.common.Numbers; +import org.elasticsearch.common.hash.MurmurHash3; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.TreeMap; + +/** + * ID Generator for creating unique but deterministic document ids. + * + * uses MurmurHash with 128 bits + */ +public final class IDGenerator { + private static final byte[] NULL_VALUE = "__NULL_VALUE__".getBytes(StandardCharsets.UTF_8); + private static final byte DELIM = '$'; + private static final long SEED = 19; + private static final int MAX_FIRST_BYTES = 5; + + private final TreeMap objectsForIDGeneration = new TreeMap<>(); + + public IDGenerator() { + } + + /** + * Add a value to the generator + * @param key object identifier, to be used for consistent sorting + * @param value the value + */ + public void add(String key, Object value) { + if (objectsForIDGeneration.containsKey(key)) { + throw new IllegalArgumentException("Keys must be unique"); + } + objectsForIDGeneration.put(key, value); + } + + /** + * Create a document id based on the input objects + * + * @return a document id as string + */ + public String getID() { + if (objectsForIDGeneration.size() == 0) { + throw new RuntimeException("Add at least 1 object before generating the ID"); + } + + BytesRefBuilder buffer = new BytesRefBuilder(); + BytesRefBuilder hashedBytes = new BytesRefBuilder(); + + for (Object value : objectsForIDGeneration.values()) { + byte[] v = getBytes(value); + + buffer.append(v, 0, v.length); + buffer.append(DELIM); + + // keep the 1st byte of every object + if (hashedBytes.length() <= MAX_FIRST_BYTES) { + hashedBytes.append(v[0]); + } + } + MurmurHash3.Hash128 hasher = MurmurHash3.hash128(buffer.bytes(), 0, buffer.length(), SEED, new MurmurHash3.Hash128()); + hashedBytes.append(Numbers.longToBytes(hasher.h1), 0, 8); + hashedBytes.append(Numbers.longToBytes(hasher.h2), 0, 8); + return Base64.getUrlEncoder().withoutPadding().encodeToString(hashedBytes.bytes()); + } + + /** + * Turns objects into byte arrays, only supporting types returned groupBy + * + * @param value the value as object + * @return a byte representation of the input object + */ + private static byte[] getBytes(Object value) { + if (value == null) { + return NULL_VALUE; + } else if (value instanceof String) { + return ((String) value).getBytes(StandardCharsets.UTF_8); + } else if (value instanceof Long) { + return Numbers.longToBytes((Long) value); + } else if (value instanceof Double) { + return Numbers.doubleToBytes((Double) value); + } else if (value instanceof Integer) { + return Numbers.intToBytes((Integer) value); + } + + throw new IllegalArgumentException("Value of type [" + value.getClass() + "] is not supported"); + } +} diff --git a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtils.java b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtils.java index fa7536497c4f0..5d77f82e610ab 100644 --- a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtils.java +++ b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtils.java @@ -13,8 +13,10 @@ import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregation; import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregation; import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregation.SingleValue; +import org.elasticsearch.xpack.core.dataframe.DataFrameField; import org.elasticsearch.xpack.core.dataframe.transforms.DataFrameIndexerTransformStats; import org.elasticsearch.xpack.core.dataframe.transforms.pivot.GroupConfig; +import org.elasticsearch.xpack.dataframe.transforms.IDGenerator; import java.util.Collection; import java.util.HashMap; @@ -43,10 +45,17 @@ public static Stream> extractCompositeAggregationResults(Com DataFrameIndexerTransformStats stats) { return agg.getBuckets().stream().map(bucket -> { stats.incrementNumDocuments(bucket.getDocCount()); - Map document = new HashMap<>(); - groups.getGroups().keySet().forEach(destinationFieldName -> - document.put(destinationFieldName, bucket.getKey().get(destinationFieldName))); + // generator to create unique but deterministic document ids, so we + // - do not create duplicates if we re-run after failure + // - update documents + IDGenerator idGen = new IDGenerator(); + + groups.getGroups().keySet().forEach(destinationFieldName -> { + Object value = bucket.getKey().get(destinationFieldName); + idGen.add(destinationFieldName, value); + document.put(destinationFieldName, value); + }); for (AggregationBuilder aggregationBuilder : aggregationBuilders) { String aggName = aggregationBuilder.getName(); @@ -71,6 +80,9 @@ public static Stream> extractCompositeAggregationResults(Com assert false; } } + + document.put(DataFrameField.DOCUMENT_ID_FIELD, idGen.getID()); + return document; }); } diff --git a/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/IDGeneratorTests.java b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/IDGeneratorTests.java new file mode 100644 index 0000000000000..fd378a2c4c171 --- /dev/null +++ b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/IDGeneratorTests.java @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.dataframe.transforms; + +import org.elasticsearch.test.ESTestCase; + +public class IDGeneratorTests extends ESTestCase { + + public void testSupportedTypes() { + IDGenerator idGen = new IDGenerator(); + idGen.add("key1", "value1"); + String id = idGen.getID(); + idGen.add("key2", null); + assertNotEquals(id, idGen.getID()); + id = idGen.getID(); + idGen.add("key3", "value3"); + assertNotEquals(id, idGen.getID()); + id = idGen.getID(); + idGen.add("key4", 12L); + assertNotEquals(id, idGen.getID()); + id = idGen.getID(); + idGen.add("key5", 44.444); + assertNotEquals(id, idGen.getID()); + idGen.add("key6", 13); + assertNotEquals(id, idGen.getID()); + } + + public void testOrderIndependence() { + IDGenerator idGen = new IDGenerator(); + idGen.add("key1", "value1"); + idGen.add("key2", "value2"); + String id1 = idGen.getID(); + + idGen = new IDGenerator(); + idGen.add("key2", "value2"); + idGen.add("key1", "value1"); + String id2 = idGen.getID(); + + assertEquals(id1, id2); + } + + public void testEmptyThrows() { + IDGenerator idGen = new IDGenerator(); + + RuntimeException e = expectThrows(RuntimeException.class, () -> idGen.getID()); + + assertEquals("Add at least 1 object before generating the ID", e.getMessage()); + } + + public void testDuplicatedKeyThrows() { + IDGenerator idGen = new IDGenerator(); + idGen.add("key1", "value1"); + + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> idGen.add("key1", "some_other_value")); + + assertEquals("Keys must be unique", e.getMessage()); + } + +} diff --git a/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtilsTests.java b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtilsTests.java index 287f327d0f664..eedf6264f348b 100644 --- a/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtilsTests.java +++ b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/transforms/pivot/AggregationResultUtilsTests.java @@ -44,6 +44,7 @@ import org.elasticsearch.search.aggregations.pipeline.ParsedStatsBucket; import org.elasticsearch.search.aggregations.pipeline.StatsBucketPipelineAggregationBuilder; import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.xpack.core.dataframe.DataFrameField; import org.elasticsearch.xpack.core.dataframe.transforms.DataFrameIndexerTransformStats; import org.elasticsearch.xpack.core.dataframe.transforms.pivot.GroupConfig; @@ -51,8 +52,10 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static java.util.Arrays.asList; @@ -147,7 +150,7 @@ aggTypedName, asMap( executeTest(groupBy, aggregationBuilders, input, fieldTypeMap, expected, 20); } - public void testExtractCompositeAggregationResultsMultiSources() throws IOException { + public void testExtractCompositeAggregationResultsMultipleGroups() throws IOException { String targetField = randomAlphaOfLengthBetween(5, 10); String targetField2 = randomAlphaOfLengthBetween(5, 10) + "_2"; @@ -406,19 +409,159 @@ aggTypedName2, asMap( executeTest(groupBy, aggregationBuilders, input, fieldTypeMap, expected, 10); } + public void testExtractCompositeAggregationResultsDocIDs() throws IOException { + String targetField = randomAlphaOfLengthBetween(5, 10); + String targetField2 = randomAlphaOfLengthBetween(5, 10) + "_2"; + + GroupConfig groupBy = parseGroupConfig("{" + + "\"" + targetField + "\" : {" + + " \"terms\" : {" + + " \"field\" : \"doesn't_matter_for_this_test\"" + + " } }," + + "\"" + targetField2 + "\" : {" + + " \"terms\" : {" + + " \"field\" : \"doesn't_matter_for_this_test\"" + + " } }" + + "}"); + + String aggName = randomAlphaOfLengthBetween(5, 10); + String aggTypedName = "avg#" + aggName; + Collection aggregationBuilders = Collections.singletonList(AggregationBuilders.avg(aggName)); + + Map inputFirstRun = asMap( + "buckets", + asList( + asMap( + KEY, asMap( + targetField, "ID1", + targetField2, "ID1_2" + ), + aggTypedName, asMap( + "value", 42.33), + DOC_COUNT, 1), + asMap( + KEY, asMap( + targetField, "ID1", + targetField2, "ID2_2" + ), + aggTypedName, asMap( + "value", 8.4), + DOC_COUNT, 2), + asMap( + KEY, asMap( + targetField, "ID2", + targetField2, "ID1_2" + ), + aggTypedName, asMap( + "value", 28.99), + DOC_COUNT, 3), + asMap( + KEY, asMap( + targetField, "ID3", + targetField2, "ID2_2" + ), + aggTypedName, asMap( + "value", 12.55), + DOC_COUNT, 4) + )); + + Map inputSecondRun = asMap( + "buckets", + asList( + asMap( + KEY, asMap( + targetField, "ID1", + targetField2, "ID1_2" + ), + aggTypedName, asMap( + "value", 433.33), + DOC_COUNT, 12), + asMap( + KEY, asMap( + targetField, "ID1", + targetField2, "ID2_2" + ), + aggTypedName, asMap( + "value", 83.4), + DOC_COUNT, 32), + asMap( + KEY, asMap( + targetField, "ID2", + targetField2, "ID1_2" + ), + aggTypedName, asMap( + "value", 21.99), + DOC_COUNT, 2), + asMap( + KEY, asMap( + targetField, "ID3", + targetField2, "ID2_2" + ), + aggTypedName, asMap( + "value", 122.55), + DOC_COUNT, 44) + )); + DataFrameIndexerTransformStats stats = new DataFrameIndexerTransformStats(); + + Map fieldTypeMap = asStringMap( + aggName, "double", + targetField, "keyword", + targetField2, "keyword" + ); + + List> resultFirstRun = runExtraction(groupBy, aggregationBuilders, inputFirstRun, fieldTypeMap, stats); + List> resultSecondRun = runExtraction(groupBy, aggregationBuilders, inputSecondRun, fieldTypeMap, stats); + + assertNotEquals(resultFirstRun, resultSecondRun); + + Set documentIdsFirstRun = new HashSet<>(); + resultFirstRun.forEach(m -> { + documentIdsFirstRun.add((String) m.get(DataFrameField.DOCUMENT_ID_FIELD)); + }); + + assertEquals(4, documentIdsFirstRun.size()); + + Set documentIdsSecondRun = new HashSet<>(); + resultSecondRun.forEach(m -> { + documentIdsSecondRun.add((String) m.get(DataFrameField.DOCUMENT_ID_FIELD)); + }); + + assertEquals(4, documentIdsSecondRun.size()); + assertEquals(documentIdsFirstRun, documentIdsSecondRun); + } + + + private void executeTest(GroupConfig groups, Collection aggregationBuilders, Map input, Map fieldTypeMap, List> expected, long expectedDocCounts) throws IOException { DataFrameIndexerTransformStats stats = new DataFrameIndexerTransformStats(); XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); builder.map(input); + List> result = runExtraction(groups, aggregationBuilders, input, fieldTypeMap, stats); + + // remove the document ids and test uniqueness + Set documentIds = new HashSet<>(); + result.forEach(m -> { + documentIds.add((String) m.remove(DataFrameField.DOCUMENT_ID_FIELD)); + }); + + assertEquals(result.size(), documentIds.size()); + assertEquals(expected, result); + assertEquals(expectedDocCounts, stats.getNumDocuments()); + + } + + private List> runExtraction(GroupConfig groups, Collection aggregationBuilders, + Map input, Map fieldTypeMap, DataFrameIndexerTransformStats stats) throws IOException { + + XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); + builder.map(input); + try (XContentParser parser = createParser(builder)) { CompositeAggregation agg = ParsedComposite.fromXContent(parser, "my_feature"); - List> result = AggregationResultUtils - .extractCompositeAggregationResults(agg, groups, aggregationBuilders, fieldTypeMap, stats).collect(Collectors.toList()); - - assertEquals(expected, result); - assertEquals(expectedDocCounts, stats.getNumDocuments()); + return AggregationResultUtils.extractCompositeAggregationResults(agg, groups, aggregationBuilders, fieldTypeMap, stats) + .collect(Collectors.toList()); } } From 7e4d23d7cc4da949a7ae2b3faa1aa4ed44ac74d1 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gomulka Date: Wed, 27 Mar 2019 08:35:13 +0100 Subject: [PATCH 07/77] Parse composite patterns using ClassicFormat.parseObject (#40100) Java-time fails parsing composite patterns when first pattern matches only the prefix of the input. It expects pattern in longest to shortest order. Because of this constructing just one DateTimeFormatter with appendOptional is not sufficient. Parsers have to be iterated and if the parsing fails, the next one in order should be used. In order to not degrade performance parsing should not be throw exceptions on failure. Format.parseObject was used as it only returns null when parsing failed and allows to check if full input was read. closes #39916 --- .../common/time/DateFormatters.java | 4 +- .../common/time/JavaDateFormatter.java | 91 ++++++++++++------- .../common/time/JavaDateMathParser.java | 13 +-- .../joda/JavaJodaTimeDuellingTests.java | 11 +++ 4 files changed, 79 insertions(+), 40 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java index a8dce661e1c9f..2379b4f00c2bf 100644 --- a/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java +++ b/server/src/main/java/org/elasticsearch/common/time/DateFormatters.java @@ -1585,7 +1585,7 @@ static JavaDateFormatter merge(String pattern, List formatters) { if (printer == null) { printer = javaDateFormatter.getPrinter(); } - dateTimeFormatters.add(javaDateFormatter.getParser()); + dateTimeFormatters.addAll(javaDateFormatter.getParsers()); roundupBuilder.appendOptional(javaDateFormatter.getRoundupParser()); } DateTimeFormatter roundUpParser = roundupBuilder.toFormatter(Locale.ROOT); @@ -1632,7 +1632,7 @@ public static ZonedDateTime from(TemporalAccessor accessor) { if (zoneId == null) { zoneId = ZoneOffset.UTC; } - + LocalDate localDate = accessor.query(TemporalQueries.localDate()); LocalTime localTime = accessor.query(TemporalQueries.localTime()); boolean isLocalDateSet = localDate != null; diff --git a/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java b/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java index c3adcc84b5781..d0f4200b3bafe 100644 --- a/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java +++ b/server/src/main/java/org/elasticsearch/common/time/JavaDateFormatter.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.Strings; +import java.text.ParsePosition; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -29,7 +30,10 @@ import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -39,6 +43,7 @@ class JavaDateFormatter implements DateFormatter { // base fields which should be used for default parsing, when we round up for date math private static final Map ROUND_UP_BASE_FIELDS = new HashMap<>(6); + { ROUND_UP_BASE_FIELDS.put(ChronoField.MONTH_OF_YEAR, 1L); ROUND_UP_BASE_FIELDS.put(ChronoField.DAY_OF_MONTH, 1L); @@ -50,22 +55,15 @@ class JavaDateFormatter implements DateFormatter { private final String format; private final DateTimeFormatter printer; - private final DateTimeFormatter parser; + private final List parsers; private final DateTimeFormatter roundupParser; - private JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter roundupParser, DateTimeFormatter parser) { - this.format = format; - this.printer = printer; - this.roundupParser = roundupParser; - this.parser = parser; - } - JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeFormatter... parsers) { this(format, printer, builder -> ROUND_UP_BASE_FIELDS.forEach(builder::parseDefaulting), parsers); } JavaDateFormatter(String format, DateTimeFormatter printer, Consumer roundupParserConsumer, - DateTimeFormatter... parsers) { + DateTimeFormatter... parsers) { if (printer == null) { throw new IllegalArgumentException("printer may not be null"); } @@ -79,26 +77,21 @@ private JavaDateFormatter(String format, DateTimeFormatter printer, DateTimeForm } this.printer = printer; this.format = format; + if (parsers.length == 0) { - this.parser = printer; - } else if (parsers.length == 1) { - this.parser = parsers[0]; + this.parsers = Collections.singletonList(printer); } else { - DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); - for (DateTimeFormatter parser : parsers) { - builder.appendOptional(parser); - } - this.parser = builder.toFormatter(Locale.ROOT); + this.parsers = Arrays.asList(parsers); } DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); if (format.contains("||") == false) { - builder.append(this.parser); + builder.append(this.parsers.get(0)); } roundupParserConsumer.accept(builder); - DateTimeFormatter roundupFormatter = builder.toFormatter(parser.getLocale()); + DateTimeFormatter roundupFormatter = builder.toFormatter(locale()); if (printer.getZone() != null) { - roundupFormatter = roundupFormatter.withZone(printer.getZone()); + roundupFormatter = roundupFormatter.withZone(zone()); } this.roundupParser = roundupFormatter; } @@ -107,10 +100,6 @@ DateTimeFormatter getRoundupParser() { return roundupParser; } - DateTimeFormatter getParser() { - return parser; - } - DateTimeFormatter getPrinter() { return printer; } @@ -122,30 +111,64 @@ public TemporalAccessor parse(String input) { } try { - return parser.parse(input); + return doParse(input); } catch (DateTimeParseException e) { throw new IllegalArgumentException("failed to parse date field [" + input + "] with format [" + format + "]", e); } } + /** + * Attempt parsing the input without throwing exception. If multiple parsers are provided, + * it will continue iterating if the previous parser failed. The pattern must fully match, meaning whole input was used. + * This also means that this method depends on DateTimeFormatter.ClassicFormat.parseObject + * which does not throw exceptions when parsing failed. + * + * The approach with collection of parsers was taken because java-time requires ordering on optional (composite) + * patterns. Joda does not suffer from this. + * https://bugs.openjdk.java.net/browse/JDK-8188771 + * + * @param input An arbitrary string resembling the string representation of a date or time + * @return a TemporalAccessor if parsing was successful. + * @throws DateTimeParseException when unable to parse with any parsers + */ + private TemporalAccessor doParse(String input) { + if (parsers.size() > 1) { + for (DateTimeFormatter formatter : parsers) { + ParsePosition pos = new ParsePosition(0); + Object object = formatter.toFormat().parseObject(input, pos); + if (parsingSucceeded(object, input, pos) == true) { + return (TemporalAccessor) object; + } + } + throw new DateTimeParseException("Failed to parse with all enclosed parsers", input, 0); + } + return this.parsers.get(0).parse(input); + } + + private boolean parsingSucceeded(Object object, String input, ParsePosition pos) { + return object != null && pos.getIndex() == input.length(); + } + @Override public DateFormatter withZone(ZoneId zoneId) { // shortcurt to not create new objects unnecessarily - if (zoneId.equals(parser.getZone())) { + if (zoneId.equals(zone())) { return this; } - return new JavaDateFormatter(format, printer.withZone(zoneId), roundupParser.withZone(zoneId), parser.withZone(zoneId)); + return new JavaDateFormatter(format, printer.withZone(zoneId), + parsers.stream().map(p -> p.withZone(zoneId)).toArray(size -> new DateTimeFormatter[size])); } @Override public DateFormatter withLocale(Locale locale) { // shortcurt to not create new objects unnecessarily - if (locale.equals(parser.getLocale())) { + if (locale.equals(locale())) { return this; } - return new JavaDateFormatter(format, printer.withLocale(locale), roundupParser.withLocale(locale), parser.withLocale(locale)); + return new JavaDateFormatter(format, printer.withLocale(locale), + parsers.stream().map(p -> p.withLocale(locale)).toArray(size -> new DateTimeFormatter[size])); } @Override @@ -170,7 +193,7 @@ public ZoneId zone() { @Override public DateMathParser toDateMathParser() { - return new JavaDateMathParser(format, parser, roundupParser); + return new JavaDateMathParser(format, this, getRoundupParser()); } @Override @@ -186,12 +209,16 @@ public boolean equals(Object obj) { JavaDateFormatter other = (JavaDateFormatter) obj; return Objects.equals(format, other.format) && - Objects.equals(locale(), other.locale()) && - Objects.equals(this.printer.getZone(), other.printer.getZone()); + Objects.equals(locale(), other.locale()) && + Objects.equals(this.printer.getZone(), other.printer.getZone()); } @Override public String toString() { return String.format(Locale.ROOT, "format[%s] locale[%s]", format, locale()); } + + Collection getParsers() { + return parsers; + } } diff --git a/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java b/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java index 05e1e75efca39..dc7c195e2fd6c 100644 --- a/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java +++ b/server/src/main/java/org/elasticsearch/common/time/JavaDateMathParser.java @@ -35,6 +35,7 @@ import java.time.temporal.TemporalAdjusters; import java.time.temporal.TemporalQueries; import java.util.Objects; +import java.util.function.Function; import java.util.function.LongSupplier; /** @@ -46,11 +47,11 @@ */ public class JavaDateMathParser implements DateMathParser { - private final DateTimeFormatter formatter; + private final JavaDateFormatter formatter; private final DateTimeFormatter roundUpFormatter; private final String format; - JavaDateMathParser(String format, DateTimeFormatter formatter, DateTimeFormatter roundUpFormatter) { + JavaDateMathParser(String format, JavaDateFormatter formatter, DateTimeFormatter roundUpFormatter) { this.format = format; Objects.requireNonNull(formatter); this.formatter = formatter; @@ -215,12 +216,12 @@ private Instant parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNo throw new ElasticsearchParseException("cannot parse empty date"); } - DateTimeFormatter formatter = roundUpIfNoTime ? this.roundUpFormatter : this.formatter; + Function formatter = roundUpIfNoTime ? this.roundUpFormatter::parse : this.formatter::parse; try { if (timeZone == null) { - return DateFormatters.from(formatter.parse(value)).toInstant(); + return DateFormatters.from(formatter.apply(value)).toInstant(); } else { - TemporalAccessor accessor = formatter.parse(value); + TemporalAccessor accessor = formatter.apply(value); ZoneId zoneId = TemporalQueries.zone().queryFrom(accessor); if (zoneId != null) { timeZone = zoneId; @@ -228,7 +229,7 @@ private Instant parseDateTime(String value, ZoneId timeZone, boolean roundUpIfNo return DateFormatters.from(accessor).withZoneSameLocal(timeZone).toInstant(); } - } catch (DateTimeParseException e) { + } catch (IllegalArgumentException | DateTimeParseException e) { throw new ElasticsearchParseException("failed to parse date field [{}] with format [{}]: [{}]", e, value, format, e.getMessage()); } diff --git a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java index 40822d5a38b84..5798b5f799203 100644 --- a/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java +++ b/server/src/test/java/org/elasticsearch/common/joda/JavaJodaTimeDuellingTests.java @@ -343,6 +343,17 @@ public void testDuellingFormatsValidParsing() { assertSameDate("2012-W1-1", "weekyear_week_day"); } + public void testCompositeParsing(){ + //in all these examples the second pattern will be used + assertSameDate("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertSameDate("2014-06-06T12:01:02.123", "strictDateTimeNoMillis||yyyy-MM-dd'T'HH:mm:ss.SSS"); + assertSameDate("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss+HH:MM||yyyy-MM-dd'T'HH:mm:ss.SSS"); + } + + public void testExceptionWhenCompositeParsingFails(){ + assertParseException("2014-06-06T12:01:02.123", "yyyy-MM-dd'T'HH:mm:ss||yyyy-MM-dd'T'HH:mm:ss.SS"); + } + public void testDuelingStrictParsing() { assertSameDate("2018W313", "strict_basic_week_date"); assertParseException("18W313", "strict_basic_week_date"); From 2d1402b8d1940f266d1f15ab05fac949420cf5e8 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Wed, 27 Mar 2019 19:29:26 +1100 Subject: [PATCH 08/77] Fix docs link (#40504) Fix link from role mapping API to custom role provider. --- x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc b/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc index e7c5fbaf60ad1..bfd6a14d3ed7c 100644 --- a/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc +++ b/x-pack/docs/en/rest-api/security/create-role-mappings.asciidoc @@ -316,7 +316,7 @@ POST /_security/role_mapping/mapping8 A templated role can be used to automatically map every user to their own custom role. The role itself can be defined through the <> or using a -{stack-ov}/implementing-custom-roles-provider.html[custom roles provider]. +{stack-ov}/custom-roles-authorization.html#implementing-custom-roles-provider[custom roles provider]. In this example every user who authenticates using the "cloud-saml" realm will be automatically mapped to two roles - the `"saml_user"` role and a From 09d4e7eb9eeadff45c65f865ff67cd42a5f629d5 Mon Sep 17 00:00:00 2001 From: David Kyle Date: Wed, 27 Mar 2019 09:12:06 +0000 Subject: [PATCH 09/77] [ML] Data Frame HLRC Get API (#40209) --- .../elasticsearch/client/DataFrameClient.java | 44 +++++- .../client/DataFrameRequestConverters.java | 17 +++ .../GetDataFrameTransformRequest.java | 96 ++++++++++++ .../GetDataFrameTransformResponse.java | 142 ++++++++++++++++++ .../transforms/DataFrameTransformConfig.java | 2 +- .../DataFrameRequestConvertersTests.java | 26 ++++ .../client/DataFrameTransformIT.java | 120 +++++++++++---- .../client/ESRestHighLevelClientTestCase.java | 3 +- .../GetDataFrameTransformRequestTests.java | 32 ++++ .../GetDataFrameTransformResponseTests.java | 84 +++++++++++ .../DataFrameTransformDocumentationIT.java | 100 ++++++++++-- .../dataframe/get_data_frame.asciidoc | 45 ++++++ .../high-level/supported-apis.asciidoc | 2 + 13 files changed, 668 insertions(+), 45 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponse.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequestTests.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponseTests.java create mode 100644 docs/java-rest/high-level/dataframe/get_data_frame.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameClient.java index 5688324643182..b758968f0a98a 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameClient.java @@ -22,6 +22,8 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.core.AcknowledgedResponse; import org.elasticsearch.client.dataframe.DeleteDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformResponse; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsRequest; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsResponse; import org.elasticsearch.client.dataframe.PreviewDataFrameTransformRequest; @@ -275,7 +277,7 @@ public StopDataFrameTransformResponse stopDataFrameTransform(StopDataFrameTransf * @param listener Listener to be notified upon request completion */ public void stopDataFrameTransformAsync(StopDataFrameTransformRequest request, RequestOptions options, - ActionListener listener) { + ActionListener listener) { restHighLevelClient.performRequestAsyncAndParseEntity(request, DataFrameRequestConverters::stopDataFrameTransform, options, @@ -283,4 +285,44 @@ public void stopDataFrameTransformAsync(StopDataFrameTransformRequest request, R listener, Collections.emptySet()); } + + /** + * Get one or more data frame transform configurations + *

+ * For additional info + * see Get Data Frame transform documentation + * + * @param request The get data frame transform request + * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return An GetDataFrameTransformResponse containing the requested transforms + * @throws IOException when there is a serialization issue sending the request or receiving the response + */ + public GetDataFrameTransformResponse getDataFrameTransform(GetDataFrameTransformRequest request, RequestOptions options) + throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, + DataFrameRequestConverters::getDataFrameTransform, + options, + GetDataFrameTransformResponse::fromXContent, + Collections.emptySet()); + } + + /** + * Get one or more data frame transform configurations asynchronously and notifies listener on completion + *

+ * For additional info + * see Get Data Frame transform documentation + * + * @param request The get data frame transform request + * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener Listener to be notified upon request completion + */ + public void getDataFrameTransformAsync(GetDataFrameTransformRequest request, RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, + DataFrameRequestConverters::getDataFrameTransform, + options, + GetDataFrameTransformResponse::fromXContent, + listener, + Collections.emptySet()); + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameRequestConverters.java index df1207bb8a77b..309a37fedf8cd 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameRequestConverters.java @@ -24,11 +24,13 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.dataframe.DeleteDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformRequest; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsRequest; import org.elasticsearch.client.dataframe.PreviewDataFrameTransformRequest; import org.elasticsearch.client.dataframe.PutDataFrameTransformRequest; import org.elasticsearch.client.dataframe.StartDataFrameTransformRequest; import org.elasticsearch.client.dataframe.StopDataFrameTransformRequest; +import org.elasticsearch.common.Strings; import java.io.IOException; @@ -49,6 +51,21 @@ static Request putDataFrameTransform(PutDataFrameTransformRequest putRequest) th return request; } + static Request getDataFrameTransform(GetDataFrameTransformRequest getRequest) { + String endpoint = new RequestConverters.EndpointBuilder() + .addPathPartAsIs("_data_frame", "transforms") + .addPathPart(Strings.collectionToCommaDelimitedString(getRequest.getId())) + .build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + if (getRequest.getFrom() != null) { + request.addParameter("from", getRequest.getFrom().toString()); + } + if (getRequest.getSize() != null) { + request.addParameter("size", getRequest.getSize().toString()); + } + return request; + } + static Request deleteDataFrameTransform(DeleteDataFrameTransformRequest request) { String endpoint = new RequestConverters.EndpointBuilder() .addPathPartAsIs("_data_frame", "transforms") diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequest.java new file mode 100644 index 0000000000000..9577a0f5c72bf --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequest.java @@ -0,0 +1,96 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.dataframe; + +import org.elasticsearch.client.Validatable; +import org.elasticsearch.client.ValidationException; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +public class GetDataFrameTransformRequest implements Validatable { + + private final List ids; + private Integer from; + private Integer size; + + /** + * Helper method to create a request that will get ALL Data Frame Transforms + * @return new {@link GetDataFrameTransformRequest} object for the id "_all" + */ + public static GetDataFrameTransformRequest getAllDataFrameTransformsRequest() { + return new GetDataFrameTransformRequest("_all"); + } + + public GetDataFrameTransformRequest(String... ids) { + this.ids = Arrays.asList(ids); + } + + public List getId() { + return ids; + } + + public Integer getFrom() { + return from; + } + + public void setFrom(Integer from) { + this.from = from; + } + + public Integer getSize() { + return size; + } + + public void setSize(Integer size) { + this.size = size; + } + + @Override + public Optional validate() { + if (ids == null || ids.isEmpty()) { + ValidationException validationException = new ValidationException(); + validationException.addValidationError("data frame transform id must not be null"); + return Optional.of(validationException); + } else { + return Optional.empty(); + } + } + + @Override + public int hashCode() { + return Objects.hash(ids); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + GetDataFrameTransformRequest other = (GetDataFrameTransformRequest) obj; + return Objects.equals(ids, other.ids); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponse.java new file mode 100644 index 0000000000000..93fc91f08cee1 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponse.java @@ -0,0 +1,142 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.dataframe; + +import org.elasticsearch.client.dataframe.transforms.DataFrameTransformConfig; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.util.List; +import java.util.Objects; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; + +public class GetDataFrameTransformResponse { + + public static final ParseField TRANSFORMS = new ParseField("transforms"); + public static final ParseField INVALID_TRANSFORMS = new ParseField("invalid_transforms"); + public static final ParseField COUNT = new ParseField("count"); + + @SuppressWarnings("unchecked") + static final ConstructingObjectParser INVALID_TRANSFORMS_PARSER = + new ConstructingObjectParser<>("invalid_transforms", true, args -> new InvalidTransforms((List) args[0])); + + @SuppressWarnings("unchecked") + static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "get_data_frame_transform", true, args -> new GetDataFrameTransformResponse( + (List) args[0], (int) args[1], (InvalidTransforms) args[2])); + static { + // Discard the count field which is the size of the transforms array + INVALID_TRANSFORMS_PARSER.declareInt((a, b) -> {}, COUNT); + INVALID_TRANSFORMS_PARSER.declareStringArray(constructorArg(), TRANSFORMS); + + PARSER.declareObjectArray(constructorArg(), DataFrameTransformConfig.PARSER::apply, TRANSFORMS); + PARSER.declareInt(constructorArg(), COUNT); + PARSER.declareObject(optionalConstructorArg(), INVALID_TRANSFORMS_PARSER::apply, INVALID_TRANSFORMS); + } + + public static GetDataFrameTransformResponse fromXContent(final XContentParser parser) { + return GetDataFrameTransformResponse.PARSER.apply(parser, null); + } + + private List transformConfigurations; + private int count; + private InvalidTransforms invalidTransforms; + + public GetDataFrameTransformResponse(List transformConfigurations, + int count, + @Nullable InvalidTransforms invalidTransforms) { + this.transformConfigurations = transformConfigurations; + this.count = count; + this.invalidTransforms = invalidTransforms; + } + + @Nullable + public InvalidTransforms getInvalidTransforms() { + return invalidTransforms; + } + + public int getCount() { + return count; + } + + public List getTransformConfigurations() { + return transformConfigurations; + } + + @Override + public int hashCode() { + return Objects.hash(transformConfigurations, count, invalidTransforms); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || getClass() != other.getClass()) { + return false; + } + + final GetDataFrameTransformResponse that = (GetDataFrameTransformResponse) other; + return Objects.equals(this.transformConfigurations, that.transformConfigurations) + && Objects.equals(this.count, that.count) + && Objects.equals(this.invalidTransforms, that.invalidTransforms); + } + + static class InvalidTransforms { + private final List transformIds; + + InvalidTransforms(List transformIds) { + this.transformIds = transformIds; + } + + public int getCount() { + return transformIds.size(); + } + + public List getTransformIds() { + return transformIds; + } + + @Override + public int hashCode() { + return Objects.hash(transformIds); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || getClass() != other.getClass()) { + return false; + } + + final InvalidTransforms that = (InvalidTransforms) other; + return Objects.equals(this.transformIds, that.transformIds); + } + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/transforms/DataFrameTransformConfig.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/transforms/DataFrameTransformConfig.java index 0dd648e7e3516..88670a7b36d1f 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/transforms/DataFrameTransformConfig.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/dataframe/transforms/DataFrameTransformConfig.java @@ -46,7 +46,7 @@ public class DataFrameTransformConfig implements ToXContentObject { private final DestConfig dest; private final PivotConfig pivotConfig; - public static final ConstructingObjectParser PARSER = + public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("data_frame_transform", true, (args) -> { String id = (String) args[0]; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameRequestConvertersTests.java index 1301588bf8fff..8c6b1c6045855 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameRequestConvertersTests.java @@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.dataframe.DeleteDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformRequest; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsRequest; import org.elasticsearch.client.dataframe.PreviewDataFrameTransformRequest; import org.elasticsearch.client.dataframe.PutDataFrameTransformRequest; @@ -147,4 +148,29 @@ public void testGetDataFrameTransformStats() { assertEquals(HttpGet.METHOD_NAME, request.getMethod()); assertThat(request.getEndpoint(), equalTo("/_data_frame/transforms/foo/_stats")); } + + public void testGetDataFrameTransform() { + GetDataFrameTransformRequest getRequest = new GetDataFrameTransformRequest("bar"); + Request request = DataFrameRequestConverters.getDataFrameTransform(getRequest); + + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + assertThat(request.getEndpoint(), equalTo("/_data_frame/transforms/bar")); + + assertFalse(request.getParameters().containsKey("from")); + assertFalse(request.getParameters().containsKey("size")); + + getRequest.setFrom(0); + getRequest.setSize(10); + request = DataFrameRequestConverters.getDataFrameTransform(getRequest); + assertEquals("0", request.getParameters().get("from")); + assertEquals("10", request.getParameters().get("size")); + } + + public void testGetDataFrameTransform_givenMulitpleIds() { + GetDataFrameTransformRequest getRequest = new GetDataFrameTransformRequest("foo", "bar", "baz"); + Request request = DataFrameRequestConverters.getDataFrameTransform(getRequest); + + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + assertThat(request.getEndpoint(), equalTo("/_data_frame/transforms/foo,bar,baz")); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameTransformIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameTransformIT.java index 25b09866e1567..e8724cc071dae 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameTransformIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/DataFrameTransformIT.java @@ -27,6 +27,8 @@ import org.elasticsearch.client.core.AcknowledgedResponse; import org.elasticsearch.client.core.IndexerState; import org.elasticsearch.client.dataframe.DeleteDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformResponse; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsRequest; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsResponse; import org.elasticsearch.client.dataframe.PreviewDataFrameTransformRequest; @@ -52,6 +54,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.MatchAllQueryBuilder; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.AggregatorFactories; import org.junit.After; @@ -67,6 +70,7 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; @@ -153,16 +157,8 @@ public void testCreateDelete() throws IOException { String sourceIndex = "transform-source"; createIndex(sourceIndex); - QueryConfig queryConfig = new QueryConfig(new MatchAllQueryBuilder()); - GroupConfig groupConfig = new GroupConfig(Collections.singletonMap("reviewer", new TermsGroupSource("user_id"))); - AggregatorFactories.Builder aggBuilder = new AggregatorFactories.Builder(); - aggBuilder.addAggregator(AggregationBuilders.avg("avg_rating").field("stars")); - AggregationConfig aggConfig = new AggregationConfig(aggBuilder); - PivotConfig pivotConfig = new PivotConfig(groupConfig, aggConfig); - String id = "test-crud"; - DataFrameTransformConfig transform = new DataFrameTransformConfig(id, - new SourceConfig(new String[]{sourceIndex}, queryConfig), new DestConfig("pivot-dest"), pivotConfig); + DataFrameTransformConfig transform = validDataFrameTransformConfig(id, sourceIndex, "pivot-dest"); DataFrameClient client = highLevelClient().dataFrame(); AcknowledgedResponse ack = execute(new PutDataFrameTransformRequest(transform), client::putDataFrameTransform, @@ -180,20 +176,78 @@ public void testCreateDelete() throws IOException { assertThat(deleteError.getMessage(), containsString("Transform with id [test-crud] could not be found")); } - public void testStartStop() throws IOException { + public void testGetTransform() throws IOException { String sourceIndex = "transform-source"; createIndex(sourceIndex); - QueryConfig queryConfig = new QueryConfig(new MatchAllQueryBuilder()); - GroupConfig groupConfig = new GroupConfig(Collections.singletonMap("reviewer", new TermsGroupSource("user_id"))); - AggregatorFactories.Builder aggBuilder = new AggregatorFactories.Builder(); - aggBuilder.addAggregator(AggregationBuilders.avg("avg_rating").field("stars")); - AggregationConfig aggConfig = new AggregationConfig(aggBuilder); - PivotConfig pivotConfig = new PivotConfig(groupConfig, aggConfig); + String id = "test-get"; + DataFrameTransformConfig transform = validDataFrameTransformConfig(id, sourceIndex, "pivot-dest"); + + DataFrameClient client = highLevelClient().dataFrame(); + AcknowledgedResponse ack = execute(new PutDataFrameTransformRequest(transform), client::putDataFrameTransform, + client::putDataFrameTransformAsync); + assertTrue(ack.isAcknowledged()); + + GetDataFrameTransformRequest getRequest = new GetDataFrameTransformRequest(id); + GetDataFrameTransformResponse getResponse = execute(getRequest, client::getDataFrameTransform, + client::getDataFrameTransformAsync); + assertNull(getResponse.getInvalidTransforms()); + assertThat(getResponse.getTransformConfigurations(), hasSize(1)); + assertEquals(transform, getResponse.getTransformConfigurations().get(0)); + } + + public void testGetAllAndPageTransforms() throws IOException { + String sourceIndex = "transform-source"; + createIndex(sourceIndex); + + DataFrameClient client = highLevelClient().dataFrame(); + + DataFrameTransformConfig transform = validDataFrameTransformConfig("test-get-all-1", sourceIndex, "pivot-dest-1"); + AcknowledgedResponse ack = execute(new PutDataFrameTransformRequest(transform), client::putDataFrameTransform, + client::putDataFrameTransformAsync); + assertTrue(ack.isAcknowledged()); + + transform = validDataFrameTransformConfig("test-get-all-2", sourceIndex, "pivot-dest-2"); + ack = execute(new PutDataFrameTransformRequest(transform), client::putDataFrameTransform, + client::putDataFrameTransformAsync); + assertTrue(ack.isAcknowledged()); + + GetDataFrameTransformRequest getRequest = new GetDataFrameTransformRequest("_all"); + GetDataFrameTransformResponse getResponse = execute(getRequest, client::getDataFrameTransform, + client::getDataFrameTransformAsync); + assertNull(getResponse.getInvalidTransforms()); + assertThat(getResponse.getTransformConfigurations(), hasSize(2)); + assertEquals(transform, getResponse.getTransformConfigurations().get(1)); + + getRequest.setFrom(0); + getRequest.setSize(1); + getResponse = execute(getRequest, client::getDataFrameTransform, + client::getDataFrameTransformAsync); + assertNull(getResponse.getInvalidTransforms()); + assertThat(getResponse.getTransformConfigurations(), hasSize(1)); + + GetDataFrameTransformRequest getMulitple = new GetDataFrameTransformRequest("test-get-all-1", "test-get-all-2"); + getResponse = execute(getMulitple, client::getDataFrameTransform, + client::getDataFrameTransformAsync); + assertNull(getResponse.getInvalidTransforms()); + assertThat(getResponse.getTransformConfigurations(), hasSize(2)); + } + + public void testGetMissingTransform() { + DataFrameClient client = highLevelClient().dataFrame(); + + ElasticsearchStatusException missingError = expectThrows(ElasticsearchStatusException.class, + () -> execute(new GetDataFrameTransformRequest("unknown"), client::getDataFrameTransform, + client::getDataFrameTransformAsync)); + assertThat(missingError.status(), equalTo(RestStatus.NOT_FOUND)); + } + + public void testStartStop() throws IOException { + String sourceIndex = "transform-source"; + createIndex(sourceIndex); String id = "test-stop-start"; - DataFrameTransformConfig transform = new DataFrameTransformConfig(id, - new SourceConfig(new String[]{sourceIndex}, queryConfig), new DestConfig("pivot-dest"), pivotConfig); + DataFrameTransformConfig transform = validDataFrameTransformConfig(id, sourceIndex, "pivot-dest"); DataFrameClient client = highLevelClient().dataFrame(); AcknowledgedResponse ack = execute(new PutDataFrameTransformRequest(transform), client::putDataFrameTransform, @@ -226,15 +280,7 @@ public void testPreview() throws IOException { createIndex(sourceIndex); indexData(sourceIndex); - QueryConfig queryConfig = new QueryConfig(new MatchAllQueryBuilder()); - GroupConfig groupConfig = new GroupConfig(Collections.singletonMap("reviewer", new TermsGroupSource("user_id"))); - AggregatorFactories.Builder aggBuilder = new AggregatorFactories.Builder(); - aggBuilder.addAggregator(AggregationBuilders.avg("avg_rating").field("stars")); - AggregationConfig aggConfig = new AggregationConfig(aggBuilder); - PivotConfig pivotConfig = new PivotConfig(groupConfig, aggConfig); - - DataFrameTransformConfig transform = new DataFrameTransformConfig("test-preview", - new SourceConfig(new String[]{sourceIndex}, queryConfig), null, pivotConfig); + DataFrameTransformConfig transform = validDataFrameTransformConfig("test-preview", sourceIndex, null); DataFrameClient client = highLevelClient().dataFrame(); PreviewDataFrameTransformResponse preview = execute(new PreviewDataFrameTransformRequest(transform), @@ -245,11 +291,27 @@ public void testPreview() throws IOException { assertThat(docs, hasSize(2)); Optional> theresa = docs.stream().filter(doc -> "theresa".equals(doc.get("reviewer"))).findFirst(); assertTrue(theresa.isPresent()); - assertEquals(2.5d, (double)theresa.get().get("avg_rating"), 0.01d); + assertEquals(2.5d, (double) theresa.get().get("avg_rating"), 0.01d); Optional> michel = docs.stream().filter(doc -> "michel".equals(doc.get("reviewer"))).findFirst(); assertTrue(michel.isPresent()); - assertEquals(3.6d, (double)michel.get().get("avg_rating"), 0.1d); + assertEquals(3.6d, (double) michel.get().get("avg_rating"), 0.1d); + } + + private DataFrameTransformConfig validDataFrameTransformConfig(String id, String source, String destination) { + QueryConfig queryConfig = new QueryConfig(new MatchAllQueryBuilder()); + GroupConfig groupConfig = new GroupConfig(Collections.singletonMap("reviewer", new TermsGroupSource("user_id"))); + AggregatorFactories.Builder aggBuilder = new AggregatorFactories.Builder(); + aggBuilder.addAggregator(AggregationBuilders.avg("avg_rating").field("stars")); + AggregationConfig aggConfig = new AggregationConfig(aggBuilder); + PivotConfig pivotConfig = new PivotConfig(groupConfig, aggConfig); + + DestConfig destConfig = (destination != null) ? new DestConfig(destination) : null; + + return new DataFrameTransformConfig(id, + new SourceConfig(new String[]{source}, queryConfig), + destConfig, + pivotConfig); } public void testGetStats() throws Exception { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java index 976ae754d335f..f758156c222a8 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ESRestHighLevelClientTestCase.java @@ -34,6 +34,7 @@ import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.ingest.Pipeline; import org.elasticsearch.search.SearchHit; +import org.elasticsearch.search.SearchModule; import org.elasticsearch.test.rest.ESRestTestCase; import org.junit.AfterClass; import org.junit.Before; @@ -130,7 +131,7 @@ protected interface AsyncMethodNoRequest { private static class HighLevelClient extends RestHighLevelClient { private HighLevelClient(RestClient restClient) { - super(restClient, (client) -> {}, Collections.emptyList()); + super(restClient, (client) -> {}, new SearchModule(Settings.EMPTY, false, Collections.emptyList()).getNamedXContents()); } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequestTests.java new file mode 100644 index 0000000000000..818eea4520ac4 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformRequestTests.java @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.dataframe; + +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.containsString; + +public class GetDataFrameTransformRequestTests extends ESTestCase { + public void testValidate() { + assertFalse(new GetDataFrameTransformRequest("valid-id").validate().isPresent()); + assertThat(new GetDataFrameTransformRequest(new String[0]).validate().get().getMessage(), + containsString("data frame transform id must not be null")); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponseTests.java new file mode 100644 index 0000000000000..f7386e936301b --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/dataframe/GetDataFrameTransformResponseTests.java @@ -0,0 +1,84 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.dataframe; + +import org.elasticsearch.client.dataframe.transforms.DataFrameTransformConfig; +import org.elasticsearch.client.dataframe.transforms.DataFrameTransformConfigTests; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.search.SearchModule; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester; + + +public class GetDataFrameTransformResponseTests extends ESTestCase { + + public void testXContentParser() throws IOException { + xContentTester(this::createParser, + GetDataFrameTransformResponseTests::createTestInstance, + GetDataFrameTransformResponseTests::toXContent, + GetDataFrameTransformResponse::fromXContent) + .supportsUnknownFields(false) + .test(); + } + + private static GetDataFrameTransformResponse createTestInstance() { + int numTransforms = randomIntBetween(0, 3); + List transforms = new ArrayList<>(); + for (int i=0; i invalidIds = Arrays.asList(generateRandomStringArray(5, 6, false, false)); + invalidTransforms = new GetDataFrameTransformResponse.InvalidTransforms(invalidIds); + } + return new GetDataFrameTransformResponse(transforms, transforms.size() + 10, invalidTransforms); + } + + private static void toXContent(GetDataFrameTransformResponse response, XContentBuilder builder) throws IOException { + builder.startObject(); + { + builder.field("count", response.getCount()); + builder.field("transforms", response.getTransformConfigurations()); + if (response.getInvalidTransforms() != null) { + builder.startObject("invalid_transforms"); + builder.field("count", response.getInvalidTransforms().getCount()); + builder.field("transforms", response.getInvalidTransforms().getTransformIds()); + builder.endObject(); + } + } + builder.endObject(); + } + + @Override + protected NamedXContentRegistry xContentRegistry() { + SearchModule searchModule = new SearchModule(Settings.EMPTY, false, Collections.emptyList()); + return new NamedXContentRegistry(searchModule.getNamedXContents()); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/DataFrameTransformDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/DataFrameTransformDocumentationIT.java index bf4940654effe..b7d6967206c2c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/DataFrameTransformDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/DataFrameTransformDocumentationIT.java @@ -27,6 +27,8 @@ import org.elasticsearch.client.core.AcknowledgedResponse; import org.elasticsearch.client.core.IndexerState; import org.elasticsearch.client.dataframe.DeleteDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformRequest; +import org.elasticsearch.client.dataframe.GetDataFrameTransformResponse; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsRequest; import org.elasticsearch.client.dataframe.GetDataFrameTransformStatsResponse; import org.elasticsearch.client.dataframe.PreviewDataFrameTransformRequest; @@ -178,7 +180,6 @@ public void onFailure(Exception e) { // Replace the empty listener by a blocking listener in test final CountDownLatch latch = new CountDownLatch(1); - ActionListener ackListener = listener; listener = new LatchedActionListener<>(listener, latch); // tag::put-data-frame-transform-execute-async @@ -264,7 +265,6 @@ public void onFailure(Exception e) { // Replace the empty listener by a blocking listener in test final CountDownLatch latch = new CountDownLatch(1); - ActionListener ackListener = listener; listener = new LatchedActionListener<>(listener, latch); StartDataFrameTransformRequest request = new StartDataFrameTransformRequest("mega-transform"); @@ -294,7 +294,6 @@ public void onFailure(Exception e) { // Replace the empty listener by a blocking listener in test final CountDownLatch latch = new CountDownLatch(1); - ActionListener ackListener = listener; listener = new LatchedActionListener<>(listener, latch); StopDataFrameTransformRequest request = new StopDataFrameTransformRequest("mega-transform"); @@ -392,14 +391,14 @@ public void testPreview() throws IOException, InterruptedException { pivotConfig); PreviewDataFrameTransformRequest request = - new PreviewDataFrameTransformRequest(transformConfig); // <3> + new PreviewDataFrameTransformRequest(transformConfig); // <3> // end::preview-data-frame-transform-request { // tag::preview-data-frame-transform-execute PreviewDataFrameTransformResponse response = - client.dataFrame() - .previewDataFrameTransform(request, RequestOptions.DEFAULT); + client.dataFrame() + .previewDataFrameTransform(request, RequestOptions.DEFAULT); // end::preview-data-frame-transform-execute assertNotNull(response.getDocs()); @@ -482,10 +481,83 @@ public void testGetStats() throws IOException, InterruptedException { { // tag::get-data-frame-transform-stats-execute-listener ActionListener listener = - new ActionListener() { + new ActionListener() { + @Override + public void onResponse( + GetDataFrameTransformStatsResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::get-data-frame-transform-stats-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::get-data-frame-transform-stats-execute-async + client.dataFrame().getDataFrameTransformStatsAsync( + request, RequestOptions.DEFAULT, listener); // <1> + // end::get-data-frame-transform-stats-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + + + public void testGetDataFrameTransform() throws IOException, InterruptedException { + createIndex("source-data"); + + QueryConfig queryConfig = new QueryConfig(new MatchAllQueryBuilder()); + GroupConfig groupConfig = new GroupConfig(Collections.singletonMap("reviewer", new TermsGroupSource("user_id"))); + AggregatorFactories.Builder aggBuilder = new AggregatorFactories.Builder(); + aggBuilder.addAggregator(AggregationBuilders.avg("avg_rating").field("stars")); + AggregationConfig aggConfig = new AggregationConfig(aggBuilder); + PivotConfig pivotConfig = new PivotConfig(groupConfig, aggConfig); + + + DataFrameTransformConfig putTransformConfig = new DataFrameTransformConfig("mega-transform", + new SourceConfig(new String[]{"source-data"}, queryConfig), + new DestConfig("pivot-dest"), pivotConfig); + + RestHighLevelClient client = highLevelClient(); + client.dataFrame().putDataFrameTransform(new PutDataFrameTransformRequest(putTransformConfig), RequestOptions.DEFAULT); + transformsToClean.add(putTransformConfig.getId()); + + { + // tag::get-data-frame-transform-request + GetDataFrameTransformRequest request = + new GetDataFrameTransformRequest("mega-transform"); // <1> + // end::get-data-frame-transform-request + + // tag::get-data-frame-transform-request-options + request.setFrom(0); // <1> + request.setSize(100); // <2> + // end::get-data-frame-transform-request-options + + // tag::get-data-frame-transform-execute + GetDataFrameTransformResponse response = + client.dataFrame() + .getDataFrameTransform(request, RequestOptions.DEFAULT); + // end::get-data-frame-transform-execute + + // tag::get-data-frame-transform-response + List transformConfigs = + response.getTransformConfigurations(); + // end::get-data-frame-transform-response + + assertEquals(1, transformConfigs.size()); + } + { + // tag::get-data-frame-transform-execute-listener + ActionListener listener = + new ActionListener() { @Override - public void onResponse( - GetDataFrameTransformStatsResponse response) { + public void onResponse(GetDataFrameTransformResponse response) { // <1> } @@ -494,16 +566,18 @@ public void onFailure(Exception e) { // <2> } }; - // end::get-data-frame-transform-stats-execute-listener + // end::get-data-frame-transform-execute-listener // Replace the empty listener by a blocking listener in test final CountDownLatch latch = new CountDownLatch(1); listener = new LatchedActionListener<>(listener, latch); - // tag::get-data-frame-transform-stats-execute-async - client.dataFrame().getDataFrameTransformStatsAsync( + GetDataFrameTransformRequest request = new GetDataFrameTransformRequest("mega-transform"); + + // tag::get-data-frame-transform-execute-async + client.dataFrame().getDataFrameTransformAsync( request, RequestOptions.DEFAULT, listener); // <1> - // end::get-data-frame-transform-stats-execute-async + // end::get-data-frame-transform-execute-async assertTrue(latch.await(30L, TimeUnit.SECONDS)); } diff --git a/docs/java-rest/high-level/dataframe/get_data_frame.asciidoc b/docs/java-rest/high-level/dataframe/get_data_frame.asciidoc new file mode 100644 index 0000000000000..41fa841060b30 --- /dev/null +++ b/docs/java-rest/high-level/dataframe/get_data_frame.asciidoc @@ -0,0 +1,45 @@ +-- +:api: get-data-frame-transform +:request: GetDataFrameTransformRequest +:response: GetDataFrameTransformResponse +-- +[id="{upid}-{api}"] +=== Get Data Frame Transform API + +The Get Data Frame Transform API is used get one or more {dataframe-transform}. +The API accepts a +{request}+ object and returns a +{response}+. + +[id="{upid}-{api}-request"] +==== Get Data Frame Request + +A +{request}+ requires either a data frame transform id, a comma separated list of ids or +the special wildcard `_all` to get all {dataframe-transform}s + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request] +-------------------------------------------------- +<1> Constructing a new GET request referencing an existing {dataframe-transform} + +==== Optional Arguments + +The following arguments are optional. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-request-options] +-------------------------------------------------- +<1> Page {dataframe-transform}s starting from this value +<2> Return at most `size` {dataframe-transform}s + +include::../execution.asciidoc[] + +[id="{upid}-{api}-response"] +==== Response + +The returned +{response}+ contains the requested {dataframe-transform}s. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[{api}-response] +-------------------------------------------------- \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index d077ad2fcf844..4e28efc2941db 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -554,6 +554,7 @@ include::ilm/remove_lifecycle_policy_from_index.asciidoc[] The Java High Level REST Client supports the following Data Frame APIs: +* <<{upid}-get-data-frame-transform>> * <<{upid}-get-data-frame-transform-stats>> * <<{upid}-put-data-frame-transform>> * <<{upid}-delete-data-frame-transform>> @@ -561,6 +562,7 @@ The Java High Level REST Client supports the following Data Frame APIs: * <<{upid}-start-data-frame-transform>> * <<{upid}-stop-data-frame-transform>> +include::dataframe/get_data_frame.asciidoc[] include::dataframe/get_data_frame_stats.asciidoc[] include::dataframe/put_data_frame.asciidoc[] include::dataframe/delete_data_frame.asciidoc[] From a71c94df2cc7bd2d7251f8da5462c141f56d1d0a Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Wed, 27 Mar 2019 11:15:25 +0100 Subject: [PATCH 10/77] Fix random failures in SearchResponseMerger#testMergeSearchHits (#40223) This commit fixes the expectation in the test when the search hits are empty. Closes #40214 --- .../action/search/SearchResponseMergerTests.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java index ec9681a7b62fb..a7216853129a4 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java @@ -396,7 +396,6 @@ public void testMergeAggs() throws InterruptedException { assertEquals(totalCount, bucket.getDocCount()); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40214") public void testMergeSearchHits() throws InterruptedException { final long currentRelativeTime = randomLong(); final SearchTimeProvider timeProvider = new SearchTimeProvider(randomLong(), 0, () -> currentRelativeTime); @@ -513,8 +512,14 @@ public void testMergeSearchHits() throws InterruptedException { assertNull(searchResponse.getScrollId()); SearchHits searchHits = searchResponse.getHits(); - assertArrayEquals(sortFields, searchHits.getSortFields()); - assertEquals(collapseField, searchHits.getCollapseField()); + // the sort fields and the collapse field are not returned when hits are empty + if (searchHits.getHits().length > 0) { + assertArrayEquals(sortFields, searchHits.getSortFields()); + assertEquals(collapseField, searchHits.getCollapseField()); + } else { + assertNull(searchHits.getSortFields()); + assertNull(searchHits.getCollapseField()); + } if (expectedTotalHits == null) { assertNull(searchHits.getTotalHits()); } else { @@ -532,7 +537,9 @@ public void testMergeSearchHits() throws InterruptedException { priorityQueue.poll(); } SearchHit[] hits = searchHits.getHits(); - if (collapseField != null) { + if (collapseField != null + // the collapse field is not returned when hits are empty + && hits.length > 0) { assertEquals(hits.length, searchHits.getCollapseValues().length); } else { assertNull(searchHits.getCollapseValues()); From 79a3b93deb858961ecbe4516ed9d8f209016666f Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 27 Mar 2019 12:20:16 +0200 Subject: [PATCH 11/77] SQL: Update JDBC class name in client screenshots --- .../sql/client-apps/dbeaver-1-new-conn.png | Bin 25260 -> 29062 bytes .../sql/client-apps/dbeaver-2-conn-es.png | Bin 23146 -> 24534 bytes .../sql/client-apps/dbeaver-3-conn-props.png | Bin 20325 -> 23260 bytes .../sql/client-apps/dbeaver-4-driver-ver.png | Bin 29626 -> 29102 bytes .../sql/client-apps/dbeaver-5-test-conn.png | Bin 28780 -> 30135 bytes .../images/sql/client-apps/dbeaver-6-data.png | Bin 87596 -> 94666 bytes .../client-apps/dbvis-1-driver-manager.png | Bin 60954 -> 61264 bytes .../images/sql/client-apps/dbvis-2-driver.png | Bin 57074 -> 56920 bytes .../sql/client-apps/dbvis-3-new-conn.png | Bin 55342 -> 55354 bytes .../sql/client-apps/dbvis-4-conn-props.png | Bin 57094 -> 55848 bytes .../images/sql/client-apps/dbvis-5-data.png | Bin 99284 -> 98369 bytes .../sql/client-apps/squirell-3-add-driver.png | Bin 17121 -> 16985 bytes .../client-apps/squirell-4-driver-list.png | Bin 29004 -> 29042 bytes .../sql/client-apps/squirell-5-add-alias.png | Bin 22199 -> 21957 bytes .../sql/client-apps/squirell-7-data.png | Bin 44964 -> 59419 bytes .../client-apps/workbench-2-add-driver.png | Bin 26008 -> 26418 bytes .../client-apps/workbench-3-connection.png | Bin 35138 -> 38845 bytes .../sql/client-apps/workbench-4-data.png | Bin 75863 -> 75052 bytes .../endpoints/client-apps/dbeaver.asciidoc | 2 +- .../sql/endpoints/client-apps/index.asciidoc | 2 +- 20 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/images/sql/client-apps/dbeaver-1-new-conn.png b/docs/reference/images/sql/client-apps/dbeaver-1-new-conn.png index 2307f0393266361aa9ba3772126145be29e8cb84..bf7f1c63135afc4c5321776216e890997d5b4e4a 100644 GIT binary patch literal 29062 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 L;ZT9K*R_8D#VK=l diff --git a/docs/reference/images/sql/client-apps/dbeaver-2-conn-es.png b/docs/reference/images/sql/client-apps/dbeaver-2-conn-es.png index 1ca209a57e5553c35c1648825bada8d25daa389e..f63df0987c167dd219102ddf7a6cbe0596df7c2f 100644 GIT binary patch literal 24534 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 literal 23146 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 QWue2(*%S8S^p({A0Y25mLI3~& diff --git a/docs/reference/images/sql/client-apps/dbeaver-3-conn-props.png b/docs/reference/images/sql/client-apps/dbeaver-3-conn-props.png index 7561e94bdd9914399befe0c892a9a2f9268e4cb8..825ce1b6357fb0d4ff26131cc6e2ac1fa9aaa1c3 100644 GIT binary patch literal 23260 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≫pkueC&a`!M@-tYme^)!pR$%k| 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 Q}#&7Bl9Wo~J9 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 diff --git a/docs/reference/images/sql/client-apps/dbeaver-5-test-conn.png b/docs/reference/images/sql/client-apps/dbeaver-5-test-conn.png index 70f2a1dd4dc2f33e0e251323cd5c7b22d4279cd5..c76ae19937a08f49797155a804120c879238cb38 100644 GIT binary patch literal 30135 zcma(2cT|&E7dQ$t&Zrxoz>^0`F|EMIA0B!dkD1B`t`mVTf7w3LdBnt270bH+@+9z`#qFty z2NToz4#v+PU9O+3nV7t^HJ>~(@U>WLIsN+j*USdxS-ORHV}N*e%^?2zA6hr!$WDJs z^jsl?-M_&;y6Y4g-E+!1KBJW+`)_nTH{^HX+tSoVH4{sze3x-Bc92p<2 zl&7tvudp;r)<`xF4hH`q8bEoZ4x9{qT>rW7nE7Vd?_W>TkJtdZm`?rW9RK3$^`p~_ zFA52%lk%nsKi*q$9JDbWe2f1wCAReX9K=Z=SfKs=p;}nmU%=zn^|zLsa>i%_TpsOj zZ{L#=Oe=qbSKl`bYh&=^b+EvaT~626zoZ~a^vi8G8cx7uRKI(2Y|AdYo;c)Wz}-v! zHW=u`dQ$2#WR@k!c-3(Kgm7e`cv|_;;i-enu1Ut4kD@<$`h8?!kArA^?2uV*5?El| zvy=3pP(0N|b%wlgKGx?1ug@71V!zc+HL1{oF29@*m)i2IPO_ZG?mJy;{(jwJpbMazX5d5psy{uTiE<=*vR$Gqv(01;67l)U@q{5je z#Rq#6!zE(|hE0O<^QuLuHQU0PArHEX3p6~^S0DGx;fxI5J zCF}D%a9S+Ylsh1MN*j%3^47bc%RQaly+kw04fUOA^QWenVG_t>dkCFBU15RKRYx9RSd1vF=7l8MZkhN7!ak>f_A3I(8jzgo<);I^F<_D84}-u3~unx4#(BGIOrEQ z`!GIhN{m*mgp-cVGG8)Jq8N-oXOc{P%8)Cs$p^@lw?UT%eje(KWs*T@Wb(9US;oZr z5ldJi!;JGi@>2dvlS~z-ne4f2C2HXB zP5OyvP%}7UgL^Txh#o$@au|ecxde8;>eWsJW%7EXxERaTFF+R6*+?jHwX42cJPiTy8OwkN`=UXERHl!-FuRl{*#kgh_UG@L68F+5yQgBhQUn!G4oexnC%HiV(JWGd{KV^Lmi-|3S1kq z&v##K%@~<#se3u&^Q0Dn$4U}~C48RdAeq?Pb2il(X@(C=K*aSM+_rPY7#xlBek!Ur z8q;3%*zYsn;f3UZXE^_B5V>O#RsvUooo^ylsbSP)V~doGisnx597k-Sdh?LseVG4- zn&19+jfFPQ7OZ-$OU2sn=Rfo{rDRKZk>;(@>KjOgw6unb+KVD1;gZ%2%{b)l8(<$e z+2CasujDr9in4k#6F48esh?pIS6&Jb#_@b|DnAw-i`QDsyo zi)~?RPOZ`1j>~%KoU5}A^ejNHzb|{`nmVMR+U8hoYOM8fPI$nX9G1d&hjxDQAoqMR zt%#!CF!W235^%veUQ`L6 zfz%O+3W1N#tQZ-c<~PPpKNJ;x8}C=_YV6nUFSOMsMqQ&PeKXT`VO@~#*;e$v)*Z;SW%sk46+i>5JmyZ6B-$zO_jvA`@DK99#XMO5X}%q zrdtK?B?2tV%F5c*mk~jTRk3jxYU^0$lzoin6c@SgGXBIx0@~`O6I;e_BDhf?ELwH( z9}%r_r8+0$)LjXqQywC=m&%*DwxY}Vle+H*b2m*II?u8{X0|sn7crBbxXWzoO!UQL zR@+&JAe!V>#{%=9@vlVP1g@+Q3$QXC;l7#^k#FUm`#3?w*xsG6p(b$a4mWqP#B@2o z{b$xHtU(D%+{llGP-NfyX5ndu^UbrX_w1Fo>IKYw@>`Qrp5yPeMbu!*Y4F=!54+&S z4m+W;Z5oxD3p59?@S^JYX+)7F{t*pWNYrtNs|3u} zQU;|-6m)Zz-b;3$UJ$V!pHp-eu^Y3PtzzRc)UM-{(9y9RWyY7@N?N;tl*d|!>my{_ zIm=6i+|FSQxCX9{mQ>N9;S$p^NatGsW=oG2L_TRm8z?y{8d)V#ToNt_bAWny-*>(B%P)SEE4&FMG*-ypkV$VnhB! zN{x15vtl)dW9Zqf7abDc#k^pgjwY@TdNwLgWYp*|4C7v=Ff#c|HTO`F7z*&pP+Jy4 z2b)T2u&~E>M)^b+=SOD!JC6S$G45PcudQbs!Y9~O_F~Kd?%Rda z?zgL<*aC*cUm(pg1f@1gbF-{6P`TWXBW8q?dQzD zrAhFUor3%iM+${r>2bg``9 zc2;(H(TiTZW&f7cORrBkc*tY$UIIhH({re+%6rv^ljF5&Eq}kSAWHxFe&L@A%fZ7;g{RL zFf@GZg@9mI^ypX2d8!}!Yya8C&D0}T8H~NJsx{Sdi9j-WKkvyaSs{kY|Gno=0VCe% zr!F!HCa#|2{T1oiu1_Hlt3`JFZ9sm^#05K2K8G5A)JxBa4Dt7vy?2`!&+JW%Z^gSD zLV?$WX%9FEBgOjoX zwiC0Go3#dqoC|Efa}L$~OdRq+Jpzey3oDCwCuTM8Bxlh>;3;XADbfi9e@n^1G!upTg=-x%OdIcjAJ+)W=w53Ze1IE=J;~1 zuGZ+cP7MvbDvNY&y{)`+sB8VTE%F|XZgBRfV_(};A|tf7v_9qVL%i9VgY3Lfkg>nT zbkgwMz<=)u{SUJ`Y@!@w;iv1L5;VX(3%bIxuMs-qF|Pgn>~}AF))i2i0!H7T(^)`y zP`t*AY6hde`K6MzRzoQ|^uNDlW?wMbmeE9`&F-e(M3Y0VE-q>0xb zw#!4&XHHJ1Ug8(*-t%-I&8<9mEVya2ND}IPr#(G}4ZAoa!@+`$b!y9T^jBy~rO?z3 zO6^*x4Q6}3N)C~VHoS}UmInJr2S6HD3)`$XNsQ%M=lmWZgxJr#*m7znZ~U|jY4GIH z&FU8g)E0FG!Xz3!`s0PdW4RDo2|I3O$=cs|$r5J$kk$6F=3(#G`=3iRo?|5NI7 zDAT>i!tcbR)jnqZ5<9L@$875drRN+1CNm*k4yPtZtg%kOiL+O~JF<=UeH<#&seQRe_pHmF3l3KvzJQuRLjc#{%(=6Y*I%JI_#Ucegz0z>HQ3$Ds11OTgPJ!> z<>iVR1Sweq16%`^q(b&GjPHqpMiI2QuZ5O_aFBaSY=IztIH_0r zJ?j|hEpE|a24e4!4*DS$GUGt66Q@rLB5h(G{kY_`^Rf{p74*p`F4t|T{$XeR%^?Tn zTrIRiQHbc42ldX^1HIzxL{p6dTs+|SMf5M#Wzo;Gn`|9L{U9nQ9{3H~oG0Qos`u8^ z-?!bNbW%0H%62u6C|byMa26&o?Q-FC?_XL~L;L>0_Zh|D>2wh)cp>ZuCAs`( ztL|-n_pqAVqLEHtR!mFolJ=hx2PhmCJ@yBr1ZXROC%!4=0g z-{(KNiG))OFm@TN{u?LO7W$z7CF1u|MyyZfGzLDuQSwC`#Qoe*F0uIQqM(mrasI^q z;v~l~syDbyre!EN+u~(*ijStTBCpq|V!9qT%Sgkc55;9#%4SCnTOuGAc^uS_p(Fg{ znJ!t2` z_Zj|-lBmEHx*RM(p=f6bBn0x1a0`=+V>@bxqPtHmOZ@X!YLny}m$b1jR9 zQAxUCCmwu%Ktz`XUN)`P%4ruJA;1 z{i6@>Gp~)Tz{-LbC%HyvlL?>T%|Hy2l2!gn=M5n9@mJ$#pBSys#rg*gk1Th`wjYx6 zAO+%+7XG~QFn|A6d49Om>RKjA(tufNrQas zypXH!N$0;j>B^`7o043U?HOp?Cs*8;4I4B_>D|_;1!aUkaAq zr-+Q`xQf28^PJTg*EHJ-;jGqL&Va(9IMclL9 zOZZW<7!eUy5UngISa>kIp>^WRrzPSaX$Ry)7whM_m(9H0l=e*5@5cw+O4fF?f33*7 z=v9_;*fQnn;=h`@#KW*sQ7{Cum-AbDZN*RZkh4U<$~%h3?F6@=1Nl zBT2`MI=hidjE=6Z5VYXLKMJh<)7*@db^-O;6Ii>EWqLYdjbc2bjUEh`xgV;~x_$OK z5Ym%Sy)BK5B_Ltfinfinn3sNKvwoz0*-M|b9SGm)KvHn_)9{ZW9e}N(dahb8ODIbj zuBI$520&ClWzZYWK7KnaZaXSbC%L(cZ}v7Di1JQp`^P+G*($a!OM|_ZH{{TRSmfdC zMqwa&~bwVk;U=)dRObNLc?kU)}EwTeel_ z1Rd=VypylFAk#;yU56rn2O2ro7DNAnxO&0!*Q#m)x2dosXN|x|Z_llV7$`+J)r6is z%7()CL>mgF*IGPp{UTO0W~QO&yUSK+h=2Y?Os3O^xzS1T56(|+F}tf`)gz;mSc3O% zZcBr~?*z;9DblTVcTTH%a5>a^xt>4Tjm(+ccDPV88S?z?xml9|`5m_U*7TXDLcj93 zp*cBxAf*Sj;5fUR<@`k5TjaF6DrneiEH6P5Z)X9(gUUY4d%fZ0G3AeMcg%RsJ;0u89J zR%9h0TK9SUhy@6zO#9zC-RoWG3hbbBXVvlDM3dIRzkJvAF6;bGQU06@qtXblxRz~| zN>RS6Lqa2n|myr&AD2F|&_Z;(-4G!C+4Qf|p-{8Oc9NaPVam@i$JFT)kG~mS#^atvDFuMn` z^VUCpX0B$}8xWnOC#uuAWtMjdv#|zkT?^=WbMk`| zk9142&bfAz2D#d1x3hoUbU-;XlO!%|i!)jT??^_lmOb9_9XcrK`K+RDYuiyQd_9OQ zAxQn`+o5OnbR|gTOZMP7I>>5%6V>XgLZhj2w;UbyzKq~=JZhy&4k}rZ#t9tw-vuBqow5qep%P-Dgs@!pV+TzK| zx5i=|iV(6h$;vq;a%BeI1@?mSlcpA@MNv}Mr@LB;v$IWOHC`afg}q9ugDdtgvJ3iR z7l$J*JnnuSlPGsUqhKq3v?**3X^HPBl25K*O`dD*EwqVawk29xB=Ep4@B7Z9uD!a` zycdC`bwtV~X@W%;Q~i1uyR$VwbU2|@B(vha<(o9s(}ux;pFN5&fky4sUUyuy3RfL) zmXoGM=V$iQ@Yc#(V}l=J#5w;#&h-8he^N_9#W}wB%<~snvQC6pW^gIe)7(DuZPcg64HxKVf9j}V zQE;&EsI;PmnR#Ndp>T71Eyj7RUuF{>Y%#Pq?5$LsOk^`oEnIAdop5?%|1t>B__OJs zhFdl|GwP)`qGkT;ZB@6RJ9G=THx15mE9}X#1^m}3M}w`sYa9b{vNEnI z#^+qq2e(e)hc_m2m#zPOuR+@v{&S@H1r_~Ht0#p$O}0S{?;n8USwjs`vNo-slC?uIivv!^ z_L}#nPJ%A-DF!`3cs3b*)gJRM+A7)AjjfAFwQhv#eeS+16kjle(e#t|x@#6ISY2)x z)V^qLiOsMEYW8~}jlKSPlf{u^Iddvx!voB)mr^@Atm9CjxOCRcbtxwYbBy-#E77+V zn61L=b=zX&LF0kr^l~-IbTTV^Fxh8Y(KNQDZ!mkN`LURvb*R!F!PqHCx~|^){0aQ+ z{Bu@|oJ~N1nD8ZXa$Tl0xqaoNUL!SD?}cv(O}<3u{+5Y#uvnJp(S$NIro}ZGv)OsA ze!zA3Mke6Z4lYbSOK(B}-Pi8FV-oAfX8g6(#fbIpJI2()o+H^E8ybjOIFY^fI423Z4!~*1HEl5RLRH`3p(_gmdfrX?H1)?BK^Ue)O|Wsp9~RC`_+dDQh2q5h_Bgla{#?G6%GaV0@D{})kY-nH@)%W4a#U%Y&@ zUJ{k~ee@T_4aiCxj;9Tjj!facc)mq_pD9x(Vjsyu<5zV(aT_crJ!?_*?^A-g2vRuf z!3GI(Sn;|3-*Tk*>2iyNQ3byOy(_;RYBa?kG9-O4MXKm|r{=L5sxT2g4IVOa!b^Eg zQer^8f7bqrR-Q%{)B7n0O&f~{>rWTa?FsS}<7wPPZUEFwrp!q!U6d~sq)PTbe*~SLHf9irUVs5$ztAliVw;!|L+X*VPqXD z^~;tIc6P8$fM_XuV%M8LYkN9X=MqHJY;0p}q8M$LvOk?WPN4=Mupfw8vzZr+|5s)% zZG3f^!9ltQw9t!34vvI3W{a*-XIt|E{D&^&a~PS4!#li%(o5P@wFXAFBED8^bz0Tby005 z3WJBlUj6m92H%RprT(n`G@tO%-(8ygj=7l;A_b5-whzUN=ol@Hd#Qf;=p~-dnUoJF zng<-g@i7a7aL%P`wHf#C?PR+Cn4UsK-KNJqeoPi4D1ycHO7=l}eiBM`@&b12T4{FPyh7QWPTeiO`#`W0;s zpf-n2!xi2!3A%9laUUHpTBdi!IcM^RuIa3jD9OKT`m>dr`9q)BxQUqvPD!!QYpzS* z{k+m_PW~eLCg50^3Hh3dj7y!5f98ZA-@E{gEFOO3Am~Vm@!0bQvk>D`iX)7j@tJ<6 zorUqKa}s}iTUt2uz1?p%Xy=@r<>Xo5`8Rt{jN5sj@df;SU-zNhq*Qq_`(DHk8sk#1 z;HSz&v0C?jnU6SA204TQpFAA+WB9Q&${8w#pffq@=K0Z zB@$Yh375Io8iudd%lr9RwfI(r`-RKW(#YPj zuEOE-&F?K`r)0mJ!Fqa~6OYN5SzLN>xvZh0orpCM{+NgIrJj{(4;u2h06r!dAf*`w z(472;c=4aUvq&ofG}Hd`t{in^suzFXrj_hR}|ZNLb*+yn@w1G zb(AQ`@0y4S*_K98iT7Fri>!}|gFhr?iz6-MrN>Iz-CVd?(0rS_x4rKE^XurJZz6e8 zC&Kh2f?gKivJ(D#GAiZ!+k`Lycv8c5I6NGOflF9&m4q61Rr`q~>ua?LpCcqczr{a$ z_gGw3j{v2nwG-<~Q&?U~b8GRXu&l#sa|y|2RtWUbf8f^5s}hTdF>woUBl0H7V!|?A zQQ7G2Ooz#JHqs* zL%5L2bq1GN+s6xE4N<2&1|NMeY4&#Z1Pt|I9?P`N$2V@wUI;d@wTe&dn$Tm1|C*olNhcki zX&;{pJ3RxEPtuh-VDHQHJY=B(Hz78=)a)aMU*UP?3*D)pB`-({Wvnl=p}X? z*!}4qV!iCPP!$71kl*&WEw;0X=DXN63X&@EaN}^%MOe-{( z^-k0|()-wgfBs4L=t&R&&iL(diWtB|wMcPjfk7paCYA>Er-Rhs5Rxg=XFMKXH>0LqC3Ex07x5t{|1U z`a5QNmvxbM`beh6T|WyXmmnkGZXkK1xdwJ<;_ z#>nyd4O>gx+t*B7R?39Jv+hk8#4vo&>s;{19lsUj%wQ2zK=)HKc|!%L2r6;l+4RbT zkm9$DGq<{-2h$DiHx~wUbik+3wc*s(SJhDb!=~{%ml?X^_-J3-(K!p!QtxbckA7B- z(A&eexk48i4vXo%!skq%8GF9lXgaz1Lm3jRa8SPGP=;i-?K*?%+1rr7e1jL-oxo2i z>72jkB!nt6#GCWCO@iT)^sBdnUUYk*NUnGMRo$fVNCmIr15pjg)iJdSp~S3rvNOI` zo(%h5RvKpEy!Ybo?7zocwxk_Lt+PxA9T%U-olbY__2&j*ZfNq`-az8cC$;~hpGe-6 zold7*BZE* zN2b|HFGXxuEx=(O4_Q$@AvXAjY3Va<_RY8kWkd3ImbOm_dT6?>sI6exRrCSgV0<{S zXHXyFl%Pq-N;)=20ExpL^Hd$%K+z@0uQ^ zx;gkXWls_Y;vLHy`@B&EwT#&g_s20;h^*Bw7cPFMF*HcQUsRcxpO}lgoLNZyQutL@ zXqil|-+6^bjntVzgFY))ksI9iDxSJeuP_rHUKl_I$R&>5EKA1Ki*)}XOC-gS>maCg zA5|l{t^Y_YIc>?ynt6afZ1?LpC;Nk9Uf#CytSK)bs}BuN-``vASp0)(7Gv}OKN7P2 zs2~+17=>s7>4~LkthO{n?Yspn=%cs6og+%ck)Uv9a^Ygm*oGfz8drU{d@XvMhB{iWEQWWBbz-RoN@N8j(xv+c};R4MKd7b zH~Wf&2Hs4f%bxN!5*lz`kr?~lEy<0k8hAvGS$AaD^|Y9abhB8PQ6#^0zR6KHza=0( zUgo0Y;du8j+1cF|(44btJ!S%Rk|b|w9cVh^3)r+w2Up)rZ(%O@e!%wXT&U^8?mO?`N#Gncs!F6OC`dkJ@#g%i&JHojPFnC>DH}euyJWxIQ81|C&1) zGmtN5wZLv;DX)fh;#;~j3XQo2too0#jgG*rLM^at;n?lpHPk3lqlYlUM%h8lSa$h8CB&DV=~HsO1C8Y@v?i zWn2X=fWsuyZI}sf6v=TMTAWz!HlB=ffgZEM)j?+(3vI^O8$l9;-NbUv+M7LU-rkD| zYJ8ekq4yJ8{Ep+9P+I@x0i?Z(H&i(FGsbToQ2dFo)W5flfNsYxu!hD^fiAvnNs-i- zn>TOnSnqMLi`PO>d_F%Q&RVD$dXae44z@UImV@^q7{)2-e)Zd=J^XfA`r=ONQZz8% zx(pMJguCYJXFh~QE?Df9zaf&x45~c!iX@6$a$<7Ia|UwAIp=f5a>2PSb4mrWfRj0u z>t-s}A_Y~w>t9irr~@BrB6NQxWQ$nE$PJG^3gH(P>B^bo!h}lFKSCzLGl`ai_4fqD zqjyZN>ZFlBe?%7&(dvb47rBc>z0!odDm(n*Ho7Y>b zf#9rVY>e3d839V-J6P7BBxdhFIj^$&xBNn!>VhqZo_5`TIfnwIdgkX9SMSU3|Lj|ch)LQxVPSXRHfgstd{i23P_XxWh%@xl_LRZ_7^FAxny~dr61*# zEyuAZaF19X6%kwT!%pA`r%nKB#H~{ku3xnSw+K$#w>$b?J=oAHNDn5FD2adWAZ8D2 zoIHbprOVpKieYqIB%eLc_;U5QP`-MyJ!rm)LCpBp#a4DyAS0q4%jiON+!F@3_{Kwht%j3_$P9C#7s8}u>g*@y;U_A z87uDm1Hk$If57>Fz20*)1 z3p@C5=mM}KDU^YG%Lk?Np6LU5)D;P|Q>iwv z(@y1B5{fK@x96^{e!ed2g6zvw9LtI$jg}$CJzNnb<=OlMsDd$AF{FHdX_ecZLLBSf zt*MCXF`m7#Gu7NVP=Z~qHhr7IVFp+UlN8(W(V<};<3k1R0Mgz*Zb>K7V!I+2Hma#) zflXFLtSf7$a({I*nlg{$P2k)24!wSVJ9vCOU%L9J!l-vAwoU)M!XYO&H6v1jIx6pN zBC3iVlCUDJaOkU(G*cu@3%@Mc%j;X$?wkmQZ(f!SPK|={4#0%4UA>j+Nx1u0YoPk- z%?E}Sqm-%+3A*G>cT5ZQ<&PgPO3;cjGYZA`tj3g`O7HDN;AmQ2FT|`OnqHB8QH>NG zZhiN9E2o)l4$`O?L59gw_!n!KQCX?%ow10K5@YP2*JQfOJP^G!S!I!k%&pY@pS3ze zTg^h&ZTeYBEs%-ZFwpk%@%iOw$`(xN&a6E758Af)Xtz&{$<0}1hksT*7Nls4u40vC zxuz*8Mewds6D}hRhP8^Dmjqj;M=Sk7Dod4qYLKBmOF~t1bA6E$4qBu-Do@dLiC1RsG;YM4GL{bH|Y>s=S~C5(28csA%}D-V9Ra)60xD-T?`-b%j(=#$KWD01RV zk!9nnd36(vb)@-GSDpK+Uo7?!N=1;KO-Tw2Sxuz0Bm$I!f83s>jR?vgOmHny#w)rX zwFQ{B`uv0W7F!`}8p#JLZiK?+r$&=_un2&N}+F(IA@w(?l! zvfAC%be;MKim(^KrHZns53*>epBGELtPj*as4%Hl3?$g&{{6sN?Ukp!Vjg|CqYu_% zct35B>)T&tj`q!o{!KyL+PI(stZlwj^TBtIibf03igt|rWv_k?)ZX47;iqK61OpF#L{nPWdE*=2P}h3Rvg7cl%cZKD zo~a-D7Od`e6jUEP`e5C~HznB2BW1Wv+aTH;mDiBECy(m%FTGCa%RjLYR_gl)RF0kS zaz(+Xk22rWCkJ4~h!}BLL&xAxDz+ZkvoPhQ zM}WrdVD~g;wH-4TxNJ+~jvll(CC2yj1UH(i9EA!{#{VQI4U*d)sx-`AANM7`gM38 zzKidwG10EfmS@{nYm8ch`wl2ud3uy5|CfN0Fn9dz*oJY6&i zpXZ8L+s+a?su>im#p5y!hlE+3Yw%6T>EiC90ZG`HbVt92=GzLU|F zg7;ddKgq8d7S57YX%(l`^Q)+s*)qMJd3`Ll2)7m3$sw*f63b4rq&VULjL~TIUTC2# zcC05V$Gi{I1eEEaCk9 z_NLiPiNM*D)oUsZ*vghA?!!CTlk}^^J)-Lx+B36d@52-QPJb?U4uP?We6Hme4NePl z6_=#@nBV4jKB{=-mz-#aHMUm9`-Yf^xWBr4*Etq6&9~jqTw4mMMH%|4=USDX)cx*H0w9`RY~<(x$d@Ext3rB#SS5@X3KhAQZ;ehMr=I}s8HFFs3K zs*`XZtbvadkGN~@+1|JEDvq*v*TQ+H&9JZcp`7UY9N#@N5``liXEv&PNu?nWZIS76 z@*YnGoD(6U`SyJr57%_e>RxH-ViNmoITD>z))g%x%E#65!#(Eew$Cfm(kWfc8$Zli zOx=ynUfr8M*rXAy?`V$L8nJBP-2(m}6$M@qlmB&o{i}Kg$n)!t6OBzk)U%*5kfz_) z1;@pJu0F&x8&t6Qdi68!`qGrlkhvEEia_5zN~&6(qEWIil))DV6!jKa!48Riu)W4W zhXoc&{Y^x7vv=dOnYsh(4nvB|0j9&+uYo%_mbRa*2o=j7pI4HnPiYQKPdktCl(0Xv zB0lj`mHXr4`Y#W76N$=le}AC3Yz0w-wR$fH@+V`Xbj#y-LTdHA@2~E*srdA~TNjTn zYgN`Tx(5>zL5SAR?f%G%?fy0mPHG5R((@aP8N1qqwDj9KKXxTyu$Qg@PJxiOPt#_z zGwsHTNHHTUAFDoL>_x0CSe-JSJ5{Lx*j@jJz3X~VZIJsup({%ireNK=`m;lTLJLjl zA%y5!(nF293Flu3nqB>FkhA~qz!-L}Kx;8(lF-waCb<|NA=?`VHINgp!Oga`Tdv&caZgPT7|JF05h5tg={DXaW*WrkxwUj}gM%_cgYc&dp zE}k+deEMFzbE{88pj|_Tv*TiOlw7cH!!`KEoSKB=lBoN9v}|u@gv{;srjbRPEYiYs zw(L;a=cg!qi=w$G-!##3952#V`G4ZP?shy*+5z9+^ZX_#Q|`W}FV`F#Tpx%(?E(K1 z?9=`8J1cy5hpYG9`s`h^z9IdzV6k)d}`57%Wo&TNG6S@LY2t=;>{;NIiGCOJg zL&c}No-P?|O{*5S}uY}x?aPR%>lx$BJ^0x6^h z!RjX5mbwi4O!NF!mYY~@yWh0eSWjoPFj9yckLhJ5Nb#rXF9I*d(f0dOcCK#h|9gI{ z7v-IK`}E;1BFNXSTbW5Ocb_MocY8#-KlnvH2E1W>dMhEtNmY5c26~7YiGj`pZ*Eu+ z@`tzH7rV7439ir1k;-n7x-nTr%xtWGJhXZ!cRJIp9I?@%#g$Y}^gbo&k2x!DVU4y< zSB}FiJgk}e=d_D$C(jL8cofF3&(i!qB=+xcj|Z;D$X>x?EL1R)o|EnJTT3MPL@klBoOcbrEGC3itVbKo zGr%T6KMO!ZT;`707*W0i~t_71+NU^wq4Wg3WCuIo8*zsxNDoy2Vo z4`$1ngCh>CxU?H?E%zAj&0wR#(qCC&^J^#kO*J+PC>(cMfVm(ZrjNWA20jZSan1R< zwc3ckxg`gGz9Q-2?{#~l%A#xCirrSispiFg72iYs*QmdY!7znptXmbA!**EXvz%@YgrYzU-NM#adnxapZ(#Eb;k=xY=(=Ssn zA#}6FAT^t-HmR?0i&lzI5mj%MxwC#J9$>3`iASzdE5%gx%OQPoqh*PGqY zAY`pmN`r6br}`|Tmn#>Y|LIpH>5v4Oq|S$VRI=DkB*;%?-^8KQhNML~2Jr3^6E6_{ z6YWjyUPX|U7FIKCDy_&Zi(ZBf#~tBPK=P{%5(|#T-4Q-5ovnM_XHfBgK)xFo$ju(m zGNRoEl9EM?yW3AT^vnrp^;JW9N1J1keO$cqFd-35>SBcaJ}@mW(09Re+_IskdhD>R zmbAB+`k;l5OL>si-ThP|Xwz=3c=$s@2I$tkL)f`zW`hY<@f;s5O%C_DExm;KU@NB5 zm(3=tpP1fp>5T61L-}c=@RDFPv9MGkQcI zs;&%mm+;PPK$LV8z3o7;UqX4YGhJnAm;WzJzq#*EeC^|Lcx_G6)_lYyYpnBXls4qN zwRpJ4c2%`uPB7j;ta(!pkVd(7hKg$a!3sBJW4a=VjU1R6`0p9pTNy@jK8@qd1Or;! z#esm)(&T`3le=s#zPc8Hc=>VvhS_e#2Ba|GRF#=ZWvIj;}AiirhU8zQ<1n$C06Nl_dcyJ|GfJV498*L zCOV6Ar&aY8E^IUI8!~0QSiqpboAw&ln9ui_TmO~oy>Ri#w zR7!K3ZzmpB25(#6pKwpMEa0y%zv4$dIbZg=(JOx*=VhyQ``+NS0Y~D8yvZmjwxpD4)nD$_&pM{=h>x(SvcXTEkP zLIT)qHy$t6IlP@-30l$17S(VuJW^FXKP@C_+ZR{oxSlA!=7({rqJO2tLo1SZVlkRC zuHhc(7YEE1l(VyM@?Dq5AwLeLCa$0Jxa3mb-n2$W0WwCbsVKSs6W@1|g6CO}i(Qq2 zsnp}HbEE0!sMX+<~KxhYH3PTSGv zGwF+2Mltn;iz)ixLL|n|gLJ91Vd>z~dHOEy78O2v5j=)&3YrTkS8pNpoR?j_UIgU1 z&^qU9Ob>zA!v55gqx1UQ*!sBgS(Iy_vyHQ->Sf0%d1{{|d~0Py!gfH3*4_Y4f`n{b zqP_Dji^Z6z{2fL;P#lQc2|hIPJKC0nFJVR~y&@qpm|Ls}l^bT!N+yRJl9JJGGxpXV z{{&hu@K7Q%3B98*w2Aj_uWJFlUU7lkyk1ZGc6_u4CO_5ktF>Rex*}t$e(f10E-Bug z=U&WCis8%dHN9BP2-z9fXs;G+U*IzfmKwpnAza}Y=_DV|IA^)6D>Qk9D{kDjYEc;Q zULbpuG+LscL(sDp0m4(V6fCs^?rCZPTzItninb8EL5e6;n9-?-z9!o{)ihmA_vz~9 zY}QGLT~km~*$mHE4CQx?XCr@tXc?$U(R1op3oHAUy>^f<68M?$=lqRvDls>C?ado zs~_6sR9}IU!oShAF=%6|Abg?yAHaD?ucqRDo&xjJ^P0jk$T3Z!k8 z*TB2EP`%%vhc`g-n+7I8YpwpCs6IBPj4^j}mF*bE`Sv0^k$mqUl5*x=++eh3?(P04|8 zwZIurSN>q+eD+*;gH08!X~^D_rdA0}NOv>YRKPY%@;N$q-;qWEq|{x3{2TZqg{|mcJXWjJDq?#>CQV_5ZIO)88G~XpVs*_rG6; zMZr*vOtp`|LShfGSHCcT&OM#~QSmmsF{;AN?SJiB!pBPk3D)I&AoDXFU0O36{}(Bt z26EVln%`ev;E@!`@gb5kk8}^$fZ}5Q%L4n^HeiyPv?CHNa^212Brv|ju6{Y+(SPoN z43N3pw|uPd4R{H_tDqXV0Hd7$BySq&R-X8(Lu;O`Q8S#%D-WL>YWzNy%G2WdXgh5+ zjCzUYf|qn(5IsKs?N981BfcH71JBIlYT=@aj-Rdv;H#=fj5KaBGW@@Z$!_HeYV{(kLb4N_1JV*M zzS8*1CLlnH_x~i@^1pE#6nY8U)hq!tUkv z``pb+GHZS&D&DD=)ge{cs3jdnl)BpSJI{$tn8(tM0p_n(DTF z^|Mk`6afJR8$`f>^ne1=R9ZklIz&21i&W{KN>S-0AP}nb5|k>T_)|_*#`J2C4TMSTeU8VWV@m>W{v1&hL z&{C$9fjLO<9317V3mZV&nB-uQ9K`t3SFqU*L6Vp#dDyJ#5mIlz#z4LEq^JOTCN?nPNVLZ@|lnRZM(zvYIWaPRO*J8V(I{mU1lufG2SLd}H~P-AeqA_wRO* zIBmo8%LQ-I&gF5G_9|*y_8D293id=6!W2U$rgrf%1JK)dQ!j3Hbumc=r0sQev6N{( zPWH94L!q&z(#{(PU2mU&qB*uqRIfOXsy|2eqv-MW#^NIf#c?d}S>g4eqalxoHh!BwR~k+MNgF*8`Dhp1qt{k4bQZNB575f@UfpixGr6l=o6%P;4bq>-s_mXe(J;|g;9`cMO zcTgm*zwR=>NsR|xYhi`oowP>jjq%3Si5AS6c5ZqL-E)l|`sd))d43}(Ba4FvGWsft zgUjjUVJYVTi_4jA{;G!|N>|?s%h%Qe;(_719_qw$7ewegnY3kWj!sXT8;CAzi^#%h1zZAJm9n3Gh3{YPshiUdn z9(IY1rK!qUrntWY@cNV-{dF<(OqF%HhB1>iqRcD;mM8hZcuirB%R^7BFEr)Z+f zUc~qTL8FHL9<=*f#{|Kqgib}CSUCVzcC=$;^WIm2?BmhH`D>+US&GQUk% z!+M{})!<5p0i%N>Z_S^hx&S!QfXjd7zCc!rgcE;jKDLRL>y@KBqWH3;h!sHTX{z=2 zG|CaeOL_h<*qYbycd2f0(HW~GkCqoK4D_yBnz^>{xB?hxt97C&FTm*qn|M#6M7)=* zYAA(8#a}d%L_TFr@(q~zI*r)*{|`^4;h;W4tG`+pOV`%cT7P(U#C1B=t2LHK&1$gB zy3BP_)v)pr>MP((a$TrjZ%r0=C_ZIBJH4|$=CY$;EYI!tQ-1hN;YW%AU6AMjnva^# z4N>YcvDj)j^A8P>dQUwvWAWpf|2`1qzY6kG`O!Xr`387E05)g>@U|lU)Ripc=;P)K z`^YY`=@CA64b9Lc5s<;Zf8=^Y0}-U7Nxu&ZINyNWU5pBJOh!`D4?%bQsbC@eDa$Yo z;Cg;f65US-CAK(H1<}+$^!9(gln*>e>?}sM3hCMQI&%=GvhW;vbOktUQQU%!5z5%~ z&CFohE+bj3$Os0FyQ5;>n>`~IB%0xZuQ6+?P0$$bks1wO%o+z|0#bMGH0R+9+RlDl z{hKif-Om$D8n-GrO-MO)Z>su7pdvqdpcOb*QkxV0VZ@Ngq)r*yTSm*LezV+4kNeu_ zy)>`A(aW`j(qx+`8ox!lHOr~}g>o-xR3EI@D~h3!G4r2kO>5$lV$?Ndohp>euOH{P7%hoJ9>t=CK2lj{6jbo$L)Go>!g*G&IH zP+X@RzRBj9fEZhH7_5I=i_X$4NMB>&^Ybvd59_4fk#F$!yu!ti4AjpXD{U(dJbM=# ziLti3L^pv^)NXO5ws+MnakG?uT8zvg3!is0rATr=G`_)Hh0@W(T^6c$PxV6>!xCj| z_g?SDX_{Ty2Yk4NBS&4&?qfk%J)Ut!jeYX@nAk)}7Tt@`t z`dPMJv7!_{SnGSmk9HWqpgd;#R~2B(3*$<#LjVe*!jQ{%^Wy22R=i z$6Bh)8S+lU{k3-r@-X1+`|H;eWi}n^`%?>ux0clt#cZ2)4Z-iH0o z`98Z80j-X|k~_-60!MWy!Gz*v7!)TqYWUI25xRib4Wun71MfXW2rzDSe9<8%IeVab zDkjTUy0k=ETI@3&lGo#@Mp90-6*=y;hpYmJ@dcWN5TF`9F({L^)99z`LVu{x)E^BZ z`*RVqJPGjesJ6gu6+S;IQw^`b!cKT8CvKz3FbC4xxsz~K)-C+&Ba?i6e2WeRYDaQ> zh(==duaFfLOL4nCgGDiBPe{Svi(&xu+Uit(+OKq)Ym~$DrHnl5E`rg@VXQ4ZAYh|@ z1w96v#bnXWaG&{Y)%`f6_-YpJWlG6Z=l#h=h-3qz8et>on}HeVA;8}IOWZ!b2_z082Hf%Wz0_q`LV%3+w8beGKPStq{G zG2LDK`l#FuU3RdRQiF8>Y#3%aLeZ4lshHCn(1XheyWd=y=pHj#5jqZl)?X#kXI;dH z;xRaqT{$+*uX-$?cBcdYaUNMVg|oZ%s6dp3<`0x8DbE8vQXZ%=6T;~no#KpDac`bd(Gtc}KHdiaDUuP~r?2?a-*f^+DmnDbYdulX@M9{%`~lJNA~ z0}1MoBq~FUXXdP;!cZ95N+-~;EU>lbb8cF*cS#C`(R`K{FZH+J@&rBj`p;-z3)37O z?#n&C(v_7upL8^tWbFMmd!722)dPzT9{6*p+qp%Q06Ua1i4LHWmA}3RrjO{_Z)n>x zXqyh5M!Q9B@2=w#0YF`!Ah+jN^X(bK1fD@CK1>jMj~9-<#)j~FXYyCW2NEZ0Odfw? zah*izw@$RgO%@hm%0%+ilSFuiBE~p-AJ8n{1Pp*Br4(tDfx_h81b97QM}(wTl%(>f z4Sg?ID!^Ii`%zr@;kPRXO?b|Nkyfm|dT{7qy)vQF;89dgl~Ks4g9XDg-bDj`^}9t3 zc9!uzA1tF}Y-Hp)3wt>$x>YYIaK7z@ob`tp?%+>jb!2*(gUVZ8(%JHmdoio8-Kiz1 znNKJ$rzVJW*T4*QXPuXxSIah=T}>_>2=F-IaS##>Ki9|Jd!hGIFHbLDFSJ*@lMU#2~zrl?*^qjE;1MZ<5?4wrWB7r4KFJnu7a zB;eoeUV_4)v@d)})bM!m+7WOia`ikK1o>^y7dPL+rAJ)Yg-PzFv*5ot@W<34k6F`6 z#A}Is7`!`2sHf#074#l#pJaYA`v%$+bB??$7(X%!iHm~r%Lq%_%G zV%aey45*q<_duxB0i&mTLxOvxOa5~)zN;~NOfgj6UP_?{#tnlv408`L{gL@3WQklI06gpQF5k%tPNM1d!Q__? zpTboD|7e3_p^$!l(%AdJO#j^YV@ddYWNQ?oJkok}rYv@p91GoW!GISpwf@UJ6 zfgr+c)BKvQ?hjuBhYCV`9I;W}9*bb)oB1SS{#nRM&%Ht7VZV^oJ@Xga7J61jitleX z5c8KrJxXw24RPl#2QQc~rS{WpxU!#N?s!y^9Qpcoh&soE=uAuO8{%1@H!lS1Ff+qH zB@(o5DQ+?z0WBpz2Q7^`oU8pgCbwm9Mfu3)eDL7oW@_V3Yw(ikl}F8lNDYUhmBpo! z9))h~GJ=YcsKIykJMf+11qPdINtt$Q4SgA1l(AO$U=UJijXp5u+%e5T8aU?!6D$%}FQ=+x30|tiypwi{4qLm)7FY|^f?;WM`fwBVksF+N} z=6hcI{umve4B<`j{x|_VRF0^IZ6Gig4SutqpMm1mDETkQenCG9Ds}?JH zJN?mWNC}lGW1UrPvMZ*4M+6!ZUE{jlb^D&hvTaD*YA-@A-I3ibiu=*ja@&hO@pI`P z(i4^q0XY_n^7QI#VPmCp7*4i4{APnEyUU(U>(4~BTom`Q^rg+6xg@*8u)*{}>04q# zGP29MS0Sy}mc7Gw9ACp!fvY><)zW^WrPwlF{gt{R?2xnutns4~=(SPUE|26+Epgt$ z>?8f{fHrMM>c{&>*v0S5LGaO^6=5dk$x!0G7oGVpSt!~i#iPBQifuO(#7be95NuPc z2wpe2Q)78rK*(!mk>^8e)-rXN1tWA0gPrGZtQjUxop_0wq&Ud3t=q!Vz5J#kUKWdv z?u_^@?YofDrZ5MUA8n_B?CxTavSOVk(Wx2AZ?&}yXI?>dlSuT$%hpCqO#_B{POHmZ zA|a7{%!F4SC)!%u^R{|vts?g7golzjhWDgPaid5>m-o3I`s{=e!YzDRtW!6u>bj^X z;{Jy2fsQq}NP1=MaIt7V^$W}nY)xsTVb^_Rx)~gwbXU$iXW^IOKj@5(c`EE_AoOLl zBK+!%7=m}31b8t@i&d<${rpzTD5dF|RpHJ8Px~kw@i|*vBLa4_--NigOcVy##@Kf2 zNM)l)jaiIDak}Mj(p`fD`2iXC_;CKBYJKOyMe>~Vvh1-h(vMH=GsR@{bkC|_koo6L7ms68iTa`wUIrbq)|4$g*)Kalgm5c_RcVdW~GXCOn$1hh<&ek)pg~j{Cjq{We z=8$)=+a5mY^{eDYk$0@`i(lvsB^dRaHXItcy~5MYg|-?*{-~s9`5b;xFVk`ZFt+-N6od}aB=7W)UT-IvGc!)8rvkF**amtRC5q!AEAH{2-&1ae}> z26DKW+o;Ac<0E+XKWPLAWQP?W;0?#$h8@~Z`1g-6(`17GdFHt70a~7Vc4_x49iC3> z4-(^T+R&_s(}w2yf%vXKw%@t&{8v5w4;~0AKmI$feeXYWAx7sO{d+$8@7#X{K&mJj zs}jcxtnq={NB*7?PrLm06!l-U3I83g#q6;&wE>E@TAR=!ngDQ$*LL-x6W!8SS1G#}69*ANX9qpP>g*+1c1-1N^0-v8&_OPt4!z zCR=~Y^+R}Qc#U(u)f{pjldz-Fd3~8#l@>(DS?MDE*~R(L%^i}uP3|e3-~=jv%S0kw z(|b^{+5PU6T0zZMvm5sD`cd~q_#`w-#LCmSj@#ZI3nC`I(k(m_cRA_rtUQ>J*}KOt zrLxqUO*7b*ES*a!?O7sK=-$4ou{33WP>)HbMXb>>OMC7`Uu}YauL0QfH__$2bh4in z(YYtFKRZV6mJaL)p+mHGjhXM5SS)L3tRcs84i6^Epm|XTH;8Ugh0LO_^y}|7KYMCCtF3%>L&||)>jCoRs_@~ zAk6Mmzynf?nw)&44b(mT>M$+4uH&`2v;V-3xVt+BPWSN`YOcXPwrb3d&yBsRHU@)N zaIW5L0E*T-8Mp8))3WKwa*g$hEqSZ9Zc;?gvpSref}M*azqxZlysan*ySd}7?PtG2 z0ptm%+LjV3iLTh?wpP_Cs1q^&p+R+b9}(pw8^z}_XxkrGdn(S!z3rnjKuH}+7C<-H zJy|X)X5qPW?9hEa!5)+#H+Z8Yz3ZB7f^pgr2v2M{^PSpo{eawXv^4pa2c0eXV>`6= zoJ$?1d01ZsKeg;SA?Z_(QG?BGpy$7}i+Hy+=UTUgYp#csyp4#x9};_(7qA5| zhFE*6bBsr5V*Axhrz_(h*uNjDsPi|te5LpIT6AeW8Jv~uezi=yU&iT3v%mnn^y+m~ ze;h`!?VamvlYWu$jq^o1BX}~@Y(FCeX}~#*xFmnZ zX06gC0$wg{ppa9A&^ZeabFRs)1t>`hu61c?hm0cZrv+*u!q;!cmpDpIHj@1l&1DJB)@eS%X*AG<=koF0^pINe- z{LMkPd+L{g`JY;eqd~|=JeL3QX54n+fAi)#+Huf<+k4Dz;&<=vCrurwklPRM{MWoF z`$tFzZiBrA&4^_b>>tY>=mLCw;3KFu@PFs_oGU>(0_;V%Xl?vI+Qq*U$NXyrr21E; z8}hl|4o51;vLzTSU;kH%iyb5@12VqWxv=?IQNcQRr1a(=WNC}K^X|Q5*LD}d_+`O* zLslpbf;}&m=1^MCYV_yunAh&y7%PO{SsJ{Q7eH4- zO)kkvtzF-ov}bE88Bv>f8WF{Q(BCW#=+Ux(#w)JG_9J=T6FJC(!O2|wYXBU|X(lHi?n2I;HPPO{Z0~Wj|jZ5Fv zgp9lWW@0p!C=R78%|a%gQBIq6y9t}!OBupHm5F}fp+V)Fy;soh#*YAKxg+q|lcs;% z(v;l^KOcbizGur!3d`NxKrkJ*T?PR^?x1r2d}S@z$y7yXv#6C8-86SCTbpK-+}$+a z-6P(sefpD=`eyGgla)l3YKb0Psp~KEcCQ}el8u~S<^8~>vAl@82%?D~ln6qCTUucm z$n(;nMv=CkGforo(s$#wzkQ}Fvl)55^eX1wWK)vY*~ulmKweu&C>M&QXkaijvKy zH@DQBTh(<)6XeyV#(&~iJE^_G7p(hHiI^166n|@N z#|vG)L+fESvElq@SGZMT?#Edy_dQDx2mWMpt~mD|LslhT>Ve=b(qSnQz943i*~+zA z&iPwc1KuzIe(C}d%30g|xyy*qS-8Bq;5|r$2?LBqf#Hu%$A5YJ zTC8>WF4r3R#MB(;j`uA}c7I@zMmBBg*aZO^uCFT}b(X-%qwWO8w}03>6**czwcGA~ zp`W$;i*&~NN+(+Y^f01*jhlKcNml{^WwaroFEaC@FS5a*+z1@fGbF#N8Y!`SO9@tF zfxG(*Vb<|pxPI5V%1rt$Ol>8H@E~Dsef)mJ+NR`W{^XMoMh?=~<-3r(&0F9UO*;Km zu53ZTTv}e^^QZjZOd>W8#@(6Ru{^n#DY>Xt+5)B(;CznDgG=;C8Dbk3 zPp}Z2lWvg|l8hu8RxhI8*_Y&iV6eT)tiPtp6}uhbLS-#}?ob>5^{U3j{5xCQDXzV) zw-k>?AOjX4xENaC_41x?JG-95I(5;Y#&XfL+nY-QUjWT`=W%<%evYl7Is1Oq*44c> z-i=Vqa#XS|I*mNP*c3VaV|=N{pWL;=I&{@d{IY z)A~koua{vmh>p3=+hSzpRYHnniHCa{GGsb$^A_*sdS*QK#?B@`dO9vv@6C_0k{oDo zd5Br;#=2^bW!A^Y;7d;RYE8AP)aJM1V5Os?6tfB4P7LeM1@$=uw+JPqCsqUF2qFFp zas15!MqW7y)MHy&`m3{?mFGL7<*%T(E>LfJ`w^L!07Z?GH-(%RHZnuBg}xxXDaLwT z&@7aEnh-z|nCG^8Kw{W^Hyt3ni8#>0c4t%0QBqT+ekG@iV_WFO?is?6EN8e0Z&tzh z1KE+z&voAU861o4p5xV#Ypn1+wC8kI;K{t@_krANY~?SC599u{ZuMIKn{|Q{)JXs> zKE`b@D_pRa8h0|qI@bHyA~b_lzQFH1UV7TmcwQ>0y#7H{sNk$WV%Q;6Kp-h$Zy`G* zP?20Q@xJ%TdC|reA4(}=?Du@RwcGnrWRJkIRB^iP+-(@`INggkDHuasoa#Y zZmlHnLY%RzhxfRVTp#3C+U0DX?;I<(_MRN{w%E&$D+{@U(@KuoJJKD9x7RvdQ4%$} zp+0NVx;Z(3C5gc(5C9h=$lE%M3Iw*YtpK0m1hnWx2I_ZF}4lDv2vy z+wiCWcZnQt<#E-?ai(4=m*Gd!5ShXYQJ>xG1LF5$Sx@S})_XUGof3ckX zbCftFv>mOwcl^2i8n>5paS0Pfpou#@;OPSjYqwC49^fni3q$}NViA_hS-QNj?j`N+ zCu2(CPuUSjj{I;H{mSfHoEw%VWSL-Mbk#$ftId9)U-GitaQggn1FB?y4|i6yB7IL_ADQ~y!j)YSuxxhIP?Z1MTncNp>g}?NCsv<+!B6S3HT(I zT;0+QL0pWIU3={~RKeHioj9udLpyz~dAKcCu42)Avs>8qvlD~9Qd>wv_ma?da(5sy zU?6laN@&yd?RaQp?EOy(FP0`-1BcZUOE%3g4da3GE44@tEJX8jl>)%Lv%v|s#H^+@ z(_IHbqeMpLojzwU5!44<>e*_hh*kIk1L14_cjFrvmKyX6=kzZKq7qr6do~Udb1)yP zpgwS3+(cs4m_TEU?=8H2U*uCxj93SgjG%Kv&l9in<*TK-dchtECz_k4{R!H%iKL4! z*q&e4Z{eFZ;oGN>w)sfxu78*We3Qd98x&)aany-ZvR1V{5O@QR5${TBz$TvC3Mi@) z7QzRWE&AzinI*w)(f9viOqmIgbe|#Byejw6N*|3RlaT9XRjMat*RI!mTPkqRkv!dC zM_V^fkrKe5gR`OGP@}`a<6Tnw>NFK8UEyoGK)yBiwa99IPR&suH?Dn)`-zrKyWvGh z`r{=3mMviQ@3#Hf^iM513tXA|YERxkWv$7i4q^inXnzm!Bj9_Gnl<%D@X`t~@RcA# zM6giu6p%ka909lyK)bAEvdWWSD?-U%HqPzqJt+E;d(t|$U$+>roqv!VWslqLJx5lZ zsyt5LoEiA|r3OO*Fh3X3U9A7X1EN7WMnf0Ur*vpyp|V9h7Tgk*7|8<$@)=UWI0GUi z#$Anj5R$REG5sX`^?CX6mvg@9UvMmTx8C_LFdH8=3T=Yv-}4UwK0yj5$K~1IY(E6fMyLuXwA}WwP;7y&2AVk$IRrLf(Ad$$Y_l zc)oLfWIiUJkUyV)((t|nsCNQ-a+?!0j0)qeGAVI?hCKOeyOT`ov9j1ZYlllLgi9e+ zOoVj~S-XvV5_1TY@z-t4-RGwHuOxg~>iZc)t84YEoK`6helsd0P4!yo62(S0n9c!DIAKw<+ z$7Xchvd}IN=IUo6s|V}U5=VAYC_WUwcV0+olCCW%Ue;u`o3{QfdRA}m>aX7$i8VEH z@S%sWWGr!+>)fmQmHGjEfkw8G!H*A@ytr-Op_pl)paXU?9OD-L2?Sl(PJhRjos{{f zmpqg_KAF+wZ7nCv%fi>Qv1@(J*ginF_3OoFyU(#X#3QLCDevapJ@7y2JUp5U1N*gg zRA3|ipjsO?86)uyt4D~k<8dH83lr>QN}+LN9C*~sBT>TDOV+N|X?81Bq^~9Ro!GjL z9=ck{umxD7^xy*}fG~BEqO;5$=-c?@E&c|&i%PY^RXzau&K=VHkRFX6H7-a#~OGYiy>^hR7u0ZUIqoE%>1Nri%l z_XX!G=okB^Gqa1S?Ox*3E^ral+Xv;DWd)BJrd0CAhQLFGXk+cHNNYYLaFJGdbJ`6q zmg*?I*A}Odqrb?244!&vdoBH>zI`Z8p7ygY9|gJPk&eW6)vSUI%$K~4BYc_eO&^IQ z{vYEmG!C2t6=|>E1aq;y$^|r!IhW`d9(<)&+|)HW;*XD@ef{Sy&%c)ug;brvor!## zMf;+QeZTA|RK@XAq=EAO+0f3*n$$PYGezfU-@9|sA`-M(gS0;)GdkC{_v9Omgne-* QJ89pmD8rR73J;(C8z-#jX8-^I literal 28780 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 literal 87596 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 O&(%w27t1c(eeyq89jxg9 diff --git a/docs/reference/images/sql/client-apps/dbvis-1-driver-manager.png b/docs/reference/images/sql/client-apps/dbvis-1-driver-manager.png index b0ff89cc9d75ae34907eab420427dcf109b39716..cde4d9cc7cf26e48e3174918f80f8652828b740d 100644 GIT binary patch literal 61264 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 Qf7-?JGRo40lBTc!ABvVnR{#J2 literal 57074 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

wwHbO_Lz%yXpnC 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 K$E9-5-~KNxky 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 diff --git a/docs/reference/images/sql/client-apps/dbvis-4-conn-props.png b/docs/reference/images/sql/client-apps/dbvis-4-conn-props.png index 2027949c401a76ec0e5ec1153f2fbae1a780e63a..d854dc826b1e1a3a89bad9b1a3338bfe43aa2752 100644 GIT binary patch literal 55848 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 literal 57094 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

ILJ13iO$}FTy9sF&V?WLOinG(2`wh|aLFq~ z33+>;&V^(bq1=8?yyQOgSa)t{whKkik2Dq!pPfYAtNXyK5p7be-Uh`t~A zq)OueI(qcRUNa+}q>kQUGT-K`AvK?{A0eh1GI^-#YY_IQzW$#uGie0w0+Fsw8vCCLY_i=$p zHO{4VIUY9jzmh`q%Q}kU4^hXm5#tP5LUmn80i`NmyaFrei!7*I9w|R}N=k9(qk#w@ zQO|e}9tuWogx_840|YeM(d=w&Utx!)$GAY$3yirVY%cTQ>h!-G3h6m;EL zq>Bz;v*BM1*sW#v@aJvWUtA02Z5nRbC5<%tht4jpO`i+P34XInq2n5@1CMfv<;^rZ zBEny3Kxyxfh^D$`(lg>MJvp^FglT?5aj{HE>Awe9&X)y}SMWs; z4jpS-R;|iPQCc0ttV)4fdvio>s8Ijb<_R~(*Ed`&@9#G|uHs|#IdoKgoH$}TUZ?g0 z21xt0vWz^Q!Zw#b1YE7plyCwj^JdNAm!{d^~H< zZNF}ijM-!{=jo$!q<26_Rqww;EI**<*p=i^$*Q>q!|we`m2kCZrJ?RpS3GK_WCJo( zLIQ5DamMix>LX+^YceUGq^y~}mo6d8`XOik0IkqRS^xp_0LB_+?(fejQFVw}yTmQl zH~|}r3ESOjSqND)_ZehoSLNt2R^2l9Ne#8~$(LlT;a=TR#}k9Av3^{;Eb0bv<@>S2 zTmoEEXjTDUEZ1$Td-wR*fY`4|T;6QSrJtD7rkWaK#ZDIsnj9^MYLw*I)h%Qd!{@`L zh{fjS+&yQUG9FCGaOF$g-vlkTe}_|Que7})ehsX1xA{%|)NvH~Pb0;|dC9m6=gi40 z#Fa*;0E3yR8Jlv)K2zC>eWrFX?#>mh8ZZ4y%`5p*7)zo1vf>4mC{A&~*SCSK`l~1R zT#r>Xj_JKbydiJl(_rEXcW37oFO5u5tvNs~R2lUHiF9TFiVBAM&925KMJ7chMJL7R zUlF*{E;SC#2x*Qqyq{}|i}{L)>G!pmxJ#BddX6YB>emU`6 zo@68HWygR^>C5?&Jcv*%e`d6ka3QdUiO{0LV`76-K5Ro&`>D>VHO)$OqB*$`sl?R& z5Z@Z`z1)pqW+&iP^!sA3y-hVqgp`@~JZ~U2eoH(beoMoys9e=wM9a~q8TfwfwJY3} zU=;%n9i=Uk^Raoq8QwDpE=BbFG`S1sEpqx9T-7PbD@yLrcNZ=w$~XzwVkLcR2B*cd z`!_bPy~fDNu=U?6k5=0h(05VNxf%;@MkN>6zU~qN)=Kzkr}pDiho)o0wMNbJO1>O` z55Zr|HejfUzhfqnU-W8z4v6d@ufE(jTk?Q>cO%A8IJ@>5;QmhD*y;@wr6vV(aEs*O z!5UkL0YhyAxl_kf(+t0r8f3oYRrGkCug2mmw@6MPM^DqE+thES^X|MZPwePS&aV+I z&FAX+l@Ket)cq|fZKDoGg@^!Lm%}Ok!+(KyXzJ{`w{6i~E zT(nan7LZRSwC)&bGcMI1+kA1}vT~TOt*dC2YxCLjgRk#|d0z#20gsI9QJSib5cG=S zt8Hoe>4HM%EV4}WF~DD#(B`Ft@T$|pjkeynA{{efT|whuU_(r<V;_I~^G=&H}*y-?n%x8z?v(4>0l#bSm@+6vUmklP$|P3w*?+p?Y7X?9Rid F{|n#@SjYeX diff --git a/docs/reference/images/sql/client-apps/dbvis-5-data.png b/docs/reference/images/sql/client-apps/dbvis-5-data.png index fb5ce8b86aa74d48a6139836bb2c2a5e845c9cb6..c67336568edc017c85fc9fd3a6868987f8028070 100644 GIT binary patch literal 98369 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 JH|;->|6hma(L(?L literal 99284 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 M`vjr*UT1#ze;WesHvj+t diff --git a/docs/reference/images/sql/client-apps/squirell-3-add-driver.png b/docs/reference/images/sql/client-apps/squirell-3-add-driver.png index 9a9c2c2634e3cbe73d56dd2dfc1a7ff2de9b9828..29f06b7033d7245754716214f4b4c1410d346217 100644 GIT binary patch literal 16985 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 Kkix6?VE+%re>X?~ diff --git a/docs/reference/images/sql/client-apps/squirell-4-driver-list.png b/docs/reference/images/sql/client-apps/squirell-4-driver-list.png index 35f389747c9703a4ce4dd83eeed44a24abdaa1f4..a269e29d672ea5da39dc84f1815c7c9e8055d69c 100644 GIT binary patch literal 29042 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

Oi9(K>{LD6Z?OIlxJ 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 U$UB+2%i0fCmi87^r!HLkKi+F`kpKVy literal 29004 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 HFIWBx(4vv% diff --git a/docs/reference/images/sql/client-apps/squirell-5-add-alias.png b/docs/reference/images/sql/client-apps/squirell-5-add-alias.png index d5587060d2eaaf3db4931c0d94f209032efea3c9..1fc8e9ad60191cef6aac6eaf5f38e45d29a257bf 100644 GIT binary patch literal 21957 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 literal 22199 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 z

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 diff --git a/docs/reference/images/sql/client-apps/squirell-7-data.png b/docs/reference/images/sql/client-apps/squirell-7-data.png index 760ade7c670fbcf4862053295615a6c9a8d8b905..70837963b74b52a1b3619222a56aa8976178bc56 100644 GIT binary patch literal 59419 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 diff --git a/docs/reference/images/sql/client-apps/workbench-2-add-driver.png b/docs/reference/images/sql/client-apps/workbench-2-add-driver.png index 03e740f400ae10fa13ec39d458cc936a5ca3e08f..659cfd0c40760797954388619d05af64d34185eb 100644 GIT binary patch literal 26418 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

>RxDj(@c;cKrqjOjt&yUcf}0@LW;| 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 literal 26008 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 diff --git a/docs/reference/images/sql/client-apps/workbench-3-connection.png b/docs/reference/images/sql/client-apps/workbench-3-connection.png index 32643375e3de9f483e377feb6b54c63b1766646f..9262ef0f533a26d66d8b88dc11dfd9eac1e5600e 100644 GIT binary patch literal 38845 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 Y#n+uK3+|?hBY#&#Q9~i`w)xBd1<*wnmH+?% literal 35138 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

}gL!GJ#h7IreU$SGxR0S=`Br1KB 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∋Qet@idqx$8k) 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 Nx~le#yz3T${|ieXV_pCN diff --git a/docs/reference/images/sql/client-apps/workbench-4-data.png b/docs/reference/images/sql/client-apps/workbench-4-data.png index 602f09d06e46ff252cba0b24adad1f9fe7bb21de..7b8251fc9588a8b871a41507f20e6050480eecfe 100644 GIT binary patch literal 75052 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%0A(q(dM+P4U^U>-afpS&Q1 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 literal 75863 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 ==== New Connection diff --git a/docs/reference/sql/endpoints/client-apps/index.asciidoc b/docs/reference/sql/endpoints/client-apps/index.asciidoc index 87f3c2f609d3f..a84b8c2fb09e2 100644 --- a/docs/reference/sql/endpoints/client-apps/index.asciidoc +++ b/docs/reference/sql/endpoints/client-apps/index.asciidoc @@ -33,5 +33,5 @@ include::ps1.asciidoc[] include::microstrat.asciidoc[] include::qlik.asciidoc[] include::squirrel.asciidoc[] -include::tableau.asciidoc[] include::workbench.asciidoc[] +include::tableau.asciidoc[] From 78636e31cbd2f64fdc4b236338c6fc73b3959b93 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Wed, 27 Mar 2019 12:05:53 +0100 Subject: [PATCH 12/77] Remove TransportSingleItemBulkWriteAction as replication action (#40424) The implementation of TransportIndexAction and TransportDeleteAction as TransportReplicationAction existed for interoperability with older 5.x nodes, as these older nodes coordinated single index / deletes as replication requests. This BWC layer is no longer needed in 7.x, where these single actions are now mapped to bulk requests. Completely removing the deprecated transport actions is not possible yet if we want to keep BWC with a 6.x transport client. The best way here is to wait for the transport client to go away and then just remove the actions. --- .../TransportSingleItemBulkWriteAction.java | 64 ++----------------- .../action/delete/TransportDeleteAction.java | 21 +----- .../action/index/TransportIndexAction.java | 22 +------ .../TransportReplicationAction.java | 2 +- .../bulk/TransportBulkActionIngestTests.java | 12 +--- 5 files changed, 12 insertions(+), 109 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/bulk/TransportSingleItemBulkWriteAction.java b/server/src/main/java/org/elasticsearch/action/bulk/TransportSingleItemBulkWriteAction.java index 892daae4bb275..cc97b6237e30c 100644 --- a/server/src/main/java/org/elasticsearch/action/bulk/TransportSingleItemBulkWriteAction.java +++ b/server/src/main/java/org/elasticsearch/action/bulk/TransportSingleItemBulkWriteAction.java @@ -23,19 +23,12 @@ import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.DocWriteResponse; import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.HandledTransportAction; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.WriteResponse; import org.elasticsearch.action.support.replication.ReplicatedWriteRequest; import org.elasticsearch.action.support.replication.ReplicationResponse; -import org.elasticsearch.action.support.replication.TransportWriteAction; -import org.elasticsearch.cluster.action.shard.ShardStateAction; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.shard.IndexShard; -import org.elasticsearch.indices.IndicesService; import org.elasticsearch.tasks.Task; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import java.util.function.Supplier; @@ -45,68 +38,21 @@ public abstract class TransportSingleItemBulkWriteAction< Request extends ReplicatedWriteRequest, Response extends ReplicationResponse & WriteResponse - > extends TransportWriteAction { + > extends HandledTransportAction { private final TransportBulkAction bulkAction; - private final TransportShardBulkAction shardBulkAction; - - protected TransportSingleItemBulkWriteAction(Settings settings, String actionName, TransportService transportService, - ClusterService clusterService, IndicesService indicesService, ThreadPool threadPool, - ShardStateAction shardStateAction, ActionFilters actionFilters, - IndexNameExpressionResolver indexNameExpressionResolver, Supplier request, - Supplier replicaRequest, String executor, - TransportBulkAction bulkAction, TransportShardBulkAction shardBulkAction) { - super(settings, actionName, transportService, clusterService, indicesService, threadPool, shardStateAction, actionFilters, - indexNameExpressionResolver, request, replicaRequest, executor); + protected TransportSingleItemBulkWriteAction(String actionName, TransportService transportService, ActionFilters actionFilters, + Supplier request, TransportBulkAction bulkAction) { + super(actionName, transportService, actionFilters, request); this.bulkAction = bulkAction; - this.shardBulkAction = shardBulkAction; } - @Override protected void doExecute(Task task, final Request request, final ActionListener listener) { bulkAction.execute(task, toSingleItemBulkRequest(request), wrapBulkResponse(listener)); } - @Override - protected WritePrimaryResult shardOperationOnPrimary( - Request request, final IndexShard primary) throws Exception { - BulkItemRequest[] itemRequests = new BulkItemRequest[1]; - WriteRequest.RefreshPolicy refreshPolicy = request.getRefreshPolicy(); - request.setRefreshPolicy(WriteRequest.RefreshPolicy.NONE); - itemRequests[0] = new BulkItemRequest(0, ((DocWriteRequest) request)); - BulkShardRequest bulkShardRequest = new BulkShardRequest(request.shardId(), refreshPolicy, itemRequests); - WritePrimaryResult bulkResult = - shardBulkAction.shardOperationOnPrimary(bulkShardRequest, primary); - assert bulkResult.finalResponseIfSuccessful.getResponses().length == 1 : "expected only one bulk shard response"; - BulkItemResponse itemResponse = bulkResult.finalResponseIfSuccessful.getResponses()[0]; - final Response response; - final Exception failure; - if (itemResponse.isFailed()) { - failure = itemResponse.getFailure().getCause(); - response = null; - } else { - response = (Response) itemResponse.getResponse(); - failure = null; - } - return new WritePrimaryResult<>(request, response, bulkResult.location, failure, primary, logger); - } - - @Override - protected WriteReplicaResult shardOperationOnReplica( - Request replicaRequest, IndexShard replica) throws Exception { - BulkItemRequest[] itemRequests = new BulkItemRequest[1]; - WriteRequest.RefreshPolicy refreshPolicy = replicaRequest.getRefreshPolicy(); - itemRequests[0] = new BulkItemRequest(0, ((DocWriteRequest) replicaRequest)); - BulkShardRequest bulkShardRequest = new BulkShardRequest(replicaRequest.shardId(), refreshPolicy, itemRequests); - WriteReplicaResult result = shardBulkAction.shardOperationOnReplica(bulkShardRequest, replica); - // a replica operation can never throw a document-level failure, - // as the same document has been already indexed successfully in the primary - return new WriteReplicaResult<>(replicaRequest, result.location, null, replica, logger); - } - - public static ActionListener wrapBulkResponse(ActionListener listener) { return ActionListener.wrap(bulkItemResponses -> { diff --git a/server/src/main/java/org/elasticsearch/action/delete/TransportDeleteAction.java b/server/src/main/java/org/elasticsearch/action/delete/TransportDeleteAction.java index 32c599a9f5804..5b85f2f908516 100644 --- a/server/src/main/java/org/elasticsearch/action/delete/TransportDeleteAction.java +++ b/server/src/main/java/org/elasticsearch/action/delete/TransportDeleteAction.java @@ -20,16 +20,9 @@ package org.elasticsearch.action.delete; import org.elasticsearch.action.bulk.TransportBulkAction; -import org.elasticsearch.action.bulk.TransportShardBulkAction; import org.elasticsearch.action.bulk.TransportSingleItemBulkWriteAction; import org.elasticsearch.action.support.ActionFilters; -import org.elasticsearch.cluster.action.shard.ShardStateAction; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.indices.IndicesService; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; /** @@ -41,17 +34,7 @@ public class TransportDeleteAction extends TransportSingleItemBulkWriteAction { @Inject - public TransportDeleteAction(Settings settings, TransportService transportService, ClusterService clusterService, - IndicesService indicesService, ThreadPool threadPool, ShardStateAction shardStateAction, - ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, - TransportBulkAction bulkAction, TransportShardBulkAction shardBulkAction) { - super(settings, DeleteAction.NAME, transportService, clusterService, indicesService, threadPool, shardStateAction, - actionFilters, indexNameExpressionResolver, DeleteRequest::new, DeleteRequest::new, ThreadPool.Names.WRITE, - bulkAction, shardBulkAction); - } - - @Override - protected DeleteResponse newResponseInstance() { - return new DeleteResponse(); + public TransportDeleteAction(TransportService transportService, ActionFilters actionFilters, TransportBulkAction bulkAction) { + super(DeleteAction.NAME, transportService, actionFilters, DeleteRequest::new, bulkAction); } } diff --git a/server/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java b/server/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java index 8480c7be3bb6a..b8e3b9b89b3b4 100644 --- a/server/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/index/TransportIndexAction.java @@ -20,16 +20,9 @@ package org.elasticsearch.action.index; import org.elasticsearch.action.bulk.TransportBulkAction; -import org.elasticsearch.action.bulk.TransportShardBulkAction; import org.elasticsearch.action.bulk.TransportSingleItemBulkWriteAction; import org.elasticsearch.action.support.ActionFilters; -import org.elasticsearch.cluster.action.shard.ShardStateAction; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.indices.IndicesService; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; /** @@ -48,18 +41,7 @@ public class TransportIndexAction extends TransportSingleItemBulkWriteAction { @Inject - public TransportIndexAction(Settings settings, TransportService transportService, ClusterService clusterService, - IndicesService indicesService, - ThreadPool threadPool, ShardStateAction shardStateAction, - ActionFilters actionFilters, IndexNameExpressionResolver indexNameExpressionResolver, - TransportBulkAction bulkAction, TransportShardBulkAction shardBulkAction) { - super(settings, IndexAction.NAME, transportService, clusterService, indicesService, threadPool, shardStateAction, - actionFilters, indexNameExpressionResolver, IndexRequest::new, IndexRequest::new, ThreadPool.Names.WRITE, - bulkAction, shardBulkAction); - } - - @Override - protected IndexResponse newResponseInstance() { - return new IndexResponse(); + public TransportIndexAction(ActionFilters actionFilters, TransportService transportService, TransportBulkAction bulkAction) { + super(IndexAction.NAME, transportService, actionFilters, IndexRequest::new, bulkAction); } } diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java index 3178dc5d937bf..2ff1ff3e93a9f 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/TransportReplicationAction.java @@ -166,6 +166,7 @@ protected void registerRequestHandlers(String actionName, TransportService trans @Override protected void doExecute(Task task, Request request, ActionListener listener) { + assert request.shardId() != null : "request shardId must be set"; new ReroutePhase((ReplicationTask) task, request, listener).run(); } @@ -779,7 +780,6 @@ protected void doRun() { // resolve all derived request fields, so we can route and apply it resolveRequest(indexMetaData, request); - assert request.shardId() != null : "request shardId must be set in resolveRequest"; assert request.waitForActiveShards() != ActiveShardCount.DEFAULT : "request waitForActiveShards must be set in resolveRequest"; diff --git a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java index a13e8af919b2a..b3ecc59076759 100644 --- a/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java +++ b/server/src/test/java/org/elasticsearch/action/bulk/TransportBulkActionIngestTests.java @@ -50,7 +50,6 @@ import org.elasticsearch.ingest.IngestService; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; import org.junit.Before; @@ -156,15 +155,8 @@ void createIndex(String index, TimeValue timeout, ActionListener { TestSingleItemBulkWriteAction(TestTransportBulkAction bulkAction) { - super(SETTINGS, IndexAction.NAME, TransportBulkActionIngestTests.this.transportService, - TransportBulkActionIngestTests.this.clusterService, - null, null, null, new ActionFilters(Collections.emptySet()), null, - IndexRequest::new, IndexRequest::new, ThreadPool.Names.WRITE, bulkAction, null); - } - - @Override - protected IndexResponse newResponseInstance() { - return new IndexResponse(); + super(IndexAction.NAME, TransportBulkActionIngestTests.this.transportService, + new ActionFilters(Collections.emptySet()), IndexRequest::new, bulkAction); } } From 8affd39c39674778b3a092266646c58f0ce76b1f Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 27 Mar 2019 12:16:57 +0100 Subject: [PATCH 13/77] Deprecate types in `_graph/explore` calls. (#40466) Any call that uses a path that sets a type will trigger a deprecation warning. --- .../client/graph/GraphExploreRequest.java | 16 +++++++++ .../xpack/graph/GraphExploreRequest.java | 16 +++++++++ .../graph/rest/action/RestGraphAction.java | 7 +++- .../rest/action/RestGraphActionTests.java | 35 +++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/graph/GraphExploreRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/graph/GraphExploreRequest.java index 4d2a000a00c89..3040b8a121cf7 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/graph/GraphExploreRequest.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/graph/GraphExploreRequest.java @@ -108,10 +108,26 @@ public GraphExploreRequest indicesOptions(IndicesOptions indicesOptions) { return this; } + /** + * The document types to execute the explore against. Defaults to be executed against + * all types. + * + * @deprecated Types are in the process of being removed. Instead of using a type, prefer to + * filter on a field on the document. + */ + @Deprecated public String[] types() { return this.types; } + /** + * The document types to execute the explore request against. Defaults to be executed against + * all types. + * + * @deprecated Types are in the process of being removed. Instead of using a type, prefer to + * filter on a field on the document. + */ + @Deprecated public GraphExploreRequest types(String... types) { this.types = types; return this; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/graph/GraphExploreRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/graph/GraphExploreRequest.java index 196982c0a35fb..c1a682757d140 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/graph/GraphExploreRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/protocol/xpack/graph/GraphExploreRequest.java @@ -96,10 +96,26 @@ public GraphExploreRequest indicesOptions(IndicesOptions indicesOptions) { return this; } + /** + * The document types to execute the explore against. Defaults to be executed against + * all types. + * + * @deprecated Types are in the process of being removed. Instead of using a type, prefer to + * filter on a field on the document. + */ + @Deprecated public String[] types() { return this.types; } + /** + * The document types to execute the explore request against. Defaults to be executed against + * all types. + * + * @deprecated Types are in the process of being removed. Instead of using a type, prefer to + * filter on a field on the document. + */ + @Deprecated public GraphExploreRequest types(String... types) { this.types = types; return this; diff --git a/x-pack/plugin/graph/src/main/java/org/elasticsearch/xpack/graph/rest/action/RestGraphAction.java b/x-pack/plugin/graph/src/main/java/org/elasticsearch/xpack/graph/rest/action/RestGraphAction.java index 470260a7efac0..130d6deed567f 100644 --- a/x-pack/plugin/graph/src/main/java/org/elasticsearch/xpack/graph/rest/action/RestGraphAction.java +++ b/x-pack/plugin/graph/src/main/java/org/elasticsearch/xpack/graph/rest/action/RestGraphAction.java @@ -41,6 +41,8 @@ public class RestGraphAction extends XPackRestHandler { private static final DeprecationLogger deprecationLogger = new DeprecationLogger(LogManager.getLogger(RestGraphAction.class)); + public static final String TYPES_DEPRECATION_MESSAGE = "[types removal]" + + " Specifying types in graph requests is deprecated."; public static final ParseField TIMEOUT_FIELD = new ParseField("timeout"); public static final ParseField SIGNIFICANCE_FIELD = new ParseField("use_significance"); @@ -111,7 +113,10 @@ public RestChannelConsumer doPrepareRequest(final RestRequest request, final XPa parseHop(parser, currentHop, graphRequest); } - graphRequest.types(Strings.splitStringByCommaToArray(request.param("type"))); + if (request.hasParam("type")) { + deprecationLogger.deprecatedAndMaybeLog("graph_with_types", TYPES_DEPRECATION_MESSAGE); + graphRequest.types(Strings.splitStringByCommaToArray(request.param("type"))); + } return channel -> client.es().execute(INSTANCE, graphRequest, new RestToXContentListener<>(channel)); } diff --git a/x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java b/x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java new file mode 100644 index 0000000000000..486ac4e70e346 --- /dev/null +++ b/x-pack/plugin/graph/src/test/java/org/elasticsearch/xpack/graph/rest/action/RestGraphActionTests.java @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.graph.rest.action; + +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.elasticsearch.test.rest.RestActionTestCase; +import org.junit.Before; + +public class RestGraphActionTests extends RestActionTestCase { + + @Before + public void setUpAction() { + new RestGraphAction(Settings.EMPTY, controller()); + } + + public void testTypeInPath() { + RestRequest request = new FakeRestRequest.Builder(xContentRegistry()) + .withMethod(RestRequest.Method.GET) + .withPath("/some_index/some_type/_graph/explore") + .withContent(new BytesArray("{}"), XContentType.JSON) + .build(); + + dispatchRequest(request); + assertWarnings(RestGraphAction.TYPES_DEPRECATION_MESSAGE); + } + +} From db03109fab9a0684ce92a0be2275c46f4d42a87c Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 27 Mar 2019 12:18:28 +0100 Subject: [PATCH 14/77] Remove type from VersionConflictEngineException. (#37490) It initially mentioned the type in the exception because the type used to be required to uniquely identify a document. This is not necessary anymore given that indices have at most one type. --- .../test/java/org/elasticsearch/client/CrudIT.java | 10 +++++----- .../reindex/AsyncBulkByScrollActionTests.java | 2 +- .../index/reindex/ReindexFailureTests.java | 2 +- .../test/delete_by_query/10_basic.yml | 4 ++-- .../rest-api-spec/test/reindex/10_basic.yml | 2 +- .../test/update_by_query/10_basic.yml | 4 ++-- .../org/elasticsearch/index/engine/Engine.java | 4 ++-- .../elasticsearch/index/engine/InternalEngine.java | 12 ++++++------ .../engine/VersionConflictEngineException.java | 14 +++++++------- .../action/bulk/TransportShardBulkActionTests.java | 4 ++-- .../java/org/elasticsearch/get/GetActionIT.java | 8 ++++---- 11 files changed, 33 insertions(+), 33 deletions(-) diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java index e2102236cc422..301a32d97fe0c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java @@ -137,7 +137,7 @@ public void testDelete() throws IOException { ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(deleteRequest, highLevelClient()::delete, highLevelClient()::deleteAsync)); assertEquals(RestStatus.CONFLICT, exception.status()); - assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[_doc][" + docId + "]: " + + assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[" + docId + "]: " + "version conflict, required seqNo [2], primary term [2]. current document has seqNo [3] and primary term [1]]", exception.getMessage()); assertEquals("index", exception.getMetadata("es.index").get(0)); @@ -166,7 +166,7 @@ public void testDelete() throws IOException { execute(deleteRequest, highLevelClient()::delete, highLevelClient()::deleteAsync); }); assertEquals(RestStatus.CONFLICT, exception.status()); - assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[_doc][" + + assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[" + docId + "]: version conflict, current version [12] is higher or equal to the one provided [10]]", exception.getMessage()); assertEquals("index", exception.getMetadata("es.index").get(0)); } @@ -301,7 +301,7 @@ public void testGet() throws IOException { ElasticsearchException exception = expectThrows(ElasticsearchException.class, () -> execute(getRequest, highLevelClient()::get, highLevelClient()::getAsync)); assertEquals(RestStatus.CONFLICT, exception.status()); - assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, " + "reason=[_doc][id]: " + + assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, " + "reason=[id]: " + "version conflict, current version [1] is different than the one provided [2]]", exception.getMessage()); assertEquals("index", exception.getMetadata("es.index").get(0)); } @@ -527,7 +527,7 @@ public void testIndex() throws IOException { execute(wrongRequest, highLevelClient()::index, highLevelClient()::indexAsync); }); assertEquals(RestStatus.CONFLICT, exception.status()); - assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[_doc][id]: " + + assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[id]: " + "version conflict, required seqNo [1], primary term [5]. current document has seqNo [2] and primary term [1]]", exception.getMessage()); assertEquals("index", exception.getMetadata("es.index").get(0)); @@ -574,7 +574,7 @@ public void testIndex() throws IOException { }); assertEquals(RestStatus.CONFLICT, exception.status()); - assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[_doc][with_create_op_type]: " + + assertEquals("Elasticsearch exception [type=version_conflict_engine_exception, reason=[with_create_op_type]: " + "version conflict, document already exists (current version [1])]", exception.getMessage()); } } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java index 92b2180f4da6c..bdedc65b7a6d3 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java @@ -276,7 +276,7 @@ public void testBulkResponseSetsLotsOfStatus() { versionConflicts++; responses[i] = new BulkItemResponse(i, randomFrom(DocWriteRequest.OpType.values()), new Failure(shardId.getIndexName(), "type", "id" + i, - new VersionConflictEngineException(shardId, "type", "id", "test"))); + new VersionConflictEngineException(shardId, "id", "test"))); continue; } boolean createdResponse; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java index c077c992beb60..917d196b6e9fb 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFailureTests.java @@ -81,7 +81,7 @@ public void testAbortOnVersionConflict() throws Exception { BulkByScrollResponse response = copy.get(); assertThat(response, matcher().batches(1).versionConflicts(1).failures(1).created(99)); for (Failure failure: response.getBulkFailures()) { - assertThat(failure.getMessage(), containsString("VersionConflictEngineException[[_doc][")); + assertThat(failure.getMessage(), containsString("VersionConflictEngineException[[")); } } diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yml b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yml index dd29e7701ba1c..d11f160bcf571 100644 --- a/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yml +++ b/modules/reindex/src/test/resources/rest-api-spec/test/delete_by_query/10_basic.yml @@ -129,7 +129,7 @@ - match: {failures.0.status: 409} - match: {failures.0.cause.type: version_conflict_engine_exception} # Use a regex so we don't mind if the current version isn't always 1. Sometimes it comes out 2. - - match: {failures.0.cause.reason: "/\\[_doc\\]\\[1\\]:.version.conflict,.current.version.\\[\\d+\\].is.different.than.the.one.provided.\\[\\d+\\]/"} + - match: {failures.0.cause.reason: "/\\[1\\]:.version.conflict,.current.version.\\[\\d+\\].is.different.than.the.one.provided.\\[\\d+\\]/"} - match: {failures.0.cause.shard: /\d+/} - match: {failures.0.cause.index: test} - gte: { took: 0 } @@ -185,7 +185,7 @@ - match: {failures.0.id: "1"} - match: {failures.0.status: 409} - match: {failures.0.cause.type: version_conflict_engine_exception} - - match: {failures.0.cause.reason: "/\\[_doc\\]\\[1\\]:.version.conflict,.required.seqNo.\\[\\d+\\]/"} + - match: {failures.0.cause.reason: "/\\[1\\]:.version.conflict,.required.seqNo.\\[\\d+\\]/"} - match: {failures.0.cause.shard: /\d+/} - match: {failures.0.cause.index: test} - gte: { took: 0 } diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yml b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yml index 312a88ace5e92..9ef6c1a90c400 100644 --- a/modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yml +++ b/modules/reindex/src/test/resources/rest-api-spec/test/reindex/10_basic.yml @@ -160,7 +160,7 @@ - match: {failures.0.status: 409} - match: {failures.0.cause.type: version_conflict_engine_exception} # Use a regex so we don't mind if the version isn't always 1. Sometimes it comes out 2. - - match: {failures.0.cause.reason: "/\\[_doc\\]\\[1\\]:.version.conflict,.document.already.exists.\\(current.version.\\[\\d+\\]\\)/"} + - match: {failures.0.cause.reason: "/\\[1\\]:.version.conflict,.document.already.exists.\\(current.version.\\[\\d+\\]\\)/"} - match: {failures.0.cause.shard: /\d+/} - match: {failures.0.cause.index: dest} - gte: { took: 0 } diff --git a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yml b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yml index 15bc62214ebfb..08c8465c40960 100644 --- a/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yml +++ b/modules/reindex/src/test/resources/rest-api-spec/test/update_by_query/10_basic.yml @@ -109,7 +109,7 @@ - match: {failures.0.status: 409} - match: {failures.0.cause.type: version_conflict_engine_exception} # Use a regex so we don't mind if the current version isn't always 1. Sometimes it comes out 2. - - match: {failures.0.cause.reason: "/\\[_doc\\]\\[1\\]:.version.conflict,.current.version.\\[\\d+\\].is.different.than.the.one.provided.\\[\\d+\\]/"} + - match: {failures.0.cause.reason: "/\\[1\\]:.version.conflict,.current.version.\\[\\d+\\].is.different.than.the.one.provided.\\[\\d+\\]/"} - match: {failures.0.cause.shard: /\d+/} - match: {failures.0.cause.index: test} - gte: { took: 0 } @@ -151,7 +151,7 @@ - match: {failures.0.id: "1"} - match: {failures.0.status: 409} - match: {failures.0.cause.type: version_conflict_engine_exception} - - match: {failures.0.cause.reason: "/\\[_doc\\]\\[1\\]:.version.conflict,.required.seqNo.\\[\\d+\\]/"} + - match: {failures.0.cause.reason: "/\\[1\\]:.version.conflict,.required.seqNo.\\[\\d+\\]/"} - match: {failures.0.cause.shard: /\d+/} - match: {failures.0.cause.index: test} - gte: { took: 0 } diff --git a/server/src/main/java/org/elasticsearch/index/engine/Engine.java b/server/src/main/java/org/elasticsearch/index/engine/Engine.java index edea3952ce4c5..9bed93c371696 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -633,14 +633,14 @@ protected final GetResult getFromSearcher(Get get, BiFunction search return GetResult.NOT_EXISTS; } if (get.versionType().isVersionConflictForReads(versionValue.version, get.version())) { - throw new VersionConflictEngineException(shardId, get.type(), get.id(), + throw new VersionConflictEngineException(shardId, get.id(), get.versionType().explainConflictForReads(versionValue.version, get.version())); } if (get.getIfSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO && ( get.getIfSeqNo() != versionValue.seqNo || get.getIfPrimaryTerm() != versionValue.term )) { - throw new VersionConflictEngineException(shardId, get.type(), get.id(), + throw new VersionConflictEngineException(shardId, get.id(), get.getIfSeqNo(), get.getIfPrimaryTerm(), versionValue.seqNo, versionValue.term); } if (get.isReadFromTranslog()) { @@ -1004,13 +1004,13 @@ protected final IndexingStrategy planIndexingAsPrimary(Index index) throws IOExc currentNotFoundOrDeleted = versionValue.isDelete(); } if (index.getIfSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO && versionValue == null) { - final VersionConflictEngineException e = new VersionConflictEngineException(shardId, index.type(), index.id(), + final VersionConflictEngineException e = new VersionConflictEngineException(shardId, index.id(), index.getIfSeqNo(), index.getIfPrimaryTerm(), SequenceNumbers.UNASSIGNED_SEQ_NO, 0); plan = IndexingStrategy.skipDueToVersionConflict(e, currentNotFoundOrDeleted, currentVersion, getPrimaryTerm()); } else if (index.getIfSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO && ( versionValue.seqNo != index.getIfSeqNo() || versionValue.term != index.getIfPrimaryTerm() )) { - final VersionConflictEngineException e = new VersionConflictEngineException(shardId, index.type(), index.id(), + final VersionConflictEngineException e = new VersionConflictEngineException(shardId, index.id(), index.getIfSeqNo(), index.getIfPrimaryTerm(), versionValue.seqNo, versionValue.term); plan = IndexingStrategy.skipDueToVersionConflict(e, currentNotFoundOrDeleted, currentVersion, getPrimaryTerm()); } else if (index.versionType().isVersionConflictForWrites( @@ -1335,13 +1335,13 @@ protected final DeletionStrategy planDeletionAsPrimary(Delete delete) throws IOE } final DeletionStrategy plan; if (delete.getIfSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO && versionValue == null) { - final VersionConflictEngineException e = new VersionConflictEngineException(shardId, delete.type(), delete.id(), + final VersionConflictEngineException e = new VersionConflictEngineException(shardId, delete.id(), delete.getIfSeqNo(), delete.getIfPrimaryTerm(), SequenceNumbers.UNASSIGNED_SEQ_NO, 0); plan = DeletionStrategy.skipDueToVersionConflict(e, currentVersion, getPrimaryTerm(), currentlyDeleted); } else if (delete.getIfSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO && ( versionValue.seqNo != delete.getIfSeqNo() || versionValue.term != delete.getIfPrimaryTerm() )) { - final VersionConflictEngineException e = new VersionConflictEngineException(shardId, delete.type(), delete.id(), + final VersionConflictEngineException e = new VersionConflictEngineException(shardId, delete.id(), delete.getIfSeqNo(), delete.getIfPrimaryTerm(), versionValue.seqNo, versionValue.term); plan = DeletionStrategy.skipDueToVersionConflict(e, currentVersion, getPrimaryTerm(), currentlyDeleted); } else if (delete.versionType().isVersionConflictForWrites(currentVersion, delete.version(), currentlyDeleted)) { diff --git a/server/src/main/java/org/elasticsearch/index/engine/VersionConflictEngineException.java b/server/src/main/java/org/elasticsearch/index/engine/VersionConflictEngineException.java index 357c9c107836e..0f6c217409c30 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/VersionConflictEngineException.java +++ b/server/src/main/java/org/elasticsearch/index/engine/VersionConflictEngineException.java @@ -28,25 +28,25 @@ public class VersionConflictEngineException extends EngineException { public VersionConflictEngineException(ShardId shardId, Engine.Operation op, long currentVersion, boolean deleted) { - this(shardId, op.type(), op.id(), op.versionType().explainConflictForWrites(currentVersion, op.version(), deleted)); + this(shardId, op.id(), op.versionType().explainConflictForWrites(currentVersion, op.version(), deleted)); } - public VersionConflictEngineException(ShardId shardId, String type, String id, + public VersionConflictEngineException(ShardId shardId, String id, long compareAndWriteSeqNo, long compareAndWriteTerm, long currentSeqNo, long currentTerm) { - this(shardId, type, id, "required seqNo [" + compareAndWriteSeqNo + "], primary term [" + compareAndWriteTerm +"]." + + this(shardId, id, "required seqNo [" + compareAndWriteSeqNo + "], primary term [" + compareAndWriteTerm +"]." + (currentSeqNo == SequenceNumbers.UNASSIGNED_SEQ_NO ? " but no document was found" : " current document has seqNo [" + currentSeqNo + "] and primary term ["+ currentTerm + "]" )); } - public VersionConflictEngineException(ShardId shardId, String type, String id, String explanation) { - this(shardId, null, type, id, explanation); + public VersionConflictEngineException(ShardId shardId, String id, String explanation) { + this(shardId, null, id, explanation); } - public VersionConflictEngineException(ShardId shardId, Throwable cause, String type, String id, String explanation) { - this(shardId, "[{}][{}]: version conflict, {}", cause, type, id, explanation); + public VersionConflictEngineException(ShardId shardId, Throwable cause, String id, String explanation) { + this(shardId, "[{}]: version conflict, {}", cause, id, explanation); } public VersionConflictEngineException(ShardId shardId, String msg, Throwable cause, Object... params) { diff --git a/server/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java b/server/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java index 8fa8060b38536..610a72de6ecfd 100644 --- a/server/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/bulk/TransportShardBulkActionTests.java @@ -550,7 +550,7 @@ public void testUpdateRequestWithConflictFailure() throws Exception { IndexRequest updateResponse = new IndexRequest("index", "_doc", "id").source(Requests.INDEX_CONTENT_TYPE, "field", "value"); - Exception err = new VersionConflictEngineException(shardId, "_doc", "id", + Exception err = new VersionConflictEngineException(shardId, "id", "I'm conflicted <(;_;)>"); Engine.IndexResult indexResult = new Engine.IndexResult(err, 0, 0, 0); IndexShard shard = mock(IndexShard.class); @@ -784,7 +784,7 @@ public void testRetries() throws Exception { IndexRequest updateResponse = new IndexRequest("index", "_doc", "id").source(Requests.INDEX_CONTENT_TYPE, "field", "value"); - Exception err = new VersionConflictEngineException(shardId, "_doc", "id", + Exception err = new VersionConflictEngineException(shardId, "id", "I'm conflicted <(;_;)>"); Engine.IndexResult conflictedResult = new Engine.IndexResult(err, 0, 0); Engine.IndexResult mappingUpdate = diff --git a/server/src/test/java/org/elasticsearch/get/GetActionIT.java b/server/src/test/java/org/elasticsearch/get/GetActionIT.java index 77303995f7494..8be9a991d17e9 100644 --- a/server/src/test/java/org/elasticsearch/get/GetActionIT.java +++ b/server/src/test/java/org/elasticsearch/get/GetActionIT.java @@ -441,7 +441,7 @@ public void testMultiGetWithVersion() throws Exception { assertThat(response.getResponses()[1].getResponse().getSourceAsMap().get("field").toString(), equalTo("value1")); assertThat(response.getResponses()[2].getFailure(), notNullValue()); assertThat(response.getResponses()[2].getFailure().getId(), equalTo("1")); - assertThat(response.getResponses()[2].getFailure().getMessage(), startsWith("[type1][1]: version conflict")); + assertThat(response.getResponses()[2].getFailure().getMessage(), startsWith("[1]: version conflict")); assertThat(response.getResponses()[2].getFailure().getFailure(), instanceOf(VersionConflictEngineException.class)); //Version from Lucene index @@ -464,7 +464,7 @@ public void testMultiGetWithVersion() throws Exception { assertThat(response.getResponses()[1].getResponse().getSourceAsMap().get("field").toString(), equalTo("value1")); assertThat(response.getResponses()[2].getFailure(), notNullValue()); assertThat(response.getResponses()[2].getFailure().getId(), equalTo("1")); - assertThat(response.getResponses()[2].getFailure().getMessage(), startsWith("[type1][1]: version conflict")); + assertThat(response.getResponses()[2].getFailure().getMessage(), startsWith("[1]: version conflict")); assertThat(response.getResponses()[2].getFailure().getFailure(), instanceOf(VersionConflictEngineException.class)); @@ -489,7 +489,7 @@ public void testMultiGetWithVersion() throws Exception { assertThat(response.getResponses()[1].getFailure(), notNullValue()); assertThat(response.getResponses()[1].getFailure().getId(), equalTo("2")); assertThat(response.getResponses()[1].getIndex(), equalTo("test")); - assertThat(response.getResponses()[1].getFailure().getMessage(), startsWith("[type1][2]: version conflict")); + assertThat(response.getResponses()[1].getFailure().getMessage(), startsWith("[2]: version conflict")); assertThat(response.getResponses()[2].getId(), equalTo("2")); assertThat(response.getResponses()[2].getIndex(), equalTo("test")); assertThat(response.getResponses()[2].getFailure(), nullValue()); @@ -515,7 +515,7 @@ public void testMultiGetWithVersion() throws Exception { assertThat(response.getResponses()[1].getFailure(), notNullValue()); assertThat(response.getResponses()[1].getFailure().getId(), equalTo("2")); assertThat(response.getResponses()[1].getIndex(), equalTo("test")); - assertThat(response.getResponses()[1].getFailure().getMessage(), startsWith("[type1][2]: version conflict")); + assertThat(response.getResponses()[1].getFailure().getMessage(), startsWith("[2]: version conflict")); assertThat(response.getResponses()[2].getId(), equalTo("2")); assertThat(response.getResponses()[2].getIndex(), equalTo("test")); assertThat(response.getResponses()[2].getFailure(), nullValue()); From fed3580644386693eadb902badd8f7c1ee5cd897 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 27 Mar 2019 12:19:15 +0100 Subject: [PATCH 15/77] Remove String interning from `o.e.index.Index`. (#40350) `Index` interns its name and uuid. My guess is that the main goal is to avoid having duplicate strings in the representation of the cluster state. However I doubt it helps much given that we have many other objects in the cluster state that we don't try to reuse, and interning has some cost. When looking into #40263 my profiler pointed to string interning because of the `Index` object that is created in `QueryShardContext` as one of the bottlenecks of the `can_match` phase. --- server/src/main/java/org/elasticsearch/index/Index.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/Index.java b/server/src/main/java/org/elasticsearch/index/Index.java index ac5a2763644fa..9b6f4dbd98afb 100644 --- a/server/src/main/java/org/elasticsearch/index/Index.java +++ b/server/src/main/java/org/elasticsearch/index/Index.java @@ -50,8 +50,8 @@ public class Index implements Writeable, ToXContentObject { private final String uuid; public Index(String name, String uuid) { - this.name = Objects.requireNonNull(name).intern(); - this.uuid = Objects.requireNonNull(uuid).intern(); + this.name = Objects.requireNonNull(name); + this.uuid = Objects.requireNonNull(uuid); } /** From 1557d77b07bbeef99604857c1ca824e20771c283 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 27 Mar 2019 14:14:06 +0200 Subject: [PATCH 16/77] SQL: Adjust the precision and scale for drivers (#40467) Fix #40357 --- .../sql/language/data-types.asciidoc | 6 +-- .../sql/qa/security/RestSqlSecurityIT.java | 12 +++--- .../xpack/sql/qa/security/UserFunctionIT.java | 12 +++--- .../xpack/sql/qa/SqlProtocolTestCase.java | 8 ++-- .../xpack/sql/qa/rest/RestSqlTestCase.java | 10 ++--- .../setup_mock_metadata_get_columns.sql | 4 +- .../xpack/sql/proto/ColumnInfo.java | 2 +- .../plan/logical/command/sys/SysTypes.java | 3 +- .../xpack/sql/type/DataType.java | 12 +++--- .../xpack/sql/type/DataTypes.java | 42 ++++++++++--------- .../xpack/sql/action/SqlActionIT.java | 2 +- .../logical/command/sys/SysParserTests.java | 2 +- .../logical/command/sys/SysTypesTests.java | 2 +- .../xpack/sql/type/DataTypesTests.java | 2 +- .../xpack/sql/type/TypesTests.java | 2 +- .../src/test/resources/mapping-nested.json | 3 +- 16 files changed, 62 insertions(+), 62 deletions(-) diff --git a/docs/reference/sql/language/data-types.asciidoc b/docs/reference/sql/language/data-types.asciidoc index e59fd528fd80a..42e5c842a4187 100644 --- a/docs/reference/sql/language/data-types.asciidoc +++ b/docs/reference/sql/language/data-types.asciidoc @@ -21,9 +21,9 @@ s|SQL precision | <> | long | BIGINT | 19 | <> | double | DOUBLE | 15 | <> | float | REAL | 7 -| <> | half_float | FLOAT | 16 -| <> | scaled_float | FLOAT | 19 -| <> | keyword | VARCHAR | based on <> +| <> | half_float | FLOAT | 3 +| <> | scaled_float | DOUBLE | 15 +| <> | keyword | VARCHAR | 32,766 | <> | text | VARCHAR | 2,147,483,647 | <> | binary | VARBINARY | 2,147,483,647 | <> | datetime | TIMESTAMP | 24 diff --git a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java index d5e7e7cc5084c..6a4a2662810e3 100644 --- a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java +++ b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/RestSqlSecurityIT.java @@ -29,10 +29,10 @@ import java.util.Map; import java.util.stream.Collectors; +import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.SQL_QUERY_REST_ENDPOINT; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.columnInfo; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.mode; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.randomMode; -import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.SQL_QUERY_REST_ENDPOINT; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; @@ -101,9 +101,9 @@ public void expectDescribe(Map> columns, String user) throw String mode = randomMode(); Map expected = new HashMap<>(3); expected.put("columns", Arrays.asList( - columnInfo(mode, "column", "keyword", JDBCType.VARCHAR, 0), - columnInfo(mode, "type", "keyword", JDBCType.VARCHAR, 0), - columnInfo(mode, "mapping", "keyword", JDBCType.VARCHAR, 0))); + columnInfo(mode, "column", "keyword", JDBCType.VARCHAR, 32766), + columnInfo(mode, "type", "keyword", JDBCType.VARCHAR, 32766), + columnInfo(mode, "mapping", "keyword", JDBCType.VARCHAR, 32766))); List> rows = new ArrayList<>(columns.size()); for (Map.Entry> column : columns.entrySet()) { List cols = new ArrayList<>(); @@ -120,8 +120,8 @@ public void expectDescribe(Map> columns, String user) throw public void expectShowTables(List tables, String user) throws Exception { String mode = randomMode(); List columns = new ArrayList<>(); - columns.add(columnInfo(mode, "name", "keyword", JDBCType.VARCHAR, 0)); - columns.add(columnInfo(mode, "type", "keyword", JDBCType.VARCHAR, 0)); + columns.add(columnInfo(mode, "name", "keyword", JDBCType.VARCHAR, 32766)); + columns.add(columnInfo(mode, "type", "keyword", JDBCType.VARCHAR, 32766)); Map expected = new HashMap<>(); expected.put("columns", columns); List> rows = new ArrayList<>(); diff --git a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java index 2d93597efc108..65eb991280ff1 100644 --- a/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java +++ b/x-pack/plugin/sql/qa/security/src/test/java/org/elasticsearch/xpack/sql/qa/security/UserFunctionIT.java @@ -34,10 +34,10 @@ import java.util.List; import java.util.Map; +import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.SQL_QUERY_REST_ENDPOINT; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.columnInfo; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.mode; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.randomMode; -import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.SQL_QUERY_REST_ENDPOINT; public class UserFunctionIT extends ESRestTestCase { @@ -81,7 +81,7 @@ public void testSingleRandomUser() throws IOException { Map expected = new HashMap<>(); expected.put("columns", Arrays.asList( - columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 0))); + columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 32766))); expected.put("rows", Arrays.asList(Arrays.asList(randomUserName))); Map actual = runSql(randomUserName, mode, SQL); @@ -97,7 +97,7 @@ public void testSingleRandomUserWithWhereEvaluatingTrue() throws IOException { Map expected = new HashMap<>(); expected.put("columns", Arrays.asList( - columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 0))); + columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 32766))); expected.put("rows", Arrays.asList(Arrays.asList(randomUserName), Arrays.asList(randomUserName), Arrays.asList(randomUserName))); @@ -114,7 +114,7 @@ public void testSingleRandomUserWithWhereEvaluatingFalse() throws IOException { Map expected = new HashMap<>(); expected.put("columns", Arrays.asList( - columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 0))); + columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 32766))); expected.put("rows", Collections.>emptyList()); String anotherRandomUserName = randomValueOtherThan(randomUserName, () -> randomAlphaOfLengthBetween(1, 15)); Map actual = runSql(randomUserName, mode, SQL + " FROM test WHERE USER()='" + anotherRandomUserName + "' LIMIT 3"); @@ -129,7 +129,7 @@ public void testMultipleRandomUsersAccess() throws IOException { Map expected = new HashMap<>(); expected.put("columns", Arrays.asList( - columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 0))); + columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 32766))); expected.put("rows", Arrays.asList(Arrays.asList(randomlyPickedUsername))); Map actual = runSql(randomlyPickedUsername, mode, SQL); @@ -147,7 +147,7 @@ public void testSingleUserSelectFromIndex() throws IOException { Map expected = new HashMap<>(); expected.put("columns", Arrays.asList( - columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 0))); + columnInfo(mode, "USER()", "keyword", JDBCType.VARCHAR, 32766))); expected.put("rows", Arrays.asList(Arrays.asList(randomUserName), Arrays.asList(randomUserName), Arrays.asList(randomUserName))); diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java index 233c4b6a42024..1a47bb0add85b 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/SqlProtocolTestCase.java @@ -28,10 +28,10 @@ import java.util.Locale; import java.util.Map; +import static org.elasticsearch.xpack.sql.proto.Mode.CLI; import static org.elasticsearch.xpack.sql.proto.Protocol.SQL_QUERY_REST_ENDPOINT; import static org.elasticsearch.xpack.sql.proto.RequestInfo.CLIENT_IDS; import static org.elasticsearch.xpack.sql.qa.rest.RestSqlTestCase.mode; -import static org.elasticsearch.xpack.sql.proto.Mode.CLI; public abstract class SqlProtocolTestCase extends ESRestTestCase { @@ -62,7 +62,7 @@ public void testNumericTypes() throws IOException { } public void testTextualType() throws IOException { - assertQuery("SELECT 'abc123'", "'abc123'", "keyword", "abc123", 0); + assertQuery("SELECT 'abc123'", "'abc123'", "keyword", "abc123", 32766); } public void testDateTimes() throws IOException { @@ -141,7 +141,7 @@ private void assertQuery(String sql, String columnName, String columnType, Objec List row = (ArrayList) rows.get(0); assertEquals(1, row.size()); - // from xcontent we can get float or double, depending on the conversion + // from xcontent we can get float or double, depending on the conversion // method of the specific xcontent format implementation if (columnValue instanceof Float && row.get(0) instanceof Double) { assertEquals(columnValue, (float)((Number) row.get(0)).doubleValue()); @@ -209,7 +209,7 @@ private Map runSql(String mode, String sql, boolean columnar) th return XContentHelper.convertToMap(SmileXContent.smileXContent, content, false); } default: - return XContentHelper.convertToMap(JsonXContent.jsonXContent, content, false); + return XContentHelper.convertToMap(JsonXContent.jsonXContent, content, false); } } } diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java index afcabeedf59ee..c88f31bb2fd71 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/rest/RestSqlTestCase.java @@ -74,7 +74,7 @@ public void testBasicQuery() throws IOException { String mode = randomMode(); boolean columnar = randomBoolean(); - expected.put("columns", singletonList(columnInfo(mode, "test", "text", JDBCType.VARCHAR, 0))); + expected.put("columns", singletonList(columnInfo(mode, "test", "text", JDBCType.VARCHAR, Integer.MAX_VALUE))); if (columnar) { expected.put("values", singletonList(Arrays.asList("test", "test"))); } else { @@ -118,7 +118,7 @@ public void testNextPage() throws IOException { Map expected = new HashMap<>(); if (i == 0) { expected.put("columns", Arrays.asList( - columnInfo(mode, "text", "text", JDBCType.VARCHAR, 0), + columnInfo(mode, "text", "text", JDBCType.VARCHAR, Integer.MAX_VALUE), columnInfo(mode, "number", "long", JDBCType.BIGINT, 20), columnInfo(mode, "s", "double", JDBCType.DOUBLE, 25), columnInfo(mode, "SCORE()", "float", JDBCType.REAL, 15))); @@ -184,7 +184,7 @@ public void testScoreWithFieldNamedScore() throws IOException { Map expected = new HashMap<>(); boolean columnar = randomBoolean(); expected.put("columns", Arrays.asList( - columnInfo(mode, "name", "text", JDBCType.VARCHAR, 0), + columnInfo(mode, "name", "text", JDBCType.VARCHAR, Integer.MAX_VALUE), columnInfo(mode, "score", "long", JDBCType.BIGINT, 20), columnInfo(mode, "SCORE()", "float", JDBCType.REAL, 15))); if (columnar) { @@ -427,7 +427,7 @@ public void testBasicQueryWithFilter() throws IOException { "{\"test\":\"bar\"}"); Map expected = new HashMap<>(); - expected.put("columns", singletonList(columnInfo(mode, "test", "text", JDBCType.VARCHAR, 0))); + expected.put("columns", singletonList(columnInfo(mode, "test", "text", JDBCType.VARCHAR, Integer.MAX_VALUE))); expected.put("rows", singletonList(singletonList("foo"))); assertResponse(expected, runSql(new StringEntity("{\"query\":\"SELECT * FROM test\", " + "\"filter\":{\"match\": {\"test\": \"foo\"}}" + mode(mode) + "}", @@ -442,7 +442,7 @@ public void testBasicQueryWithParameters() throws IOException { Map expected = new HashMap<>(); expected.put("columns", Arrays.asList( - columnInfo(mode, "test", "text", JDBCType.VARCHAR, 0), + columnInfo(mode, "test", "text", JDBCType.VARCHAR, Integer.MAX_VALUE), columnInfo(mode, "param", "integer", JDBCType.INTEGER, 11) )); if (columnar) { diff --git a/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_columns.sql b/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_columns.sql index f61d48af4ff37..6292a6296ff69 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_columns.sql +++ b/x-pack/plugin/sql/qa/src/main/resources/setup_mock_metadata_get_columns.sql @@ -23,12 +23,12 @@ CREATE TABLE mock ( IS_AUTOINCREMENT VARCHAR, IS_GENERATEDCOLUMN VARCHAR ) AS -SELECT null, 'test1', 'name', 12, 'TEXT', 0, 2147483647, null, null, +SELECT null, 'test1', 'name', 12, 'TEXT', 2147483647, 2147483647, null, null, 1, -- columnNullable null, null, 12, 0, 2147483647, 1, 'YES', null, null, null, null, 'NO', 'NO' FROM DUAL UNION ALL -SELECT null, 'test1', 'name.keyword', 12, 'KEYWORD', 0, 2147483647, null, null, +SELECT null, 'test1', 'name.keyword', 12, 'KEYWORD', 32766, 2147483647, null, null, 1, -- columnNullable null, null, 12, 0, 2147483647, 1, 'YES', null, null, null, null, 'NO', 'NO' FROM DUAL diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java index ef4e603564ef6..7cf31781d9e8f 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/proto/ColumnInfo.java @@ -119,7 +119,7 @@ public boolean equals(Object o) { return false; } ColumnInfo that = (ColumnInfo) o; - return displaySize == that.displaySize && + return Objects.equals(displaySize, that.displaySize) && Objects.equals(table, that.table) && Objects.equals(name, that.name) && Objects.equals(esType, that.esType); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java index 0d4ee07603161..2112128b41b00 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypes.java @@ -82,8 +82,7 @@ public final void execute(SqlSession session, ActionListener liste .sorted(Comparator.comparing((DataType t) -> t.sqlType.getVendorTypeNumber()).thenComparing(DataType::sqlName)) .map(t -> asList(t.toString(), t.sqlType.getVendorTypeNumber(), - //https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size?view=sql-server-2017 - t.defaultPrecision, + DataTypes.precision(t), "'", "'", null, diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java index f75d0a8f7352a..deeeed1c1ca16 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataType.java @@ -36,19 +36,19 @@ public enum DataType { DOUBLE( "double", JDBCType.DOUBLE, Double.BYTES, 15, 25, false, true, true), // 24 bits defaultPrecision - 24*log10(2) =~ 7 (7.22) FLOAT( "float", JDBCType.REAL, Float.BYTES, 7, 15, false, true, true), - HALF_FLOAT( "half_float", JDBCType.FLOAT, Double.BYTES, 16, 25, false, true, true), + HALF_FLOAT( "half_float", JDBCType.FLOAT, Float.BYTES, 3, 25, false, true, true), // precision is based on long - SCALED_FLOAT( "scaled_float", JDBCType.FLOAT, Double.BYTES, 19, 25, false, true, true), - KEYWORD( "keyword", JDBCType.VARCHAR, Integer.MAX_VALUE, 256, 0, false, false, true), - TEXT( "text", JDBCType.VARCHAR, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, false, false, false), + SCALED_FLOAT( "scaled_float", JDBCType.DOUBLE, Long.BYTES, 15, 25, false, true, true), + KEYWORD( "keyword", JDBCType.VARCHAR, Integer.MAX_VALUE, 32766, 32766, false, false, true), + TEXT( "text", JDBCType.VARCHAR, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, false, false, false), OBJECT( "object", JDBCType.STRUCT, -1, 0, 0, false, false, false), NESTED( "nested", JDBCType.STRUCT, -1, 0, 0, false, false, false), - BINARY( "binary", JDBCType.VARBINARY, -1, Integer.MAX_VALUE, 0, false, false, false), + BINARY( "binary", JDBCType.VARBINARY, -1, Integer.MAX_VALUE, Integer.MAX_VALUE, false, false, false), DATE( JDBCType.DATE, Long.BYTES, 24, 24, false, false, true), // since ODBC and JDBC interpret precision for Date as display size // the precision is 23 (number of chars in ISO8601 with millis) + Z (the UTC timezone) // see https://github.com/elastic/elasticsearch/issues/30386#issuecomment-386807288 - DATETIME( "date", JDBCType.TIMESTAMP, Long.BYTES, 24, 24, false, false, true), + DATETIME( "date", JDBCType.TIMESTAMP, Long.BYTES, 3, 24, false, false, true), // // specialized types // diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java index 5a3fa235e9a73..f8d657447923a 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypes.java @@ -175,7 +175,7 @@ public static Integer metaSqlDataType(DataType t) { } // https://github.com/elastic/elasticsearch/issues/30386 - // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function?view=sql-server-2017 + // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function public static Integer metaSqlDateTimeSub(DataType t) { if (t == DATETIME) { // ODBC SQL_CODE_TIMESTAMP @@ -185,37 +185,30 @@ public static Integer metaSqlDateTimeSub(DataType t) { return 0; } - // https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/decimal-digits?view=sql-server-2017 public static Short metaSqlMinimumScale(DataType t) { - // TODO: return info for HALF/SCALED_FLOATS (should be based on field not type) - if (t == DATETIME) { - return Short.valueOf((short) 3); - } - if (t.isInteger()) { - return Short.valueOf((short) 0); - } - // minimum scale? - if (t.isRational()) { - return Short.valueOf((short) 0); - } - return null; + return metaSqlSameScale(t); } public static Short metaSqlMaximumScale(DataType t) { - // TODO: return info for HALF/SCALED_FLOATS (should be based on field not type) - if (t == DATETIME) { - return Short.valueOf((short) 3); - } + return metaSqlSameScale(t); + } + + // https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/decimal-digits + // https://github.com/elastic/elasticsearch/issues/40357 + // since the scale is fixed, minimum and maximum should return the same value + // hence why this method exists + private static Short metaSqlSameScale(DataType t) { + // TODO: return info for SCALED_FLOATS (should be based on field not type) if (t.isInteger()) { return Short.valueOf((short) 0); } - if (t.isRational()) { + if (t.isDateBased() || t.isRational()) { return Short.valueOf((short) t.defaultPrecision); } return null; } - // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function?view=sql-server-2017 + // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function public static Integer metaSqlRadix(DataType t) { // RADIX - Determines how numbers returned by COLUMN_SIZE and DECIMAL_DIGITS should be interpreted. // 10 means they represent the number of decimal digits allowed for the column. @@ -223,4 +216,13 @@ public static Integer metaSqlRadix(DataType t) { // null means radix is not applicable for the given type. return t.isInteger() ? Integer.valueOf(10) : (t.isRational() ? Integer.valueOf(2) : null); } + + //https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlgettypeinfo-function#comments + //https://docs.microsoft.com/en-us/sql/odbc/reference/appendixes/column-size + public static Integer precision(DataType t) { + if (t.isNumeric()) { + return t.defaultPrecision; + } + return t.displaySize; + } } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java index 60047fcdbe799..5f85ff90e344c 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/action/SqlActionIT.java @@ -33,7 +33,7 @@ public void testSqlAction() { assertThat(response.columns(), hasSize(2)); int dataIndex = dataBeforeCount ? 0 : 1; int countIndex = dataBeforeCount ? 1 : 0; - assertEquals(new ColumnInfo("", "data", "text", 0), response.columns().get(dataIndex)); + assertEquals(new ColumnInfo("", "data", "text", 2147483647), response.columns().get(dataIndex)); assertEquals(new ColumnInfo("", "count", "long", 20), response.columns().get(countIndex)); assertThat(response.rows(), hasSize(2)); diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java index a1accd28ab4d9..c6c993967dd1b 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysParserTests.java @@ -60,7 +60,7 @@ private Tuple sql(String sql) { public void testSysTypes() throws Exception { Command cmd = sql("SYS TYPES").v1(); - List names = asList("BYTE", "LONG", "BINARY", "NULL", "INTEGER", "SHORT", "HALF_FLOAT", "SCALED_FLOAT", "FLOAT", "DOUBLE", + List names = asList("BYTE", "LONG", "BINARY", "NULL", "INTEGER", "SHORT", "HALF_FLOAT", "FLOAT", "DOUBLE", "SCALED_FLOAT", "KEYWORD", "TEXT", "IP", "BOOLEAN", "DATE", "DATETIME", "INTERVAL_YEAR", "INTERVAL_MONTH", "INTERVAL_DAY", "INTERVAL_HOUR", "INTERVAL_MINUTE", "INTERVAL_SECOND", "INTERVAL_YEAR_TO_MONTH", "INTERVAL_DAY_TO_HOUR", "INTERVAL_DAY_TO_MINUTE", "INTERVAL_DAY_TO_SECOND", diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java index 4e428846dc2f4..9c1ef31fcb170 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTypesTests.java @@ -43,7 +43,7 @@ private Tuple sql(String sql) { public void testSysTypes() { Command cmd = sql("SYS TYPES").v1(); - List names = asList("BYTE", "LONG", "BINARY", "NULL", "INTEGER", "SHORT", "HALF_FLOAT", "SCALED_FLOAT", "FLOAT", "DOUBLE", + List names = asList("BYTE", "LONG", "BINARY", "NULL", "INTEGER", "SHORT", "HALF_FLOAT", "FLOAT", "DOUBLE", "SCALED_FLOAT", "KEYWORD", "TEXT", "IP", "BOOLEAN", "DATE", "DATETIME", "INTERVAL_YEAR", "INTERVAL_MONTH", "INTERVAL_DAY", "INTERVAL_HOUR", "INTERVAL_MINUTE", "INTERVAL_SECOND", "INTERVAL_YEAR_TO_MONTH", "INTERVAL_DAY_TO_HOUR", "INTERVAL_DAY_TO_MINUTE", "INTERVAL_DAY_TO_SECOND", diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java index 47f01be917867..a789324e0b478 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypesTests.java @@ -55,7 +55,7 @@ public void testMetaDateTypeSub() { public void testMetaMinimumScale() { assertEquals(Short.valueOf((short) 3), metaSqlMinimumScale(DATETIME)); assertEquals(Short.valueOf((short) 0), metaSqlMinimumScale(LONG)); - assertEquals(Short.valueOf((short) 0), metaSqlMinimumScale(FLOAT)); + assertEquals(Short.valueOf((short) FLOAT.defaultPrecision), metaSqlMaximumScale(FLOAT)); assertNull(metaSqlMinimumScale(KEYWORD)); } diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/TypesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/TypesTests.java index 2a2488dda722f..65b491fe71a1d 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/TypesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/TypesTests.java @@ -81,7 +81,7 @@ public void testDateField() { EsField field = mapping.get("date"); assertThat(field.getDataType(), is(DATETIME)); assertThat(field.isAggregatable(), is(true)); - assertThat(field.getPrecision(), is(24)); + assertThat(field.getPrecision(), is(3)); } public void testDateNoFormat() { diff --git a/x-pack/plugin/sql/src/test/resources/mapping-nested.json b/x-pack/plugin/sql/src/test/resources/mapping-nested.json index d9b6398458f14..1251d17525a00 100644 --- a/x-pack/plugin/sql/src/test/resources/mapping-nested.json +++ b/x-pack/plugin/sql/src/test/resources/mapping-nested.json @@ -10,8 +10,7 @@ "type" : "text", "fields" : { "keyword" : { - "type" : "keyword", - "ignore_above" : 256 + "type" : "keyword" } } }, From 15c924e287075575c520e6bb8c3ffd8ec58201fc Mon Sep 17 00:00:00 2001 From: David Kyle Date: Wed, 27 Mar 2019 12:31:28 +0000 Subject: [PATCH 17/77] [ML] Fix serialisation of Start Data Frame request (#40483) --- .../action/TransportStartDataFrameTransformAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportStartDataFrameTransformAction.java b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportStartDataFrameTransformAction.java index a6f52f22da407..f68e246ed860b 100644 --- a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportStartDataFrameTransformAction.java +++ b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportStartDataFrameTransformAction.java @@ -55,8 +55,8 @@ public TransportStartDataFrameTransformAction(TransportService transportService, ThreadPool threadPool, IndexNameExpressionResolver indexNameExpressionResolver, DataFrameTransformsConfigManager dataFrameTransformsConfigManager, PersistentTasksService persistentTasksService, Client client) { - super(StartDataFrameTransformAction.NAME, transportService, clusterService, threadPool, actionFilters, indexNameExpressionResolver, - StartDataFrameTransformAction.Request::new); + super(StartDataFrameTransformAction.NAME, transportService, clusterService, threadPool, actionFilters, + StartDataFrameTransformAction.Request::new, indexNameExpressionResolver); this.licenseState = licenseState; this.dataFrameTransformsConfigManager = dataFrameTransformsConfigManager; this.persistentTasksService = persistentTasksService; From ca90190226c0a8c63f51c2603b27df299ce8d503 Mon Sep 17 00:00:00 2001 From: Bogdan Pintea Date: Wed, 27 Mar 2019 14:11:59 +0100 Subject: [PATCH 18/77] SQL: ODBC: document extra connection string parameters (#40476) * document ODBC's extra connection string parameters Document the connection string parameters that are currently not configurable from the GUI. Add a note about SmartScreen possible warning on driver MSI installation. Add a note about the TLS certificate file not being supported as bundled or password-protected. * rephrasing and restructuring the list of params - addressing PR review notes * rephrasings and reference linking - addressing PR review notes --- docs/reference/sql/endpoints/odbc.asciidoc | 9 +- .../sql/endpoints/odbc/configuration.asciidoc | 96 ++++++++++++++++++- .../sql/endpoints/odbc/installation.asciidoc | 5 + 3 files changed, 104 insertions(+), 6 deletions(-) diff --git a/docs/reference/sql/endpoints/odbc.asciidoc b/docs/reference/sql/endpoints/odbc.asciidoc index 1a7dd974281c8..fd92a37dca650 100644 --- a/docs/reference/sql/endpoints/odbc.asciidoc +++ b/docs/reference/sql/endpoints/odbc.asciidoc @@ -9,9 +9,12 @@ [float] === Overview -{odbc} is a feature-rich 3.80 ODBC driver for {es}. -It is a core level driver, exposing all of the functionality accessible through the {es}'s SQL ODBC API, converting ODBC calls into -{es-sql}. +{odbc} is a 3.80 compliant ODBC driver for {es}. +It is a core level driver, exposing all of the functionality accessible through +the {es}'s SQL API, converting ODBC calls into {es-sql}. + +In order to make use of the driver, the server must have {es-sql} installed and +running with the valid license. * <> * <> diff --git a/docs/reference/sql/endpoints/odbc/configuration.asciidoc b/docs/reference/sql/endpoints/odbc/configuration.asciidoc index 8bda67ce063d5..70ba437b64851 100644 --- a/docs/reference/sql/endpoints/odbc/configuration.asciidoc +++ b/docs/reference/sql/endpoints/odbc/configuration.asciidoc @@ -162,6 +162,8 @@ In case the server uses a certificate that is not part of the PKI, for example u + The driver will only read the contents of the file just before a connection is attempted. See <> section further on how to check the validity of the provided parameters. + +NOTE: The certificate file can not be bundled or password protected since the driver will not prompt for a password. ++ If using the file browser to locate the certificate - by pressing the _Browse..._ button - only files with _.pem_ and _.der_ extensions will be considered by default. Choose _All Files (\*.*)_ from the drop down, if your file ends with a different extension: + @@ -260,7 +262,95 @@ image:images/sql/odbc/env_var_log.png[] NOTE: When enabling the logging through the environment variable, the driver will create *one log file per process*. -Both ways of configuring the logging can coexist and both can use the same destination logging directory. However, one logging message -will only be logged once, the connection logging taking precedence over the environment variable logging. +Both ways of configuring the logging can coexist and both can use the same +destination logging directory. However, one logging message will only be logged +once, the connection logging taking precedence over the environment variable +logging. + +[[odbc-cfg-dsnparams]] +[float] +==== Connection string parameters + +The following is a list of additional parameters that can be configured for a +particular connection, in case the default behavior of the driver is not +suitable. This can be done within the client application, in a manner +particular to that application, generally in a free text input box (sometimes +named "Connection string", "String extras", or similar). The format of the +string is `Attribute1=Value1`. Multiple attributes can be specified, separated +by a semicolon `Attribute1=Value1;Attribute2=Value2;`. The attribute names are +given below. + +`Timeout` (default: `0`):: +The maximum time (in seconds) a request to the server can take. This can be +overridden by a larger statement-level timeout setting. The value 0 means no +timeout. + +`Follow` (default: `yes`):: +A boolean value (`yes`|`no` / `true`|`false` / `0`|`1`) controlling if the +driver will follow HTTP redirects. + + +`MaxFetchSize` (default: `0`):: +The maximum number of rows that {es-sql} server should send the driver for one +page. This corresponds to {es-sql}'s request parameter `fetch_size` (see +<>). The value 0 means server default. + + +`MaxBodySizeMB` (default: `100`):: +The maximum size (in megabytes) that an answer can grow to, before being +rejected as too large by the driver. +This is concerning the HTTP answer body of one page, not the cumulated data +volume that a query might generate. + + +`ApplyTZ` (default: `no`):: +A boolean value controlling the timezone of: + +* the context in which the query will execute (especially relevant for functions dealing with timestamp components); + +* the timestamps received from / sent to the server. +If disabled, the UTC timezone will apply; otherwise, the local machine's set +timezone. + + +`ScientificFloats` (default: `default`):: +Controls how the floating point numbers will be printed, when these are +converted to string by the driver. Possible values given to this parameter: + +* `scientific`: the exponential notation (ex.: 1.23E01); + +* `default`: the default notation (ex.: 12.3); + +* `auto`: the driver will choose one of the above depending on the value to be +printed. +Note that the number of decimals is dependent on the precision (or ODBC scale) +of the value being printed and varies with the different floating point types +supported by {es-sql}. +This setting is not effective when the application fetches from the driver the +values as numbers and then does the conversion subsequently itself. + + +`VersionChecking` (default: `strict`):: +By default, the version of the driver and that of the server must be the same. +This parameter will allow a driver to connect to a server of different version. +The variation however can only be of the minor version, both endpoints must be +of same major version number. +Possible values: + +* `strict`: the versions must be in sync; + +* `major`: the versions must have the same major number. + +WARNING: This parameter can only be used for troubleshooting purposes. Running +with versions out of sync is not supported. + + +`MultiFieldLenient` (default: `true`):: +This boolean parameter controls the behavior of the server in case a +multi-value field is queried. In case this is set and the server encounters +such a field, it will pick a value in the set - without any guarantees of what +that will be, but typically the first in natural ascending order - and return +it as the value for the column. If not set, the server will return an error. +This corresponds to {es-sql}'s request parameter `field_multi_value_leniency` +(see <>). -// vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 tw=138 diff --git a/docs/reference/sql/endpoints/odbc/installation.asciidoc b/docs/reference/sql/endpoints/odbc/installation.asciidoc index 08f0c66ee2a8f..3a024e443d7c8 100644 --- a/docs/reference/sql/endpoints/odbc/installation.asciidoc +++ b/docs/reference/sql/endpoints/odbc/installation.asciidoc @@ -21,6 +21,11 @@ If you fail to meet any of the prerequisites the installer will show an error me NOTE: It is not possible to inline upgrade using the MSI. In order to upgrade, you will first have to uninstall the old driver and then install the new driver. +NOTE: When installing the MSI, the Windows Defender SmartScreen might warn +about running an unrecognized app. If the MSI has been downloaded from +Elastic's web site, it is safe to acknowledge the message by allowing the +installation to continue (`Run anyway`). + [[download]] ==== Download the `.msi` package(s) From 35fa5824943a870bd5c8baf471af16c01e1ef955 Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Wed, 27 Mar 2019 14:17:29 +0100 Subject: [PATCH 19/77] Only recommend disabling allocation of replicas in a rolling upgrade (#40299) The current documentation recommends disabling allocations of all shards. This prevents shards of new indices from being allocated as well. That means that during a rolling upgrade, operations like roll over and index shrinking will operate correctly. This, in turn, can cause issues for data ingestion and ILM. --- docs/reference/upgrade/disable-shard-alloc.asciidoc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/reference/upgrade/disable-shard-alloc.asciidoc b/docs/reference/upgrade/disable-shard-alloc.asciidoc index abd40336e9b08..839488f541f51 100644 --- a/docs/reference/upgrade/disable-shard-alloc.asciidoc +++ b/docs/reference/upgrade/disable-shard-alloc.asciidoc @@ -3,17 +3,18 @@ When you shut down a node, the allocation process waits for `index.unassigned.node_left.delayed_timeout` (by default, one minute) before starting to replicate the shards on that node to other nodes in the cluster, which can involve a lot of I/O. Since the node is shortly going to be -restarted, this I/O is unnecessary. You can avoid racing the clock by disabling -allocation before shutting down the node: +restarted, this I/O is unnecessary. You can avoid racing the clock by +<> of replicas before shutting down +the node: [source,js] -------------------------------------------------- PUT _cluster/settings { "persistent": { - "cluster.routing.allocation.enable": "none" + "cluster.routing.allocation.enable": "primaries" } } -------------------------------------------------- // CONSOLE -// TEST[skip:indexes don't assign] \ No newline at end of file +// TEST[skip:indexes don't assign] From cd6d8ca50b0ec4b255c8adb2ba894547884658d1 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Wed, 27 Mar 2019 10:02:37 -0400 Subject: [PATCH 20/77] Fix typo in rollup_index definition (#40520) --- docs/reference/rollup/apis/rollup-job-config.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/rollup/apis/rollup-job-config.asciidoc b/docs/reference/rollup/apis/rollup-job-config.asciidoc index 885d4e82cf6b0..852f7b879fb38 100644 --- a/docs/reference/rollup/apis/rollup-job-config.asciidoc +++ b/docs/reference/rollup/apis/rollup-job-config.asciidoc @@ -69,7 +69,7 @@ In the above example, there are several pieces of logistical configuration for t `rollup_index` (required):: (string) The index that you wish to store rollup results into. All the rollup data that is generated by the job will be stored in this index. When searching the rollup data, this index will be used in the <> endpoint's URL. - The rollup index be shared with other rollup jobs. The data is stored so that it doesn't interfere with unrelated jobs. + The rollup index can be shared with other rollup jobs. The data is stored so that it doesn't interfere with unrelated jobs. `cron` (required):: (string) A cron string which defines when the rollup job should be executed. The cron string defines an interval of when to run From 8df07d61cd9e0333340c096f212c9687a789fd3a Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Wed, 27 Mar 2019 09:15:55 -0500 Subject: [PATCH 21/77] Muting watcher tests for issue #35503 (#40526) * Muting watcher tests for issue #35503 * blocking the two troublsome suites --- .../xpack/watcher/actions/webhook/WebhookIntegrationTests.java | 2 ++ .../xpack/watcher/test/integration/BasicWatcherTests.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookIntegrationTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookIntegrationTests.java index 02ce97d4ea218..521cc2d49fc3f 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookIntegrationTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/webhook/WebhookIntegrationTests.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.watcher.actions.webhook; +import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.transport.TransportAddress; @@ -43,6 +44,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; +@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35503") public class WebhookIntegrationTests extends AbstractWatcherIntegrationTestCase { private MockWebServer webServer = new MockWebServer(); diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java index 2f2299d7d65e0..05d8b4ef29ded 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/BasicWatcherTests.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.watcher.test.integration; +import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; @@ -62,6 +63,7 @@ @TestLogging("org.elasticsearch.xpack.watcher:DEBUG," + "org.elasticsearch.xpack.watcher.WatcherIndexingListener:TRACE") +@LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35503") public class BasicWatcherTests extends AbstractWatcherIntegrationTestCase { public void testIndexWatch() throws Exception { From 2a025a5b05110037a8a2ea67f803d2482cae1da1 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Wed, 27 Mar 2019 10:31:23 -0400 Subject: [PATCH 22/77] Deprecate elasticsearch.yml as supported Slack config method (#40410) --- x-pack/docs/en/watcher/actions/slack.asciidoc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/docs/en/watcher/actions/slack.asciidoc b/x-pack/docs/en/watcher/actions/slack.asciidoc index ef8b907677b8c..0e9177c604d05 100644 --- a/x-pack/docs/en/watcher/actions/slack.asciidoc +++ b/x-pack/docs/en/watcher/actions/slack.asciidoc @@ -196,16 +196,20 @@ image::images/slack-add-webhook-integration.jpg[] image::images/slack-copy-webhook-url.jpg[] To configure a Slack account, at a minimum you need to specify the account -name and webhook URL in the elasticsearch keystore (se {ref}/secure-settings.html[secure settings]): +name and webhook URL in the {es} keystore (see {ref}/secure-settings.html[secure settings]): [source,shell] -------------------------------------------------- bin/elasticsearch-keystore add xpack.notification.slack.account.monitoring.secure_url -------------------------------------------------- -deprecated[You can also configure this via settings in the `elasticsearch.yml` file, using the keystore is the preferred and secure way of doing this] +[WARNING] +====== +You can no longer configure Slack accounts using `elasticsearch.yml` settings. +Please use {es}'s secure {ref}/secure-settings.html[keystore] method instead. +====== -You can also specify defaults for the {ref}/notification-settings.html#slack-account-attributes[Slack +You can specify defaults for the {ref}/notification-settings.html#slack-account-attributes[Slack notification attributes]: [source,yaml] From de4704b6bdff40c4906ea4c0c5a11a0b0317f8e9 Mon Sep 17 00:00:00 2001 From: Oghenovo Usiwoma <37949128+Eunovo@users.noreply.github.com> Date: Wed, 27 Mar 2019 08:03:09 -0700 Subject: [PATCH 23/77] Improve error message for absence of indices (#39789) "no indices exist" has been added to the error message for absence of indices --- .../metadata/IndexNameExpressionResolver.java | 20 ++++++++++++++++--- .../IndexNameExpressionResolverTests.java | 19 ++++++++++++++++++ .../validate/SimpleValidateQueryIT.java | 2 +- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java index 050d97ba54cf0..03fa790a87175 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolver.java @@ -157,10 +157,19 @@ Index[] concreteIndices(Context context, String... indexExpressions) { for (ExpressionResolver expressionResolver : expressionResolvers) { expressions = expressionResolver.resolve(context, expressions); } - + if (expressions.isEmpty()) { if (!options.allowNoIndices()) { - IndexNotFoundException infe = new IndexNotFoundException((String)null); + IndexNotFoundException infe; + if (indexExpressions.length == 1) { + if (indexExpressions[0].equals(MetaData.ALL)) { + infe = new IndexNotFoundException("no indices exist", (String)null); + } else { + infe = new IndexNotFoundException((String)null); + } + } else { + infe = new IndexNotFoundException((String)null); + } infe.setResources("index_expression", indexExpressions); throw infe; } else { @@ -173,7 +182,12 @@ Index[] concreteIndices(Context context, String... indexExpressions) { AliasOrIndex aliasOrIndex = metaData.getAliasAndIndexLookup().get(expression); if (aliasOrIndex == null ) { if (failNoIndices) { - IndexNotFoundException infe = new IndexNotFoundException(expression); + IndexNotFoundException infe; + if (expression.equals(MetaData.ALL)) { + infe = new IndexNotFoundException("no indices exist", expression); + } else { + infe = new IndexNotFoundException(expression); + } infe.setResources("index_expression", expression); throw infe; } else { diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java index 571843126f98c..228d05c51c462 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexNameExpressionResolverTests.java @@ -580,7 +580,26 @@ public void testConcreteIndicesIgnoreIndicesEmptyRequest() { assertThat(newHashSet(indexNameExpressionResolver.concreteIndexNames(context, new String[]{})), equalTo(newHashSet("kuku", "testXXX"))); } + public void testConcreteIndicesNoIndicesErrorMessage() { + MetaData.Builder mdBuilder = MetaData.builder(); + ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build(); + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, + IndicesOptions.fromOptions(false, false, true, true)); + IndexNotFoundException infe = expectThrows(IndexNotFoundException.class, + () -> indexNameExpressionResolver.concreteIndices(context, new String[]{})); + assertThat(infe.getMessage(), is("no such index [null] and no indices exist")); + } + public void testConcreteIndicesNoIndicesErrorMessageNoExpand() { + MetaData.Builder mdBuilder = MetaData.builder(); + ClusterState state = ClusterState.builder(new ClusterName("_name")).metaData(mdBuilder).build(); + IndexNameExpressionResolver.Context context = new IndexNameExpressionResolver.Context(state, + IndicesOptions.fromOptions(false, false, false, false)); + IndexNotFoundException infe = expectThrows(IndexNotFoundException.class, + () -> indexNameExpressionResolver.concreteIndices(context, new String[]{})); + assertThat(infe.getMessage(), is("no such index [_all] and no indices exist")); + } + public void testConcreteIndicesWildcardExpansion() { MetaData.Builder mdBuilder = MetaData.builder() .put(indexBuilder("testXXX").state(State.OPEN)) diff --git a/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java b/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java index d9f25a369d613..54d9a015b4e4a 100644 --- a/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java +++ b/server/src/test/java/org/elasticsearch/validate/SimpleValidateQueryIT.java @@ -160,7 +160,7 @@ public void testValidateEmptyCluster() { client().admin().indices().prepareValidateQuery().get(); fail("Expected IndexNotFoundException"); } catch (IndexNotFoundException e) { - assertThat(e.getMessage(), is("no such index [null]")); + assertThat(e.getMessage(), is("no such index [null] and no indices exist")); } } From 4cec0ae1b962ec7ea011a290aec72740386eb808 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Wed, 27 Mar 2019 17:18:14 +0200 Subject: [PATCH 24/77] SQL: MATCH and QUERY documentation; one list of functions (#40494) * Document MATCH and QUERY function predicates. * Polish the functions pages and add a list of functions to the main Functions & Operators page. --- docs/reference/sql/endpoints/jdbc.asciidoc | 3 +- docs/reference/sql/functions/aggs.asciidoc | 42 ++++--- .../sql/functions/conditional.asciidoc | 112 +++++++++--------- docs/reference/sql/functions/index.asciidoc | 103 +++++++++++++++- docs/reference/sql/functions/math.asciidoc | 64 +++++----- docs/reference/sql/functions/search.asciidoc | 112 ++++++++++++++++++ .../language/syntax/commands/select.asciidoc | 8 -- .../qa/src/main/resources/docs/docs.csv-spec | 78 ++++++++++++ 8 files changed, 407 insertions(+), 115 deletions(-) diff --git a/docs/reference/sql/endpoints/jdbc.asciidoc b/docs/reference/sql/endpoints/jdbc.asciidoc index 9b4e2fa748197..37f3d59ef6410 100644 --- a/docs/reference/sql/endpoints/jdbc.asciidoc +++ b/docs/reference/sql/endpoints/jdbc.asciidoc @@ -124,7 +124,8 @@ Query timeout (in seconds). That is the maximum amount of time waiting for a que [float] ==== Mapping -`field.multi.value.leniency` (default `true`):: Whether to be lenient and return the first value for fields with multiple values (true) or throw an exception. +`field.multi.value.leniency` (default `true`):: Whether to be lenient and return the first value (without any guarantees of what that +will be - typically the first in natural ascending order) for fields with multiple values (true) or throw an exception. [float] ==== Additional diff --git a/docs/reference/sql/functions/aggs.asciidoc b/docs/reference/sql/functions/aggs.asciidoc index c5ae050add9d7..cc0f06cb3bb5e 100644 --- a/docs/reference/sql/functions/aggs.asciidoc +++ b/docs/reference/sql/functions/aggs.asciidoc @@ -6,10 +6,12 @@ Functions for computing a _single_ result from a set of input values. {es-sql} supports aggregate functions only alongside <> (implicit or explicit). -==== General Purpose +[[sql-functions-aggs-general]] +[float] +=== General Purpose [[sql-functions-aggs-avg]] -===== `AVG` +==== `AVG` .Synopsis: [source, sql] @@ -33,7 +35,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggAvg] -------------------------------------------------- [[sql-functions-aggs-count]] -===== `COUNT` +==== `COUNT` .Synopsis: [source, sql] @@ -63,7 +65,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggCountStar] [[sql-functions-aggs-count-all]] -===== `COUNT(ALL)` +==== `COUNT(ALL)` .Synopsis: [source, sql] @@ -88,7 +90,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggCountAll] [[sql-functions-aggs-count-distinct]] -===== `COUNT(DISTINCT)` +==== `COUNT(DISTINCT)` .Synopsis: [source, sql] @@ -112,7 +114,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggCountDistinct] -------------------------------------------------- [[sql-functions-aggs-first]] -===== `FIRST/FIRST_VALUE` +==== `FIRST/FIRST_VALUE` .Synopsis: [source, sql] @@ -207,7 +209,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[firstValueWithTwoArgsAndGroupBy] the field is also <>. [[sql-functions-aggs-last]] -===== `LAST/LAST_VALUE` +==== `LAST/LAST_VALUE` .Synopsis: [source, sql] @@ -302,7 +304,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[lastValueWithTwoArgsAndGroupBy] the field is also <>. [[sql-functions-aggs-max]] -===== `MAX` +==== `MAX` .Synopsis: [source, sql] @@ -330,7 +332,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggMax] <> and therefore, it cannot be used in `HAVING` clause. [[sql-functions-aggs-min]] -===== `MIN` +==== `MIN` .Synopsis: [source, sql] @@ -358,7 +360,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggMin] <> and therefore, it cannot be used in `HAVING` clause. [[sql-functions-aggs-sum]] -===== `SUM` +==== `SUM` .Synopsis: [source, sql] @@ -381,10 +383,12 @@ Returns the sum of input values in the field `field_name`. include-tagged::{sql-specs}/docs/docs.csv-spec[aggSum] -------------------------------------------------- -==== Statistics +[[sql-functions-aggs-statistics]] +[float] +=== Statistics [[sql-functions-aggs-kurtosis]] -===== `KURTOSIS` +==== `KURTOSIS` .Synopsis: [source, sql] @@ -408,7 +412,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggKurtosis] -------------------------------------------------- [[sql-functions-aggs-mad]] -===== `MAD` +==== `MAD` .Synopsis: [source, sql] @@ -432,7 +436,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggMad] -------------------------------------------------- [[sql-functions-aggs-percentile]] -===== `PERCENTILE` +==== `PERCENTILE` .Synopsis: [source, sql] @@ -458,7 +462,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggPercentile] -------------------------------------------------- [[sql-functions-aggs-percentile-rank]] -===== `PERCENTILE_RANK` +==== `PERCENTILE_RANK` .Synopsis: [source, sql] @@ -484,7 +488,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggPercentileRank] -------------------------------------------------- [[sql-functions-aggs-skewness]] -===== `SKEWNESS` +==== `SKEWNESS` .Synopsis: [source, sql] @@ -508,7 +512,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggSkewness] -------------------------------------------------- [[sql-functions-aggs-stddev-pop]] -===== `STDDEV_POP` +==== `STDDEV_POP` .Synopsis: [source, sql] @@ -532,7 +536,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggStddevPop] -------------------------------------------------- [[sql-functions-aggs-sum-squares]] -===== `SUM_OF_SQUARES` +==== `SUM_OF_SQUARES` .Synopsis: [source, sql] @@ -556,7 +560,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[aggSumOfSquares] -------------------------------------------------- [[sql-functions-aggs-var-pop]] -===== `VAR_POP` +==== `VAR_POP` .Synopsis: [source, sql] diff --git a/docs/reference/sql/functions/conditional.asciidoc b/docs/reference/sql/functions/conditional.asciidoc index 122edf42feaab..ce8d5c3e66ced 100644 --- a/docs/reference/sql/functions/conditional.asciidoc +++ b/docs/reference/sql/functions/conditional.asciidoc @@ -45,14 +45,13 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[coalesceReturnNonNull] include-tagged::{sql-specs}/docs/docs.csv-spec[coalesceReturnNull] ---- - -[[sql-functions-conditional-ifnull]] -==== `IFNULL` +[[sql-functions-conditional-greatest]] +==== `GREATEST` .Synopsis: [source, sql] ---- -IFNULL(expression<1>, expression<2>) +GREATEST(expression<1>, expression<2>, ...) ---- *Input*: @@ -61,35 +60,39 @@ IFNULL(expression<1>, expression<2>) <2> 2nd expression +... -*Output*: 2nd expression if 1st expression is null, otherwise 1st expression. +**N**th expression + +GREATEST can take an arbitrary number of arguments and +all of them must be of the same data type. + +*Output*: one of the expressions or `null` .Description -Variant of <> with only two arguments. -Returns the first of its arguments that is not null. +Returns the argument that has the largest value which is not null. If all arguments are null, then it returns `null`. ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[ifNullReturnFirst] +include-tagged::{sql-specs}/docs/docs.csv-spec[greatestReturnNonNull] ---- ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[ifNullReturnSecond] +include-tagged::{sql-specs}/docs/docs.csv-spec[greatestReturnNull] ---- - -[[sql-functions-conditional-isnull]] -==== `ISNULL` +[[sql-functions-conditional-ifnull]] +==== `IFNULL` .Synopsis: [source, sql] ---- -ISNULL(expression<1>, expression<2>) +IFNULL(expression<1>, expression<2>) ---- *Input*: @@ -111,22 +114,22 @@ If all arguments are null, then it returns `null`. ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[isNullReturnFirst] +include-tagged::{sql-specs}/docs/docs.csv-spec[ifNullReturnFirst] ---- ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[isNullReturnSecond] +include-tagged::{sql-specs}/docs/docs.csv-spec[ifNullReturnSecond] ---- -[[sql-functions-conditional-nvl]] -==== `NVL` +[[sql-functions-conditional-isnull]] +==== `ISNULL` .Synopsis: [source, sql] ---- -NVL(expression<1>, expression<2>) +ISNULL(expression<1>, expression<2>) ---- *Input*: @@ -148,22 +151,22 @@ If all arguments are null, then it returns `null`. ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[nvlReturnFirst] +include-tagged::{sql-specs}/docs/docs.csv-spec[isNullReturnFirst] ---- ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[nvlReturnSecond] +include-tagged::{sql-specs}/docs/docs.csv-spec[isNullReturnSecond] ---- -[[sql-functions-conditional-nullif]] -==== `NULLIF` +[[sql-functions-conditional-least]] +==== `LEAST` .Synopsis: [source, sql] ---- -NULLIF(expression<1>, expression<2>) +LEAST(expression<1>, expression<2>, ...) ---- *Input*: @@ -172,33 +175,40 @@ NULLIF(expression<1>, expression<2>) <2> 2nd expression +... -*Output*: `null` if the 2 expressions are equal, otherwise the 1st expression. +**N**th expression + +LEAST can take an arbitrary number of arguments and +all of them must be of the same data type. + +*Output*: one of the expressions or `null` .Description -Returns `null` when the two input expressions are equal and -if not, it returns the 1st expression. +Returns the argument that has the smallest value which is not null. +If all arguments are null, then it returns `null`. + ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[nullIfReturnFirst] +include-tagged::{sql-specs}/docs/docs.csv-spec[leastReturnNonNull] ---- ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[nullIfReturnNull] +include-tagged::{sql-specs}/docs/docs.csv-spec[leastReturnNull] ---- -[[sql-functions-conditional-greatest]] -==== `GREATEST` +[[sql-functions-conditional-nullif]] +==== `NULLIF` .Synopsis: [source, sql] ---- -GREATEST(expression<1>, expression<2>, ...) +NULLIF(expression<1>, expression<2>) ---- *Input*: @@ -207,40 +217,33 @@ GREATEST(expression<1>, expression<2>, ...) <2> 2nd expression -... - -**N**th expression - -GREATEST can take an arbitrary number of arguments and -all of them must be of the same data type. -*Output*: one of the expressions or `null` +*Output*: `null` if the 2 expressions are equal, otherwise the 1st expression. .Description -Returns the argument that has the largest value which is not null. -If all arguments are null, then it returns `null`. - +Returns `null` when the two input expressions are equal and +if not, it returns the 1st expression. ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[greatestReturnNonNull] +include-tagged::{sql-specs}/docs/docs.csv-spec[nullIfReturnFirst] ---- ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[greatestReturnNull] +include-tagged::{sql-specs}/docs/docs.csv-spec[nullIfReturnNull] ---- -[[sql-functions-conditional-least]] -==== `LEAST` +[[sql-functions-conditional-nvl]] +==== `NVL` .Synopsis: [source, sql] ---- -LEAST(expression<1>, expression<2>, ...) +NVL(expression<1>, expression<2>) ---- *Input*: @@ -249,28 +252,25 @@ LEAST(expression<1>, expression<2>, ...) <2> 2nd expression -... - -**N**th expression - -LEAST can take an arbitrary number of arguments and -all of them must be of the same data type. -*Output*: one of the expressions or `null` +*Output*: 2nd expression if 1st expression is null, otherwise 1st expression. .Description -Returns the argument that has the smallest value which is not null. +Variant of <> with only two arguments. +Returns the first of its arguments that is not null. If all arguments are null, then it returns `null`. ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[leastReturnNonNull] +include-tagged::{sql-specs}/docs/docs.csv-spec[nvlReturnFirst] ---- ["source","sql",subs="attributes,callouts,macros"] ---- -include-tagged::{sql-specs}/docs/docs.csv-spec[leastReturnNull] +include-tagged::{sql-specs}/docs/docs.csv-spec[nvlReturnSecond] ---- + + diff --git a/docs/reference/sql/functions/index.asciidoc b/docs/reference/sql/functions/index.asciidoc index 94b5f767f86f9..4848fffebae71 100644 --- a/docs/reference/sql/functions/index.asciidoc +++ b/docs/reference/sql/functions/index.asciidoc @@ -7,14 +7,115 @@ * <> * <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> +** <> * <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> +** <> +** <> +** <> * <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> -* <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +* <> +** <> +** <> * <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> +** <> +** <> include::operators.asciidoc[] include::aggs.asciidoc[] diff --git a/docs/reference/sql/functions/math.asciidoc b/docs/reference/sql/functions/math.asciidoc index 895ba077b0961..a520a89ebaa2f 100644 --- a/docs/reference/sql/functions/math.asciidoc +++ b/docs/reference/sql/functions/math.asciidoc @@ -6,10 +6,12 @@ All math and trigonometric functions require their input (where applicable) to be numeric. -==== Generic +[[sql-functions-math-generic]] +[float] +=== Generic [[sql-functions-math-abs]] -===== `ABS` +==== `ABS` .Synopsis: [source, sql] @@ -33,7 +35,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[abs] -------------------------------------------------- [[sql-functions-math-cbrt]] -===== `CBRT` +==== `CBRT` .Synopsis: [source, sql] @@ -57,7 +59,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineCbrtWithNegativeValue] -------------------------------------------------- [[sql-functions-math-ceil]] -===== `CEIL/CEILING` +==== `CEIL/CEILING` .Synopsis: [source, sql] @@ -81,7 +83,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineCeiling] -------------------------------------------------- [[sql-functions-math-e]] -===== `E` +==== `E` .Synopsis: [source, sql] @@ -103,7 +105,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathEulersNumber] -------------------------------------------------- [[sql-functions-math-exp]] -===== `EXP` +==== `EXP` .Synopsis: [source, sql] @@ -127,7 +129,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathExpInline] -------------------------------------------------- [[sql-functions-math-expm1]] -===== `EXPM1` +==== `EXPM1` .Synopsis: [source, sql] @@ -151,7 +153,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathExpm1Inline] -------------------------------------------------- [[sql-functions-math-floor]] -===== `FLOOR` +==== `FLOOR` .Synopsis: [source, sql] @@ -175,7 +177,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineFloor] -------------------------------------------------- [[sql-functions-math-log]] -===== `LOG` +==== `LOG` .Synopsis: [source, sql] @@ -199,7 +201,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineLog] -------------------------------------------------- [[sql-functions-math-log10]] -===== `LOG10` +==== `LOG10` .Synopsis: [source, sql] @@ -223,7 +225,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineLog10] -------------------------------------------------- [[sql-functions-math-pi]] -===== `PI` +==== `PI` .Synopsis: [source, sql] @@ -245,7 +247,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathPINumber] -------------------------------------------------- [[sql-functions-math-power]] -===== `POWER` +==== `POWER` .Synopsis: [source, sql] @@ -275,7 +277,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlinePowerNegative] -------------------------------------------------- [[sql-functions-math-random]] -===== `RANDOM/RAND` +==== `RANDOM/RAND` .Synopsis: [source, sql] @@ -299,7 +301,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathRandom] -------------------------------------------------- [[sql-functions-math-round]] -===== `ROUND` +==== `ROUND` .Synopsis: [source, sql] @@ -330,7 +332,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathRoundWithNegativeParameter] -------------------------------------------------- [[sql-functions-math-sign]] -===== `SIGN/SIGNUM` +==== `SIGN/SIGNUM` .Synopsis: [source, sql] @@ -355,7 +357,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineSign] [[sql-functions-math-sqrt]] -===== `SQRT` +==== `SQRT` .Synopsis: [source, sql] @@ -379,7 +381,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineSqrt] -------------------------------------------------- [[sql-functions-math-truncate]] -===== `TRUNCATE` +==== `TRUNCATE` .Synopsis: [source, sql] @@ -409,10 +411,12 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathTruncateWithPositiveParameter include-tagged::{sql-specs}/docs/docs.csv-spec[mathTruncateWithNegativeParameter] -------------------------------------------------- -==== Trigonometric +[[sql-functions-math-trigonometric]] +[float] +=== Trigonometric [[sql-functions-math-acos]] -===== `ACOS` +==== `ACOS` .Synopsis: [source, sql] @@ -436,7 +440,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineAcos] -------------------------------------------------- [[sql-functions-math-asin]] -===== `ASIN` +==== `ASIN` .Synopsis: [source, sql] @@ -460,7 +464,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineAsin] -------------------------------------------------- [[sql-functions-math-atan]] -===== `ATAN` +==== `ATAN` .Synopsis: [source, sql] @@ -484,7 +488,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineAtan] -------------------------------------------------- [[sql-functions-math-atan2]] -===== `ATAN2` +==== `ATAN2` .Synopsis: [source, sql] @@ -509,7 +513,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineAtan2] -------------------------------------------------- [[sql-functions-math-cos]] -===== `COS` +==== `COS` .Synopsis: [source, sql] @@ -533,7 +537,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineCosine] -------------------------------------------------- [[sql-functions-math-cosh]] -===== `COSH` +==== `COSH` .Synopsis: [source, sql] @@ -557,7 +561,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineCosh] -------------------------------------------------- [[sql-functions-math-cot]] -===== `COT` +==== `COT` .Synopsis: [source, sql] @@ -581,7 +585,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineCotangent] -------------------------------------------------- [[sql-functions-math-degrees]] -===== `DEGREES` +==== `DEGREES` .Synopsis: [source, sql] @@ -606,7 +610,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineDegrees] -------------------------------------------------- [[sql-functions-math-radians]] -===== `RADIANS` +==== `RADIANS` .Synopsis: [source, sql] @@ -631,7 +635,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineRadians] -------------------------------------------------- [[sql-functions-math-sin]] -===== `SIN` +==== `SIN` .Synopsis: [source, sql] @@ -655,7 +659,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineSine] -------------------------------------------------- [[sql-functions-math-sinh]] -===== `SINH` +==== `SINH` .Synopsis: [source, sql] @@ -679,7 +683,7 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[mathInlineSinh] -------------------------------------------------- [[sql-functions-math-tan]] -===== `TAN` +==== `TAN` .Synopsis: [source, sql] diff --git a/docs/reference/sql/functions/search.asciidoc b/docs/reference/sql/functions/search.asciidoc index 295fbfd220770..210be00e3034b 100644 --- a/docs/reference/sql/functions/search.asciidoc +++ b/docs/reference/sql/functions/search.asciidoc @@ -8,6 +8,118 @@ when the `MATCH` or `QUERY` predicates are being used. Outside a, so-called, search context, these functions will return default values such as `0` or `NULL`. +[[sql-functions-search-match]] +==== `MATCH` + +.Synopsis: +[source, sql] +-------------------------------------------------- +MATCH(field_exp<1>, constant_exp<2>[, options]<3>) +-------------------------------------------------- + +*Input*: + +<1> field(s) to match +<2> matching text +<3> additional parameters; optional + +.Description: + +A full-text search option, in the form of a predicate, available in {es-sql} that gives the user control over powerful <> +and <> {es} queries. + +The first parameter is the field or fields to match against. In case it receives one value only, {es-sql} will use a `match` query to perform the search: + +["source","sql",subs="attributes,callouts,macros"] +---- +include-tagged::{sql-specs}/docs/docs.csv-spec[simpleMatch] +---- + +However, it can also receive a list of fields and their corresponding optional `boost` value. In this case, {es-sql} will use a +`multi_match` query to match the documents: + +["source","sql",subs="attributes,callouts,macros"] +---- +include-tagged::{sql-specs}/docs/docs.csv-spec[multiFieldsMatch] +---- + +NOTE: The `multi_match` query in {es} has the option of <> that gives preferential weight +(in terms of scoring) to fields being searched in, using the `^` character. In the example above, the `name` field has a greater weight in +the final score than the `author` field when searching for `frank dune` text in both of them. + +Both options above can be used in combination with the optional third parameter of the `MATCH()` predicate, where one can specify +additional configuration parameters (separated by semicolon `;`) for either `match` or `multi_match` queries. For example: + +["source","sql",subs="attributes,callouts,macros"] +---- +include-tagged::{sql-specs}/docs/docs.csv-spec[optionalParamsForMatch] +---- + +In the more advanced example above, the `cutoff_frequency` parameter allows specifying an absolute or relative document frequency where +high frequency terms are moved into an optional subquery and are only scored if one of the low frequency (below the cutoff) terms in the +case of an `or` operator or all of the low frequency terms in the case of an `and` operator match. More about this you can find in the +<> page. + +NOTE: The allowed optional parameters for a single-field `MATCH()` variant (for the `match` {es} query) are: `analyzer`, `auto_generate_synonyms_phrase_query`, +`cutoff_frequency`, `lenient`, `fuzzy_transpositions`, `fuzzy_rewrite`, `minimum_should_match`, `operator`, +`max_expansions`, `prefix_length`. + +NOTE: The allowed optional parameters for a multi-field `MATCH()` variant (for the `multi_match` {es} query) are: `analyzer`, `auto_generate_synonyms_phrase_query`, +`cutoff_frequency`, `lenient`, `fuzzy_transpositions`, `fuzzy_rewrite`, `minimum_should_match`, `operator`, +`max_expansions`, `prefix_length`, `slop`, `tie_breaker`, `type`. + + +[[sql-functions-search-query]] +==== `QUERY` + +.Synopsis: +[source, sql] +-------------------------------------------------- +QUERY(constant_exp<1>[, options]<2>) +-------------------------------------------------- + +*Input*: + +<1> query text +<2> additional parameters; optional + +.Description: + +Just like `MATCH`, `QUERY` is a full-text search predicate that gives the user control over the <> query in {es}. + +The first parameter is basically the input that will be passed as is to the `query_string` query, which means that anything that `query_string` +accepts in its `query` field can be used here as well: + +["source","sql",subs="attributes,callouts,macros"] +---- +include-tagged::{sql-specs}/docs/docs.csv-spec[simpleQueryQuery] +---- + +A more advanced example, showing more of the features that `query_string` supports, of course possible with {es-sql}: + +["source","sql",subs="attributes,callouts,macros"] +---- +include-tagged::{sql-specs}/docs/docs.csv-spec[advancedQueryQuery] +---- + +The query above uses the `_exists_` query to select documents that have values in the `author` field, a range query for `page_count` and +regex and fuzziness queries for the `name` field. + +If one needs to customize various configuration options that `query_string` exposes, this can be done using the second _optional_ parameter. +Multiple settings can be specified separated by a semicolon `;`: + +["source","sql",subs="attributes,callouts,macros"] +---- +include-tagged::{sql-specs}/docs/docs.csv-spec[optionalParameterQuery] +---- + +NOTE: The allowed optional parameters for `QUERY()` are: `allow_leading_wildcard`, `analyze_wildcard`, `analyzer`, +`auto_generate_synonyms_phrase_query`, `default_field`, `default_operator`, `enable_position_increments`, +`escape`, `fuzzy_max_expansions`, `fuzzy_prefix_length`, `fuzzy_rewrite`, `fuzzy_transpositions`, `lenient`, +`locale`, `lowercase_expanded_terms`, `max_determinized_states`, `minimum_should_match`, `phrase_slop`, `rewrite`, +`quote_analyzer`, `quote_field_suffix`, `tie_breaker`, `time_zone`, `type`. + + [[sql-functions-search-score]] ==== `SCORE` diff --git a/docs/reference/sql/language/syntax/commands/select.asciidoc b/docs/reference/sql/language/syntax/commands/select.asciidoc index 8e1715d98b15e..26fdb2f337ebc 100644 --- a/docs/reference/sql/language/syntax/commands/select.asciidoc +++ b/docs/reference/sql/language/syntax/commands/select.asciidoc @@ -71,7 +71,6 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[wildcardWithOrder] which essentially returns all(top-level fields, sub-fields, such as multi-fields are ignored] columns found. [[sql-syntax-from]] -[float] ==== FROM Clause The `FROM` clause specifies one table for the `SELECT` and has the following syntax: @@ -111,7 +110,6 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[fromTableAlias] ---- [[sql-syntax-where]] -[float] ==== WHERE Clause The optional `WHERE` clause is used to filter rows from the query and has the following syntax: @@ -133,7 +131,6 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[basicWhere] ---- [[sql-syntax-group-by]] -[float] ==== GROUP BY The `GROUP BY` clause is used to divide the results into groups of rows on matching values from the designated columns. It has the following syntax: @@ -208,7 +205,6 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[groupByAndMultipleAggs] ---- [[sql-syntax-group-by-implicit]] -[float] ===== Implicit Grouping When an aggregation is used without an associated `GROUP BY`, an __implicit grouping__ is applied, meaning all selected rows are considered to form a single default, or implicit group. @@ -229,7 +225,6 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[groupByImplicitMultipleAggs] ---- [[sql-syntax-having]] -[float] ==== HAVING The `HAVING` clause can be used _only_ along aggregate functions (and thus `GROUP BY`) to filter what groups are kept or not and has the following syntax: @@ -263,7 +258,6 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[groupByHavingMultiple] ---- [[sql-syntax-having-group-by-implicit]] -[float] ===== Implicit Grouping As indicated above, it is possible to have a `HAVING` clause without a `GROUP BY`. In this case, the so-called <> is applied, meaning all selected rows are considered to form a single group and `HAVING` can be applied on any of the aggregate functions specified on this group. @@ -285,7 +279,6 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[groupByHavingImplicitMatch] [[sql-syntax-order-by]] -[float] ==== ORDER BY The `ORDER BY` clause is used to sort the results of `SELECT` by one or more expressions: @@ -373,7 +366,6 @@ Trying to return `score` from a non full-text query will return the same value f all are equally relevant. [[sql-syntax-limit]] -[float] ==== LIMIT The `LIMIT` clause restricts (limits) the number of rows returns using the format: diff --git a/x-pack/plugin/sql/qa/src/main/resources/docs/docs.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/docs/docs.csv-spec index 41a82ac0f84bb..820c358ab2f62 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/docs/docs.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/docs/docs.csv-spec @@ -929,6 +929,84 @@ null |25324 // end::orderByAgg ; +simpleMatch +// tag::simpleMatch +SELECT author, name FROM library WHERE MATCH(author, 'frank'); + + author | name +---------------+------------------- +Frank Herbert |Dune +Frank Herbert |Dune Messiah +Frank Herbert |Children of Dune +Frank Herbert |God Emperor of Dune + +// end::simpleMatch +; + +multiFieldsMatch +// tag::multiFieldsMatch +SELECT author, name, SCORE() FROM library WHERE MATCH('author^2,name^5', 'frank dune'); + + author | name | SCORE() +---------------+-------------------+--------------- +Frank Herbert |Dune |11.443176 +Frank Herbert |Dune Messiah |9.446629 +Frank Herbert |Children of Dune |8.043278 +Frank Herbert |God Emperor of Dune|7.0029488 + +// end::multiFieldsMatch +; + +optionalParamsForMatch +// tag::optionalParamsForMatch +SELECT author, name, SCORE() FROM library WHERE MATCH(name, 'to the star', 'operator=or;cutoff_frequency=0.2'); + + author | name | SCORE() +-----------------+------------------------------------+--------------- +Peter F. Hamilton|Pandora's Star |3.0997515 +Douglas Adams |The Hitchhiker's Guide to the Galaxy|3.1756816 + +// end::optionalParamsForMatch +; + +simpleQueryQuery +// tag::simpleQueryQuery +SELECT author, name, SCORE() FROM library WHERE QUERY('name:dune'); + + author | name | SCORE() +---------------+-------------------+--------------- +Frank Herbert |Dune |2.2886353 +Frank Herbert |Dune Messiah |1.8893257 +Frank Herbert |Children of Dune |1.6086556 +Frank Herbert |God Emperor of Dune|1.4005898 +// end::simpleQueryQuery +; + +advancedQueryQuery +// tag::advancedQueryQuery +SELECT author, name, page_count, SCORE() FROM library WHERE QUERY('_exists_:"author" AND page_count:>200 AND (name:/star.*/ OR name:duna~)'); + + author | name | page_count | SCORE() +------------------+-------------------+---------------+--------------- +Frank Herbert |Dune |604 |3.7164764 +Frank Herbert |Dune Messiah |331 |3.4169943 +Frank Herbert |Children of Dune |408 |3.2064917 +Frank Herbert |God Emperor of Dune|454 |3.0504425 +Peter F. Hamilton |Pandora's Star |768 |3.0 +Robert A. Heinlein|Starship Troopers |335 |3.0 +// end::advancedQueryQuery +; + +optionalParameterQuery +// tag::optionalParameterQuery +SELECT author, name, SCORE() FROM library WHERE QUERY('dune god', 'default_operator=and;default_field=name'); + + author | name | SCORE() +---------------+-------------------+--------------- +Frank Herbert |God Emperor of Dune|3.6984892 +// end::optionalParameterQuery +; + orderByScore // tag::orderByScore SELECT SCORE(), * FROM library WHERE MATCH(name, 'dune') ORDER BY SCORE() DESC; From 7d2d140c6381ddd2fe1edfca431ffce3846edf57 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 27 Mar 2019 15:19:04 +0000 Subject: [PATCH 25/77] Get node ID from nodes info in REST tests (#40052) * Get master identity from nodes info in REST tests We discussed recently that the cluster state API should be considered "internal" and therefore our usual cast-iron stability guarantees do not hold for this API. However, there are a good number of REST tests that need to identify the master node. Today they call `GET /_cluster/state` API and extract the master node ID from the response. An alternative that avoids the unstable cluster state API is to call `GET _nodes/_master`: the master node ID is the unique key in the `nodes` map in the response. This change adds the ability for YAML-based REST tests to extract the unique key of a singleton map so that they can obtain the master node ID from the nodes info API instead of the cluster state API. * Allow access to an arbitrary node ID and adjust all YAML tests to use this * Warnings --- .../cluster.allocation_explain/10_basic.yml | 8 - .../test/cluster.reroute/11_explain.yml | 10 +- .../test/indices.shrink/10_basic.yml | 18 +- .../test/indices.shrink/20_source_mapping.yml | 12 +- .../test/indices.shrink/30_copy_settings.yml | 16 +- .../test/indices.split/30_copy_settings.yml | 16 +- .../test/nodes.info/10_basic.yml | 13 +- .../test/nodes.info/20_transport.yml | 10 +- .../test/nodes.info/30_settings.yml | 13 +- .../test/nodes.stats/10_basic.yml | 11 +- .../test/nodes.stats/11_indices_metrics.yml | 308 +++++++++--------- .../nodes.stats/20_response_filtering.yml | 198 +++++------ .../test/nodes.stats/30_discovery.yml | 12 +- .../rest-api-spec/test/tasks.get/10_basic.yml | 3 - .../test/tasks.list/10_basic.yml | 12 +- .../test/rest/yaml/Features.java | 3 +- .../test/rest/yaml/ObjectPath.java | 14 +- .../test/rest/yaml/ObjectPathTests.java | 51 +++ 18 files changed, 404 insertions(+), 324 deletions(-) diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yml index b8c922c98c15b..732a53aeea4f8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.allocation_explain/10_basic.yml @@ -12,10 +12,6 @@ - match: { acknowledged: true } - - do: - cluster.state: - metric: [ master_node ] - - do: cluster.allocation_explain: body: { "index": "test", "shard": 0, "primary": true } @@ -37,10 +33,6 @@ index: test body: { "settings": { "index.number_of_shards": 1, "index.number_of_replicas": 9 } } - - do: - cluster.state: - metric: [ master_node ] - - do: cluster.allocation_explain: include_disk_info: true diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yml index 5419acb9321f4..248b47d07a71e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/cluster.reroute/11_explain.yml @@ -25,12 +25,14 @@ setup: --- "Explain API for non-existent node & shard": + - skip: + features: [arbitrary_key] - do: - cluster.state: - metric: [ master_node ] - - - set: {master_node: node_id} + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id - do: cluster.reroute: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml index a6d6bb0730548..41c851b71cc6c 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/10_basic.yml @@ -3,18 +3,20 @@ - skip: version: " - 6.9.99" reason: expects warnings that pre-7.0.0 will not send - features: "warnings" - # creates an index with one document solely allocated on the master node + features: [warnings, arbitrary_key] + + # creates an index with one document solely allocated on a particular data node # and shrinks it into a new index with a single shard # we don't do the relocation to a single node after the index is created # here since in a mixed version cluster we can't identify # which node is the one with the highest version and that is the only one that can safely # be used to shrink the index. - - do: - cluster.state: {} - # Get master node id - - set: { master_node: master } + - do: + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id - do: indices.create: @@ -22,8 +24,8 @@ wait_for_active_shards: 1 body: settings: - # ensure everything is allocated on a single node - index.routing.allocation.include._id: $master + # ensure everything is allocated on the same data node + index.routing.allocation.include._id: $node_id index.number_of_shards: 2 index.number_of_replicas: 0 - do: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml index f12864236d7bd..dec0760fc6b19 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/20_source_mapping.yml @@ -3,13 +3,13 @@ - skip: version: " - 6.9.99" reason: expects warnings that pre-7.0.0 will not send - features: "warnings" + features: [warnings, arbitrary_key] - do: - cluster.state: {} - # Get master node id - - - set: { master_node: master } + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id # create index - do: @@ -19,7 +19,7 @@ body: settings: # ensure everything is allocated on a single node - index.routing.allocation.include._id: $master + index.routing.allocation.include._id: $node_id index.number_of_shards: 2 index.number_of_replicas: 0 mappings: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml index 3add4b100d812..eda095ff91f98 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.shrink/30_copy_settings.yml @@ -3,13 +3,13 @@ - skip: version: " - 6.9.99" reason: expects warnings that pre-7.0.0 will not send - features: "warnings" + features: [warnings, arbitrary_key] - do: - cluster.state: {} - - # get master node id - - set: { master_node: master } + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id - do: indices.create: @@ -17,8 +17,8 @@ wait_for_active_shards: 1 body: settings: - # ensure everything is allocated on the master node - index.routing.allocation.include._id: $master + # ensure everything is allocated on the same node + index.routing.allocation.include._id: $node_id index.number_of_shards: 2 index.number_of_replicas: 0 index.merge.scheduler.max_merge_count: 4 @@ -60,4 +60,4 @@ - match: { copy-settings-target.settings.index.merge.scheduler.max_merge_count: "4" } - match: { copy-settings-target.settings.index.merge.scheduler.max_thread_count: "2" } - match: { copy-settings-target.settings.index.blocks.write: "true" } - - match: { copy-settings-target.settings.index.routing.allocation.include._id: $master } + - match: { copy-settings-target.settings.index.routing.allocation.include._id: $node_id } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml index 8cf932b1c1159..df9eae0adf340 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.split/30_copy_settings.yml @@ -3,13 +3,13 @@ - skip: version: " - 6.9.99" reason: expects warnings that pre-7.0.0 will not send - features: "warnings" + features: [arbitrary_key, warnings] - do: - cluster.state: {} - - # get master node id - - set: { master_node: master } + nodes.info: + node_id: data:true + - set: + nodes._arbitrary_key_: node_id - do: indices.create: @@ -17,8 +17,8 @@ wait_for_active_shards: 1 body: settings: - # ensure everything is allocated on the master node - index.routing.allocation.include._id: $master + # ensure everything is allocated on the same node + index.routing.allocation.include._id: $node_id index.number_of_replicas: 0 index.number_of_shards: 1 index.number_of_routing_shards: 4 @@ -62,4 +62,4 @@ - match: { copy-settings-target.settings.index.merge.scheduler.max_merge_count: "4" } - match: { copy-settings-target.settings.index.merge.scheduler.max_thread_count: "2" } - match: { copy-settings-target.settings.index.blocks.write: "true" } - - match: { copy-settings-target.settings.index.routing.allocation.include._id: $master } + - match: { copy-settings-target.settings.index.routing.allocation.include._id: $node_id } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yml index 47f6c3e21141a..5821117f4c005 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/10_basic.yml @@ -1,14 +1,13 @@ +setup: + - skip: + features: [arbitrary_key] --- "node_info test": - - do: - cluster.state: {} - - # Get master node id - - set: { master_node: master } - - do: nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - is_true: nodes - is_true: cluster_name - - is_true: nodes.$master.roles + - is_true: nodes.$node_id.roles diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yml index efd2260356a2d..09102157bcb99 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/20_transport.yml @@ -2,15 +2,15 @@ "node_info test profile is empty": - skip: - features: stash_in_path + features: [stash_in_path, arbitrary_key] - do: - cluster.state: {} - - - set: {master_node: master} + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.info: metric: [ transport ] - - is_true: nodes.$master.transport.profiles + - is_true: nodes.$node_id.transport.profiles diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yml index a63c246b6033e..99b8b6f361a47 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.info/30_settings.yml @@ -1,19 +1,22 @@ --- "node_info test flat_settings": - - do: - cluster.state: {} + - skip: + features: [arbitrary_key] - - set: { master_node: master } + - do: + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.info: metric: [ settings ] - - match : { nodes.$master.settings.client.type: node } + - match : { nodes.$node_id.settings.client.type: node } - do: nodes.info: metric: [ settings ] flat_settings: true - - match : { nodes.$master.settings.client\.type: node } + - match : { nodes.$node_id.settings.client\.type: node } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yml index 61614e7f8e1b7..099483be9aded 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/10_basic.yml @@ -9,17 +9,20 @@ --- "Nodes stats level": - - do: - cluster.state: {} + - skip: + features: [arbitrary_key] - - set: { master_node: master } + - do: + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: metric: [ indices ] level: "indices" - - is_true: nodes.$master.indices.indices + - is_true: nodes.$node_id.indices.indices --- "Nodes stats unrecognized parameter": diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml index 998909dd9cf1b..a09619b7255c3 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/11_indices_metrics.yml @@ -1,211 +1,227 @@ --- "Metric - blank": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: {} - - is_true: nodes.$master.indices.docs - - is_true: nodes.$master.indices.store - - is_true: nodes.$master.indices.indexing - - is_true: nodes.$master.indices.get - - is_true: nodes.$master.indices.search - - is_true: nodes.$master.indices.merges - - is_true: nodes.$master.indices.refresh - - is_true: nodes.$master.indices.flush - - is_true: nodes.$master.indices.warmer - - is_true: nodes.$master.indices.query_cache - - is_true: nodes.$master.indices.fielddata - - is_true: nodes.$master.indices.completion - - is_true: nodes.$master.indices.segments - - is_true: nodes.$master.indices.translog - - is_true: nodes.$master.indices.recovery + - is_true: nodes.$node_id.indices.docs + - is_true: nodes.$node_id.indices.store + - is_true: nodes.$node_id.indices.indexing + - is_true: nodes.$node_id.indices.get + - is_true: nodes.$node_id.indices.search + - is_true: nodes.$node_id.indices.merges + - is_true: nodes.$node_id.indices.refresh + - is_true: nodes.$node_id.indices.flush + - is_true: nodes.$node_id.indices.warmer + - is_true: nodes.$node_id.indices.query_cache + - is_true: nodes.$node_id.indices.fielddata + - is_true: nodes.$node_id.indices.completion + - is_true: nodes.$node_id.indices.segments + - is_true: nodes.$node_id.indices.translog + - is_true: nodes.$node_id.indices.recovery --- "Metric - _all": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: { metric: _all } - - is_true: nodes.$master.indices.docs - - is_true: nodes.$master.indices.store - - is_true: nodes.$master.indices.indexing - - is_true: nodes.$master.indices.get - - is_true: nodes.$master.indices.search - - is_true: nodes.$master.indices.merges - - is_true: nodes.$master.indices.refresh - - is_true: nodes.$master.indices.flush - - is_true: nodes.$master.indices.warmer - - is_true: nodes.$master.indices.query_cache - - is_true: nodes.$master.indices.fielddata - - is_true: nodes.$master.indices.completion - - is_true: nodes.$master.indices.segments - - is_true: nodes.$master.indices.translog - - is_true: nodes.$master.indices.recovery + - is_true: nodes.$node_id.indices.docs + - is_true: nodes.$node_id.indices.store + - is_true: nodes.$node_id.indices.indexing + - is_true: nodes.$node_id.indices.get + - is_true: nodes.$node_id.indices.search + - is_true: nodes.$node_id.indices.merges + - is_true: nodes.$node_id.indices.refresh + - is_true: nodes.$node_id.indices.flush + - is_true: nodes.$node_id.indices.warmer + - is_true: nodes.$node_id.indices.query_cache + - is_true: nodes.$node_id.indices.fielddata + - is_true: nodes.$node_id.indices.completion + - is_true: nodes.$node_id.indices.segments + - is_true: nodes.$node_id.indices.translog + - is_true: nodes.$node_id.indices.recovery --- "Metric - indices _all": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: { metric: indices, index_metric: _all } - - is_true: nodes.$master.indices.docs - - is_true: nodes.$master.indices.store - - is_true: nodes.$master.indices.indexing - - is_true: nodes.$master.indices.get - - is_true: nodes.$master.indices.search - - is_true: nodes.$master.indices.merges - - is_true: nodes.$master.indices.refresh - - is_true: nodes.$master.indices.flush - - is_true: nodes.$master.indices.warmer - - is_true: nodes.$master.indices.query_cache - - is_true: nodes.$master.indices.fielddata - - is_true: nodes.$master.indices.completion - - is_true: nodes.$master.indices.segments - - is_true: nodes.$master.indices.translog - - is_true: nodes.$master.indices.recovery + - is_true: nodes.$node_id.indices.docs + - is_true: nodes.$node_id.indices.store + - is_true: nodes.$node_id.indices.indexing + - is_true: nodes.$node_id.indices.get + - is_true: nodes.$node_id.indices.search + - is_true: nodes.$node_id.indices.merges + - is_true: nodes.$node_id.indices.refresh + - is_true: nodes.$node_id.indices.flush + - is_true: nodes.$node_id.indices.warmer + - is_true: nodes.$node_id.indices.query_cache + - is_true: nodes.$node_id.indices.fielddata + - is_true: nodes.$node_id.indices.completion + - is_true: nodes.$node_id.indices.segments + - is_true: nodes.$node_id.indices.translog + - is_true: nodes.$node_id.indices.recovery --- "Metric - one": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: { metric: indices, index_metric: docs } - - is_true: nodes.$master.indices.docs - - is_false: nodes.$master.indices.store - - is_false: nodes.$master.indices.indexing - - is_false: nodes.$master.indices.get - - is_false: nodes.$master.indices.search - - is_false: nodes.$master.indices.merges - - is_false: nodes.$master.indices.refresh - - is_false: nodes.$master.indices.flush - - is_false: nodes.$master.indices.warmer - - is_false: nodes.$master.indices.query_cache - - is_false: nodes.$master.indices.fielddata - - is_false: nodes.$master.indices.completion - - is_false: nodes.$master.indices.segments - - is_false: nodes.$master.indices.translog - - is_false: nodes.$master.indices.recovery + - is_true: nodes.$node_id.indices.docs + - is_false: nodes.$node_id.indices.store + - is_false: nodes.$node_id.indices.indexing + - is_false: nodes.$node_id.indices.get + - is_false: nodes.$node_id.indices.search + - is_false: nodes.$node_id.indices.merges + - is_false: nodes.$node_id.indices.refresh + - is_false: nodes.$node_id.indices.flush + - is_false: nodes.$node_id.indices.warmer + - is_false: nodes.$node_id.indices.query_cache + - is_false: nodes.$node_id.indices.fielddata + - is_false: nodes.$node_id.indices.completion + - is_false: nodes.$node_id.indices.segments + - is_false: nodes.$node_id.indices.translog + - is_false: nodes.$node_id.indices.recovery --- "Metric - multi": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: { metric: indices, index_metric: [ store, get, merge ] } - - is_false: nodes.$master.indices.docs - - is_true: nodes.$master.indices.store - - is_false: nodes.$master.indices.indexing - - is_true: nodes.$master.indices.get - - is_false: nodes.$master.indices.search - - is_true: nodes.$master.indices.merges - - is_false: nodes.$master.indices.refresh - - is_false: nodes.$master.indices.flush - - is_false: nodes.$master.indices.warmer - - is_false: nodes.$master.indices.query_cache - - is_false: nodes.$master.indices.fielddata - - is_false: nodes.$master.indices.completion - - is_false: nodes.$master.indices.segments - - is_false: nodes.$master.indices.translog - - is_false: nodes.$master.indices.recovery + - is_false: nodes.$node_id.indices.docs + - is_true: nodes.$node_id.indices.store + - is_false: nodes.$node_id.indices.indexing + - is_true: nodes.$node_id.indices.get + - is_false: nodes.$node_id.indices.search + - is_true: nodes.$node_id.indices.merges + - is_false: nodes.$node_id.indices.refresh + - is_false: nodes.$node_id.indices.flush + - is_false: nodes.$node_id.indices.warmer + - is_false: nodes.$node_id.indices.query_cache + - is_false: nodes.$node_id.indices.fielddata + - is_false: nodes.$node_id.indices.completion + - is_false: nodes.$node_id.indices.segments + - is_false: nodes.$node_id.indices.translog + - is_false: nodes.$node_id.indices.recovery --- "Metric - recovery": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: { metric: indices, index_metric: [ recovery ] } - - is_false: nodes.$master.indices.docs - - is_false: nodes.$master.indices.store - - is_false: nodes.$master.indices.indexing - - is_false: nodes.$master.indices.get - - is_false: nodes.$master.indices.search - - is_false: nodes.$master.indices.merges - - is_false: nodes.$master.indices.refresh - - is_false: nodes.$master.indices.flush - - is_false: nodes.$master.indices.warmer - - is_false: nodes.$master.indices.query_cache - - is_false: nodes.$master.indices.fielddata - - is_false: nodes.$master.indices.completion - - is_false: nodes.$master.indices.segments - - is_false: nodes.$master.indices.translog - - is_true: nodes.$master.indices.recovery + - is_false: nodes.$node_id.indices.docs + - is_false: nodes.$node_id.indices.store + - is_false: nodes.$node_id.indices.indexing + - is_false: nodes.$node_id.indices.get + - is_false: nodes.$node_id.indices.search + - is_false: nodes.$node_id.indices.merges + - is_false: nodes.$node_id.indices.refresh + - is_false: nodes.$node_id.indices.flush + - is_false: nodes.$node_id.indices.warmer + - is_false: nodes.$node_id.indices.query_cache + - is_false: nodes.$node_id.indices.fielddata + - is_false: nodes.$node_id.indices.completion + - is_false: nodes.$node_id.indices.segments + - is_false: nodes.$node_id.indices.translog + - is_true: nodes.$node_id.indices.recovery --- "Metric - _all include_segment_file_sizes": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: { metric: indices, index_metric: _all, include_segment_file_sizes: true } - - is_true: nodes.$master.indices.docs - - is_true: nodes.$master.indices.store - - is_true: nodes.$master.indices.indexing - - is_true: nodes.$master.indices.get - - is_true: nodes.$master.indices.search - - is_true: nodes.$master.indices.merges - - is_true: nodes.$master.indices.refresh - - is_true: nodes.$master.indices.flush - - is_true: nodes.$master.indices.warmer - - is_true: nodes.$master.indices.query_cache - - is_true: nodes.$master.indices.fielddata - - is_true: nodes.$master.indices.completion - - is_true: nodes.$master.indices.segments - - is_true: nodes.$master.indices.translog - - is_true: nodes.$master.indices.recovery - - is_true: nodes.$master.indices.segments.file_sizes + - is_true: nodes.$node_id.indices.docs + - is_true: nodes.$node_id.indices.store + - is_true: nodes.$node_id.indices.indexing + - is_true: nodes.$node_id.indices.get + - is_true: nodes.$node_id.indices.search + - is_true: nodes.$node_id.indices.merges + - is_true: nodes.$node_id.indices.refresh + - is_true: nodes.$node_id.indices.flush + - is_true: nodes.$node_id.indices.warmer + - is_true: nodes.$node_id.indices.query_cache + - is_true: nodes.$node_id.indices.fielddata + - is_true: nodes.$node_id.indices.completion + - is_true: nodes.$node_id.indices.segments + - is_true: nodes.$node_id.indices.translog + - is_true: nodes.$node_id.indices.recovery + - is_true: nodes.$node_id.indices.segments.file_sizes --- "Metric - segments include_segment_file_sizes": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: nodes.stats: { metric: indices, index_metric: segments, include_segment_file_sizes: true } - - is_false: nodes.$master.indices.docs - - is_false: nodes.$master.indices.store - - is_false: nodes.$master.indices.indexing - - is_false: nodes.$master.indices.get - - is_false: nodes.$master.indices.search - - is_false: nodes.$master.indices.merges - - is_false: nodes.$master.indices.refresh - - is_false: nodes.$master.indices.flush - - is_false: nodes.$master.indices.warmer - - is_false: nodes.$master.indices.query_cache - - is_false: nodes.$master.indices.fielddata - - is_false: nodes.$master.indices.completion - - is_true: nodes.$master.indices.segments - - is_false: nodes.$master.indices.translog - - is_false: nodes.$master.indices.recovery - - is_true: nodes.$master.indices.segments.file_sizes + - is_false: nodes.$node_id.indices.docs + - is_false: nodes.$node_id.indices.store + - is_false: nodes.$node_id.indices.indexing + - is_false: nodes.$node_id.indices.get + - is_false: nodes.$node_id.indices.search + - is_false: nodes.$node_id.indices.merges + - is_false: nodes.$node_id.indices.refresh + - is_false: nodes.$node_id.indices.flush + - is_false: nodes.$node_id.indices.warmer + - is_false: nodes.$node_id.indices.query_cache + - is_false: nodes.$node_id.indices.fielddata + - is_false: nodes.$node_id.indices.completion + - is_true: nodes.$node_id.indices.segments + - is_false: nodes.$node_id.indices.translog + - is_false: nodes.$node_id.indices.recovery + - is_true: nodes.$node_id.indices.segments.file_sizes diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yml index 432e5d8c207ec..a478fd7d3f235 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/20_response_filtering.yml @@ -1,10 +1,11 @@ --- "Nodes Stats with response filtering": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - # Get master node id - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id # Nodes Stats with no filtering - do: @@ -12,18 +13,18 @@ - is_true: cluster_name - is_true: nodes - - is_true: nodes.$master.name - - is_true: nodes.$master.indices - - is_true: nodes.$master.indices.docs - - gte: { nodes.$master.indices.docs.count: 0 } - - is_true: nodes.$master.indices.segments - - gte: { nodes.$master.indices.segments.count: 0 } - - is_true: nodes.$master.jvm - - is_true: nodes.$master.jvm.threads - - gte: { nodes.$master.jvm.threads.count: 0 } - - is_true: nodes.$master.jvm.buffer_pools.direct - - gte: { nodes.$master.jvm.buffer_pools.direct.count: 0 } - - gte: { nodes.$master.jvm.buffer_pools.direct.used_in_bytes: 0 } + - is_true: nodes.$node_id.name + - is_true: nodes.$node_id.indices + - is_true: nodes.$node_id.indices.docs + - gte: { nodes.$node_id.indices.docs.count: 0 } + - is_true: nodes.$node_id.indices.segments + - gte: { nodes.$node_id.indices.segments.count: 0 } + - is_true: nodes.$node_id.jvm + - is_true: nodes.$node_id.jvm.threads + - gte: { nodes.$node_id.jvm.threads.count: 0 } + - is_true: nodes.$node_id.jvm.buffer_pools.direct + - gte: { nodes.$node_id.jvm.buffer_pools.direct.count: 0 } + - gte: { nodes.$node_id.jvm.buffer_pools.direct.used_in_bytes: 0 } # Nodes Stats with only "cluster_name" field - do: @@ -32,9 +33,9 @@ - is_true: cluster_name - is_false: nodes - - is_false: nodes.$master.name - - is_false: nodes.$master.indices - - is_false: nodes.$master.jvm + - is_false: nodes.$node_id.name + - is_false: nodes.$node_id.indices + - is_false: nodes.$node_id.jvm # Nodes Stats with "nodes" field and sub-fields - do: @@ -43,18 +44,18 @@ - is_false: cluster_name - is_true: nodes - - is_true: nodes.$master.name - - is_true: nodes.$master.indices - - is_true: nodes.$master.indices.docs - - gte: { nodes.$master.indices.docs.count: 0 } - - is_true: nodes.$master.indices.segments - - gte: { nodes.$master.indices.segments.count: 0 } - - is_true: nodes.$master.jvm - - is_true: nodes.$master.jvm.threads - - gte: { nodes.$master.jvm.threads.count: 0 } - - is_true: nodes.$master.jvm.buffer_pools.direct - - gte: { nodes.$master.jvm.buffer_pools.direct.count: 0 } - - gte: { nodes.$master.jvm.buffer_pools.direct.used_in_bytes: 0 } + - is_true: nodes.$node_id.name + - is_true: nodes.$node_id.indices + - is_true: nodes.$node_id.indices.docs + - gte: { nodes.$node_id.indices.docs.count: 0 } + - is_true: nodes.$node_id.indices.segments + - gte: { nodes.$node_id.indices.segments.count: 0 } + - is_true: nodes.$node_id.jvm + - is_true: nodes.$node_id.jvm.threads + - gte: { nodes.$node_id.jvm.threads.count: 0 } + - is_true: nodes.$node_id.jvm.buffer_pools.direct + - gte: { nodes.$node_id.jvm.buffer_pools.direct.count: 0 } + - gte: { nodes.$node_id.jvm.buffer_pools.direct.used_in_bytes: 0 } # Nodes Stats with "nodes.*.indices" field and sub-fields - do: @@ -63,13 +64,13 @@ - is_false: cluster_name - is_true: nodes - - is_false: nodes.$master.name - - is_true: nodes.$master.indices - - is_true: nodes.$master.indices.docs - - gte: { nodes.$master.indices.docs.count: 0 } - - is_true: nodes.$master.indices.segments - - gte: { nodes.$master.indices.segments.count: 0 } - - is_false: nodes.$master.jvm + - is_false: nodes.$node_id.name + - is_true: nodes.$node_id.indices + - is_true: nodes.$node_id.indices.docs + - gte: { nodes.$node_id.indices.docs.count: 0 } + - is_true: nodes.$node_id.indices.segments + - gte: { nodes.$node_id.indices.segments.count: 0 } + - is_false: nodes.$node_id.jvm # Nodes Stats with "nodes.*.name" and "nodes.*.indices.docs.count" fields - do: @@ -78,12 +79,12 @@ - is_false: cluster_name - is_true: nodes - - is_true: nodes.$master.name - - is_true: nodes.$master.indices - - is_true: nodes.$master.indices.docs - - gte: { nodes.$master.indices.docs.count: 0 } - - is_false: nodes.$master.indices.segments - - is_false: nodes.$master.jvm + - is_true: nodes.$node_id.name + - is_true: nodes.$node_id.indices + - is_true: nodes.$node_id.indices.docs + - gte: { nodes.$node_id.indices.docs.count: 0 } + - is_false: nodes.$node_id.indices.segments + - is_false: nodes.$node_id.jvm # Nodes Stats with all "count" fields - do: @@ -92,18 +93,18 @@ - is_false: cluster_name - is_true: nodes - - is_false: nodes.$master.name - - is_true: nodes.$master.indices - - is_true: nodes.$master.indices.docs - - gte: { nodes.$master.indices.docs.count: 0 } - - is_true: nodes.$master.indices.segments - - gte: { nodes.$master.indices.segments.count: 0 } - - is_true: nodes.$master.jvm - - is_true: nodes.$master.jvm.threads - - gte: { nodes.$master.jvm.threads.count: 0 } - - is_true: nodes.$master.jvm.buffer_pools.direct - - gte: { nodes.$master.jvm.buffer_pools.direct.count: 0 } - - is_false: nodes.$master.jvm.buffer_pools.direct.used_in_bytes + - is_false: nodes.$node_id.name + - is_true: nodes.$node_id.indices + - is_true: nodes.$node_id.indices.docs + - gte: { nodes.$node_id.indices.docs.count: 0 } + - is_true: nodes.$node_id.indices.segments + - gte: { nodes.$node_id.indices.segments.count: 0 } + - is_true: nodes.$node_id.jvm + - is_true: nodes.$node_id.jvm.threads + - gte: { nodes.$node_id.jvm.threads.count: 0 } + - is_true: nodes.$node_id.jvm.buffer_pools.direct + - gte: { nodes.$node_id.jvm.buffer_pools.direct.count: 0 } + - is_false: nodes.$node_id.jvm.buffer_pools.direct.used_in_bytes # Nodes Stats with all "count" fields in sub-fields of "jvm" field - do: @@ -112,16 +113,16 @@ - is_false: cluster_name - is_true: nodes - - is_false: nodes.$master.name - - is_false: nodes.$master.indices - - is_false: nodes.$master.indices.docs.count - - is_false: nodes.$master.indices.segments.count - - is_true: nodes.$master.jvm - - is_true: nodes.$master.jvm.threads - - gte: { nodes.$master.jvm.threads.count: 0 } - - is_true: nodes.$master.jvm.buffer_pools.direct - - gte: { nodes.$master.jvm.buffer_pools.direct.count: 0 } - - is_false: nodes.$master.jvm.buffer_pools.direct.used_in_bytes + - is_false: nodes.$node_id.name + - is_false: nodes.$node_id.indices + - is_false: nodes.$node_id.indices.docs.count + - is_false: nodes.$node_id.indices.segments.count + - is_true: nodes.$node_id.jvm + - is_true: nodes.$node_id.jvm.threads + - gte: { nodes.$node_id.jvm.threads.count: 0 } + - is_true: nodes.$node_id.jvm.buffer_pools.direct + - gte: { nodes.$node_id.jvm.buffer_pools.direct.count: 0 } + - is_false: nodes.$node_id.jvm.buffer_pools.direct.used_in_bytes # Nodes Stats with "nodes.*.fs.data" fields - do: @@ -130,13 +131,13 @@ - is_false: cluster_name - is_true: nodes - - is_false: nodes.$master.name - - is_false: nodes.$master.indices - - is_false: nodes.$master.jvm - - is_true: nodes.$master.fs.data - - is_true: nodes.$master.fs.data.0.path - - is_true: nodes.$master.fs.data.0.type - - is_true: nodes.$master.fs.data.0.total_in_bytes + - is_false: nodes.$node_id.name + - is_false: nodes.$node_id.indices + - is_false: nodes.$node_id.jvm + - is_true: nodes.$node_id.fs.data + - is_true: nodes.$node_id.fs.data.0.path + - is_true: nodes.$node_id.fs.data.0.type + - is_true: nodes.$node_id.fs.data.0.total_in_bytes # Nodes Stats with "nodes.*.fs.data.t*" fields - do: @@ -145,21 +146,22 @@ - is_false: cluster_name - is_true: nodes - - is_false: nodes.$master.name - - is_false: nodes.$master.indices - - is_false: nodes.$master.jvm - - is_true: nodes.$master.fs.data - - is_false: nodes.$master.fs.data.0.path - - is_true: nodes.$master.fs.data.0.type - - is_true: nodes.$master.fs.data.0.total_in_bytes + - is_false: nodes.$node_id.name + - is_false: nodes.$node_id.indices + - is_false: nodes.$node_id.jvm + - is_true: nodes.$node_id.fs.data + - is_false: nodes.$node_id.fs.data.0.path + - is_true: nodes.$node_id.fs.data.0.type + - is_true: nodes.$node_id.fs.data.0.total_in_bytes --- "Nodes Stats filtered using both includes and excludes filters": + - skip: + features: [arbitrary_key] - do: - cluster.state: {} - - # Get master node id - - set: { master_node: master } + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id # Nodes Stats with "nodes" field but no JVM stats - do: @@ -168,10 +170,10 @@ - is_false: cluster_name - is_true: nodes - - is_true: nodes.$master.name - - is_true: nodes.$master.os - - is_false: nodes.$master.indices - - is_false: nodes.$master.jvm + - is_true: nodes.$node_id.name + - is_true: nodes.$node_id.os + - is_false: nodes.$node_id.indices + - is_false: nodes.$node_id.jvm # Nodes Stats with "nodes.*.indices" field and sub-fields but no indices segments - do: @@ -180,10 +182,10 @@ - is_false: cluster_name - is_true: nodes - - is_false: nodes.$master.name - - is_true: nodes.$master.indices - - is_true: nodes.$master.indices.docs - - is_false: nodes.$master.indices.segments + - is_false: nodes.$node_id.name + - is_true: nodes.$node_id.indices + - is_true: nodes.$node_id.indices.docs + - is_false: nodes.$node_id.indices.segments # Nodes Stats with "nodes.*.fs.data.t*" fields but no "type" field - do: @@ -192,9 +194,9 @@ - is_false: cluster_name - is_true: nodes - - is_false: nodes.$master.name - - is_false: nodes.$master.indices - - is_false: nodes.$master.jvm - - is_true: nodes.$master.fs.data - - is_false: nodes.$master.fs.data.0.type - - is_true: nodes.$master.fs.data.0.total_in_bytes + - is_false: nodes.$node_id.name + - is_false: nodes.$node_id.indices + - is_false: nodes.$node_id.jvm + - is_true: nodes.$node_id.fs.data + - is_false: nodes.$node_id.fs.data.0.type + - is_true: nodes.$node_id.fs.data.0.total_in_bytes diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yml index ad8058876ae49..a6b7f29a183c8 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/nodes.stats/30_discovery.yml @@ -1,13 +1,13 @@ --- "Discovery stats": - skip: - version: " - 6.0.99" - reason: "published_cluster_states_received arrived in 6.1.0" - - do: - cluster.state: {} + features: [arbitrary_key] - # Get master node id - - set: { master_node: master } + - do: + nodes.info: + node_id: _master + - set: + nodes._arbitrary_key_: master - do: nodes.stats: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yml index caf97b302f132..addeb3226c575 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.get/10_basic.yml @@ -1,9 +1,6 @@ --- "get task test": # Note that this gets much better testing in reindex's tests because it actually saves the task - - do: - cluster.state: {} - - do: catch: missing tasks.get: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml index 4fdfc378bee26..1742134af2b75 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/tasks.list/10_basic.yml @@ -1,16 +1,18 @@ --- "tasks_list test": - - do: - cluster.state: {} + - skip: + features: [arbitrary_key] - # Get master node id - - set: { master_node: master } + - do: + nodes.info: {} + - set: + nodes._arbitrary_key_: node_id - do: tasks.list: {} - is_true: nodes - - is_true: nodes.$master.roles + - is_true: nodes.$node_id.roles - do: tasks.list: diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java index fea1c3997530c..bdcf426d118f3 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/Features.java @@ -47,7 +47,8 @@ public final class Features { "warnings", "yaml", "contains", - "transform_and_set" + "transform_and_set", + "arbitrary_key" )); private Features() { diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ObjectPath.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ObjectPath.java index 8ebeca4233abd..36d1ff04a5596 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ObjectPath.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ObjectPath.java @@ -102,7 +102,17 @@ private Object evaluate(String key, Object object, Stash stash) throws IOExcepti } if (object instanceof Map) { - return ((Map) object).get(key); + final Map objectAsMap = (Map) object; + if ("_arbitrary_key_".equals(key)) { + if (objectAsMap.isEmpty()) { + throw new IllegalArgumentException("requested [" + key + "] but the map was empty"); + } + if (objectAsMap.containsKey(key)) { + throw new IllegalArgumentException("requested meta-key [" + key + "] but the map unexpectedly contains this key"); + } + return objectAsMap.keySet().iterator().next(); + } + return objectAsMap.get(key); } if (object instanceof List) { List list = (List) object; @@ -149,7 +159,7 @@ private String[] parsePath(String path) { list.add(current.toString()); } - return list.toArray(new String[list.size()]); + return list.toArray(new String[0]); } /** diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java index 79d6d42092a85..9345d73733076 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java @@ -34,6 +34,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -181,6 +182,56 @@ public void testEvaluateObjectKeys() throws Exception { assertThat(strings, contains("template_1", "template_2")); } + public void testEvaluateArbitraryKey() throws Exception { + XContentBuilder xContentBuilder = randomXContentBuilder(); + xContentBuilder.startObject(); + xContentBuilder.startObject("metadata"); + xContentBuilder.startObject("templates"); + xContentBuilder.startObject("template_1"); + xContentBuilder.field("field1", "value"); + xContentBuilder.endObject(); + xContentBuilder.startObject("template_2"); + xContentBuilder.field("field2", "value"); + xContentBuilder.field("field3", "value"); + xContentBuilder.endObject(); + xContentBuilder.startObject("template_3"); + xContentBuilder.endObject(); + xContentBuilder.startObject("template_4"); + xContentBuilder.field("_arbitrary_key_", "value"); + xContentBuilder.endObject(); + xContentBuilder.endObject(); + xContentBuilder.endObject(); + xContentBuilder.endObject(); + ObjectPath objectPath = ObjectPath.createFromXContent(xContentBuilder.contentType().xContent(), + BytesReference.bytes(xContentBuilder)); + + { + final Object object = objectPath.evaluate("metadata.templates.template_1._arbitrary_key_"); + assertThat(object, instanceOf(String.class)); + final String key = (String) object; + assertThat(key, equalTo("field1")); + } + + { + final Object object = objectPath.evaluate("metadata.templates.template_2._arbitrary_key_"); + assertThat(object, instanceOf(String.class)); + final String key = (String) object; + assertThat(key, isOneOf("field2", "field3")); + } + + { + final IllegalArgumentException exception + = expectThrows(IllegalArgumentException.class, () -> objectPath.evaluate("metadata.templates.template_3._arbitrary_key_")); + assertThat(exception.getMessage(), equalTo("requested [_arbitrary_key_] but the map was empty")); + } + + { + final IllegalArgumentException exception + = expectThrows(IllegalArgumentException.class, () -> objectPath.evaluate("metadata.templates.template_4._arbitrary_key_")); + assertThat(exception.getMessage(), equalTo("requested meta-key [_arbitrary_key_] but the map unexpectedly contains this key")); + } + } + public void testEvaluateStashInPropertyName() throws Exception { XContentBuilder xContentBuilder = randomXContentBuilder(); xContentBuilder.startObject(); From c88222d29dcfadc1650bfa0dc1433286f9e28828 Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 27 Mar 2019 15:49:12 +0000 Subject: [PATCH 26/77] Stabilise testStaleMasterNotHijackingMajority (#40253) This test inadvertently asserts that the election occurs after a master failure is clean. However, messy elections are a fact of life so we should not fail on a messy election. This change moves this test away from an `AbstractDisruptionTestCase` since it does not need the fault detector to be so enthusiastic, and weakens the assertions to merely say that we ignore states published by the old master without saying anything about the cleanliness of the election. Closes #36556 --- .../discovery/MasterDisruptionIT.java | 116 ----------------- .../discovery/StableMasterDisruptionIT.java | 117 +++++++++++++++++- 2 files changed, 115 insertions(+), 118 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java b/server/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java index 60e2fa0b5687e..aea0c8c5c25f9 100644 --- a/server/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java +++ b/server/src/test/java/org/elasticsearch/discovery/MasterDisruptionIT.java @@ -19,26 +19,19 @@ package org.elasticsearch.discovery; -import org.apache.logging.log4j.message.ParameterizedMessage; import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse; import org.elasticsearch.action.admin.indices.stats.ShardStats; import org.elasticsearch.action.bulk.BulkRequestBuilder; import org.elasticsearch.action.bulk.BulkResponse; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.coordination.NoMasterBlockService; import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.service.ClusterService; -import org.elasticsearch.common.Priority; -import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.disruption.BlockMasterServiceOnMaster; import org.elasticsearch.test.disruption.IntermittentLongGCDisruption; -import org.elasticsearch.test.disruption.LongGCDisruption; import org.elasticsearch.test.disruption.NetworkDisruption; import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; import org.elasticsearch.test.disruption.ServiceDisruptionScheme; @@ -46,21 +39,13 @@ import org.elasticsearch.test.junit.annotations.TestLogging; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Objects; import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.nullValue; /** * Tests relating to the loss of the master. @@ -69,107 +54,6 @@ @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, transportClientRatio = 0) public class MasterDisruptionIT extends AbstractDisruptionTestCase { - /** - * Tests that emulates a frozen elected master node that unfreezes and pushes his cluster state to other nodes - * that already are following another elected master node. These nodes should reject this cluster state and prevent - * them from following the stale master. - */ - @TestLogging("_root:DEBUG,org.elasticsearch.cluster.service:TRACE,org.elasticsearch.test.disruption:TRACE") - public void testStaleMasterNotHijackingMajority() throws Exception { - final List nodes = startCluster(3); - - // Save the current master node as old master node, because that node will get frozen - final String oldMasterNode = internalCluster().getMasterName(); - for (String node : nodes) { - ensureStableCluster(3, node); - } - assertMaster(oldMasterNode, nodes); - - // Simulating a painful gc by suspending all threads for a long time on the current elected master node. - SingleNodeDisruption masterNodeDisruption = new LongGCDisruption(random(), oldMasterNode); - - // Save the majority side - final List majoritySide = new ArrayList<>(nodes); - majoritySide.remove(oldMasterNode); - - // Keeps track of the previous and current master when a master node transition took place on each node on the majority side: - final Map>> masters = Collections.synchronizedMap(new HashMap<>()); - for (final String node : majoritySide) { - masters.put(node, new ArrayList<>()); - internalCluster().getInstance(ClusterService.class, node).addListener(event -> { - DiscoveryNode previousMaster = event.previousState().nodes().getMasterNode(); - DiscoveryNode currentMaster = event.state().nodes().getMasterNode(); - if (!Objects.equals(previousMaster, currentMaster)) { - logger.info("node {} received new cluster state: {} \n and had previous cluster state: {}", node, event.state(), - event.previousState()); - String previousMasterNodeName = previousMaster != null ? previousMaster.getName() : null; - String currentMasterNodeName = currentMaster != null ? currentMaster.getName() : null; - masters.get(node).add(new Tuple<>(previousMasterNodeName, currentMasterNodeName)); - } - }); - } - - final CountDownLatch oldMasterNodeSteppedDown = new CountDownLatch(1); - internalCluster().getInstance(ClusterService.class, oldMasterNode).addListener(event -> { - if (event.state().nodes().getMasterNodeId() == null) { - oldMasterNodeSteppedDown.countDown(); - } - }); - - internalCluster().setDisruptionScheme(masterNodeDisruption); - logger.info("freezing node [{}]", oldMasterNode); - masterNodeDisruption.startDisrupting(); - - // Wait for the majority side to get stable - assertDifferentMaster(majoritySide.get(0), oldMasterNode); - assertDifferentMaster(majoritySide.get(1), oldMasterNode); - - // The old master node is frozen, but here we submit a cluster state update task that doesn't get executed, - // but will be queued and once the old master node un-freezes it gets executed. - // The old master node will send this update + the cluster state where he is flagged as master to the other - // nodes that follow the new master. These nodes should ignore this update. - internalCluster().getInstance(ClusterService.class, oldMasterNode).submitStateUpdateTask("sneaky-update", new - ClusterStateUpdateTask(Priority.IMMEDIATE) { - @Override - public ClusterState execute(ClusterState currentState) { - return ClusterState.builder(currentState).build(); - } - - @Override - public void onFailure(String source, Exception e) { - logger.warn(() -> new ParameterizedMessage("failure [{}]", source), e); - } - }); - - // Save the new elected master node - final String newMasterNode = internalCluster().getMasterName(majoritySide.get(0)); - logger.info("new detected master node [{}]", newMasterNode); - - // Stop disruption - logger.info("Unfreeze node [{}]", oldMasterNode); - masterNodeDisruption.stopDisrupting(); - - oldMasterNodeSteppedDown.await(30, TimeUnit.SECONDS); - // Make sure that the end state is consistent on all nodes: - assertMaster(newMasterNode, nodes); - - assertThat(masters.size(), equalTo(2)); - for (Map.Entry>> entry : masters.entrySet()) { - String nodeName = entry.getKey(); - List> recordedMasterTransition = entry.getValue(); - assertThat("[" + nodeName + "] Each node should only record two master node transitions", - recordedMasterTransition, hasSize(2)); - assertThat("[" + nodeName + "] First transition's previous master should be [" + oldMasterNode + "]", - recordedMasterTransition.get(0).v1(), equalTo(oldMasterNode)); - assertThat("[" + nodeName + "] First transition's current master should be [null]", - recordedMasterTransition.get(0).v2(), nullValue()); - assertThat("[" + nodeName + "] Second transition's previous master should be [null]", - recordedMasterTransition.get(1).v1(), nullValue()); - assertThat("[" + nodeName + "] Second transition's current master should be [" + newMasterNode + "]", - recordedMasterTransition.get(1).v2(), equalTo(newMasterNode)); - } - } - /** * Test that cluster recovers from a long GC on master that causes other nodes to elect a new one */ diff --git a/server/src/test/java/org/elasticsearch/discovery/StableMasterDisruptionIT.java b/server/src/test/java/org/elasticsearch/discovery/StableMasterDisruptionIT.java index 51fef980e3777..c4655bcf7ce9a 100644 --- a/server/src/test/java/org/elasticsearch/discovery/StableMasterDisruptionIT.java +++ b/server/src/test/java/org/elasticsearch/discovery/StableMasterDisruptionIT.java @@ -18,28 +18,44 @@ */ package org.elasticsearch.discovery; +import org.apache.logging.log4j.message.ParameterizedMessage; import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateUpdateTask; +import org.elasticsearch.cluster.coordination.Coordinator; import org.elasticsearch.cluster.coordination.FollowersChecker; import org.elasticsearch.cluster.coordination.LeaderChecker; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.disruption.LongGCDisruption; import org.elasticsearch.test.disruption.NetworkDisruption; import org.elasticsearch.test.disruption.NetworkDisruption.NetworkDisconnect; import org.elasticsearch.test.disruption.NetworkDisruption.NetworkLinkDisruptionType; import org.elasticsearch.test.disruption.NetworkDisruption.NetworkUnresponsive; import org.elasticsearch.test.disruption.NetworkDisruption.TwoPartitions; +import org.elasticsearch.test.disruption.SingleNodeDisruption; import org.elasticsearch.test.junit.annotations.TestLogging; -import org.elasticsearch.test.transport.MockTransportService.TestPlugin; +import org.elasticsearch.test.transport.MockTransportService; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static java.util.Collections.singleton; @@ -55,7 +71,7 @@ public class StableMasterDisruptionIT extends ESIntegTestCase { @Override protected Collection> nodePlugins() { - return Collections.singletonList(TestPlugin.class); + return Collections.singletonList(MockTransportService.TestPlugin.class); } /** @@ -152,4 +168,101 @@ private void testFollowerCheckerAfterMasterReelection(NetworkLinkDisruptionType networkDisruption.stopDisrupting(); ensureStableCluster(3); } + + + /** + * Tests that emulates a frozen elected master node that unfreezes and pushes its cluster state to other nodes that already are + * following another elected master node. These nodes should reject this cluster state and prevent them from following the stale master. + */ + public void testStaleMasterNotHijackingMajority() throws Exception { + final List nodes = internalCluster().startNodes(3, Settings.builder() + .put(LeaderChecker.LEADER_CHECK_TIMEOUT_SETTING.getKey(), "1s") + .put(Coordinator.PUBLISH_TIMEOUT_SETTING.getKey(), "1s") + .build()); + ensureStableCluster(3); + + // Save the current master node as old master node, because that node will get frozen + final String oldMasterNode = internalCluster().getMasterName(); + + // Simulating a painful gc by suspending all threads for a long time on the current elected master node. + SingleNodeDisruption masterNodeDisruption = new LongGCDisruption(random(), oldMasterNode); + + // Save the majority side + final List majoritySide = new ArrayList<>(nodes); + majoritySide.remove(oldMasterNode); + + // Keeps track of the previous and current master when a master node transition took place on each node on the majority side: + final Map>> masters = Collections.synchronizedMap(new HashMap<>()); + for (final String node : majoritySide) { + masters.put(node, new ArrayList<>()); + internalCluster().getInstance(ClusterService.class, node).addListener(event -> { + DiscoveryNode previousMaster = event.previousState().nodes().getMasterNode(); + DiscoveryNode currentMaster = event.state().nodes().getMasterNode(); + if (!Objects.equals(previousMaster, currentMaster)) { + logger.info("--> node {} received new cluster state: {} \n and had previous cluster state: {}", node, event.state(), + event.previousState()); + String previousMasterNodeName = previousMaster != null ? previousMaster.getName() : null; + String currentMasterNodeName = currentMaster != null ? currentMaster.getName() : null; + masters.get(node).add(new Tuple<>(previousMasterNodeName, currentMasterNodeName)); + } + }); + } + + final CountDownLatch oldMasterNodeSteppedDown = new CountDownLatch(1); + internalCluster().getInstance(ClusterService.class, oldMasterNode).addListener(event -> { + if (event.state().nodes().getMasterNodeId() == null) { + oldMasterNodeSteppedDown.countDown(); + } + }); + + internalCluster().setDisruptionScheme(masterNodeDisruption); + logger.info("--> freezing node [{}]", oldMasterNode); + masterNodeDisruption.startDisrupting(); + + // Wait for majority side to elect a new master + assertBusy(() -> { + for (final Map.Entry>> entry : masters.entrySet()) { + final List> transitions = entry.getValue(); + assertTrue(entry.getKey() + ": " + transitions, + transitions.stream().anyMatch(transition -> transition.v2() != null)); + } + }); + + // The old master node is frozen, but here we submit a cluster state update task that doesn't get executed, but will be queued and + // once the old master node un-freezes it gets executed. The old master node will send this update + the cluster state where it is + // flagged as master to the other nodes that follow the new master. These nodes should ignore this update. + internalCluster().getInstance(ClusterService.class, oldMasterNode).submitStateUpdateTask("sneaky-update", new + ClusterStateUpdateTask(Priority.IMMEDIATE) { + @Override + public ClusterState execute(ClusterState currentState) { + return ClusterState.builder(currentState).build(); + } + + @Override + public void onFailure(String source, Exception e) { + logger.warn(() -> new ParameterizedMessage("failure [{}]", source), e); + } + }); + + // Save the new elected master node + final String newMasterNode = internalCluster().getMasterName(majoritySide.get(0)); + logger.info("--> new detected master node [{}]", newMasterNode); + + // Stop disruption + logger.info("--> unfreezing node [{}]", oldMasterNode); + masterNodeDisruption.stopDisrupting(); + + oldMasterNodeSteppedDown.await(30, TimeUnit.SECONDS); + logger.info("--> [{}] stepped down as master", oldMasterNode); + ensureStableCluster(3); + + assertThat(masters.size(), equalTo(2)); + for (Map.Entry>> entry : masters.entrySet()) { + String nodeName = entry.getKey(); + List> transitions = entry.getValue(); + assertTrue("[" + nodeName + "] should not apply state from old master [" + oldMasterNode + "] but it did: " + transitions, + transitions.stream().noneMatch(t -> oldMasterNode.equals(t.v2()))); + } + } + } From 2221cc71a7782c557dcb37c22241836a7a8693ca Mon Sep 17 00:00:00 2001 From: Marios Trivyzas Date: Wed, 27 Mar 2019 17:13:18 +0100 Subject: [PATCH 27/77] SQL: Fix getTime() methods in JDBC (#40484) Previously, `getTime(colIdx/colLabel)` and `getObject(colIdx/colLabel, java.sql.Time.class)` methods were computing the time from a `ZonedDateTime` by applying day in millis modulo on the epoch millis of the `ZonedDateTime` object. This is wrong as we need to keep the time related fields at the timezone of the `ZonedDateTime` object and just set the date info to the epoch date (01/01/1970). Additionally fixes a testing issue as the original timezone id is converted to an offset string when parsing the response from the server. --- .../xpack/sql/jdbc/JdbcDateUtils.java | 25 ++------ .../xpack/sql/qa/jdbc/JdbcTestUtils.java | 59 ++++++++++--------- .../xpack/sql/qa/jdbc/ResultSetTestCase.java | 18 +++--- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java index b4210f2c44d3f..c0f2e6e46ea03 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDateUtils.java @@ -9,6 +9,7 @@ import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.time.LocalDate; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -27,10 +28,9 @@ */ final class JdbcDateUtils { - private JdbcDateUtils() { - } + private JdbcDateUtils() {} - private static final long DAY_IN_MILLIS = 60 * 60 * 24 * 1000L; + private static final LocalDate EPOCH = LocalDate.of(1970, 1, 1); static final DateTimeFormatter ISO_WITH_MILLIS = new DateTimeFormatterBuilder() .parseCaseInsensitive() @@ -58,20 +58,9 @@ static Date asDate(String date) { return new Date(zdt.toLocalDate().atStartOfDay(zdt.getZone()).toInstant().toEpochMilli()); } - /** - * In contrast to {@link JdbcDateUtils#asDate(String)} here we just want to eliminate - * the date part and just set it to EPOCH (1970-01-1) - */ - static Time asTime(long millisSinceEpoch) { - return new Time(utcMillisRemoveDate(millisSinceEpoch)); - } - - /** - * In contrast to {@link JdbcDateUtils#asDate(String)} here we just want to eliminate - * the date part and just set it to EPOCH (1970-01-1) - */ static Time asTime(String date) { - return asTime(asMillisSinceEpoch(date)); + ZonedDateTime zdt = asDateTime(date); + return new Time(zdt.toLocalTime().atDate(EPOCH).atZone(zdt.getZone()).toInstant().toEpochMilli()); } static Timestamp asTimestamp(long millisSinceEpoch) { @@ -93,8 +82,4 @@ static R asDateTimeField(Object value, Function asDateTimeMethod, return ctor.apply(((Number) value).longValue()); } } - - private static long utcMillisRemoveDate(long l) { - return l % DAY_IN_MILLIS; - } } diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java index 6896c76aff004..19c30b55e92b1 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/JdbcTestUtils.java @@ -23,10 +23,13 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import java.sql.Date; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Time; import java.time.Instant; +import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; @@ -37,15 +40,17 @@ import static org.elasticsearch.xpack.sql.action.BasicFormatter.FormatOption.CLI; -public abstract class JdbcTestUtils { +final class JdbcTestUtils { - public static final String SQL_TRACE = "org.elasticsearch.xpack.sql:TRACE"; + private JdbcTestUtils() {} - public static final String JDBC_TIMEZONE = "timezone"; - - public static ZoneId UTC = ZoneId.of("Z"); + private static final int MAX_WIDTH = 20; + + static final String SQL_TRACE = "org.elasticsearch.xpack.sql:TRACE"; + static final String JDBC_TIMEZONE = "timezone"; + static final LocalDate EPOCH = LocalDate.of(1970, 1, 1); - public static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLException { + static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLException { ResultSetMetaData metaData = rs.getMetaData(); // header StringBuilder sb = new StringBuilder(); @@ -75,35 +80,24 @@ public static void logResultSetMetadata(ResultSet rs, Logger logger) throws SQLE logger.info(sb.toString()); } - private static final int MAX_WIDTH = 20; - - public static void logResultSetData(ResultSet rs, Logger log) throws SQLException { + static void logResultSetData(ResultSet rs, Logger log) throws SQLException { ResultSetMetaData metaData = rs.getMetaData(); - StringBuilder sb = new StringBuilder(); - StringBuilder column = new StringBuilder(); int columns = metaData.getColumnCount(); while (rs.next()) { - sb.setLength(0); - for (int i = 1; i <= columns; i++) { - column.setLength(0); - if (i > 1) { - sb.append(" | "); - } - sb.append(trimOrPad(column.append(rs.getString(i)))); - } - log.info(sb); + log.info(rowAsString(rs, columns)); } } - public static String resultSetCurrentData(ResultSet rs) throws SQLException { + static String resultSetCurrentData(ResultSet rs) throws SQLException { ResultSetMetaData metaData = rs.getMetaData(); - StringBuilder column = new StringBuilder(); - - int columns = metaData.getColumnCount(); + return rowAsString(rs, metaData.getColumnCount()); + } + private static String rowAsString(ResultSet rs, int columns) throws SQLException { StringBuilder sb = new StringBuilder(); + StringBuilder column = new StringBuilder(); for (int i = 1; i <= columns; i++) { column.setLength(0); if (i > 1) { @@ -153,7 +147,7 @@ public static void logLikeCLI(ResultSet rs, Logger logger) throws SQLException { logger.info("\n" + formatter.formatWithHeader(cols, data)); } - public static String of(long millis, String zoneId) { + static String of(long millis, String zoneId) { return StringUtils.toString(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(zoneId))); } @@ -165,7 +159,7 @@ public static String of(long millis, String zoneId) { * folders in the file-system (typically IDEs) or * inside jars (gradle). */ - public static List classpathResources(String pattern) throws Exception { + static List classpathResources(String pattern) throws Exception { while (pattern.startsWith("/")) { pattern = pattern.substring(1); } @@ -234,4 +228,15 @@ static Tuple pathAndName(String string) { } return new Tuple<>(folder, file); } -} \ No newline at end of file + + static Date asDate(long millis, ZoneId zoneId) { + return new java.sql.Date( + ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId) + .toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli()); + } + + static Time asTime(long millis, ZoneId zoneId) { + return new Time(ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), zoneId) + .toLocalTime().atDate(JdbcTestUtils.EPOCH).atZone(zoneId).toInstant().toEpochMilli()); + } +} diff --git a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java index f9bc90a093e69..b8cd81e39f545 100644 --- a/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java +++ b/x-pack/plugin/sql/qa/src/main/java/org/elasticsearch/xpack/sql/qa/jdbc/ResultSetTestCase.java @@ -34,7 +34,6 @@ import java.sql.Types; import java.time.Instant; import java.time.ZoneId; -import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Calendar; import java.util.Date; @@ -61,6 +60,8 @@ import static java.util.Calendar.SECOND; import static java.util.Calendar.YEAR; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.JDBC_TIMEZONE; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asDate; +import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.asTime; import static org.elasticsearch.xpack.sql.qa.jdbc.JdbcTestUtils.of; public class ResultSetTestCase extends JdbcIntegrationTestCase { @@ -880,10 +881,7 @@ public void testGettingDateWithoutCalendar() throws Exception { doWithQuery(SELECT_ALL_FIELDS, (results) -> { results.next(); - ZoneId zoneId = ZoneId.of(timeZoneId); - java.sql.Date expectedDate = new java.sql.Date( - ZonedDateTime.ofInstant(Instant.ofEpochMilli(randomLongDate), zoneId) - .toLocalDate().atStartOfDay(zoneId).toInstant().toEpochMilli()); + java.sql.Date expectedDate = asDate(randomLongDate, getZoneFromOffset(randomLongDate)); assertEquals(expectedDate, results.getDate("test_date")); assertEquals(expectedDate, results.getDate(9)); @@ -939,11 +937,11 @@ public void testGettingTimeWithoutCalendar() throws Exception { }); Long randomLongDate = randomNonNegativeLong(); indexSimpleDocumentWithTrueValues(randomLongDate); - + doWithQuery(SELECT_ALL_FIELDS, (results) -> { results.next(); - java.sql.Time expectedTime = new java.sql.Time(randomLongDate % 86400000L); + java.sql.Time expectedTime = asTime(randomLongDate, getZoneFromOffset(randomLongDate)); assertEquals(expectedTime, results.getTime("test_date")); assertEquals(expectedTime, results.getTime(9)); @@ -953,7 +951,7 @@ public void testGettingTimeWithoutCalendar() throws Exception { validateErrorsForTimeTestsWithoutCalendar(results::getTime); }); } - + public void testGettingTimeWithCalendar() throws Exception { createIndex("test"); updateMappingForNumericValuesTests("test"); @@ -1748,4 +1746,8 @@ private Connection esWithLeniency(boolean multiValueLeniency) throws SQLExceptio private String asDateString(long millis) { return of(millis, timeZoneId); } + + private ZoneId getZoneFromOffset(Long randomLongDate) { + return ZoneId.of(ZoneId.of(timeZoneId).getRules().getOffset(Instant.ofEpochMilli(randomLongDate)).toString()); + } } From 8c2beca5b80863406b223b79e29fe94751b4d18c Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Wed, 27 Mar 2019 17:30:05 +0100 Subject: [PATCH 28/77] Increase suite timeout to 30 minutes for docs tests (#40521) I have been hitting the suite timeout on `DocsClientYamlTestSuiteIT` As far as I can see, the docs tests are taking quite a while, I assume it's because more and more docs snippets get added over time, which means more tests. The current suite timeout is the default 20 minutes. It takes me just a little less than 20 minutes to run these on my laptop. On my CI, I end up hitting the suite timeout. Hereby I propose that we increase the suite timeout to 30 minutes. --- .../smoketest/DocsClientYamlTestSuiteIT.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/src/test/java/org/elasticsearch/smoketest/DocsClientYamlTestSuiteIT.java b/docs/src/test/java/org/elasticsearch/smoketest/DocsClientYamlTestSuiteIT.java index b4a6c49754869..bbc6a64dcdb2b 100644 --- a/docs/src/test/java/org/elasticsearch/smoketest/DocsClientYamlTestSuiteIT.java +++ b/docs/src/test/java/org/elasticsearch/smoketest/DocsClientYamlTestSuiteIT.java @@ -19,11 +19,12 @@ package org.elasticsearch.smoketest; -import org.apache.http.HttpHost; -import org.apache.lucene.util.BytesRef; - import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; +import org.apache.http.HttpHost; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.TimeUnits; import org.elasticsearch.Version; import org.elasticsearch.client.RestClient; import org.elasticsearch.common.ParseField; @@ -48,12 +49,13 @@ import java.util.List; import java.util.Map; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; - import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +//The default 20 minutes timeout isn't always enough, please do not increase further than 30 before analyzing what makes this suite so slow +@TimeoutSuite(millis = 30 * TimeUnits.MINUTE) public class DocsClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { public DocsClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { From 2b2bd8fd3fbc67a50a19f4333c4b84af40f1cada Mon Sep 17 00:00:00 2001 From: Andy Bristol Date: Wed, 27 Mar 2019 09:40:38 -0700 Subject: [PATCH 29/77] ignore 409 conflict in reindex responses (#39543) The reindex family of APIs (reindex, update-by-query, delete-by-query) can sometimes return responses that have an error status code (409 Conflict in this case) but still have a body in the usual BulkByScrollResponse format. When the HLRC tries to handle such responses, it blows up because it tris to parse it expecting the error format that errors in general use. This change prompts the HLRC to parse the response using the expected BulkByScrollResponse format. --- .../client/RestHighLevelClient.java | 12 +- .../java/org/elasticsearch/client/CrudIT.java | 243 ------------ .../org/elasticsearch/client/ReindexIT.java | 359 ++++++++++++++++++ 3 files changed, 365 insertions(+), 249 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java index d0917b8d45461..77eac4a6e2a85 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java @@ -511,7 +511,7 @@ public final void bulkAsync(BulkRequest bulkRequest, RequestOptions options, Act */ public final BulkByScrollResponse reindex(ReindexRequest reindexRequest, RequestOptions options) throws IOException { return performRequestAndParseEntity( - reindexRequest, RequestConverters::reindex, options, BulkByScrollResponse::fromXContent, emptySet() + reindexRequest, RequestConverters::reindex, options, BulkByScrollResponse::fromXContent, singleton(409) ); } @@ -537,7 +537,7 @@ public final TaskSubmissionResponse submitReindexTask(ReindexRequest reindexRequ */ public final void reindexAsync(ReindexRequest reindexRequest, RequestOptions options, ActionListener listener) { performRequestAsyncAndParseEntity( - reindexRequest, RequestConverters::reindex, options, BulkByScrollResponse::fromXContent, listener, emptySet() + reindexRequest, RequestConverters::reindex, options, BulkByScrollResponse::fromXContent, listener, singleton(409) ); } @@ -551,7 +551,7 @@ public final void reindexAsync(ReindexRequest reindexRequest, RequestOptions opt */ public final BulkByScrollResponse updateByQuery(UpdateByQueryRequest updateByQueryRequest, RequestOptions options) throws IOException { return performRequestAndParseEntity( - updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, emptySet() + updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, singleton(409) ); } @@ -566,7 +566,7 @@ public final BulkByScrollResponse updateByQuery(UpdateByQueryRequest updateByQue public final void updateByQueryAsync(UpdateByQueryRequest updateByQueryRequest, RequestOptions options, ActionListener listener) { performRequestAsyncAndParseEntity( - updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet() + updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, listener, singleton(409) ); } @@ -580,7 +580,7 @@ public final void updateByQueryAsync(UpdateByQueryRequest updateByQueryRequest, */ public final BulkByScrollResponse deleteByQuery(DeleteByQueryRequest deleteByQueryRequest, RequestOptions options) throws IOException { return performRequestAndParseEntity( - deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, emptySet() + deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, singleton(409) ); } @@ -595,7 +595,7 @@ public final BulkByScrollResponse deleteByQuery(DeleteByQueryRequest deleteByQue public final void deleteByQueryAsync(DeleteByQueryRequest deleteByQueryRequest, RequestOptions options, ActionListener listener) { performRequestAsyncAndParseEntity( - deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, listener, emptySet() + deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, listener, singleton(409) ); } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java index 301a32d97fe0c..6c161444e2475 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/CrudIT.java @@ -21,12 +21,8 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchStatusException; -import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.DocWriteResponse; -import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; -import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskGroup; import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkProcessor; import org.elasticsearch.action.bulk.BulkRequest; @@ -39,7 +35,6 @@ import org.elasticsearch.action.get.MultiGetResponse; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; @@ -58,12 +53,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.get.GetResult; -import org.elasticsearch.index.query.IdsQueryBuilder; -import org.elasticsearch.index.reindex.BulkByScrollResponse; -import org.elasticsearch.index.reindex.DeleteByQueryAction; -import org.elasticsearch.index.reindex.DeleteByQueryRequest; -import org.elasticsearch.index.reindex.UpdateByQueryAction; -import org.elasticsearch.index.reindex.UpdateByQueryRequest; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.action.document.RestBulkAction; import org.elasticsearch.rest.action.document.RestDeleteAction; @@ -74,8 +63,6 @@ import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.search.fetch.subphase.FetchSourceContext; -import org.elasticsearch.tasks.RawTaskStatus; -import org.elasticsearch.tasks.TaskId; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.format.DateTimeFormat; @@ -85,18 +72,12 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.lessThan; public class CrudIT extends ESRestHighLevelClientTestCase { @@ -857,230 +838,6 @@ public void testBulk() throws IOException { validateBulkResponses(nbItems, errors, bulkResponse, bulkRequest); } - private TaskId findTaskToRethrottle(String actionName) throws IOException { - long start = System.nanoTime(); - ListTasksRequest request = new ListTasksRequest(); - request.setActions(actionName); - request.setDetailed(true); - do { - ListTasksResponse list = highLevelClient().tasks().list(request, RequestOptions.DEFAULT); - list.rethrowFailures("Finding tasks to rethrottle"); - assertThat("tasks are left over from the last execution of this test", - list.getTaskGroups(), hasSize(lessThan(2))); - if (0 == list.getTaskGroups().size()) { - // The parent task hasn't started yet - continue; - } - TaskGroup taskGroup = list.getTaskGroups().get(0); - assertThat(taskGroup.getChildTasks(), empty()); - return taskGroup.getTaskInfo().getTaskId(); - } while (System.nanoTime() - start < TimeUnit.SECONDS.toNanos(10)); - throw new AssertionError("Couldn't find tasks to rethrottle. Here are the running tasks " + - highLevelClient().tasks().list(request, RequestOptions.DEFAULT)); - } - - public void testUpdateByQuery() throws Exception { - final String sourceIndex = "source1"; - { - // Prepare - Settings settings = Settings.builder() - .put("number_of_shards", 1) - .put("number_of_replicas", 0) - .build(); - createIndex(sourceIndex, settings); - assertEquals( - RestStatus.OK, - highLevelClient().bulk( - new BulkRequest() - .add(new IndexRequest(sourceIndex).id("1") - .source(Collections.singletonMap("foo", 1), XContentType.JSON)) - .add(new IndexRequest(sourceIndex).id("2") - .source(Collections.singletonMap("foo", 2), XContentType.JSON)) - .setRefreshPolicy(RefreshPolicy.IMMEDIATE), - RequestOptions.DEFAULT - ).status() - ); - } - { - // test1: create one doc in dest - UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(); - updateByQueryRequest.indices(sourceIndex); - updateByQueryRequest.setQuery(new IdsQueryBuilder().addIds("1")); - updateByQueryRequest.setRefresh(true); - BulkByScrollResponse bulkResponse = - execute(updateByQueryRequest, highLevelClient()::updateByQuery, highLevelClient()::updateByQueryAsync); - assertEquals(1, bulkResponse.getTotal()); - assertEquals(1, bulkResponse.getUpdated()); - assertEquals(0, bulkResponse.getNoops()); - assertEquals(0, bulkResponse.getVersionConflicts()); - assertEquals(1, bulkResponse.getBatches()); - assertTrue(bulkResponse.getTook().getMillis() > 0); - assertEquals(1, bulkResponse.getBatches()); - assertEquals(0, bulkResponse.getBulkFailures().size()); - assertEquals(0, bulkResponse.getSearchFailures().size()); - } - { - // test2: update using script - UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(); - updateByQueryRequest.indices(sourceIndex); - updateByQueryRequest.setScript(new Script("if (ctx._source.foo == 2) ctx._source.foo++;")); - updateByQueryRequest.setRefresh(true); - BulkByScrollResponse bulkResponse = - execute(updateByQueryRequest, highLevelClient()::updateByQuery, highLevelClient()::updateByQueryAsync); - assertEquals(2, bulkResponse.getTotal()); - assertEquals(2, bulkResponse.getUpdated()); - assertEquals(0, bulkResponse.getDeleted()); - assertEquals(0, bulkResponse.getNoops()); - assertEquals(0, bulkResponse.getVersionConflicts()); - assertEquals(1, bulkResponse.getBatches()); - assertTrue(bulkResponse.getTook().getMillis() > 0); - assertEquals(1, bulkResponse.getBatches()); - assertEquals(0, bulkResponse.getBulkFailures().size()); - assertEquals(0, bulkResponse.getSearchFailures().size()); - assertEquals( - 3, - (int) (highLevelClient().get(new GetRequest(sourceIndex, "2"), RequestOptions.DEFAULT) - .getSourceAsMap().get("foo")) - ); - } - { - // test update-by-query rethrottling - UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(); - updateByQueryRequest.indices(sourceIndex); - updateByQueryRequest.setQuery(new IdsQueryBuilder().addIds("1")); - updateByQueryRequest.setRefresh(true); - - // this following settings are supposed to halt reindexing after first document - updateByQueryRequest.setBatchSize(1); - updateByQueryRequest.setRequestsPerSecond(0.00001f); - final CountDownLatch taskFinished = new CountDownLatch(1); - highLevelClient().updateByQueryAsync(updateByQueryRequest, RequestOptions.DEFAULT, new ActionListener() { - - @Override - public void onResponse(BulkByScrollResponse response) { - taskFinished.countDown(); - } - - @Override - public void onFailure(Exception e) { - fail(e.toString()); - } - }); - - TaskId taskIdToRethrottle = findTaskToRethrottle(UpdateByQueryAction.NAME); - float requestsPerSecond = 1000f; - ListTasksResponse response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), - highLevelClient()::updateByQueryRethrottle, highLevelClient()::updateByQueryRethrottleAsync); - assertThat(response.getTasks(), hasSize(1)); - assertEquals(taskIdToRethrottle, response.getTasks().get(0).getTaskId()); - assertThat(response.getTasks().get(0).getStatus(), instanceOf(RawTaskStatus.class)); - assertEquals(Float.toString(requestsPerSecond), - ((RawTaskStatus) response.getTasks().get(0).getStatus()).toMap().get("requests_per_second").toString()); - taskFinished.await(2, TimeUnit.SECONDS); - - // any rethrottling after the update-by-query is done performed with the same taskId should result in a failure - response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), - highLevelClient()::updateByQueryRethrottle, highLevelClient()::updateByQueryRethrottleAsync); - assertTrue(response.getTasks().isEmpty()); - assertFalse(response.getNodeFailures().isEmpty()); - assertEquals(1, response.getNodeFailures().size()); - assertEquals("Elasticsearch exception [type=resource_not_found_exception, reason=task [" + taskIdToRethrottle + "] is missing]", - response.getNodeFailures().get(0).getCause().getMessage()); - } - } - - public void testDeleteByQuery() throws Exception { - final String sourceIndex = "source1"; - { - // Prepare - Settings settings = Settings.builder() - .put("number_of_shards", 1) - .put("number_of_replicas", 0) - .build(); - createIndex(sourceIndex, settings); - assertEquals( - RestStatus.OK, - highLevelClient().bulk( - new BulkRequest() - .add(new IndexRequest(sourceIndex).id("1") - .source(Collections.singletonMap("foo", 1), XContentType.JSON)) - .add(new IndexRequest(sourceIndex).id("2") - .source(Collections.singletonMap("foo", 2), XContentType.JSON)) - .add(new IndexRequest(sourceIndex).id("3") - .source(Collections.singletonMap("foo", 3), XContentType.JSON)) - .setRefreshPolicy(RefreshPolicy.IMMEDIATE), - RequestOptions.DEFAULT - ).status() - ); - } - { - // test1: delete one doc - DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(); - deleteByQueryRequest.indices(sourceIndex); - deleteByQueryRequest.setQuery(new IdsQueryBuilder().addIds("1")); - deleteByQueryRequest.setRefresh(true); - BulkByScrollResponse bulkResponse = - execute(deleteByQueryRequest, highLevelClient()::deleteByQuery, highLevelClient()::deleteByQueryAsync); - assertEquals(1, bulkResponse.getTotal()); - assertEquals(1, bulkResponse.getDeleted()); - assertEquals(0, bulkResponse.getNoops()); - assertEquals(0, bulkResponse.getVersionConflicts()); - assertEquals(1, bulkResponse.getBatches()); - assertTrue(bulkResponse.getTook().getMillis() > 0); - assertEquals(1, bulkResponse.getBatches()); - assertEquals(0, bulkResponse.getBulkFailures().size()); - assertEquals(0, bulkResponse.getSearchFailures().size()); - assertEquals( - 2, - highLevelClient().search(new SearchRequest(sourceIndex), RequestOptions.DEFAULT).getHits().getTotalHits().value - ); - } - { - // test delete-by-query rethrottling - DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(); - deleteByQueryRequest.indices(sourceIndex); - deleteByQueryRequest.setQuery(new IdsQueryBuilder().addIds("2", "3")); - deleteByQueryRequest.setRefresh(true); - - // this following settings are supposed to halt reindexing after first document - deleteByQueryRequest.setBatchSize(1); - deleteByQueryRequest.setRequestsPerSecond(0.00001f); - final CountDownLatch taskFinished = new CountDownLatch(1); - highLevelClient().deleteByQueryAsync(deleteByQueryRequest, RequestOptions.DEFAULT, new ActionListener() { - - @Override - public void onResponse(BulkByScrollResponse response) { - taskFinished.countDown(); - } - - @Override - public void onFailure(Exception e) { - fail(e.toString()); - } - }); - - TaskId taskIdToRethrottle = findTaskToRethrottle(DeleteByQueryAction.NAME); - float requestsPerSecond = 1000f; - ListTasksResponse response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), - highLevelClient()::deleteByQueryRethrottle, highLevelClient()::deleteByQueryRethrottleAsync); - assertThat(response.getTasks(), hasSize(1)); - assertEquals(taskIdToRethrottle, response.getTasks().get(0).getTaskId()); - assertThat(response.getTasks().get(0).getStatus(), instanceOf(RawTaskStatus.class)); - assertEquals(Float.toString(requestsPerSecond), - ((RawTaskStatus) response.getTasks().get(0).getStatus()).toMap().get("requests_per_second").toString()); - taskFinished.await(2, TimeUnit.SECONDS); - - // any rethrottling after the delete-by-query is done performed with the same taskId should result in a failure - response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), - highLevelClient()::deleteByQueryRethrottle, highLevelClient()::deleteByQueryRethrottleAsync); - assertTrue(response.getTasks().isEmpty()); - assertFalse(response.getNodeFailures().isEmpty()); - assertEquals(1, response.getNodeFailures().size()); - assertEquals("Elasticsearch exception [type=resource_not_found_exception, reason=task [" + taskIdToRethrottle + "] is missing]", - response.getNodeFailures().get(0).getCause().getMessage()); - } - } - public void testBulkProcessorIntegration() throws IOException { int nbItems = randomIntBetween(10, 100); boolean[] errors = new boolean[nbItems]; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ReindexIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ReindexIT.java index cfdd29cdfbfbf..73cca7827e73b 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ReindexIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ReindexIT.java @@ -19,23 +19,54 @@ package org.elasticsearch.client; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; +import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; +import org.elasticsearch.action.admin.cluster.node.tasks.list.TaskGroup; import org.elasticsearch.action.bulk.BulkRequest; +import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.ingest.PutPipelineRequest; +import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.client.tasks.TaskSubmissionResponse; import org.elasticsearch.common.CheckedRunnable; +import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.IdsQueryBuilder; import org.elasticsearch.index.reindex.BulkByScrollResponse; +import org.elasticsearch.index.reindex.DeleteByQueryAction; +import org.elasticsearch.index.reindex.DeleteByQueryRequest; import org.elasticsearch.index.reindex.ReindexRequest; +import org.elasticsearch.index.reindex.ScrollableHitSource; +import org.elasticsearch.index.reindex.UpdateByQueryAction; +import org.elasticsearch.index.reindex.UpdateByQueryRequest; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.script.Script; +import org.elasticsearch.tasks.RawTaskStatus; +import org.elasticsearch.tasks.TaskId; import java.io.IOException; import java.util.Collections; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.everyItem; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.lessThan; public class ReindexIT extends ESRestHighLevelClientTestCase { + private static final String CONFLICT_PIPELINE_ID = "conflict_pipeline"; + public void testReindex() throws IOException { final String sourceIndex = "source1"; final String destinationIndex = "dest"; @@ -122,10 +153,338 @@ public void testReindexTask() throws Exception { } } + public void testReindexConflict() throws IOException { + final String sourceIndex = "testreindexconflict_source"; + final String destIndex = "testreindexconflict_dest"; + + final Settings settings = Settings.builder() + .put("number_of_shards", 1) + .put("number_of_replicas", 0) + .build(); + createIndex(sourceIndex, settings); + createIndex(destIndex, settings); + final BulkRequest bulkRequest = new BulkRequest() + .add(new IndexRequest(sourceIndex).id("1").source(Collections.singletonMap("foo", "bar"), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2").source(Collections.singletonMap("foo", "bar"), XContentType.JSON)) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + assertThat(highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT).status(), equalTo(RestStatus.OK)); + + putConflictPipeline(); + + final ReindexRequest reindexRequest = new ReindexRequest(); + reindexRequest.setSourceIndices(sourceIndex); + reindexRequest.setDestIndex(destIndex); + reindexRequest.setRefresh(true); + reindexRequest.setDestPipeline(CONFLICT_PIPELINE_ID); + final BulkByScrollResponse response = highLevelClient().reindex(reindexRequest, RequestOptions.DEFAULT); + + assertThat(response.getVersionConflicts(), equalTo(2L)); + assertThat(response.getBulkFailures(), empty()); + assertThat(response.getSearchFailures(), hasSize(2)); + assertThat( + response.getSearchFailures().stream().map(ScrollableHitSource.SearchFailure::toString).collect(Collectors.toSet()), + everyItem(containsString("version conflict")) + ); + + assertThat(response.getTotal(), equalTo(2L)); + assertThat(response.getCreated(), equalTo(0L)); + assertThat(response.getUpdated(), equalTo(0L)); + assertThat(response.getDeleted(), equalTo(0L)); + assertThat(response.getNoops(), equalTo(0L)); + assertThat(response.getBatches(), equalTo(1)); + assertTrue(response.getTook().getMillis() > 0); + } + + public void testUpdateByQuery() throws Exception { + final String sourceIndex = "source1"; + { + // Prepare + Settings settings = Settings.builder() + .put("number_of_shards", 1) + .put("number_of_replicas", 0) + .build(); + createIndex(sourceIndex, settings); + assertEquals( + RestStatus.OK, + highLevelClient().bulk( + new BulkRequest() + .add(new IndexRequest(sourceIndex).id("1") + .source(Collections.singletonMap("foo", 1), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2") + .source(Collections.singletonMap("foo", 2), XContentType.JSON)) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE), + RequestOptions.DEFAULT + ).status() + ); + } + { + // test1: create one doc in dest + UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(); + updateByQueryRequest.indices(sourceIndex); + updateByQueryRequest.setQuery(new IdsQueryBuilder().addIds("1")); + updateByQueryRequest.setRefresh(true); + BulkByScrollResponse bulkResponse = + execute(updateByQueryRequest, highLevelClient()::updateByQuery, highLevelClient()::updateByQueryAsync); + assertEquals(1, bulkResponse.getTotal()); + assertEquals(1, bulkResponse.getUpdated()); + assertEquals(0, bulkResponse.getNoops()); + assertEquals(0, bulkResponse.getVersionConflicts()); + assertEquals(1, bulkResponse.getBatches()); + assertTrue(bulkResponse.getTook().getMillis() > 0); + assertEquals(1, bulkResponse.getBatches()); + assertEquals(0, bulkResponse.getBulkFailures().size()); + assertEquals(0, bulkResponse.getSearchFailures().size()); + } + { + // test2: update using script + UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(); + updateByQueryRequest.indices(sourceIndex); + updateByQueryRequest.setScript(new Script("if (ctx._source.foo == 2) ctx._source.foo++;")); + updateByQueryRequest.setRefresh(true); + BulkByScrollResponse bulkResponse = + execute(updateByQueryRequest, highLevelClient()::updateByQuery, highLevelClient()::updateByQueryAsync); + assertEquals(2, bulkResponse.getTotal()); + assertEquals(2, bulkResponse.getUpdated()); + assertEquals(0, bulkResponse.getDeleted()); + assertEquals(0, bulkResponse.getNoops()); + assertEquals(0, bulkResponse.getVersionConflicts()); + assertEquals(1, bulkResponse.getBatches()); + assertTrue(bulkResponse.getTook().getMillis() > 0); + assertEquals(1, bulkResponse.getBatches()); + assertEquals(0, bulkResponse.getBulkFailures().size()); + assertEquals(0, bulkResponse.getSearchFailures().size()); + assertEquals( + 3, + (int) (highLevelClient().get(new GetRequest(sourceIndex, "2"), RequestOptions.DEFAULT) + .getSourceAsMap().get("foo")) + ); + } + { + // test update-by-query rethrottling + UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(); + updateByQueryRequest.indices(sourceIndex); + updateByQueryRequest.setQuery(new IdsQueryBuilder().addIds("1")); + updateByQueryRequest.setRefresh(true); + + // this following settings are supposed to halt reindexing after first document + updateByQueryRequest.setBatchSize(1); + updateByQueryRequest.setRequestsPerSecond(0.00001f); + final CountDownLatch taskFinished = new CountDownLatch(1); + highLevelClient().updateByQueryAsync(updateByQueryRequest, RequestOptions.DEFAULT, new ActionListener() { + + @Override + public void onResponse(BulkByScrollResponse response) { + taskFinished.countDown(); + } + + @Override + public void onFailure(Exception e) { + fail(e.toString()); + } + }); + + TaskId taskIdToRethrottle = findTaskToRethrottle(UpdateByQueryAction.NAME); + float requestsPerSecond = 1000f; + ListTasksResponse response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), + highLevelClient()::updateByQueryRethrottle, highLevelClient()::updateByQueryRethrottleAsync); + assertThat(response.getTasks(), hasSize(1)); + assertEquals(taskIdToRethrottle, response.getTasks().get(0).getTaskId()); + assertThat(response.getTasks().get(0).getStatus(), instanceOf(RawTaskStatus.class)); + assertEquals(Float.toString(requestsPerSecond), + ((RawTaskStatus) response.getTasks().get(0).getStatus()).toMap().get("requests_per_second").toString()); + taskFinished.await(2, TimeUnit.SECONDS); + + // any rethrottling after the update-by-query is done performed with the same taskId should result in a failure + response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), + highLevelClient()::updateByQueryRethrottle, highLevelClient()::updateByQueryRethrottleAsync); + assertTrue(response.getTasks().isEmpty()); + assertFalse(response.getNodeFailures().isEmpty()); + assertEquals(1, response.getNodeFailures().size()); + assertEquals("Elasticsearch exception [type=resource_not_found_exception, reason=task [" + taskIdToRethrottle + "] is missing]", + response.getNodeFailures().get(0).getCause().getMessage()); + } + } + + public void testUpdateByQueryConflict() throws IOException { + final String index = "testupdatebyqueryconflict"; + + final Settings settings = Settings.builder() + .put("number_of_shards", 1) + .put("number_of_replicas", 0) + .build(); + createIndex(index, settings); + final BulkRequest bulkRequest = new BulkRequest() + .add(new IndexRequest(index).id("1").source(Collections.singletonMap("foo", "bar"), XContentType.JSON)) + .add(new IndexRequest(index).id("2").source(Collections.singletonMap("foo", "bar"), XContentType.JSON)) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + assertThat(highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT).status(), equalTo(RestStatus.OK)); + + putConflictPipeline(); + + final UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest(); + updateByQueryRequest.indices(index); + updateByQueryRequest.setRefresh(true); + updateByQueryRequest.setPipeline(CONFLICT_PIPELINE_ID); + final BulkByScrollResponse response = highLevelClient().updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT); + + assertThat(response.getVersionConflicts(), equalTo(1L)); + assertThat(response.getBulkFailures(), empty()); + assertThat(response.getSearchFailures(), hasSize(1)); + assertThat( + response.getSearchFailures().stream().map(ScrollableHitSource.SearchFailure::toString).collect(Collectors.toSet()), + everyItem(containsString("version conflict")) + ); + + assertThat(response.getTotal(), equalTo(2L)); + assertThat(response.getCreated(), equalTo(0L)); + assertThat(response.getUpdated(), equalTo(1L)); + assertThat(response.getDeleted(), equalTo(0L)); + assertThat(response.getNoops(), equalTo(0L)); + assertThat(response.getBatches(), equalTo(1)); + assertTrue(response.getTook().getMillis() > 0); + } + + public void testDeleteByQuery() throws Exception { + final String sourceIndex = "source1"; + { + // Prepare + Settings settings = Settings.builder() + .put("number_of_shards", 1) + .put("number_of_replicas", 0) + .build(); + createIndex(sourceIndex, settings); + assertEquals( + RestStatus.OK, + highLevelClient().bulk( + new BulkRequest() + .add(new IndexRequest(sourceIndex).id("1") + .source(Collections.singletonMap("foo", 1), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("2") + .source(Collections.singletonMap("foo", 2), XContentType.JSON)) + .add(new IndexRequest(sourceIndex).id("3") + .source(Collections.singletonMap("foo", 3), XContentType.JSON)) + .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE), + RequestOptions.DEFAULT + ).status() + ); + } + { + // test1: delete one doc + DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(); + deleteByQueryRequest.indices(sourceIndex); + deleteByQueryRequest.setQuery(new IdsQueryBuilder().addIds("1")); + deleteByQueryRequest.setRefresh(true); + BulkByScrollResponse bulkResponse = + execute(deleteByQueryRequest, highLevelClient()::deleteByQuery, highLevelClient()::deleteByQueryAsync); + assertEquals(1, bulkResponse.getTotal()); + assertEquals(1, bulkResponse.getDeleted()); + assertEquals(0, bulkResponse.getNoops()); + assertEquals(0, bulkResponse.getVersionConflicts()); + assertEquals(1, bulkResponse.getBatches()); + assertTrue(bulkResponse.getTook().getMillis() > 0); + assertEquals(1, bulkResponse.getBatches()); + assertEquals(0, bulkResponse.getBulkFailures().size()); + assertEquals(0, bulkResponse.getSearchFailures().size()); + assertEquals( + 2, + highLevelClient().search(new SearchRequest(sourceIndex), RequestOptions.DEFAULT).getHits().getTotalHits().value + ); + } + { + // test delete-by-query rethrottling + DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(); + deleteByQueryRequest.indices(sourceIndex); + deleteByQueryRequest.setQuery(new IdsQueryBuilder().addIds("2", "3")); + deleteByQueryRequest.setRefresh(true); + + // this following settings are supposed to halt reindexing after first document + deleteByQueryRequest.setBatchSize(1); + deleteByQueryRequest.setRequestsPerSecond(0.00001f); + final CountDownLatch taskFinished = new CountDownLatch(1); + highLevelClient().deleteByQueryAsync(deleteByQueryRequest, RequestOptions.DEFAULT, new ActionListener() { + + @Override + public void onResponse(BulkByScrollResponse response) { + taskFinished.countDown(); + } + + @Override + public void onFailure(Exception e) { + fail(e.toString()); + } + }); + + TaskId taskIdToRethrottle = findTaskToRethrottle(DeleteByQueryAction.NAME); + float requestsPerSecond = 1000f; + ListTasksResponse response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), + highLevelClient()::deleteByQueryRethrottle, highLevelClient()::deleteByQueryRethrottleAsync); + assertThat(response.getTasks(), hasSize(1)); + assertEquals(taskIdToRethrottle, response.getTasks().get(0).getTaskId()); + assertThat(response.getTasks().get(0).getStatus(), instanceOf(RawTaskStatus.class)); + assertEquals(Float.toString(requestsPerSecond), + ((RawTaskStatus) response.getTasks().get(0).getStatus()).toMap().get("requests_per_second").toString()); + taskFinished.await(2, TimeUnit.SECONDS); + + // any rethrottling after the delete-by-query is done performed with the same taskId should result in a failure + response = execute(new RethrottleRequest(taskIdToRethrottle, requestsPerSecond), + highLevelClient()::deleteByQueryRethrottle, highLevelClient()::deleteByQueryRethrottleAsync); + assertTrue(response.getTasks().isEmpty()); + assertFalse(response.getNodeFailures().isEmpty()); + assertEquals(1, response.getNodeFailures().size()); + assertEquals("Elasticsearch exception [type=resource_not_found_exception, reason=task [" + taskIdToRethrottle + "] is missing]", + response.getNodeFailures().get(0).getCause().getMessage()); + } + } + + private static TaskId findTaskToRethrottle(String actionName) throws IOException { + long start = System.nanoTime(); + ListTasksRequest request = new ListTasksRequest(); + request.setActions(actionName); + request.setDetailed(true); + do { + ListTasksResponse list = highLevelClient().tasks().list(request, RequestOptions.DEFAULT); + list.rethrowFailures("Finding tasks to rethrottle"); + assertThat("tasks are left over from the last execution of this test", + list.getTaskGroups(), hasSize(lessThan(2))); + if (0 == list.getTaskGroups().size()) { + // The parent task hasn't started yet + continue; + } + TaskGroup taskGroup = list.getTaskGroups().get(0); + assertThat(taskGroup.getChildTasks(), empty()); + return taskGroup.getTaskInfo().getTaskId(); + } while (System.nanoTime() - start < TimeUnit.SECONDS.toNanos(10)); + throw new AssertionError("Couldn't find tasks to rethrottle. Here are the running tasks " + + highLevelClient().tasks().list(request, RequestOptions.DEFAULT)); + } + static CheckedRunnable checkCompletionStatus(RestClient client, String taskId) { return () -> { Response response = client.performRequest(new Request("GET", "/_tasks/" + taskId)); assertTrue((boolean) entityAsMap(response).get("completed")); }; } + + private void putConflictPipeline() throws IOException { + final XContentBuilder pipelineBuilder = jsonBuilder() + .startObject() + .startArray("processors") + .startObject() + .startObject("set") + .field("field", "_version") + .field("value", 1) + .endObject() + .endObject() + .startObject() + .startObject("set") + .field("field", "_id") + .field("value", "1") + .endObject() + .endObject() + .endArray() + .endObject(); + final PutPipelineRequest putPipelineRequest = new PutPipelineRequest(CONFLICT_PIPELINE_ID, BytesReference.bytes(pipelineBuilder), + pipelineBuilder.contentType()); + assertTrue(highLevelClient().ingest().putPipeline(putPipelineRequest, RequestOptions.DEFAULT).isAcknowledged()); + } } From 2a9ee840cd7fb621dd58a6d89132c413b9caf1b2 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Wed, 27 Mar 2019 11:52:59 -0500 Subject: [PATCH 30/77] Muting test related to #40537 (#40538) --- .../xpack/dataframe/integration/DataFramePivotRestIT.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFramePivotRestIT.java b/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFramePivotRestIT.java index 34d1a388ff607..99c08f1a50583 100644 --- a/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFramePivotRestIT.java +++ b/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFramePivotRestIT.java @@ -268,6 +268,7 @@ public void testPreviewTransform() throws Exception { }); } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40537") public void testPivotWithMaxOnDateField() throws Exception { String transformId = "simpleDateHistogramPivotWithMaxTime"; String dataFrameIndex = "pivot_reviews_via_date_histogram_with_max_time"; From 6bba9fc83bc30d29c67e66e8e4c70f4dcc3b7494 Mon Sep 17 00:00:00 2001 From: Andy Bristol Date: Wed, 27 Mar 2019 10:03:30 -0700 Subject: [PATCH 31/77] search as you type fieldmapper (#35600) Adds the search_as_you_type field type that acts like a text field optimized for as-you-type search completion. It creates a couple subfields that analyze the indexed terms as shingles, against which full terms are queried, and a prefix subfield that analyze terms as the largest shingle size used and edge-ngrams, against which partial terms are queried Adds a match_bool_prefix query type that creates a boolean clause of a term query for each term except the last, for which a boolean clause with a prefix query is created. The match_bool_prefix query is the recommended way of querying a search as you type field, which will boil down to term queries for each shingle of the input text on the appropriate shingle field, and the final (possibly partial) term as a term query on the prefix field. This field type also supports phrase and phrase prefix queries however --- docs/reference/mapping/types.asciidoc | 3 + .../mapping/types/search-as-you-type.asciidoc | 258 ++++ .../query-dsl/full-text-queries.asciidoc | 9 +- .../match-bool-prefix-query.asciidoc | 85 ++ .../match-phrase-prefix-query.asciidoc | 2 +- docs/reference/query-dsl/match-query.asciidoc | 3 +- .../query-dsl/multi-match-query.asciidoc | 37 + .../suggesters/completion-suggest.asciidoc | 4 +- .../index/mapper/MapperExtrasPlugin.java | 1 + .../mapper/SearchAsYouTypeFieldMapper.java | 826 +++++++++++ .../mapper/SearchAsYouTypeAnalyzerTests.java | 197 +++ .../SearchAsYouTypeFieldMapperTests.java | 758 ++++++++++ .../mapper/SearchAsYouTypeFieldTypeTests.java | 113 ++ .../test/search-as-you-type/10_basic.yml | 1249 +++++++++++++++++ .../search-as-you-type/20_highlighting.yml | 202 +++ .../AnnotatedTextFieldMapper.java | 2 +- .../test/search/310_match_bool_prefix.yml | 363 +++++ .../index/mapper/TextFieldMapper.java | 127 +- .../query/MatchBoolPrefixQueryBuilder.java | 393 ++++++ .../index/query/MultiMatchQueryBuilder.java | 17 +- .../index/search/MatchQuery.java | 194 ++- .../index/search/MultiMatchQuery.java | 16 +- .../elasticsearch/search/SearchModule.java | 3 + .../MatchBoolPrefixQueryBuilderTests.java | 284 ++++ .../index/query/MatchQueryBuilderTests.java | 72 + .../query/MultiMatchQueryBuilderTests.java | 79 +- .../search/SearchModuleTests.java | 1 + 27 files changed, 5198 insertions(+), 100 deletions(-) create mode 100644 docs/reference/mapping/types/search-as-you-type.asciidoc create mode 100644 docs/reference/query-dsl/match-bool-prefix-query.asciidoc create mode 100644 modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java create mode 100644 modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeAnalyzerTests.java create mode 100644 modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java create mode 100644 modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldTypeTests.java create mode 100644 modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml create mode 100644 modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml create mode 100644 server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java create mode 100644 server/src/test/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilderTests.java diff --git a/docs/reference/mapping/types.asciidoc b/docs/reference/mapping/types.asciidoc index 76b832a529fb4..c0db156dc3a1c 100644 --- a/docs/reference/mapping/types.asciidoc +++ b/docs/reference/mapping/types.asciidoc @@ -52,6 +52,7 @@ string:: <> and <> <>:: Record sparse vectors of float values. +<>:: A text-like field optimized for queries to implement as-you-type completion [float] === Multi-fields @@ -110,3 +111,5 @@ include::types/rank-features.asciidoc[] include::types/dense-vector.asciidoc[] include::types/sparse-vector.asciidoc[] + +include::types/search-as-you-type.asciidoc[] diff --git a/docs/reference/mapping/types/search-as-you-type.asciidoc b/docs/reference/mapping/types/search-as-you-type.asciidoc new file mode 100644 index 0000000000000..aec21f2e3ca6c --- /dev/null +++ b/docs/reference/mapping/types/search-as-you-type.asciidoc @@ -0,0 +1,258 @@ +[[search-as-you-type]] +=== Search as you type datatype + +experimental[] + +The `search_as_you_type` field type is a text-like field that is optimized to +provide out-of-the-box support for queries that serve an as-you-type completion +use case. It creates a series of subfields that are analyzed to index terms +that can be efficiently matched by a query that partially matches the entire +indexed text value. Both prefix completion (i.e matching terms starting at the +beginning of the input) and infix completion (i.e. matching terms at any +position within the input) are supported. + +When adding a field of this type to a mapping + +[source,js] +-------------------------------------------------- +PUT my_index +{ + "mappings": { + "properties": { + "my_field": { + "type": "search_as_you_type" + } + } + } +} +-------------------------------------------------- +// CONSOLE + +This creates the following fields + +[horizontal] + +`my_field`:: + + Analyzed as configured in the mapping. If an analyzer is not configured, + the default analyzer for the index is used + +`my_field._2gram`:: + + Wraps the analyzer of `my_field` with a shingle token filter of shingle + size 2 + +`my_field._3gram`:: + + Wraps the analyzer of `my_field` with a shingle token filter of shingle + size 3 + +`my_field._index_prefix`:: + + Wraps the analyzer of `my_field._3gram` with an edge ngram token filter + + +The size of shingles in subfields can be configured with the `max_shingle_size` +mapping parameter. The default is 3, and valid values for this parameter are +integer values 2 - 4 inclusive. Shingle subfields will be created for each +shingle size from 2 up to and including the `max_shingle_size`. The +`my_field._index_prefix` subfield will always use the analyzer from the shingle +subfield with the `max_shingle_size` when constructing its own analyzer. + +Increasing the `max_shingle_size` will improve matches for queries with more +consecutive terms, at the cost of larger index size. The default +`max_shingle_size` should usually be sufficient. + +The same input text is indexed into each of these fields automatically, with +their differing analysis chains, when an indexed document has a value for the +root field `my_field`. + +[source,js] +-------------------------------------------------- +PUT my_index/_doc/1?refresh +{ + "my_field": "quick brown fox jump lazy dog" +} +-------------------------------------------------- +// CONSOLE +// TEST[continued] + +The most efficient way of querying to serve a search-as-you-type use case is +usually a <> query of type +<> that targets the root +`search_as_you_type` field and its shingle subfields. This can match the query +terms in any order, but will score documents higher if they contain the terms +in order in a shingle subfield. + +[source,js] +-------------------------------------------------- +GET my_index/_search +{ + "query": { + "multi_match": { + "query": "brown f", + "type": "bool_prefix", + "fields": [ + "my_field", + "my_field._2gram", + "my_field._3gram" + ] + } + } +} +-------------------------------------------------- +// CONSOLE +// TEST[continued] + +[source,js] +-------------------------------------------------- +{ + "took" : 44, + "timed_out" : false, + "_shards" : { + "total" : 1, + "successful" : 1, + "skipped" : 0, + "failed" : 0 + }, + "hits" : { + "total" : { + "value" : 1, + "relation" : "eq" + }, + "max_score" : 0.8630463, + "hits" : [ + { + "_index" : "my_index", + "_type" : "_doc", + "_id" : "1", + "_score" : 0.8630463, + "_source" : { + "my_field" : "quick brown fox jump lazy dog" + } + } + ] + } +} +-------------------------------------------------- +// TESTRESPONSE[s/"took" : 44/"took" : $body.took/] +// TESTRESPONSE[s/"max_score" : 0.8630463/"max_score" : $body.hits.max_score/] +// TESTRESPONSE[s/"_score" : 0.8630463/"_score" : $body.hits.hits.0._score/] + +To search for documents that strictly match the query terms in order, or to +search using other properties of phrase queries, use a +<> on the root +field. A <> can also be used +if the last term should be matched exactly, and not as a prefix. Using phrase +queries may be less efficient than using the `match_bool_prefix` query. + +[source,js] +-------------------------------------------------- +GET my_index/_search +{ + "query": { + "match_phrase_prefix": { + "my_field": "brown f" + } + } +} +-------------------------------------------------- +// CONSOLE +// TEST[continued] + +[[specific-params]] +==== Parameters specific to the `search_as_you_type` field + +The following parameters are accepted in a mapping for the `search_as_you_type` +field and are specific to this field type + +[horizontal] + +`max_shingle_size`:: + + The largest shingle size to index the input with and create subfields for, + creating one subfield for each shingle size between 2 and + `max_shingle_size`. Accepts integer values between 2 and 4 inclusive. This + option defaults to 3. + + +[[general-params]] +==== Parameters of the field type as a text field + +The following parameters are accepted in a mapping for the `search_as_you_type` +field due to its nature as a text-like field, and behave similarly to their +behavior when configuring a field of the <> datatype. Unless +otherwise noted, these options configure the root fields subfields in +the same way. + +<>:: + + The <> which should be used for + <> string fields, both at index-time and at + search-time (unless overridden by the + <>). Defaults to the default index + analyzer, or the <>. + +<>:: + + Should the field be searchable? Accepts `true` (default) or `false`. + +<>:: + + What information should be stored in the index, for search and highlighting + purposes. Defaults to `positions`. + +<>:: + + Whether field-length should be taken into account when scoring queries. + Accepts `true` or `false`. This option configures the root field + and shingle subfields, where its default is `true`. It does not configure + the prefix subfield, where it it `false`. + +<>:: + + Whether the field value should be stored and retrievable separately from + the <> field. Accepts `true` or `false` + (default). This option only configures the root field, and does not + configure any subfields. + +<>:: + + The <> that should be used at search time on + <> fields. Defaults to the `analyzer` setting. + +<>:: + + The <> that should be used at search time when a + phrase is encountered. Defaults to the `search_analyzer` setting. + +<>:: + + Which scoring algorithm or _similarity_ should be used. Defaults + to `BM25`. + +<>:: + + Whether term vectors should be stored for an <> + field. Defaults to `no`. This option configures the root field and shingle + subfields, but not the prefix subfield. + + +[[prefix-queries]] +==== Optimization of prefix queries + +When making a <> query to the root field or +any of its subfields, the query will be rewritten to a +<> query on the `._index_prefix` subfield. This +matches more efficiently than is typical of `prefix` queries on text fields, +as prefixes up to a certain length of each shingle are indexed directly as +terms in the `._index_prefix` subfield. + +The analyzer of the `._index_prefix` subfield slightly modifies the +shingle-building behavior to also index prefixes of the terms at the end of the +field's value that normally would not be produced as shingles. For example, if +the value `quick brown fox` is indexed into a `search_as_you_type` field with +`max_shingle_size` of 3, prefixes for `brown fox` and `fox` are also indexed +into the `._index_prefix` subfield even though they do not appear as terms in +the `._3gram` subfield. This allows for completion of all the terms in the +field's input. diff --git a/docs/reference/query-dsl/full-text-queries.asciidoc b/docs/reference/query-dsl/full-text-queries.asciidoc index 5fb5447dbb79a..0af99b61f194f 100644 --- a/docs/reference/query-dsl/full-text-queries.asciidoc +++ b/docs/reference/query-dsl/full-text-queries.asciidoc @@ -18,7 +18,12 @@ The queries in this group are: <>:: - The poor man's _search-as-you-type_. Like the `match_phrase` query, but does a wildcard search on the final word. + Like the `match_phrase` query, but does a wildcard search on the final word. + +<>:: + + Creates a `bool` query that matches each term as a `term` query, except for + the last term, which is matched as a `prefix` query <>:: @@ -50,6 +55,8 @@ include::match-phrase-query.asciidoc[] include::match-phrase-prefix-query.asciidoc[] +include::match-bool-prefix-query.asciidoc[] + include::multi-match-query.asciidoc[] include::common-terms-query.asciidoc[] diff --git a/docs/reference/query-dsl/match-bool-prefix-query.asciidoc b/docs/reference/query-dsl/match-bool-prefix-query.asciidoc new file mode 100644 index 0000000000000..623f2423d8055 --- /dev/null +++ b/docs/reference/query-dsl/match-bool-prefix-query.asciidoc @@ -0,0 +1,85 @@ +[[query-dsl-match-bool-prefix-query]] +=== Match Bool Prefix Query + +A `match_bool_prefix` query analyzes its input and constructs a +<> from the terms. Each term except the last +is used in a `term` query. The last term is used in a `prefix` query. A +`match_bool_prefix` query such as + +[source,js] +-------------------------------------------------- +GET /_search +{ + "query": { + "match_bool_prefix" : { + "message" : "quick brown f" + } + } +} +-------------------------------------------------- +// CONSOLE + +where analysis produces the terms `quick`, `brown`, and `f` is similar to the +following `bool` query + +[source,js] +-------------------------------------------------- +GET /_search +{ + "query": { + "bool" : { + "should": [ + { "term": { "message": "quick" }}, + { "term": { "message": "brown" }}, + { "prefix": { "message": "f"}} + ] + } + } +} +-------------------------------------------------- +// CONSOLE + +An important difference between the `match_bool_prefix` query and +<> is that the +`match_phrase_prefix` query matches its terms as a phrase, but the +`match_bool_prefix` query can match its terms in any position. The example +`match_bool_prefix` query above could match a field containing containing +`quick brown fox`, but it could also match `brown fox quick`. It could also +match a field containing the term `quick`, the term `brown` and a term +starting with `f`, appearing in any position. + +==== Parameters + +By default, `match_bool_prefix` queries' input text will be analyzed using the +analyzer from the queried field's mapping. A different search analyzer can be +configured with the `analyzer` parameter + +[source,js] +-------------------------------------------------- +GET /_search +{ + "query": { + "match_bool_prefix" : { + "message": { + "query": "quick brown f", + "analyzer": "keyword" + } + } + } +} +-------------------------------------------------- +// CONSOLE + +`match_bool_prefix` queries support the +<> and `operator` +parameters as described for the +<>, applying the setting to the +constructed `bool` query. The number of clauses in the constructed `bool` +query will in most cases be the number of terms produced by analysis of the +query text. + +The <>, `prefix_length`, +`max_expansions`, `fuzzy_transpositions`, and `fuzzy_rewrite` parameters can +be applied to the `term` subqueries constructed for all terms but the final +term. They do not have any effect on the prefix query constructed for the +final term. diff --git a/docs/reference/query-dsl/match-phrase-prefix-query.asciidoc b/docs/reference/query-dsl/match-phrase-prefix-query.asciidoc index 73f1be9143cf2..304eaf9a5b4f0 100644 --- a/docs/reference/query-dsl/match-phrase-prefix-query.asciidoc +++ b/docs/reference/query-dsl/match-phrase-prefix-query.asciidoc @@ -59,6 +59,6 @@ for appears. For better solutions for _search-as-you-type_ see the <> and -{defguide}/_index_time_search_as_you_type.html[Index-Time Search-as-You-Type]. +the <>. =================================================== diff --git a/docs/reference/query-dsl/match-query.asciidoc b/docs/reference/query-dsl/match-query.asciidoc index b939364f12027..64c356ccaa80c 100644 --- a/docs/reference/query-dsl/match-query.asciidoc +++ b/docs/reference/query-dsl/match-query.asciidoc @@ -202,7 +202,6 @@ process. It does not support field name prefixes, wildcard characters, or other "advanced" features. For this reason, chances of it failing are very small / non existent, and it provides an excellent behavior when it comes to just analyze and run that text as a query behavior (which is -usually what a text search box does). Also, the <> -type can provide a great "as you type" behavior to automatically load search results. +usually what a text search box does). ************************************************** diff --git a/docs/reference/query-dsl/multi-match-query.asciidoc b/docs/reference/query-dsl/multi-match-query.asciidoc index 512eee4900b41..b8fbb61a950d0 100644 --- a/docs/reference/query-dsl/multi-match-query.asciidoc +++ b/docs/reference/query-dsl/multi-match-query.asciidoc @@ -91,6 +91,10 @@ parameter, which can be set to: `phrase_prefix`:: Runs a `match_phrase_prefix` query on each field and combines the `_score` from each field. See <>. +`bool_prefix`:: Creates a `match_bool_prefix` query on each field and + combines the `_score` from each field. See + <>. + [[type-best-fields]] ==== `best_fields` @@ -516,3 +520,36 @@ per-term `blended` queries. It accepts: =================================================== The `fuzziness` parameter cannot be used with the `cross_fields` type. =================================================== + +[[type-bool-prefix]] +==== `bool_prefix` + +The `bool_prefix` type's scoring behaves like <>, but using a +<> instead of a +`match` query. + +[source,js] +-------------------------------------------------- +GET /_search +{ + "query": { + "multi_match" : { + "query": "quick brown f", + "type": "bool_prefix", + "fields": [ "subject", "message" ] + } + } +} +-------------------------------------------------- +// CONSOLE + +The `analyzer`, `boost`, `operator`, `minimum_should_match`, `lenient`, +`zero_terms_query`, and `auto_generate_synonyms_phrase_query` parameters as +explained in <> are supported. The +`fuzziness`, `prefix_length`, `max_expansions`, `rewrite`, and +`fuzzy_transpositions` parameters are supported for the terms that are used to +construct term queries, but do not have an effect on the prefix query +constructed from the final term. + +The `slop` and `cutoff_frequency` parameters are not supported by this query +type. diff --git a/docs/reference/search/suggesters/completion-suggest.asciidoc b/docs/reference/search/suggesters/completion-suggest.asciidoc index b27e6f0ef0b54..c89dce3d24160 100644 --- a/docs/reference/search/suggesters/completion-suggest.asciidoc +++ b/docs/reference/search/suggesters/completion-suggest.asciidoc @@ -2,7 +2,9 @@ === Completion Suggester NOTE: In order to understand the format of suggestions, please -read the <> page first. +read the <> page first. For more flexible +search-as-you-type searches that do not use suggesters, see the +<>. The `completion` suggester provides auto-complete/search-as-you-type functionality. This is a navigational feature to guide users to diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java index cbafd0fd1efff..45a067d7994d2 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java @@ -41,6 +41,7 @@ public Map getMappers() { mappers.put(RankFeaturesFieldMapper.CONTENT_TYPE, new RankFeaturesFieldMapper.TypeParser()); mappers.put(DenseVectorFieldMapper.CONTENT_TYPE, new DenseVectorFieldMapper.TypeParser()); mappers.put(SparseVectorFieldMapper.CONTENT_TYPE, new SparseVectorFieldMapper.TypeParser()); + mappers.put(SearchAsYouTypeFieldMapper.CONTENT_TYPE, new SearchAsYouTypeFieldMapper.TypeParser()); return Collections.unmodifiableMap(mappers); } diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java new file mode 100644 index 0000000000000..69948bf98a6ac --- /dev/null +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java @@ -0,0 +1,826 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.AnalyzerWrapper; +import org.apache.lucene.analysis.CachingTokenFilter; +import org.apache.lucene.analysis.TokenFilter; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter; +import org.apache.lucene.analysis.shingle.FixedShingleFilter; +import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; +import org.apache.lucene.document.Field; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.AutomatonQuery; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.NormsFieldExistsQuery; +import org.apache.lucene.search.PrefixQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.spans.FieldMaskingSpanQuery; +import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; +import org.apache.lucene.search.spans.SpanQuery; +import org.apache.lucene.search.spans.SpanTermQuery; +import org.apache.lucene.util.automaton.Automata; +import org.apache.lucene.util.automaton.Automaton; +import org.apache.lucene.util.automaton.Operations; +import org.elasticsearch.common.collect.Iterators; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.index.analysis.AnalyzerScope; +import org.elasticsearch.index.analysis.NamedAnalyzer; +import org.elasticsearch.index.query.QueryShardContext; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeIntegerValue; +import static org.elasticsearch.index.mapper.TextFieldMapper.TextFieldType.hasGaps; +import static org.elasticsearch.index.mapper.TypeParsers.parseTextField; + +/** + * Mapper for a text field that optimizes itself for as-you-type completion by indexing its content into subfields. Each subfield + * modifies the analysis chain of the root field to index terms the user would create as they type out the value in the root field + * + * The structure of these fields is + * + *
+ *     [ SearchAsYouTypeFieldMapper, SearchAsYouTypeFieldType, unmodified analysis ]
+ *     ├── [ ShingleFieldMapper, ShingleFieldType, analysis wrapped with 2-shingles ]
+ *     ├── ...
+ *     ├── [ ShingleFieldMapper, ShingleFieldType, analysis wrapped with max_shingle_size-shingles ]
+ *     └── [ PrefixFieldMapper, PrefixFieldType, analysis wrapped with max_shingle_size-shingles and edge-ngrams ]
+ * 
+ */ +public class SearchAsYouTypeFieldMapper extends FieldMapper { + + public static final String CONTENT_TYPE = "search_as_you_type"; + private static final int MAX_SHINGLE_SIZE_LOWER_BOUND = 2; + private static final int MAX_SHINGLE_SIZE_UPPER_BOUND = 4; + private static final String PREFIX_FIELD_SUFFIX = "._index_prefix"; + + public static class Defaults { + + public static final int MIN_GRAM = 1; + public static final int MAX_GRAM = 20; + public static final int MAX_SHINGLE_SIZE = 3; + + public static final MappedFieldType FIELD_TYPE = new SearchAsYouTypeFieldType(); + + static { + FIELD_TYPE.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS); + FIELD_TYPE.freeze(); + } + } + + public static class TypeParser implements Mapper.TypeParser { + + @Override + public Mapper.Builder parse(String name, + Map node, + ParserContext parserContext) throws MapperParsingException { + + final Builder builder = new Builder(name); + + builder.fieldType().setIndexAnalyzer(parserContext.getIndexAnalyzers().getDefaultIndexAnalyzer()); + builder.fieldType().setSearchAnalyzer(parserContext.getIndexAnalyzers().getDefaultSearchAnalyzer()); + builder.fieldType().setSearchQuoteAnalyzer(parserContext.getIndexAnalyzers().getDefaultSearchQuoteAnalyzer()); + parseTextField(builder, name, node, parserContext); + for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { + final Map.Entry entry = iterator.next(); + final String fieldName = entry.getKey(); + final Object fieldNode = entry.getValue(); + + if (fieldName.equals("max_shingle_size")) { + builder.maxShingleSize(nodeIntegerValue(fieldNode)); + iterator.remove(); + } + // TODO should we allow to configure the prefix field + } + return builder; + } + } + + public static class Builder extends FieldMapper.Builder { + private int maxShingleSize = Defaults.MAX_SHINGLE_SIZE; + + public Builder(String name) { + super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE); + this.builder = this; + } + + public Builder maxShingleSize(int maxShingleSize) { + if (maxShingleSize < MAX_SHINGLE_SIZE_LOWER_BOUND || maxShingleSize > MAX_SHINGLE_SIZE_UPPER_BOUND) { + throw new MapperParsingException("[max_shingle_size] must be at least [" + MAX_SHINGLE_SIZE_LOWER_BOUND + "] and at most " + + "[" + MAX_SHINGLE_SIZE_UPPER_BOUND + "], got [" + maxShingleSize + "]"); + } + this.maxShingleSize = maxShingleSize; + return builder; + } + + @Override + public SearchAsYouTypeFieldType fieldType() { + return (SearchAsYouTypeFieldType) this.fieldType; + } + + @Override + public SearchAsYouTypeFieldMapper build(Mapper.BuilderContext context) { + setupFieldType(context); + + final NamedAnalyzer indexAnalyzer = fieldType().indexAnalyzer(); + final NamedAnalyzer searchAnalyzer = fieldType().searchAnalyzer(); + final NamedAnalyzer searchQuoteAnalyzer = fieldType().searchQuoteAnalyzer(); + + // set up the prefix field + final String prefixFieldName = name() + PREFIX_FIELD_SUFFIX; + final PrefixFieldType prefixFieldType = new PrefixFieldType(name(), prefixFieldName, Defaults.MIN_GRAM, Defaults.MAX_GRAM); + prefixFieldType.setIndexOptions(fieldType().indexOptions()); + // wrap the root field's index analyzer with shingles and edge ngrams + final SearchAsYouTypeAnalyzer prefixIndexWrapper = + SearchAsYouTypeAnalyzer.withShingleAndPrefix(indexAnalyzer.analyzer(), maxShingleSize); + // wrap the root field's search analyzer with only shingles + final SearchAsYouTypeAnalyzer prefixSearchWrapper = + SearchAsYouTypeAnalyzer.withShingle(searchAnalyzer.analyzer(), maxShingleSize); + // don't wrap the root field's search quote analyzer as prefix field doesn't support phrase queries + prefixFieldType.setIndexAnalyzer(new NamedAnalyzer(indexAnalyzer.name(), AnalyzerScope.INDEX, prefixIndexWrapper)); + prefixFieldType.setSearchAnalyzer(new NamedAnalyzer(searchAnalyzer.name(), AnalyzerScope.INDEX, prefixSearchWrapper)); + final PrefixFieldMapper prefixFieldMapper = new PrefixFieldMapper(prefixFieldType, context.indexSettings()); + + // set up the shingle fields + final ShingleFieldMapper[] shingleFieldMappers = new ShingleFieldMapper[maxShingleSize - 1]; + final ShingleFieldType[] shingleFieldTypes = new ShingleFieldType[maxShingleSize - 1]; + for (int i = 0; i < shingleFieldMappers.length; i++) { + final int shingleSize = i + 2; + final ShingleFieldType shingleFieldType = new ShingleFieldType(fieldType(), shingleSize); + shingleFieldType.setName(getShingleFieldName(name(), shingleSize)); + // wrap the root field's index, search, and search quote analyzers with shingles + final SearchAsYouTypeAnalyzer shingleIndexWrapper = + SearchAsYouTypeAnalyzer.withShingle(indexAnalyzer.analyzer(), shingleSize); + final SearchAsYouTypeAnalyzer shingleSearchWrapper = + SearchAsYouTypeAnalyzer.withShingle(searchAnalyzer.analyzer(), shingleSize); + final SearchAsYouTypeAnalyzer shingleSearchQuoteWrapper = + SearchAsYouTypeAnalyzer.withShingle(searchQuoteAnalyzer.analyzer(), shingleSize); + shingleFieldType.setIndexAnalyzer(new NamedAnalyzer(indexAnalyzer.name(), AnalyzerScope.INDEX, shingleIndexWrapper)); + shingleFieldType.setSearchAnalyzer(new NamedAnalyzer(searchAnalyzer.name(), AnalyzerScope.INDEX, shingleSearchWrapper)); + shingleFieldType.setSearchQuoteAnalyzer( + new NamedAnalyzer(searchQuoteAnalyzer.name(), AnalyzerScope.INDEX, shingleSearchQuoteWrapper)); + shingleFieldType.setPrefixFieldType(prefixFieldType); + shingleFieldTypes[i] = shingleFieldType; + shingleFieldMappers[i] = new ShingleFieldMapper(shingleFieldType, context.indexSettings()); + } + fieldType().setPrefixField(prefixFieldType); + fieldType().setShingleFields(shingleFieldTypes); + return new SearchAsYouTypeFieldMapper(name, fieldType(), context.indexSettings(), copyTo, + maxShingleSize, prefixFieldMapper, shingleFieldMappers); + } + } + + private static int countPosition(TokenStream stream) throws IOException { + assert stream instanceof CachingTokenFilter; + PositionIncrementAttribute posIncAtt = stream.getAttribute(PositionIncrementAttribute.class); + stream.reset(); + int positionCount = 0; + while (stream.incrementToken()) { + if (posIncAtt.getPositionIncrement() != 0) { + positionCount += posIncAtt.getPositionIncrement(); + } + } + return positionCount; + } + + /** + * The root field type, which most queries should target as it will delegate queries to subfields better optimized for the query. When + * handling phrase queries, it analyzes the query text to find the appropriate sized shingle subfield to delegate to. When handling + * prefix or phrase prefix queries, it delegates to the prefix subfield + */ + static class SearchAsYouTypeFieldType extends StringFieldType { + + PrefixFieldType prefixField; + ShingleFieldType[] shingleFields = new ShingleFieldType[0]; + + SearchAsYouTypeFieldType() { + setTokenized(true); + } + + SearchAsYouTypeFieldType(SearchAsYouTypeFieldType other) { + super(other); + + if (other.prefixField != null) { + this.prefixField = other.prefixField.clone(); + } + if (other.shingleFields != null) { + this.shingleFields = new ShingleFieldType[other.shingleFields.length]; + for (int i = 0; i < this.shingleFields.length; i++) { + if (other.shingleFields[i] != null) { + this.shingleFields[i] = other.shingleFields[i].clone(); + } + } + } + } + + public void setPrefixField(PrefixFieldType prefixField) { + checkIfFrozen(); + this.prefixField = prefixField; + } + + public void setShingleFields(ShingleFieldType[] shingleFields) { + checkIfFrozen(); + this.shingleFields = shingleFields; + } + + @Override + public MappedFieldType clone() { + return new SearchAsYouTypeFieldType(this); + } + + @Override + public String typeName() { + return CONTENT_TYPE; + } + + private ShingleFieldType shingleFieldForPositions(int positions) { + final int indexFromShingleSize = Math.max(positions - 2, 0); + return shingleFields[Math.min(indexFromShingleSize, shingleFields.length - 1)]; + } + + @Override + public Query existsQuery(QueryShardContext context) { + if (omitNorms()) { + return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name())); + } else { + return new NormsFieldExistsQuery(name()); + } + } + + @Override + public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) { + if (prefixField == null || prefixField.termLengthWithinBounds(value.length()) == false) { + return super.prefixQuery(value, method, context); + } else { + final Query query = prefixField.prefixQuery(value, method, context); + if (method == null + || method == MultiTermQuery.CONSTANT_SCORE_REWRITE + || method == MultiTermQuery.CONSTANT_SCORE_BOOLEAN_REWRITE) { + return new ConstantScoreQuery(query); + } else { + return query; + } + } + } + + @Override + public Query phraseQuery(TokenStream stream, int slop, boolean enablePositionIncrements) throws IOException { + int numPos = countPosition(stream); + if (shingleFields.length == 0 || slop > 0 || hasGaps(stream) || numPos <= 1) { + return TextFieldMapper.createPhraseQuery(stream, name(), slop, enablePositionIncrements); + } + final ShingleFieldType shingleField = shingleFieldForPositions(numPos); + stream = new FixedShingleFilter(stream, shingleField.shingleSize); + return shingleField.phraseQuery(stream, 0, true); + } + + @Override + public Query multiPhraseQuery(TokenStream stream, int slop, boolean enablePositionIncrements) throws IOException { + int numPos = countPosition(stream); + if (shingleFields.length == 0 || slop > 0 || hasGaps(stream) || numPos <= 1) { + return TextFieldMapper.createPhraseQuery(stream, name(), slop, enablePositionIncrements); + } + final ShingleFieldType shingleField = shingleFieldForPositions(numPos); + stream = new FixedShingleFilter(stream, shingleField.shingleSize); + return shingleField.multiPhraseQuery(stream, 0, true); + } + + @Override + public Query phrasePrefixQuery(TokenStream stream, int slop, int maxExpansions) throws IOException { + int numPos = countPosition(stream); + if (shingleFields.length == 0 || slop > 0 || hasGaps(stream) || numPos <= 1) { + return TextFieldMapper.createPhrasePrefixQuery(stream, name(), slop, maxExpansions, + null, null); + } + final ShingleFieldType shingleField = shingleFieldForPositions(numPos); + stream = new FixedShingleFilter(stream, shingleField.shingleSize); + return shingleField.phrasePrefixQuery(stream, 0, maxExpansions); + } + + @Override + public SpanQuery spanPrefixQuery(String value, SpanMultiTermQueryWrapper.SpanRewriteMethod method, QueryShardContext context) { + if (prefixField != null && prefixField.termLengthWithinBounds(value.length())) { + return new FieldMaskingSpanQuery(new SpanTermQuery(new Term(prefixField.name(), indexedValueForSearch(value))), name()); + } else { + SpanMultiTermQueryWrapper spanMulti = + new SpanMultiTermQueryWrapper<>(new PrefixQuery(new Term(name(), indexedValueForSearch(value)))); + spanMulti.setRewriteMethod(method); + return spanMulti; + } + } + + @Override + public void checkCompatibility(MappedFieldType other, List conflicts) { + super.checkCompatibility(other, conflicts); + final SearchAsYouTypeFieldType otherFieldType = (SearchAsYouTypeFieldType) other; + if (this.shingleFields.length != otherFieldType.shingleFields.length) { + conflicts.add("mapper [" + name() + "] has a different [max_shingle_size]"); + } else if (Arrays.equals(this.shingleFields, otherFieldType.shingleFields) == false) { + conflicts.add("mapper [" + name() + "] has shingle subfields that are configured differently"); + } + + if (Objects.equals(this.prefixField, otherFieldType.prefixField) == false) { + conflicts.add("mapper [" + name() + "] has different [index_prefixes] settings"); + } + } + + @Override + public boolean equals(Object otherObject) { + if (this == otherObject) { + return true; + } + if (otherObject == null || getClass() != otherObject.getClass()) { + return false; + } + if (!super.equals(otherObject)) { + return false; + } + final SearchAsYouTypeFieldType other = (SearchAsYouTypeFieldType) otherObject; + return Objects.equals(prefixField, other.prefixField) && + Arrays.equals(shingleFields, other.shingleFields); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), prefixField, Arrays.hashCode(shingleFields)); + } + } + + /** + * The prefix field type handles prefix and phrase prefix queries that are delegated to it by the other field types in a + * search_as_you_type structure + */ + static final class PrefixFieldType extends StringFieldType { + + final int minChars; + final int maxChars; + final String parentField; + + PrefixFieldType(String parentField, String name, int minChars, int maxChars) { + setTokenized(true); + setOmitNorms(true); + setStored(false); + setName(name); + this.minChars = minChars; + this.maxChars = maxChars; + this.parentField = parentField; + } + + PrefixFieldType(PrefixFieldType other) { + super(other); + this.minChars = other.minChars; + this.maxChars = other.maxChars; + this.parentField = other.parentField; + } + + boolean termLengthWithinBounds(int length) { + return length >= minChars - 1 && length <= maxChars; + } + + @Override + public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) { + if (value.length() >= minChars) { + return super.termQuery(value, context); + } + List automata = new ArrayList<>(); + automata.add(Automata.makeString(value)); + for (int i = value.length(); i < minChars; i++) { + automata.add(Automata.makeAnyChar()); + } + Automaton automaton = Operations.concatenate(automata); + AutomatonQuery query = new AutomatonQuery(new Term(name(), value + "*"), automaton); + query.setRewriteMethod(method); + return new BooleanQuery.Builder() + .add(query, BooleanClause.Occur.SHOULD) + .add(new TermQuery(new Term(parentField, value)), BooleanClause.Occur.SHOULD) + .build(); + } + + @Override + public PrefixFieldType clone() { + return new PrefixFieldType(this); + } + + @Override + public String typeName() { + return "prefix"; + } + + @Override + public String toString() { + return super.toString() + ",prefixChars=" + minChars + ":" + maxChars; + } + + @Override + public Query existsQuery(QueryShardContext context) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + PrefixFieldType that = (PrefixFieldType) o; + return minChars == that.minChars && + maxChars == that.maxChars; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), minChars, maxChars); + } + } + + static final class PrefixFieldMapper extends FieldMapper { + + PrefixFieldMapper(PrefixFieldType fieldType, Settings indexSettings) { + super(fieldType.name(), fieldType, fieldType, indexSettings, MultiFields.empty(), CopyTo.empty()); + } + + @Override + public PrefixFieldType fieldType() { + return (PrefixFieldType) super.fieldType(); + } + + @Override + protected void parseCreateField(ParseContext context, List fields) { + throw new UnsupportedOperationException(); + } + + @Override + protected String contentType() { + return "prefix"; + } + + @Override + public String toString() { + return fieldType().toString(); + } + } + + static final class ShingleFieldMapper extends FieldMapper { + + ShingleFieldMapper(ShingleFieldType fieldType, Settings indexSettings) { + super(fieldType.name(), fieldType, fieldType, indexSettings, MultiFields.empty(), CopyTo.empty()); + } + + @Override + public ShingleFieldType fieldType() { + return (ShingleFieldType) super.fieldType(); + } + + @Override + protected void parseCreateField(ParseContext context, List fields) { + throw new UnsupportedOperationException(); + } + + @Override + protected String contentType() { + return CONTENT_TYPE; + } + } + + /** + * The shingle field type handles phrase queries and delegates prefix and phrase prefix queries to the prefix field + */ + static class ShingleFieldType extends StringFieldType { + final int shingleSize; + PrefixFieldType prefixFieldType; + + ShingleFieldType(MappedFieldType other, int shingleSize) { + super(other); + this.shingleSize = shingleSize; + this.setStored(false); + } + + ShingleFieldType(ShingleFieldType other) { + super(other); + this.shingleSize = other.shingleSize; + if (other.prefixFieldType != null) { + this.prefixFieldType = other.prefixFieldType.clone(); + } + } + + void setPrefixFieldType(PrefixFieldType prefixFieldType) { + checkIfFrozen(); + this.prefixFieldType = prefixFieldType; + } + + @Override + public ShingleFieldType clone() { + return new ShingleFieldType(this); + } + + @Override + public String typeName() { + return CONTENT_TYPE; + } + + @Override + public Query existsQuery(QueryShardContext context) { + if (omitNorms()) { + return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name())); + } else { + return new NormsFieldExistsQuery(name()); + } + } + + @Override + public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) { + if (prefixFieldType == null || prefixFieldType.termLengthWithinBounds(value.length()) == false) { + return super.prefixQuery(value, method, context); + } else { + final Query query = prefixFieldType.prefixQuery(value, method, context); + if (method == null + || method == MultiTermQuery.CONSTANT_SCORE_REWRITE + || method == MultiTermQuery.CONSTANT_SCORE_BOOLEAN_REWRITE) { + return new ConstantScoreQuery(query); + } else { + return query; + } + } + } + + @Override + public Query phraseQuery(TokenStream stream, int slop, boolean enablePositionIncrements) throws IOException { + return TextFieldMapper.createPhraseQuery(stream, name(), slop, enablePositionIncrements); + } + + @Override + public Query multiPhraseQuery(TokenStream stream, int slop, boolean enablePositionIncrements) throws IOException { + return TextFieldMapper.createPhraseQuery(stream, name(), slop, enablePositionIncrements); + } + + @Override + public Query phrasePrefixQuery(TokenStream stream, int slop, int maxExpansions) throws IOException { + final String prefixFieldName = slop > 0 + ? null + : prefixFieldType.name(); + return TextFieldMapper.createPhrasePrefixQuery(stream, name(), slop, maxExpansions, + prefixFieldName, prefixFieldType::termLengthWithinBounds); + } + + @Override + public SpanQuery spanPrefixQuery(String value, SpanMultiTermQueryWrapper.SpanRewriteMethod method, QueryShardContext context) { + if (prefixFieldType != null && prefixFieldType.termLengthWithinBounds(value.length())) { + return new FieldMaskingSpanQuery(new SpanTermQuery(new Term(prefixFieldType.name(), indexedValueForSearch(value))), name()); + } else { + SpanMultiTermQueryWrapper spanMulti = + new SpanMultiTermQueryWrapper<>(new PrefixQuery(new Term(name(), indexedValueForSearch(value)))); + spanMulti.setRewriteMethod(method); + return spanMulti; + } + } + + @Override + public void checkCompatibility(MappedFieldType other, List conflicts) { + super.checkCompatibility(other, conflicts); + ShingleFieldType ft = (ShingleFieldType) other; + if (ft.shingleSize != this.shingleSize) { + conflicts.add("mapper [" + name() + "] has different [shingle_size] values"); + } + if (Objects.equals(this.prefixFieldType, ft.prefixFieldType) == false) { + conflicts.add("mapper [" + name() + "] has different [index_prefixes] settings"); + } + } + + @Override + public boolean equals(Object otherObject) { + if (this == otherObject) { + return true; + } + if (otherObject == null || getClass() != otherObject.getClass()) { + return false; + } + if (!super.equals(otherObject)) { + return false; + } + final ShingleFieldType other = (ShingleFieldType) otherObject; + return shingleSize == other.shingleSize + && Objects.equals(prefixFieldType, other.prefixFieldType); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), shingleSize, prefixFieldType); + } + } + + private final int maxShingleSize; + private PrefixFieldMapper prefixField; + private final ShingleFieldMapper[] shingleFields; + + public SearchAsYouTypeFieldMapper(String simpleName, + SearchAsYouTypeFieldType fieldType, + Settings indexSettings, + CopyTo copyTo, + int maxShingleSize, + PrefixFieldMapper prefixField, + ShingleFieldMapper[] shingleFields) { + super(simpleName, fieldType, Defaults.FIELD_TYPE, indexSettings, MultiFields.empty(), copyTo); + this.prefixField = prefixField; + this.shingleFields = shingleFields; + this.maxShingleSize = maxShingleSize; + } + + @Override + protected void parseCreateField(ParseContext context, List fields) throws IOException { + final String value = context.externalValueSet() ? context.externalValue().toString() : context.parser().textOrNull(); + if (value == null) { + return; + } + + List newFields = new ArrayList<>(); + newFields.add(new Field(fieldType().name(), value, fieldType())); + for (ShingleFieldMapper subFieldMapper : shingleFields) { + fields.add(new Field(subFieldMapper.fieldType().name(), value, subFieldMapper.fieldType())); + } + newFields.add(new Field(prefixField.fieldType().name(), value, prefixField.fieldType())); + if (fieldType().omitNorms()) { + createFieldNamesField(context, newFields); + } + fields.addAll(newFields); + } + + @Override + protected String contentType() { + return CONTENT_TYPE; + } + + @Override + protected void doMerge(Mapper mergeWith) { + super.doMerge(mergeWith); + SearchAsYouTypeFieldMapper mw = (SearchAsYouTypeFieldMapper) mergeWith; + if (mw.maxShingleSize != maxShingleSize) { + throw new IllegalArgumentException("mapper [" + name() + "] has different maxShingleSize setting, current [" + + this.maxShingleSize + "], merged [" + mw.maxShingleSize + "]"); + } + this.prefixField = (PrefixFieldMapper) this.prefixField.merge(mw); + + ShingleFieldMapper[] shingleFieldMappers = new ShingleFieldMapper[mw.shingleFields.length]; + for (int i = 0; i < shingleFieldMappers.length; i++) { + this.shingleFields[i] = (ShingleFieldMapper) this.shingleFields[i].merge(mw.shingleFields[i]); + } + } + + public static String getShingleFieldName(String parentField, int shingleSize) { + return parentField + "._" + shingleSize + "gram"; + } + + @Override + public SearchAsYouTypeFieldType fieldType() { + return (SearchAsYouTypeFieldType) super.fieldType(); + } + + public int maxShingleSize() { + return maxShingleSize; + } + + public PrefixFieldMapper prefixField() { + return prefixField; + } + + public ShingleFieldMapper[] shingleFields() { + return shingleFields; + } + + @Override + protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { + super.doXContentBody(builder, includeDefaults, params); + doXContentAnalyzers(builder, includeDefaults); + builder.field("max_shingle_size", maxShingleSize); + } + + @Override + public Iterator iterator() { + List subIterators = new ArrayList<>(); + subIterators.add(prefixField); + subIterators.addAll(Arrays.asList(shingleFields)); + @SuppressWarnings("unchecked") Iterator concat = Iterators.concat(super.iterator(), subIterators.iterator()); + return concat; + } + + /** + * An analyzer wrapper to add a shingle token filter, an edge ngram token filter or both to its wrapped analyzer. When adding an edge + * ngrams token filter, it also adds a {@link TrailingShingleTokenFilter} to add extra position increments at the end of the stream + * to induce the shingle token filter to create tokens at the end of the stream smaller than the shingle size + */ + static class SearchAsYouTypeAnalyzer extends AnalyzerWrapper { + + private final Analyzer delegate; + private final int shingleSize; + private final boolean indexPrefixes; + + private SearchAsYouTypeAnalyzer(Analyzer delegate, + int shingleSize, + boolean indexPrefixes) { + + super(delegate.getReuseStrategy()); + this.delegate = Objects.requireNonNull(delegate); + this.shingleSize = shingleSize; + this.indexPrefixes = indexPrefixes; + } + + static SearchAsYouTypeAnalyzer withShingle(Analyzer delegate, int shingleSize) { + return new SearchAsYouTypeAnalyzer(delegate, shingleSize, false); + } + + static SearchAsYouTypeAnalyzer withShingleAndPrefix(Analyzer delegate, int shingleSize) { + return new SearchAsYouTypeAnalyzer(delegate, shingleSize, true); + } + + @Override + protected Analyzer getWrappedAnalyzer(String fieldName) { + return delegate; + } + + @Override + protected TokenStreamComponents wrapComponents(String fieldName, TokenStreamComponents components) { + TokenStream tokenStream = components.getTokenStream(); + if (indexPrefixes) { + tokenStream = new TrailingShingleTokenFilter(tokenStream, shingleSize - 1); + } + tokenStream = new FixedShingleFilter(tokenStream, shingleSize, " ", ""); + if (indexPrefixes) { + tokenStream = new EdgeNGramTokenFilter(tokenStream, Defaults.MIN_GRAM, Defaults.MAX_GRAM, true); + } + return new TokenStreamComponents(components.getSource(), tokenStream); + } + + public int shingleSize() { + return shingleSize; + } + + public boolean indexPrefixes() { + return indexPrefixes; + } + + @Override + public String toString() { + return "<" + getClass().getCanonicalName() + " shingleSize=[" + shingleSize + "] indexPrefixes=[" + indexPrefixes + "]>"; + } + + private static class TrailingShingleTokenFilter extends TokenFilter { + + private final int extraPositionIncrements; + private final PositionIncrementAttribute positionIncrementAttribute; + + TrailingShingleTokenFilter(TokenStream input, int extraPositionIncrements) { + super(input); + this.extraPositionIncrements = extraPositionIncrements; + this.positionIncrementAttribute = addAttribute(PositionIncrementAttribute.class); + } + + @Override + public boolean incrementToken() throws IOException { + return input.incrementToken(); + } + + @Override + public void end() throws IOException { + super.end(); + positionIncrementAttribute.setPositionIncrement(extraPositionIncrements); + } + } + } +} diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeAnalyzerTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeAnalyzerTests.java new file mode 100644 index 0000000000000..6cf0dc83d9070 --- /dev/null +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeAnalyzerTests.java @@ -0,0 +1,197 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.core.SimpleAnalyzer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.SearchAsYouTypeAnalyzer; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.IntStream; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; +import static org.hamcrest.Matchers.equalTo; + +public class SearchAsYouTypeAnalyzerTests extends ESTestCase { + + private static final Analyzer SIMPLE = new SimpleAnalyzer(); + + public static List analyze(SearchAsYouTypeAnalyzer analyzer, String text) throws IOException { + final List tokens = new ArrayList<>(); + try (TokenStream tokenStream = analyzer.tokenStream("field", text)) { + final CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); + tokenStream.reset(); + while (tokenStream.incrementToken()) { + tokens.add(charTermAttribute.toString()); + } + } + return tokens; + } + + private void testCase(String text, + Function analyzerFunction, + Function> expectedTokensFunction) throws IOException { + + for (int shingleSize = 2; shingleSize <= 4; shingleSize++) { + final SearchAsYouTypeAnalyzer analyzer = analyzerFunction.apply(shingleSize); + final List expectedTokens = expectedTokensFunction.apply(shingleSize); + final List actualTokens = analyze(analyzer, text); + assertThat("analyzed correctly with " + analyzer, actualTokens, equalTo(expectedTokens)); + } + } + + public void testSingleTermShingles() throws IOException { + testCase( + "quick", + shingleSize -> SearchAsYouTypeAnalyzer.withShingle(SIMPLE, shingleSize), + shingleSize -> emptyList() + ); + } + + public void testMultiTermShingles() throws IOException { + testCase( + "quick brown fox jump lazy", + shingleSize -> SearchAsYouTypeAnalyzer.withShingle(SIMPLE, shingleSize), + shingleSize -> { + if (shingleSize == 2) { + return asList("quick brown", "brown fox", "fox jump", "jump lazy"); + } else if (shingleSize == 3) { + return asList("quick brown fox", "brown fox jump", "fox jump lazy"); + } else if (shingleSize == 4) { + return asList("quick brown fox jump", "brown fox jump lazy"); + } + throw new IllegalArgumentException(); + } + ); + } + + public void testSingleTermPrefix() throws IOException { + testCase( + "quick", + shingleSize -> SearchAsYouTypeAnalyzer.withShingleAndPrefix(SIMPLE, shingleSize), + shingleSize -> { + final List tokens = new ArrayList<>(asList("q", "qu", "qui", "quic", "quick")); + tokens.addAll(tokenWithSpaces("quick", shingleSize)); + return tokens; + } + ); + } + + public void testMultiTermPrefix() throws IOException { + testCase( + //"quick red fox lazy brown", + "quick brown fox jump lazy", + shingleSize -> SearchAsYouTypeAnalyzer.withShingleAndPrefix(SIMPLE, shingleSize), + shingleSize -> { + if (shingleSize == 2) { + final List tokens = new ArrayList<>(); + tokens.addAll(asList( + "q", "qu", "qui", "quic", "quick", "quick ", "quick b", "quick br", "quick bro", "quick brow", "quick brown" + )); + tokens.addAll(asList( + "b", "br", "bro", "brow", "brown", "brown ", "brown f", "brown fo", "brown fox" + )); + tokens.addAll(asList( + "f", "fo", "fox", "fox ", "fox j", "fox ju", "fox jum", "fox jump" + )); + tokens.addAll(asList( + "j", "ju", "jum", "jump", "jump ", "jump l", "jump la", "jump laz", "jump lazy" + )); + tokens.addAll(asList( + "l", "la", "laz", "lazy" + )); + tokens.addAll(tokenWithSpaces("lazy", shingleSize)); + return tokens; + } else if (shingleSize == 3) { + final List tokens = new ArrayList<>(); + tokens.addAll(asList( + "q", "qu", "qui", "quic", "quick", "quick ", "quick b", "quick br", "quick bro", "quick brow", "quick brown", + "quick brown ", "quick brown f", "quick brown fo", "quick brown fox" + )); + tokens.addAll(asList( + "b", "br", "bro", "brow", "brown", "brown ", "brown f", "brown fo", "brown fox", "brown fox ", "brown fox j", + "brown fox ju", "brown fox jum", "brown fox jump" + )); + tokens.addAll(asList( + "f", "fo", "fox", "fox ", "fox j", "fox ju", "fox jum", "fox jump", "fox jump ", "fox jump l", "fox jump la", + "fox jump laz", "fox jump lazy" + )); + tokens.addAll(asList( + "j", "ju", "jum", "jump", "jump ", "jump l", "jump la", "jump laz", "jump lazy" + )); + tokens.addAll(tokenWithSpaces("jump lazy", shingleSize - 1)); + tokens.addAll(asList( + "l", "la", "laz", "lazy" + )); + tokens.addAll(tokenWithSpaces("lazy", shingleSize)); + return tokens; + } else if (shingleSize == 4) { + final List tokens = new ArrayList<>(); + tokens.addAll(asList( + "q", "qu", "qui", "quic", "quick", "quick ", "quick b", "quick br", "quick bro", "quick brow", "quick brown", + "quick brown ", "quick brown f", "quick brown fo", "quick brown fox", "quick brown fox ", "quick brown fox j", + "quick brown fox ju", "quick brown fox jum", "quick brown fox jump" + )); + tokens.addAll(asList( + "b", "br", "bro", "brow", "brown", "brown ", "brown f", "brown fo", "brown fox", "brown fox ", "brown fox j", + "brown fox ju", "brown fox jum", "brown fox jump", "brown fox jump ", "brown fox jump l", "brown fox jump la", + "brown fox jump laz", "brown fox jump lazy" + )); + tokens.addAll(asList( + "f", "fo", "fox", "fox ", "fox j", "fox ju", "fox jum", "fox jump", "fox jump ", "fox jump l", "fox jump la", + "fox jump laz", "fox jump lazy" + )); + tokens.addAll(tokenWithSpaces("fox jump lazy", shingleSize - 2)); + tokens.addAll(asList( + "j", "ju", "jum", "jump", "jump ", "jump l", "jump la", "jump laz", "jump lazy" + )); + tokens.addAll(tokenWithSpaces("jump lazy", shingleSize - 1)); + tokens.addAll(asList( + "l", "la", "laz", "lazy" + )); + tokens.addAll(tokenWithSpaces("lazy", shingleSize)); + return tokens; + } + + throw new IllegalArgumentException(); + } + ); + } + + private static List tokenWithSpaces(String text, int maxShingleSize) { + return IntStream.range(1, maxShingleSize).mapToObj(i -> text + spaces(i)).collect(toList()); + } + + private static String spaces(int count) { + final StringBuilder builder = new StringBuilder(); + for (int i = 0; i < count; i++) { + builder.append(" "); + } + return builder.toString(); + } +} diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java new file mode 100644 index 0000000000000..9ed43a9505624 --- /dev/null +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java @@ -0,0 +1,758 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.index.mapper; + +import org.apache.lucene.document.FieldType; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.DisjunctionMaxQuery; +import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.MultiPhraseQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SynonymQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.spans.FieldMaskingSpanQuery; +import org.apache.lucene.search.spans.SpanNearQuery; +import org.apache.lucene.search.spans.SpanTermQuery; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.compress.CompressedXContent; +import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentFactory; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.IndexService; +import org.elasticsearch.index.analysis.NamedAnalyzer; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.PrefixFieldMapper; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.PrefixFieldType; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.SearchAsYouTypeAnalyzer; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.SearchAsYouTypeFieldType; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.ShingleFieldMapper; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.ShingleFieldType; +import org.elasticsearch.index.query.MatchPhrasePrefixQueryBuilder; +import org.elasticsearch.index.query.MatchPhraseQueryBuilder; +import org.elasticsearch.index.query.MultiMatchQueryBuilder; +import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESSingleNodeTestCase; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasProperty; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.collection.IsArrayContainingInAnyOrder.arrayContainingInAnyOrder; +import static org.hamcrest.core.IsInstanceOf.instanceOf; + +public class SearchAsYouTypeFieldMapperTests extends ESSingleNodeTestCase { + + @Override + protected Collection> getPlugins() { + return pluginList(MapperExtrasPlugin.class); + } + + public void testIndexing() throws IOException { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper mapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + ParsedDocument doc = mapper.parse(new SourceToParse("test", "_doc", "1", BytesReference + .bytes(XContentFactory.jsonBuilder() + .startObject() + .field("a_field", "new york city") + .endObject()), + XContentType.JSON)); + + for (String field : new String[] { "a_field", "a_field._index_prefix", "a_field._2gram", "a_field._3gram"}) { + IndexableField[] fields = doc.rootDoc().getFields(field); + assertEquals(1, fields.length); + assertEquals("new york city", fields[0].stringValue()); + } + } + + public void testDefaultConfiguration() throws IOException { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + final SearchAsYouTypeFieldMapper rootMapper = getRootFieldMapper(defaultMapper, "a_field"); + assertRootFieldMapper(rootMapper, 3, "default"); + + + final PrefixFieldMapper prefixFieldMapper = getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"); + assertPrefixFieldType(prefixFieldMapper.fieldType(), 3, "default"); + + assertShingleFieldType( + getShingleFieldMapper(defaultMapper, "a_field._2gram").fieldType(), 2, "default", prefixFieldMapper.fieldType()); + assertShingleFieldType( + getShingleFieldMapper(defaultMapper, "a_field._3gram").fieldType(), 3, "default", prefixFieldMapper.fieldType()); + } + + public void testConfiguration() throws IOException { + final int maxShingleSize = 4; + final String analyzerName = "simple"; + + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("analyzer", analyzerName) + .field("max_shingle_size", maxShingleSize) + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + final SearchAsYouTypeFieldMapper rootMapper = getRootFieldMapper(defaultMapper, "a_field"); + assertRootFieldMapper(rootMapper, maxShingleSize, analyzerName); + + final PrefixFieldMapper prefixFieldMapper = getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"); + assertPrefixFieldType(prefixFieldMapper.fieldType(), maxShingleSize, analyzerName); + + assertShingleFieldType( + getShingleFieldMapper(defaultMapper, "a_field._2gram").fieldType(), 2, analyzerName, prefixFieldMapper.fieldType()); + assertShingleFieldType( + getShingleFieldMapper(defaultMapper, "a_field._3gram").fieldType(), 3, analyzerName, prefixFieldMapper.fieldType()); + assertShingleFieldType( + getShingleFieldMapper(defaultMapper, "a_field._4gram").fieldType(), 4, analyzerName, prefixFieldMapper.fieldType()); + } + + public void testIndexOptions() throws IOException { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("index_options", "offsets") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + Stream.of( + getRootFieldMapper(defaultMapper, "a_field"), + getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"), + getShingleFieldMapper(defaultMapper, "a_field._2gram"), + getShingleFieldMapper(defaultMapper, "a_field._3gram") + ).forEach(mapper -> assertThat("for " + mapper.name(), + mapper.fieldType().indexOptions(), equalTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS))); + } + + public void testStore() throws IOException { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("store", "true") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + assertTrue(getRootFieldMapper(defaultMapper, "a_field").fieldType().stored()); + Stream.of( + getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"), + getShingleFieldMapper(defaultMapper, "a_field._2gram"), + getShingleFieldMapper(defaultMapper, "a_field._3gram") + ).forEach(mapper -> assertFalse("for " + mapper.name(), mapper.fieldType().stored())); + } + + public void testIndex() throws IOException { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("index", "false") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + Stream.of( + getRootFieldMapper(defaultMapper, "a_field"), + getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"), + getShingleFieldMapper(defaultMapper, "a_field._2gram"), + getShingleFieldMapper(defaultMapper, "a_field._3gram") + ).forEach(mapper -> assertThat("for " + mapper.name(), mapper.fieldType().indexOptions(), equalTo(IndexOptions.NONE))); + } + + public void testTermVectors() throws IOException { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("term_vector", "yes") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + Stream.of( + getRootFieldMapper(defaultMapper, "a_field"), + getShingleFieldMapper(defaultMapper, "a_field._2gram"), + getShingleFieldMapper(defaultMapper, "a_field._3gram") + ).forEach(mapper -> assertTrue("for " + mapper.name(), mapper.fieldType().storeTermVectors())); + + final PrefixFieldMapper prefixFieldMapper = getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"); + assertFalse(prefixFieldMapper.fieldType().storeTermVectors()); + } + + public void testNorms() throws IOException { + // default setting + { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test-1") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + Stream.of( + getRootFieldMapper(defaultMapper, "a_field"), + getShingleFieldMapper(defaultMapper, "a_field._2gram"), + getShingleFieldMapper(defaultMapper, "a_field._3gram") + ).forEach(mapper -> assertFalse("for " + mapper.name(), mapper.fieldType().omitNorms())); + + final PrefixFieldMapper prefixFieldMapper = getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"); + assertTrue(prefixFieldMapper.fieldType().omitNorms()); + } + + // can disable them on shingle fields + { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("norms", "false") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test-2") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + Stream.of( + getRootFieldMapper(defaultMapper, "a_field"), + getPrefixFieldMapper(defaultMapper, "a_field._index_prefix"), + getShingleFieldMapper(defaultMapper, "a_field._2gram"), + getShingleFieldMapper(defaultMapper, "a_field._3gram") + ).forEach(mapper -> assertTrue("for " + mapper.name(), mapper.fieldType().omitNorms())); + } + } + + + public void testDocumentParsingSingleValue() throws IOException { + documentParsingTestCase(Collections.singleton(randomAlphaOfLengthBetween(5, 20))); + } + + public void testDocumentParsingMultipleValues() throws IOException { + documentParsingTestCase(randomUnique(() -> randomAlphaOfLengthBetween(3, 20), randomIntBetween(2, 10))); + } + + public void testMatchPhrasePrefix() throws IOException { + IndexService indexService = createIndex("test", Settings.EMPTY); + QueryShardContext queryShardContext = indexService.newQueryShardContext( + randomInt(20), null, () -> { + throw new UnsupportedOperationException(); + }, null); + + String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") + .startObject("properties") + .startObject("field") + .field("type", "search_as_you_type") + .endObject() + .endObject() + .endObject().endObject()); + + queryShardContext.getMapperService().merge("type", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); + + { + Query q = new MatchPhrasePrefixQueryBuilder("field", "two words").toQuery(queryShardContext); + Query expected = new SynonymQuery(new Term("field._index_prefix", "two words")); + assertThat(q, equalTo(expected)); + } + + { + Query q = new MatchPhrasePrefixQueryBuilder("field", "three words here").toQuery(queryShardContext); + Query expected = new SynonymQuery(new Term("field._index_prefix", "three words here")); + assertThat(q, equalTo(expected)); + } + + { + Query q = new MatchPhrasePrefixQueryBuilder("field", "two words").slop(1).toQuery(queryShardContext); + MultiPhrasePrefixQuery mpq = new MultiPhrasePrefixQuery("field"); + mpq.setSlop(1); + mpq.add(new Term("field", "two")); + mpq.add(new Term("field", "words")); + assertThat(q, equalTo(mpq)); + } + + { + Query q = new MatchPhrasePrefixQueryBuilder("field", "more than three words").toQuery(queryShardContext); + Query expected = new SpanNearQuery.Builder("field._3gram", true) + .addClause(new SpanTermQuery(new Term("field._3gram", "more than three"))) + .addClause(new FieldMaskingSpanQuery( + new SpanTermQuery(new Term("field._index_prefix", "than three words")), "field._3gram") + ) + .build(); + assertThat(q, equalTo(expected)); + } + + { + Query q = new MatchPhrasePrefixQueryBuilder("field._3gram", "more than three words").toQuery(queryShardContext); + Query expected = new SpanNearQuery.Builder("field._3gram", true) + .addClause(new SpanTermQuery(new Term("field._3gram", "more than three"))) + .addClause(new FieldMaskingSpanQuery( + new SpanTermQuery(new Term("field._index_prefix", "than three words")), "field._3gram") + ) + .build(); + assertThat(q, equalTo(expected)); + } + + { + Query q = new MatchPhrasePrefixQueryBuilder("field._3gram", "two words").toQuery(queryShardContext); + Query expected = new MatchNoDocsQuery(); + assertThat(q, equalTo(expected)); + } + + { + Query actual = new MatchPhrasePrefixQueryBuilder("field._3gram", "one two three four") + .slop(1) + .toQuery(queryShardContext); + MultiPhrasePrefixQuery expected = new MultiPhrasePrefixQuery("field._3gram"); + expected.setSlop(1); + expected.add(new Term("field._3gram", "one two three")); + expected.add(new Term("field._3gram", "two three four")); + assertThat(actual, equalTo(expected)); + } + + } + + public void testMatchPhrase() throws IOException { + final IndexService indexService = createIndex("test", Settings.EMPTY); + final QueryShardContext queryShardContext = indexService.newQueryShardContext(randomInt(20), null, + () -> { throw new UnsupportedOperationException(); }, null); + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .endObject() + .endObject() + .endObject() + .endObject()); + + queryShardContext.getMapperService().merge("_doc", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); + + { + final Query actual = new MatchPhraseQueryBuilder("a_field", "one") + .toQuery(queryShardContext); + final Query expected = new TermQuery(new Term("a_field", "one")); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field", "one two") + .toQuery(queryShardContext); + final Query expected = new MultiPhraseQuery.Builder() + .add(new Term("a_field._2gram", "one two")) + .build(); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field", "one two three") + .toQuery(queryShardContext); + final Query expected = new MultiPhraseQuery.Builder() + .add(new Term("a_field._3gram", "one two three")) + .build(); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field", "one two three four") + .toQuery(queryShardContext); + final Query expected = new MultiPhraseQuery.Builder() + .add(new Term("a_field._3gram", "one two three")) + .add(new Term("a_field._3gram", "two three four")) + .build(); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field", "one two") + .slop(1) + .toQuery(queryShardContext); + final Query expected = new MultiPhraseQuery.Builder() + .add(new Term("a_field", "one")) + .add(new Term("a_field", "two")) + .setSlop(1) + .build(); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field._2gram", "one two") + .toQuery(queryShardContext); + final Query expected = new TermQuery(new Term("a_field._2gram", "one two")); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field._2gram", "one two three") + .toQuery(queryShardContext); + final Query expected = new MultiPhraseQuery.Builder() + .add(new Term("a_field._2gram", "one two")) + .add(new Term("a_field._2gram", "two three")) + .build(); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field._3gram", "one two three") + .toQuery(queryShardContext); + final Query expected = new TermQuery(new Term("a_field._3gram", "one two three")); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field._3gram", "one two three four") + .toQuery(queryShardContext); + final Query expected = new MultiPhraseQuery.Builder() + .add(new Term("a_field._3gram", "one two three")) + .add(new Term("a_field._3gram", "two three four")) + .build(); + assertThat(actual, equalTo(expected)); + } + + // todo are these queries generated for the prefix field right? + { + final Query actual = new MatchPhraseQueryBuilder("a_field._index_prefix", "one two") + .toQuery(queryShardContext); + final Query expected = new MatchNoDocsQuery("Matching no documents because no terms present"); + assertThat(actual, equalTo(expected)); + } + + { + final Query actual = new MatchPhraseQueryBuilder("a_field._index_prefix", "one two three") + .toQuery(queryShardContext); + final Query expected = new TermQuery(new Term("a_field._index_prefix", "one two three")); + assertThat(actual, equalTo(expected)); + } + + { + expectThrows(IllegalArgumentException.class, + () -> new MatchPhraseQueryBuilder("a_field._index_prefix", "one two three four").toQuery(queryShardContext)); + } + } + + private static BooleanQuery buildBoolPrefixQuery(String shingleFieldName, String prefixFieldName, List terms) { + final BooleanQuery.Builder builder = new BooleanQuery.Builder(); + for (int i = 0; i < terms.size() - 1; i++) { + final String term = terms.get(i); + builder.add(new BooleanClause(new TermQuery(new Term(shingleFieldName, term)), BooleanClause.Occur.SHOULD)); + } + final String finalTerm = terms.get(terms.size() - 1); + builder.add(new BooleanClause( + new ConstantScoreQuery(new TermQuery(new Term(prefixFieldName, finalTerm))), BooleanClause.Occur.SHOULD)); + return builder.build(); + } + + public void testMultiMatchBoolPrefix() throws IOException { + final IndexService indexService = createIndex("test", Settings.EMPTY); + final QueryShardContext queryShardContext = indexService.newQueryShardContext(randomInt(20), null, + () -> { throw new UnsupportedOperationException(); }, null); + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("max_shingle_size", 4) + .endObject() + .endObject() + .endObject() + .endObject()); + + queryShardContext.getMapperService().merge("_doc", new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); + + final MultiMatchQueryBuilder builder = new MultiMatchQueryBuilder( + "quick brown fox jump lazy dog", + "a_field", + "a_field._2gram", + "a_field._3gram", + "a_field._4gram" + ); + builder.type(MultiMatchQueryBuilder.Type.BOOL_PREFIX); + + final Query actual = builder.toQuery(queryShardContext); + assertThat(actual, instanceOf(DisjunctionMaxQuery.class)); + final DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) actual; + assertThat(disMaxQuery.getDisjuncts(), hasSize(4)); + assertThat(disMaxQuery.getDisjuncts(), containsInAnyOrder( + buildBoolPrefixQuery( + "a_field", "a_field._index_prefix", asList("quick", "brown", "fox", "jump", "lazy", "dog")), + buildBoolPrefixQuery("a_field._2gram", "a_field._index_prefix", + asList("quick brown", "brown fox", "fox jump", "jump lazy", "lazy dog")), + buildBoolPrefixQuery("a_field._3gram", "a_field._index_prefix", + asList("quick brown fox", "brown fox jump", "fox jump lazy", "jump lazy dog")), + buildBoolPrefixQuery("a_field._4gram", "a_field._index_prefix", + asList("quick brown fox jump", "brown fox jump lazy", "fox jump lazy dog")))); + } + + private void documentParsingTestCase(Collection values) throws IOException { + final String mapping = Strings.toString(XContentFactory.jsonBuilder() + .startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .endObject() + .endObject() + .endObject() + .endObject()); + + final DocumentMapper defaultMapper = createIndex("test") + .mapperService() + .documentMapperParser() + .parse("_doc", new CompressedXContent(mapping)); + + final XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.startObject(); + if (values.size() > 1) { + builder.array("a_field", values.toArray(new String[0])); + } else { + builder.field("a_field", values.iterator().next()); + } + builder.endObject(); + final ParsedDocument parsedDocument = defaultMapper.parse( + new SourceToParse("test", "_doc", "1", BytesReference.bytes(builder), XContentType.JSON)); + + + final Set> rootFieldMatchers = values.stream() + .map(value -> indexableFieldMatcher(value, SearchAsYouTypeFieldType.class)) + .collect(Collectors.toSet()); + final Set> shingleFieldMatchers = values.stream() + .map(value -> indexableFieldMatcher(value, ShingleFieldType.class)) + .collect(Collectors.toSet()); + final Set> prefixFieldMatchers = values.stream() + .map(value -> indexableFieldMatcher(value, PrefixFieldType.class)) + .collect(Collectors.toSet()); + + // the use of new ArrayList<>() here is to avoid the varargs form of arrayContainingInAnyOrder + assertThat( + parsedDocument.rootDoc().getFields("a_field"), + arrayContainingInAnyOrder(new ArrayList<>(rootFieldMatchers))); + + assertThat( + parsedDocument.rootDoc().getFields("a_field._index_prefix"), + arrayContainingInAnyOrder(new ArrayList<>(prefixFieldMatchers))); + + for (String name : asList("a_field._2gram", "a_field._3gram")) { + assertThat(parsedDocument.rootDoc().getFields(name), arrayContainingInAnyOrder(new ArrayList<>(shingleFieldMatchers))); + } + } + + private static Matcher indexableFieldMatcher(String value, Class fieldTypeClass) { + return Matchers.allOf( + hasProperty(IndexableField::stringValue, equalTo(value)), + hasProperty(IndexableField::fieldType, instanceOf(fieldTypeClass)) + ); + } + + private static void assertRootFieldMapper(SearchAsYouTypeFieldMapper mapper, + int maxShingleSize, + String analyzerName) { + + assertThat(mapper.maxShingleSize(), equalTo(maxShingleSize)); + assertThat(mapper.fieldType(), notNullValue()); + assertSearchAsYouTypeFieldType(mapper.fieldType(), maxShingleSize, analyzerName, mapper.prefixField().fieldType()); + + assertThat(mapper.prefixField(), notNullValue()); + assertThat(mapper.prefixField().fieldType().parentField, equalTo(mapper.name())); + assertPrefixFieldType(mapper.prefixField().fieldType(), maxShingleSize, analyzerName); + + + for (int shingleSize = 2; shingleSize <= maxShingleSize; shingleSize++) { + final ShingleFieldMapper shingleFieldMapper = mapper.shingleFields()[shingleSize - 2]; + assertThat(shingleFieldMapper, notNullValue()); + assertShingleFieldType(shingleFieldMapper.fieldType(), shingleSize, analyzerName, mapper.prefixField().fieldType()); + } + + final int numberOfShingleSubfields = (maxShingleSize - 2) + 1; + assertThat(mapper.shingleFields().length, equalTo(numberOfShingleSubfields)); + } + + private static void assertSearchAsYouTypeFieldType(SearchAsYouTypeFieldType fieldType, int maxShingleSize, + String analyzerName, + PrefixFieldType prefixFieldType) { + + assertThat(fieldType.shingleFields.length, equalTo(maxShingleSize-1)); + for (NamedAnalyzer analyzer : asList(fieldType.indexAnalyzer(), fieldType.searchAnalyzer())) { + assertThat(analyzer.name(), equalTo(analyzerName)); + } + int shingleSize = 2; + for (ShingleFieldType shingleField : fieldType.shingleFields) { + assertShingleFieldType(shingleField, shingleSize++, analyzerName, prefixFieldType); + } + + assertThat(fieldType.prefixField, equalTo(prefixFieldType)); + } + + private static void assertShingleFieldType(ShingleFieldType fieldType, + int shingleSize, + String analyzerName, + PrefixFieldType prefixFieldType) { + + assertThat(fieldType.shingleSize, equalTo(shingleSize)); + + for (NamedAnalyzer analyzer : asList(fieldType.indexAnalyzer(), fieldType.searchAnalyzer())) { + assertThat(analyzer.name(), equalTo(analyzerName)); + if (shingleSize > 1) { + final SearchAsYouTypeAnalyzer wrappedAnalyzer = (SearchAsYouTypeAnalyzer) analyzer.analyzer(); + assertThat(wrappedAnalyzer.shingleSize(), equalTo(shingleSize)); + assertThat(wrappedAnalyzer.indexPrefixes(), equalTo(false)); + } + } + + assertThat(fieldType.prefixFieldType, equalTo(prefixFieldType)); + + } + + private static void assertPrefixFieldType(PrefixFieldType fieldType, int shingleSize, String analyzerName) { + for (NamedAnalyzer analyzer : asList(fieldType.indexAnalyzer(), fieldType.searchAnalyzer())) { + assertThat(analyzer.name(), equalTo(analyzerName)); + } + + final SearchAsYouTypeAnalyzer wrappedIndexAnalyzer = (SearchAsYouTypeAnalyzer) fieldType.indexAnalyzer().analyzer(); + final SearchAsYouTypeAnalyzer wrappedSearchAnalyzer = (SearchAsYouTypeAnalyzer) fieldType.searchAnalyzer().analyzer(); + for (SearchAsYouTypeAnalyzer analyzer : asList(wrappedIndexAnalyzer, wrappedSearchAnalyzer)) { + assertThat(analyzer.shingleSize(), equalTo(shingleSize)); + } + assertThat(wrappedIndexAnalyzer.indexPrefixes(), equalTo(true)); + assertThat(wrappedSearchAnalyzer.indexPrefixes(), equalTo(false)); + } + + private static SearchAsYouTypeFieldMapper getRootFieldMapper(DocumentMapper defaultMapper, String fieldName) { + final Mapper mapper = defaultMapper.mappers().getMapper(fieldName); + assertThat(mapper, instanceOf(SearchAsYouTypeFieldMapper.class)); + return (SearchAsYouTypeFieldMapper) mapper; + } + + private static ShingleFieldMapper getShingleFieldMapper(DocumentMapper defaultMapper, String fieldName) { + final Mapper mapper = defaultMapper.mappers().getMapper(fieldName); + assertThat(mapper, instanceOf(ShingleFieldMapper.class)); + return (ShingleFieldMapper) mapper; + } + + private static PrefixFieldMapper getPrefixFieldMapper(DocumentMapper defaultMapper, String fieldName) { + final Mapper mapper = defaultMapper.mappers().getMapper(fieldName); + assertThat(mapper, instanceOf(PrefixFieldMapper.class)); + return (PrefixFieldMapper) mapper; + } +} diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldTypeTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldTypeTests.java new file mode 100644 index 0000000000000..523de91809145 --- /dev/null +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldTypeTests.java @@ -0,0 +1,113 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.mapper; + +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.ConstantScoreQuery; +import org.apache.lucene.search.PrefixQuery; +import org.apache.lucene.search.TermInSetQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.Defaults; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.PrefixFieldType; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.SearchAsYouTypeFieldType; +import org.elasticsearch.index.mapper.SearchAsYouTypeFieldMapper.ShingleFieldType; +import org.junit.Before; + +import static java.util.Arrays.asList; +import static org.apache.lucene.search.MultiTermQuery.CONSTANT_SCORE_REWRITE; +import static org.hamcrest.Matchers.equalTo; + +public class SearchAsYouTypeFieldTypeTests extends FieldTypeTestCase { + + private static final String NAME = "a_field"; + private static final String PREFIX_NAME = NAME + "._index_prefix"; + + @Before + public void setupProperties() { + addModifier(new Modifier("max_shingle_size", false) { + @Override + public void modify(MappedFieldType ft) { + SearchAsYouTypeFieldType fieldType = (SearchAsYouTypeFieldType) ft; + fieldType.setShingleFields(new ShingleFieldType[] { + new ShingleFieldType(fieldType, 2), + new ShingleFieldType(fieldType, 3) + }); + } + }); + addModifier(new Modifier("index_prefixes", false) { + @Override + public void modify(MappedFieldType ft) { + SearchAsYouTypeFieldType fieldType = (SearchAsYouTypeFieldType) ft; + fieldType.setPrefixField(new PrefixFieldType(NAME, PREFIX_NAME, 1, 10)); + } + }); + } + + @Override + protected SearchAsYouTypeFieldType createDefaultFieldType() { + final SearchAsYouTypeFieldType fieldType = new SearchAsYouTypeFieldType(); + fieldType.setName(NAME); + fieldType.setPrefixField(new PrefixFieldType(NAME, PREFIX_NAME, Defaults.MIN_GRAM, Defaults.MAX_GRAM)); + fieldType.setShingleFields(new ShingleFieldType[] { new ShingleFieldType(fieldType, 2) }); + return fieldType; + } + + public void testTermQuery() { + final MappedFieldType fieldType = createDefaultFieldType(); + + fieldType.setIndexOptions(IndexOptions.DOCS); + assertThat(fieldType.termQuery("foo", null), equalTo(new TermQuery(new Term(NAME, "foo")))); + + fieldType.setIndexOptions(IndexOptions.NONE); + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> fieldType.termQuery("foo", null)); + assertThat(e.getMessage(), equalTo("Cannot search on field [" + NAME + "] since it is not indexed.")); + } + + public void testTermsQuery() { + final MappedFieldType fieldType = createDefaultFieldType(); + + fieldType.setIndexOptions(IndexOptions.DOCS); + assertThat(fieldType.termsQuery(asList("foo", "bar"), null), + equalTo(new TermInSetQuery(NAME, asList(new BytesRef("foo"), new BytesRef("bar"))))); + + fieldType.setIndexOptions(IndexOptions.NONE); + final IllegalArgumentException e = + expectThrows(IllegalArgumentException.class, () -> fieldType.termsQuery(asList("foo", "bar"), null)); + assertThat(e.getMessage(), equalTo("Cannot search on field [" + NAME + "] since it is not indexed.")); + } + + public void testPrefixQuery() { + final SearchAsYouTypeFieldType fieldType = createDefaultFieldType(); + + // this term should be a length that can be rewriteable to a term query on the prefix field + final String withinBoundsTerm = "foo"; + assertThat(fieldType.prefixQuery(withinBoundsTerm, CONSTANT_SCORE_REWRITE, null), + equalTo(new ConstantScoreQuery(new TermQuery(new Term(PREFIX_NAME, withinBoundsTerm))))); + + // our defaults don't allow a situation where a term can be too small + + // this term should be too long to be rewriteable to a term query on the prefix field + final String longTerm = "toolongforourprefixfieldthistermis"; + assertThat(fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, null), + equalTo(new PrefixQuery(new Term(NAME, longTerm)))); + } +} diff --git a/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml new file mode 100644 index 0000000000000..f9b76a7399a37 --- /dev/null +++ b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml @@ -0,0 +1,1249 @@ +setup: + - skip: + version: " - 7.1.0" + reason: "added in 7.1.0" + + - do: + indices.create: + index: test + body: + settings: + number_of_replicas: 0 + mappings: + properties: + a_field: + type: search_as_you_type + analyzer: simple + max_shingle_size: 4 + + - do: + index: + index: test + type: _doc + id: 1 + body: + a_field: "quick brown fox jump lazy dog" + + # this document should not be matched + - do: + index: + index: test + type: _doc + id: 2 + body: + a_field: "xylophone xylophone xylophone" + + - do: + indices.refresh: {} + +--- +"get document": + - do: + get: + index: test + type: _doc + id: 1 + + - is_true: found + - match: { _source.a_field: "quick brown fox jump lazy dog" } + +--- +"term query on root field": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field: "quick" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + +# these "search on Xgram" tests repeat the same search for each term we expect to generate +--- +"term query on 2gram": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._2gram: "quick brown" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._2gram: "brown fox" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._2gram: "fox jump" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._2gram: "jump lazy" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._2gram: "lazy dog" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"term query on 3gram": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._3gram: "quick brown fox" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._3gram: "brown fox jump" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._3gram: "fox jump lazy" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._3gram: "jump lazy dog" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"term query on 4gram": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._4gram: "quick brown fox jump" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._4gram: "brown fox jump lazy" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._4gram: "fox jump lazy dog" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +# we won't check all the terms that this field generates because there are many +--- +"term query on prefix field with prefix term": + + # search term as prefix + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._index_prefix: "quick br" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"term query on prefix field with infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._index_prefix: "jump la" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"term query on prefix field with trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + term: + a_field._index_prefix: "do" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "quic" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "brown fo" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on 2gram with prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field._2gram: "quic" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on 2gram with infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field._2gram: "brown fo" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on 3gram with prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field._3gram: "quic" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on 3gram with infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field._3gram: "brown fo" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on 4gram with prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field._4gram: "quic" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on 4gram with infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field._4gram: "brown fo" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 1 prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "quic" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 2 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "quick b" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 3 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "quick brown fo" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 4 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "quick brown fox ju" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 1 infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "fo" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 2 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "fox jum" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 3 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "fox jump la" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with 4 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "fox jump lazy do" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"prefix query on root field with trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + prefix: + a_field: "do" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 1 prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "quick" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 2 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "quick brown" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 3 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "quick brown fox" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 4 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "quick brown fox jump" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 5 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "quick brown fox jump lazy" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 1 infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "brown" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 2 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "brown fox" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 3 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "brown fox jump" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 4 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "brown fox jump lazy" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with 5 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "brown fox jump lazy dog" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase query on root field with trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "dog" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 1 prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "qui" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 2 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "quick b" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 3 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "quick brown f" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 4 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "quick brown fox ju" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 5 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "quick brown fox jump la" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 1 infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "br" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 2 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "brown f" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 3 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "brown fox ju" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 4 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "brown fox jump la" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with 5 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "brown fox jump lazy d" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"phrase prefix query on root field with trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase_prefix: + a_field: "do" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 1 prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "qui" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 2 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "quick b" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 3 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "quick brown f" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 4 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "quick brown fox ju" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 5 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "quick brown fox jump la" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 1 infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "br" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 2 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "brown f" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 3 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "brown fox j" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 4 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "brown fox jump la" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with 5 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "brown fox jump lazy d" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field with trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "do" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field out of order partial trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "fox jump brown do" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"bool prefix query on root field out of order partial leading term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "fox jump brown qui" + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 1 prefix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "qui" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 2 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "quick br" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 3 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "quick brown f" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 4 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "quick brown fox ju" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 5 prefix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "quick brown fox jump la" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 1 infix term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "br" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 2 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown f" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 3 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox ju" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with 4 infix terms": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox jump la" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query with trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "do" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query out of order with partial trailing term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "fox jump brown do" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query out of order with partial leading term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "fox jump lazy qui" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } diff --git a/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml new file mode 100644 index 0000000000000..5a96a11a47586 --- /dev/null +++ b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml @@ -0,0 +1,202 @@ +setup: + - skip: + version: " - 7.1.0" + reason: "added in 7.1.0" + + - do: + indices.create: + index: test + body: + settings: + number_of_replicas: 0 + mappings: + properties: + a_field: + type: search_as_you_type + analyzer: simple + max_shingle_size: 4 + text_field: + type: text + analyzer: simple + + - do: + index: + index: test + type: _doc + id: 1 + body: + a_field: "quick brown fox jump lazy dog" + text_field: "quick brown fox jump lazy dog" + + - do: + indices.refresh: {} + +--- +"phrase query": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_phrase: + a_field: "brown" + highlight: + fields: + a_field: + type: unified + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0._source.text_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0.highlight.a_field.0: "quick brown fox jump lazy dog" } + +--- +"bool prefix query": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + a_field: "brown fo" + highlight: + fields: + a_field: + type: unified + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0._source.text_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0.highlight.a_field.0: "quick brown fox jump lazy dog" } + +--- +"multi match bool prefix query 1 complete term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fo" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + highlight: + fields: + a_field: + type: unified + a_field._2gram: + type: unified + a_field._3gram: + type: unified + a_field._4gram: + type: unified + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0._source.text_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0.highlight.a_field: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._2gram: null } + - match: { hits.hits.0.highlight.a_field\._3gram: null } + - match: { hits.hits.0.highlight.a_field\._4gram: null } + +--- +"multi match bool prefix query 2 complete term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox ju" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + highlight: + fields: + a_field: + type: unified + a_field._2gram: + type: unified + a_field._3gram: + type: unified + a_field._4gram: + type: unified + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0._source.text_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0.highlight.a_field: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._2gram: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._3gram: null } + - match: { hits.hits.0.highlight.a_field\._4gram: null } + +--- +"multi match bool prefix query 3 complete term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox jump la" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + highlight: + fields: + a_field: + type: unified + a_field._2gram: + type: unified + a_field._3gram: + type: unified + a_field._4gram: + type: unified + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0._source.text_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0.highlight.a_field: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._2gram: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._3gram: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._4gram: null } + +--- +"multi match bool prefix query 4 complete term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox jump lazy d" + type: "bool_prefix" + fields: [ "a_field", "a_field._2gram", "a_field._3gram", "a_field._4gram" ] + highlight: + fields: + a_field: + type: unified + a_field._2gram: + type: unified + a_field._3gram: + type: unified + a_field._4gram: + type: unified + + - match: { hits.total: 1 } + - match: { hits.hits.0._source.a_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0._source.text_field: "quick brown fox jump lazy dog" } + - match: { hits.hits.0.highlight.a_field: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._2gram: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._3gram: ["quick brown fox jump lazy dog"] } + - match: { hits.hits.0.highlight.a_field\._4gram: ["quick brown fox jump lazy dog"] } diff --git a/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java b/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java index 9777174563626..7b195bdc7b434 100644 --- a/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java +++ b/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java @@ -591,7 +591,7 @@ public Query multiPhraseQuery(TokenStream stream, int slop, boolean enablePositi @Override public Query phrasePrefixQuery(TokenStream stream, int slop, int maxExpansions) throws IOException { - return TextFieldMapper.createPhrasePrefixQuery(stream, name(), slop, maxExpansions); + return TextFieldMapper.createPhrasePrefixQuery(stream, name(), slop, maxExpansions, null, null); } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml new file mode 100644 index 0000000000000..957d26036b4a8 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml @@ -0,0 +1,363 @@ +setup: + - skip: + version: " - 7.1.0" + reason: "added in 7.1.0" + + - do: + indices.create: + index: test + body: + mappings: + properties: + my_field1: + type: text + my_field2: + type: text + + - do: + index: + index: test + id: 1 + body: + my_field1: "brown fox jump" + my_field2: "xylophone" + + - do: + index: + index: test + id: 2 + body: + my_field1: "brown emu jump" + my_field2: "xylophone" + + - do: + index: + index: test + id: 3 + body: + my_field1: "jumparound" + my_field2: "emu" + + - do: + index: + index: test + id: 4 + body: + my_field1: "dog" + my_field2: "brown fox jump lazy" + + - do: + indices.refresh: {} + +--- +"scoring complete term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + my_field1: "brown fox jump" + + - match: { hits.total: 3 } + - match: { hits.hits.0._source.my_field1: "brown fox jump" } + - match: { hits.hits.1._source.my_field1: "brown emu jump" } + - match: { hits.hits.2._source.my_field1: "jumparound" } + +--- +"scoring partial term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + my_field1: "brown fox ju" + + - match: { hits.total: 3 } + - match: { hits.hits.0._id: "1" } + - match: { hits.hits.0._source.my_field1: "brown fox jump" } + - match: { hits.hits.1._id: "2" } + - match: { hits.hits.1._source.my_field1: "brown emu jump" } + - match: { hits.hits.2._id: "3" } + - match: { hits.hits.2._source.my_field1: "jumparound" } + +--- +"minimum should match": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + my_field1: + query: "brown fox jump" + minimum_should_match: 3 + + - match: { hits.total: 1 } + - match: { hits.hits.0._id: "1" } + - match: { hits.hits.0._source.my_field1: "brown fox jump" } + +--- +"analyzer": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + my_field1: + query: "BROWN dog" + analyzer: whitespace # this analyzer doesn't lowercase terms + + - match: { hits.total: 1 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.0._source.my_field1: "dog" } + +--- +"operator": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + my_field1: + query: "brown fox jump" + operator: AND + + - match: { hits.total: 1 } + - match: { hits.hits.0._id: "1" } + - match: { hits.hits.0._source.my_field1: "brown fox jump" } + +--- +"fuzziness": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + match_bool_prefix: + my_field2: + query: "xylophoen foo" + fuzziness: 1 + prefix_length: 1 + max_expansions: 10 + fuzzy_transpositions: true + fuzzy_rewrite: constant_score + + - match: { hits.total: 2 } + - match: { hits.hits.0._source.my_field2: "xylophone" } + - match: { hits.hits.1._source.my_field2: "xylophone" } + +--- +"multi_match single field complete term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox jump" + type: bool_prefix + fields: [ "my_field1" ] + + - match: { hits.total: 3 } + - match: { hits.hits.0._id: "1" } + - match: { hits.hits.0._source.my_field1: "brown fox jump" } + - match: { hits.hits.1._id: "2" } + - match: { hits.hits.1._source.my_field1: "brown emu jump" } + - match: { hits.hits.2._id: "3" } + - match: { hits.hits.2._source.my_field1: "jumparound" } + +--- +"multi_match single field partial term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox ju" + type: bool_prefix + fields: [ "my_field1" ] + + - match: { hits.total: 3 } + - match: { hits.hits.0._id: "1" } + - match: { hits.hits.0._source.my_field1: "brown fox jump" } + - match: { hits.hits.1._id: "2" } + - match: { hits.hits.1._source.my_field1: "brown emu jump" } + - match: { hits.hits.2._id: "3" } + - match: { hits.hits.2._source.my_field1: "jumparound" } + +--- +"multi_match multiple fields complete term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox jump lazy" + type: bool_prefix + fields: [ "my_field1", "my_field2" ] + + - match: { hits.total: 3 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.0._source.my_field1: "dog" } + - match: { hits.hits.0._source.my_field2: "brown fox jump lazy" } + - match: { hits.hits.1._id: "1" } + - match: { hits.hits.1._source.my_field1: "brown fox jump" } + - match: { hits.hits.2._id: "2" } + - match: { hits.hits.2._source.my_field1: "brown emu jump" } + +--- +"multi_match multiple fields partial term": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox jump laz" + type: bool_prefix + fields: [ "my_field1", "my_field2" ] + + - match: { hits.total: 3 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.0._source.my_field1: "dog" } + - match: { hits.hits.0._source.my_field2: "brown fox jump lazy" } + - match: { hits.hits.1._id: "1" } + - match: { hits.hits.1._source.my_field1: "brown fox jump" } + - match: { hits.hits.2._id: "2" } + - match: { hits.hits.2._source.my_field1: "brown emu jump" } + +--- +"multi_match multiple fields with analyzer": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "BROWN FOX JUMP dog" + type: bool_prefix + fields: [ "my_field1", "my_field2" ] + analyzer: whitespace # this analyzer doesn't lowercase terms + + - match: { hits.total: 1 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.0._source.my_field1: "dog" } + - match: { hits.hits.0._source.my_field2: "brown fox jump lazy" } + +--- +"multi_match multiple fields with minimum_should_match": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown fox jump la" + type: bool_prefix + fields: [ "my_field1", "my_field2" ] + minimum_should_match: 4 + + - match: { hits.total: 1 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.0._source.my_field1: "dog" } + - match: { hits.hits.0._source.my_field2: "brown fox jump lazy" } + +--- +"multi_match multiple fields with fuzziness": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "dob nomatch" + type: bool_prefix + fields: [ "my_field1", "my_field2" ] + fuzziness: 1 + + - match: { hits.total: 1 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.0._source.my_field1: "dog" } + - match: { hits.hits.0._source.my_field2: "brown fox jump lazy" } + +--- +"multi_match multiple fields with boost": + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown emu" + type: bool_prefix + fields: [ "my_field1", "my_field2^10" ] + fuzziness: 1 + + - match: { hits.hits.0._id: "3" } + - match: { hits.hits.0._source.my_field2: "emu" } + +--- +"multi_match multiple fields with slop throws exception": + + - do: + catch: /\[slop\] not allowed for type \[bool_prefix\]/ + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown" + type: bool_prefix + fields: [ "my_field1", "my_field2" ] + slop: 1 + +--- +"multi_match multiple fields with cutoff_frequency throws exception": + + - do: + catch: /\[cutoff_frequency\] not allowed for type \[bool_prefix\]/ + search: + rest_total_hits_as_int: true + index: test + body: + query: + multi_match: + query: "brown" + type: bool_prefix + fields: [ "my_field1", "my_field2" ] + cutoff_frequency: 0.001 diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java index 805b50e628bb1..5790248ead807 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java @@ -74,6 +74,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.IntPredicate; import static org.elasticsearch.index.mapper.TypeParsers.parseTextField; @@ -687,69 +688,12 @@ public Query phrasePrefixQuery(TokenStream stream, int slop, int maxExpansions) } private Query analyzePhrasePrefix(TokenStream stream, int slop, int maxExpansions) throws IOException { - final MultiPhrasePrefixQuery query = createPhrasePrefixQuery(stream, name(), slop, maxExpansions); - - if (slop > 0 - || prefixFieldType == null - || prefixFieldType.indexOptions().compareTo(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) < 0) { - return query; - } - - int lastPos = query.getTerms().length - 1; - final Term[][] terms = query.getTerms(); - final int[] positions = query.getPositions(); - for (Term term : terms[lastPos]) { - String value = term.text(); - if (value.length() < prefixFieldType.minChars || value.length() > prefixFieldType.maxChars) { - return query; - } - } - - if (terms.length == 1) { - Term[] newTerms = Arrays.stream(terms[0]) - .map(term -> new Term(prefixFieldType.name(), term.bytes())) - .toArray(Term[]::new); - return new SynonymQuery(newTerms); - } - - SpanNearQuery.Builder spanQuery = new SpanNearQuery.Builder(name(), true); - spanQuery.setSlop(slop); - int previousPos = -1; - for (int i = 0; i < terms.length; i++) { - Term[] posTerms = terms[i]; - int posInc = positions[i] - previousPos; - previousPos = positions[i]; - if (posInc > 1) { - spanQuery.addGap(posInc - 1); - } - if (i == lastPos) { - if (posTerms.length == 1) { - FieldMaskingSpanQuery fieldMask = - new FieldMaskingSpanQuery(new SpanTermQuery(new Term(prefixFieldType.name(), posTerms[0].bytes())), name()); - spanQuery.addClause(fieldMask); - } else { - SpanQuery[] queries = Arrays.stream(posTerms) - .map(term -> new FieldMaskingSpanQuery( - new SpanTermQuery(new Term(prefixFieldType.name(), term.bytes())), name()) - ) - .toArray(SpanQuery[]::new); - spanQuery.addClause(new SpanOrQuery(queries)); - } - } else { - if (posTerms.length == 1) { - spanQuery.addClause(new SpanTermQuery(posTerms[0])); - } else { - SpanTermQuery[] queries = Arrays.stream(posTerms) - .map(SpanTermQuery::new) - .toArray(SpanTermQuery[]::new); - spanQuery.addClause(new SpanOrQuery(queries)); - } - } - } - return spanQuery.build(); + String prefixField = prefixFieldType == null || slop > 0 ? null : prefixFieldType.name(); + IntPredicate usePrefix = (len) -> len >= prefixFieldType.minChars && len <= prefixFieldType.maxChars; + return createPhrasePrefixQuery(stream, name(), slop, maxExpansions, prefixField, usePrefix); } - private static boolean hasGaps(TokenStream stream) throws IOException { + public static boolean hasGaps(TokenStream stream) throws IOException { assert stream instanceof CachingTokenFilter; PositionIncrementAttribute posIncAtt = stream.getAttribute(PositionIncrementAttribute.class); stream.reset(); @@ -963,8 +907,8 @@ public static Query createPhraseQuery(TokenStream stream, String field, int slop return mpqb.build(); } - public static MultiPhrasePrefixQuery createPhrasePrefixQuery(TokenStream stream, String field, - int slop, int maxExpansions) throws IOException { + public static Query createPhrasePrefixQuery(TokenStream stream, String field, int slop, int maxExpansions, + String prefixField, IntPredicate usePrefixField) throws IOException { MultiPhrasePrefixQuery builder = new MultiPhrasePrefixQuery(field); builder.setSlop(slop); builder.setMaxExpansions(maxExpansions); @@ -987,6 +931,61 @@ public static MultiPhrasePrefixQuery createPhrasePrefixQuery(TokenStream stream, currentTerms.add(new Term(field, termAtt.getBytesRef())); } builder.add(currentTerms.toArray(new Term[0]), position); - return builder; + if (prefixField == null) { + return builder; + } + + int lastPos = builder.getTerms().length - 1; + final Term[][] terms = builder.getTerms(); + final int[] positions = builder.getPositions(); + for (Term term : terms[lastPos]) { + String value = term.text(); + if (usePrefixField.test(value.length()) == false) { + return builder; + } + } + + if (terms.length == 1) { + Term[] newTerms = Arrays.stream(terms[0]) + .map(term -> new Term(prefixField, term.bytes())) + .toArray(Term[]::new); + return new SynonymQuery(newTerms); + } + + SpanNearQuery.Builder spanQuery = new SpanNearQuery.Builder(field, true); + spanQuery.setSlop(slop); + int previousPos = -1; + for (int i = 0; i < terms.length; i++) { + Term[] posTerms = terms[i]; + int posInc = positions[i] - previousPos; + previousPos = positions[i]; + if (posInc > 1) { + spanQuery.addGap(posInc - 1); + } + if (i == lastPos) { + if (posTerms.length == 1) { + FieldMaskingSpanQuery fieldMask = + new FieldMaskingSpanQuery(new SpanTermQuery(new Term(prefixField, posTerms[0].bytes())), field); + spanQuery.addClause(fieldMask); + } else { + SpanQuery[] queries = Arrays.stream(posTerms) + .map(term -> new FieldMaskingSpanQuery( + new SpanTermQuery(new Term(prefixField, term.bytes())), field) + ) + .toArray(SpanQuery[]::new); + spanQuery.addClause(new SpanOrQuery(queries)); + } + } else { + if (posTerms.length == 1) { + spanQuery.addClause(new SpanTermQuery(posTerms[0])); + } else { + SpanTermQuery[] queries = Arrays.stream(posTerms) + .map(SpanTermQuery::new) + .toArray(SpanTermQuery[]::new); + spanQuery.addClause(new SpanOrQuery(queries)); + } + } + } + return spanQuery.build(); } } diff --git a/server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java new file mode 100644 index 0000000000000..7f0c89f9df499 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilder.java @@ -0,0 +1,393 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.query; + +import org.apache.lucene.search.FuzzyQuery; +import org.apache.lucene.search.Query; +import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.lucene.search.Queries; +import org.elasticsearch.common.unit.Fuzziness; +import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.query.support.QueryParsers; +import org.elasticsearch.index.search.MatchQuery; + +import java.io.IOException; +import java.util.Objects; + +import static org.elasticsearch.index.query.MatchQueryBuilder.FUZZY_REWRITE_FIELD; +import static org.elasticsearch.index.query.MatchQueryBuilder.FUZZY_TRANSPOSITIONS_FIELD; +import static org.elasticsearch.index.query.MatchQueryBuilder.MAX_EXPANSIONS_FIELD; +import static org.elasticsearch.index.query.MatchQueryBuilder.OPERATOR_FIELD; +import static org.elasticsearch.index.query.MatchQueryBuilder.PREFIX_LENGTH_FIELD; + +/** + * The boolean prefix query analyzes the input text and creates a boolean query containing a Term query for each term, except + * for the last term, which is used to create a prefix query + */ +public class MatchBoolPrefixQueryBuilder extends AbstractQueryBuilder { + + public static final String NAME = "match_bool_prefix"; + + private static final Operator DEFAULT_OPERATOR = Operator.OR; + + private final String fieldName; + + private final Object value; + + private String analyzer; + + private Operator operator = DEFAULT_OPERATOR; + + private String minimumShouldMatch; + + private Fuzziness fuzziness; + + private int prefixLength = FuzzyQuery.defaultPrefixLength; + + private int maxExpansions = FuzzyQuery.defaultMaxExpansions; + + private boolean fuzzyTranspositions = FuzzyQuery.defaultTranspositions; + + private String fuzzyRewrite; + + public MatchBoolPrefixQueryBuilder(String fieldName, Object value) { + if (Strings.isEmpty(fieldName)) { + throw new IllegalArgumentException("[" + NAME + "] requires fieldName"); + } + if (value == null) { + throw new IllegalArgumentException("[" + NAME + "] requires query value"); + } + this.fieldName = fieldName; + this.value = value; + } + + public MatchBoolPrefixQueryBuilder(StreamInput in) throws IOException { + super(in); + fieldName = in.readString(); + value = in.readGenericValue(); + analyzer = in.readOptionalString(); + operator = Operator.readFromStream(in); + minimumShouldMatch = in.readOptionalString(); + fuzziness = in.readOptionalWriteable(Fuzziness::new); + prefixLength = in.readVInt(); + maxExpansions = in.readVInt(); + fuzzyTranspositions = in.readBoolean(); + fuzzyRewrite = in.readOptionalString(); + } + + @Override + protected void doWriteTo(StreamOutput out) throws IOException { + out.writeString(fieldName); + out.writeGenericValue(value); + out.writeOptionalString(analyzer); + operator.writeTo(out); + out.writeOptionalString(minimumShouldMatch); + out.writeOptionalWriteable(fuzziness); + out.writeVInt(prefixLength); + out.writeVInt(maxExpansions); + out.writeBoolean(fuzzyTranspositions); + out.writeOptionalString(fuzzyRewrite); + } + + /** Returns the field name used in this query. */ + public String fieldName() { + return this.fieldName; + } + + /** Returns the value used in this query. */ + public Object value() { + return this.value; + } + + /** Get the analyzer to use, if previously set, otherwise {@code null} */ + public String analyzer() { + return this.analyzer; + } + + /** + * Explicitly set the analyzer to use. Defaults to use explicit mapping + * config for the field, or, if not set, the default search analyzer. + */ + public MatchBoolPrefixQueryBuilder analyzer(String analyzer) { + this.analyzer = analyzer; + return this; + } + + /** Sets the operator to use when using a boolean query. Defaults to {@code OR}. */ + public MatchBoolPrefixQueryBuilder operator(Operator operator) { + if (operator == null) { + throw new IllegalArgumentException("[" + NAME + "] requires operator to be non-null"); + } + this.operator = operator; + return this; + } + + /** Returns the operator to use in a boolean query.*/ + public Operator operator() { + return this.operator; + } + + /** Sets optional minimumShouldMatch value to apply to the query */ + public MatchBoolPrefixQueryBuilder minimumShouldMatch(String minimumShouldMatch) { + this.minimumShouldMatch = minimumShouldMatch; + return this; + } + + /** Gets the minimumShouldMatch value */ + public String minimumShouldMatch() { + return this.minimumShouldMatch; + } + + /** Sets the fuzziness used when evaluated to a fuzzy query type. Defaults to "AUTO". */ + public MatchBoolPrefixQueryBuilder fuzziness(Object fuzziness) { + this.fuzziness = Fuzziness.build(fuzziness); + return this; + } + + /** Gets the fuzziness used when evaluated to a fuzzy query type. */ + public Fuzziness fuzziness() { + return this.fuzziness; + } + + /** + * Sets the length of a length of common (non-fuzzy) prefix for fuzzy match queries + * @param prefixLength non-negative length of prefix + * @throws IllegalArgumentException in case the prefix is negative + */ + public MatchBoolPrefixQueryBuilder prefixLength(int prefixLength) { + if (prefixLength < 0 ) { + throw new IllegalArgumentException("[" + NAME + "] requires prefix length to be non-negative."); + } + this.prefixLength = prefixLength; + return this; + } + + /** + * Gets the length of a length of common (non-fuzzy) prefix for fuzzy match queries + */ + public int prefixLength() { + return this.prefixLength; + } + + /** + * When using fuzzy or prefix type query, the number of term expansions to use. + */ + public MatchBoolPrefixQueryBuilder maxExpansions(int maxExpansions) { + if (maxExpansions <= 0 ) { + throw new IllegalArgumentException("[" + NAME + "] requires maxExpansions to be positive."); + } + this.maxExpansions = maxExpansions; + return this; + } + + /** + * Get the (optional) number of term expansions when using fuzzy or prefix type query. + */ + public int maxExpansions() { + return this.maxExpansions; + } + + /** + * Sets whether transpositions are supported in fuzzy queries.

+ * The default metric used by fuzzy queries to determine a match is the Damerau-Levenshtein + * distance formula which supports transpositions. Setting transposition to false will + * switch to classic Levenshtein distance.
+ * If not set, Damerau-Levenshtein distance metric will be used. + */ + public MatchBoolPrefixQueryBuilder fuzzyTranspositions(boolean fuzzyTranspositions) { + this.fuzzyTranspositions = fuzzyTranspositions; + return this; + } + + /** Gets the fuzzy query transposition setting. */ + public boolean fuzzyTranspositions() { + return this.fuzzyTranspositions; + } + + /** Sets the fuzzy_rewrite parameter controlling how the fuzzy query will get rewritten */ + public MatchBoolPrefixQueryBuilder fuzzyRewrite(String fuzzyRewrite) { + this.fuzzyRewrite = fuzzyRewrite; + return this; + } + + /** + * Get the fuzzy_rewrite parameter + * @see #fuzzyRewrite(String) + */ + public String fuzzyRewrite() { + return this.fuzzyRewrite; + } + + @Override + protected void doXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(NAME); + builder.startObject(fieldName); + builder.field(MatchQueryBuilder.QUERY_FIELD.getPreferredName(), value); + if (analyzer != null) { + builder.field(MatchQueryBuilder.ANALYZER_FIELD.getPreferredName(), analyzer); + } + builder.field(OPERATOR_FIELD.getPreferredName(), operator.toString()); + if (minimumShouldMatch != null) { + builder.field(MatchQueryBuilder.MINIMUM_SHOULD_MATCH_FIELD.getPreferredName(), minimumShouldMatch); + } + if (fuzziness != null) { + fuzziness.toXContent(builder, params); + } + builder.field(PREFIX_LENGTH_FIELD.getPreferredName(), prefixLength); + builder.field(MAX_EXPANSIONS_FIELD.getPreferredName(), maxExpansions); + builder.field(FUZZY_TRANSPOSITIONS_FIELD.getPreferredName(), fuzzyTranspositions); + if (fuzzyRewrite != null) { + builder.field(FUZZY_REWRITE_FIELD.getPreferredName(), fuzzyRewrite); + } + printBoostAndQueryName(builder); + builder.endObject(); + builder.endObject(); + } + + public static MatchBoolPrefixQueryBuilder fromXContent(XContentParser parser) throws IOException { + String fieldName = null; + Object value = null; + float boost = AbstractQueryBuilder.DEFAULT_BOOST; + String analyzer = null; + Operator operator = DEFAULT_OPERATOR; + String minimumShouldMatch = null; + Fuzziness fuzziness = null; + int prefixLength = FuzzyQuery.defaultPrefixLength; + int maxExpansion = FuzzyQuery.defaultMaxExpansions; + boolean fuzzyTranspositions = FuzzyQuery.defaultTranspositions; + String fuzzyRewrite = null; + String queryName = null; + XContentParser.Token token; + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.START_OBJECT) { + throwParsingExceptionOnMultipleFields(NAME, parser.getTokenLocation(), fieldName, currentFieldName); + fieldName = currentFieldName; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token.isValue()) { + if (MatchQueryBuilder.QUERY_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + value = parser.objectText(); + } else if (MatchQueryBuilder.ANALYZER_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + analyzer = parser.text(); + } else if (OPERATOR_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + operator = Operator.fromString(parser.text()); + } else if (MatchQueryBuilder.MINIMUM_SHOULD_MATCH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + minimumShouldMatch = parser.textOrNull(); + } else if (Fuzziness.FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + fuzziness = Fuzziness.parse(parser); + } else if (PREFIX_LENGTH_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + prefixLength = parser.intValue(); + } else if (MAX_EXPANSIONS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + maxExpansion = parser.intValue(); + } else if (FUZZY_TRANSPOSITIONS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + fuzzyTranspositions = parser.booleanValue(); + } else if (FUZZY_REWRITE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + fuzzyRewrite = parser.textOrNull(); + } else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + boost = parser.floatValue(); + } else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { + queryName = parser.text(); + } else { + throw new ParsingException(parser.getTokenLocation(), + "[" + NAME + "] query does not support [" + currentFieldName + "]"); + } + } else { + throw new ParsingException(parser.getTokenLocation(), + "[" + NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]"); + } + } + } else { + throwParsingExceptionOnMultipleFields(NAME, parser.getTokenLocation(), fieldName, parser.currentName()); + fieldName = parser.currentName(); + value = parser.objectText(); + } + } + + MatchBoolPrefixQueryBuilder queryBuilder = new MatchBoolPrefixQueryBuilder(fieldName, value); + queryBuilder.analyzer(analyzer); + queryBuilder.operator(operator); + queryBuilder.minimumShouldMatch(minimumShouldMatch); + queryBuilder.boost(boost); + queryBuilder.queryName(queryName); + if (fuzziness != null) { + queryBuilder.fuzziness(fuzziness); + } + queryBuilder.prefixLength(prefixLength); + queryBuilder.maxExpansions(maxExpansion); + queryBuilder.fuzzyTranspositions(fuzzyTranspositions); + queryBuilder.fuzzyRewrite(fuzzyRewrite); + return queryBuilder; + } + + @Override + protected Query doToQuery(QueryShardContext context) throws IOException { + if (analyzer != null && context.getIndexAnalyzers().get(analyzer) == null) { + throw new QueryShardException(context, "[" + NAME + "] analyzer [" + analyzer + "] not found"); + } + + final MatchQuery matchQuery = new MatchQuery(context); + if (analyzer != null) { + matchQuery.setAnalyzer(analyzer); + } + matchQuery.setOccur(operator.toBooleanClauseOccur()); + matchQuery.setFuzziness(fuzziness); + matchQuery.setFuzzyPrefixLength(prefixLength); + matchQuery.setMaxExpansions(maxExpansions); + matchQuery.setTranspositions(fuzzyTranspositions); + matchQuery.setFuzzyRewriteMethod(QueryParsers.parseRewriteMethod(fuzzyRewrite, null, LoggingDeprecationHandler.INSTANCE)); + + final Query query = matchQuery.parse(MatchQuery.Type.BOOLEAN_PREFIX, fieldName, value); + return Queries.maybeApplyMinimumShouldMatch(query, minimumShouldMatch); + } + + @Override + protected boolean doEquals(MatchBoolPrefixQueryBuilder other) { + return Objects.equals(fieldName, other.fieldName) && + Objects.equals(value, other.value) && + Objects.equals(analyzer, other.analyzer) && + Objects.equals(operator, other.operator) && + Objects.equals(minimumShouldMatch, other.minimumShouldMatch) && + Objects.equals(fuzziness, other.fuzziness) && + Objects.equals(prefixLength, other.prefixLength) && + Objects.equals(maxExpansions, other.maxExpansions) && + Objects.equals(fuzzyTranspositions, other.fuzzyTranspositions) && + Objects.equals(fuzzyRewrite, other.fuzzyRewrite); + } + + @Override + protected int doHashCode() { + return Objects.hash(fieldName, value, analyzer, operator, minimumShouldMatch, fuzziness, prefixLength, maxExpansions, + fuzzyTranspositions, fuzzyRewrite); + } + + @Override + public String getWriteableName() { + return NAME; + } +} diff --git a/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java index 0e9148e540102..267c86ea84486 100644 --- a/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java @@ -128,7 +128,12 @@ public enum Type implements Writeable { * Uses the best matching phrase-prefix field as main score and uses * a tie-breaker to adjust the score based on remaining field matches */ - PHRASE_PREFIX(MatchQuery.Type.PHRASE_PREFIX, 0.0f, new ParseField("phrase_prefix")); + PHRASE_PREFIX(MatchQuery.Type.PHRASE_PREFIX, 0.0f, new ParseField("phrase_prefix")), + + /** + * Uses the sum of the matching boolean fields to score the query + */ + BOOL_PREFIX(MatchQuery.Type.BOOLEAN_PREFIX, 1.0f, new ParseField("bool_prefix")); private MatchQuery.Type matchQueryType; private final float tieBreaker; @@ -687,6 +692,16 @@ public static MultiMatchQueryBuilder fromXContent(XContentParser parser) throws "Fuzziness not allowed for type [" + type.parseField.getPreferredName() + "]"); } + if (slop != DEFAULT_PHRASE_SLOP && type == Type.BOOL_PREFIX) { + throw new ParsingException(parser.getTokenLocation(), + "[" + SLOP_FIELD.getPreferredName() + "] not allowed for type [" + type.parseField.getPreferredName() + "]"); + } + + if (cutoffFrequency != null && type == Type.BOOL_PREFIX) { + throw new ParsingException(parser.getTokenLocation(), + "[" + CUTOFF_FREQUENCY_FIELD.getPreferredName() + "] not allowed for type [" + type.parseField.getPreferredName() + "]"); + } + MultiMatchQueryBuilder builder = new MultiMatchQueryBuilder(value) .fields(fieldsBoosts) .type(type) diff --git a/server/src/main/java/org/elasticsearch/index/search/MatchQuery.java b/server/src/main/java/org/elasticsearch/index/search/MatchQuery.java index ad4b267eef643..da7273aa66303 100644 --- a/server/src/main/java/org/elasticsearch/index/search/MatchQuery.java +++ b/server/src/main/java/org/elasticsearch/index/search/MatchQuery.java @@ -23,6 +23,7 @@ import org.apache.lucene.analysis.CachingTokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.miscellaneous.DisableGraphAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; import org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute; import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; @@ -51,7 +52,9 @@ import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.lucene.search.SpanBooleanQueryRewriteWithMaxClause; import org.elasticsearch.common.unit.Fuzziness; +import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.TextFieldMapper; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.support.QueryParsers; @@ -78,7 +81,11 @@ public enum Type implements Writeable { /** * The text is analyzed and used in a phrase query, with the last term acting as a prefix. */ - PHRASE_PREFIX(2); + PHRASE_PREFIX(2), + /** + * The text is analyzed, terms are added to a boolean query with the last term acting as a prefix. + */ + BOOLEAN_PREFIX(3); private final int ordinal; @@ -244,11 +251,18 @@ public Query parse(Type type, String fieldName, Object value) throws IOException /* * If a keyword analyzer is used, we know that further analysis isn't - * needed and can immediately return a term query. + * needed and can immediately return a term query. If the query is a bool + * prefix query and the field type supports prefix queries, we return + * a prefix query instead */ - if (analyzer == Lucene.KEYWORD_ANALYZER - && type != Type.PHRASE_PREFIX) { - return builder.newTermQuery(new Term(fieldName, value.toString())); + if (analyzer == Lucene.KEYWORD_ANALYZER && type != Type.PHRASE_PREFIX) { + final Term term = new Term(fieldName, value.toString()); + if ((fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType) + && type == Type.BOOLEAN_PREFIX) { + return builder.newPrefixQuery(fieldName, term); + } else { + return builder.newTermQuery(term); + } } return parseInternal(type, fieldName, builder, value); @@ -265,6 +279,10 @@ protected final Query parseInternal(Type type, String fieldName, MatchQueryBuild } break; + case BOOLEAN_PREFIX: + query = builder.createBooleanPrefixQuery(fieldName, value.toString(), occur); + break; + case PHRASE: query = builder.createPhraseQuery(fieldName, value.toString(), phraseSlop); break; @@ -354,10 +372,28 @@ protected Query createFieldQuery(Analyzer analyzer, BooleanClause.Occur operator return createQuery(field, queryText, type, operator, slop); } - public Query createPhrasePrefixQuery(String field, String queryText, int slop) { + /** + * Creates a phrase prefix query from the query text. + * + * @param field field name + * @param queryText text to be passed to the analyzer + * @return {@code PrefixQuery}, {@code MultiPhrasePrefixQuery}, based on the analysis of {@code queryText} + */ + protected Query createPhrasePrefixQuery(String field, String queryText, int slop) { return createQuery(field, queryText, Type.PHRASE_PREFIX, occur, slop); } + /** + * Creates a boolean prefix query from the query text. + * + * @param field field name + * @param queryText text to be passed to the analyzer + * @return {@code PrefixQuery}, {@code BooleanQuery}, based on the analysis of {@code queryText} + */ + protected Query createBooleanPrefixQuery(String field, String queryText, BooleanClause.Occur occur) { + return createQuery(field, queryText, Type.BOOLEAN_PREFIX, occur, 0); + } + private Query createFieldQuery(TokenStream source, Type type, BooleanClause.Occur operator, String field, int phraseSlop) { assert operator == BooleanClause.Occur.SHOULD || operator == BooleanClause.Occur.MUST; @@ -405,14 +441,14 @@ private Query createFieldQuery(TokenStream source, Type type, BooleanClause.Occu if (type == Type.PHRASE_PREFIX) { return analyzePhrasePrefix(field, stream, phraseSlop, positionCount); } else { - return analyzeTerm(field, stream); + return analyzeTerm(field, stream, type == Type.BOOLEAN_PREFIX); } } else if (isGraph) { // graph if (type == Type.PHRASE || type == Type.PHRASE_PREFIX) { return analyzeGraphPhrase(stream, field, type, phraseSlop); } else { - return analyzeGraphBoolean(field, stream, operator); + return analyzeGraphBoolean(field, stream, operator, type == Type.BOOLEAN_PREFIX); } } else if (type == Type.PHRASE && positionCount > 1) { // phrase @@ -433,7 +469,7 @@ private Query createFieldQuery(TokenStream source, Type type, BooleanClause.Occu return analyzeBoolean(field, stream); } else { // complex case: multiple positions - return analyzeMultiBoolean(field, stream, operator); + return analyzeMultiBoolean(field, stream, operator, type == Type.BOOLEAN_PREFIX); } } } catch (IOException e) { @@ -462,13 +498,13 @@ private Query createQuery(String field, String queryText, Type type, BooleanClau } } - private SpanQuery newSpanQuery(Term[] terms, boolean prefix) { + private SpanQuery newSpanQuery(Term[] terms, boolean isPrefix) { if (terms.length == 1) { - return prefix ? fieldType.spanPrefixQuery(terms[0].text(), spanRewriteMethod, context) : new SpanTermQuery(terms[0]); + return isPrefix ? fieldType.spanPrefixQuery(terms[0].text(), spanRewriteMethod, context) : new SpanTermQuery(terms[0]); } SpanQuery[] spanQueries = new SpanQuery[terms.length]; for (int i = 0; i < terms.length; i++) { - spanQueries[i] = prefix ? new SpanTermQuery(terms[i]) : + spanQueries[i] = isPrefix ? new SpanTermQuery(terms[i]) : fieldType.spanPrefixQuery(terms[i].text(), spanRewriteMethod, context); } return new SpanOrQuery(spanQueries); @@ -479,7 +515,7 @@ protected SpanQuery createSpanQuery(TokenStream in, String field) throws IOExcep return createSpanQuery(in, field, false); } - private SpanQuery createSpanQuery(TokenStream in, String field, boolean prefix) throws IOException { + private SpanQuery createSpanQuery(TokenStream in, String field, boolean isPrefix) throws IOException { TermToBytesRefAttribute termAtt = in.getAttribute(TermToBytesRefAttribute.class); PositionIncrementAttribute posIncAtt = in.getAttribute(PositionIncrementAttribute.class); if (termAtt == null) { @@ -498,7 +534,7 @@ private SpanQuery createSpanQuery(TokenStream in, String field, boolean prefix) lastTerm = new Term(field, termAtt.getBytesRef()); } if (lastTerm != null) { - SpanQuery spanQuery = prefix ? + SpanQuery spanQuery = isPrefix ? fieldType.spanPrefixQuery(lastTerm.text(), spanRewriteMethod, context) : new SpanTermQuery(lastTerm); builder.addClause(spanQuery); } @@ -537,6 +573,74 @@ protected Query newTermQuery(Term term) { } } + /** + * Builds a new prefix query instance. + */ + protected Query newPrefixQuery(String field, Term term) { + try { + return fieldType.prefixQuery(term.text(), null, context); + } catch (RuntimeException e) { + if (lenient) { + return newLenientFieldQuery(field, e); + } + throw e; + } + } + + private Query analyzeTerm(String field, TokenStream stream, boolean isPrefix) throws IOException { + TermToBytesRefAttribute termAtt = stream.getAttribute(TermToBytesRefAttribute.class); + OffsetAttribute offsetAtt = stream.addAttribute(OffsetAttribute.class); + + stream.reset(); + if (!stream.incrementToken()) { + throw new AssertionError(); + } + final Term term = new Term(field, termAtt.getBytesRef()); + int lastOffset = offsetAtt.endOffset(); + stream.end(); + return isPrefix && lastOffset == offsetAtt.endOffset() ? newPrefixQuery(field, term) : newTermQuery(term); + } + + private void add(BooleanQuery.Builder q, String field, List current, BooleanClause.Occur operator, boolean isPrefix) { + if (current.isEmpty()) { + return; + } + if (current.size() == 1) { + if (isPrefix) { + q.add(newPrefixQuery(field, current.get(0)), operator); + } else { + q.add(newTermQuery(current.get(0)), operator); + } + } else { + // We don't apply prefix on synonyms + q.add(newSynonymQuery(current.toArray(new Term[current.size()])), operator); + } + } + + private Query analyzeMultiBoolean(String field, TokenStream stream, + BooleanClause.Occur operator, boolean isPrefix) throws IOException { + BooleanQuery.Builder q = newBooleanQuery(); + List currentQuery = new ArrayList<>(); + + TermToBytesRefAttribute termAtt = stream.getAttribute(TermToBytesRefAttribute.class); + PositionIncrementAttribute posIncrAtt = stream.getAttribute(PositionIncrementAttribute.class); + OffsetAttribute offsetAtt = stream.addAttribute(OffsetAttribute.class); + + stream.reset(); + int lastOffset = 0; + while (stream.incrementToken()) { + if (posIncrAtt.getPositionIncrement() != 0) { + add(q, field, currentQuery, operator, false); + currentQuery.clear(); + } + currentQuery.add(new Term(field, termAtt.getBytesRef())); + lastOffset = offsetAtt.endOffset(); + } + stream.end(); + add(q, field, currentQuery, operator, isPrefix && lastOffset == offsetAtt.endOffset()); + return q.build(); + } + @Override protected Query analyzePhrase(String field, TokenStream stream, int slop) throws IOException { try { @@ -577,6 +681,62 @@ private Query analyzePhrasePrefix(String field, TokenStream stream, int slop, in } } + private Query analyzeGraphBoolean(String field, TokenStream source, + BooleanClause.Occur operator, boolean isPrefix) throws IOException { + source.reset(); + GraphTokenStreamFiniteStrings graph = new GraphTokenStreamFiniteStrings(source); + BooleanQuery.Builder builder = new BooleanQuery.Builder(); + int[] articulationPoints = graph.articulationPoints(); + int lastState = 0; + for (int i = 0; i <= articulationPoints.length; i++) { + int start = lastState; + int end = -1; + if (i < articulationPoints.length) { + end = articulationPoints[i]; + } + lastState = end; + final Query queryPos; + boolean usePrefix = isPrefix && end == -1; + if (graph.hasSidePath(start)) { + final Iterator it = graph.getFiniteStrings(start, end); + Iterator queries = new Iterator() { + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public Query next() { + TokenStream ts = it.next(); + final Type type; + if (getAutoGenerateMultiTermSynonymsPhraseQuery()) { + type = usePrefix + ? Type.PHRASE_PREFIX + : Type.PHRASE; + } else { + type = Type.BOOLEAN; + } + return createFieldQuery(ts, type, BooleanClause.Occur.MUST, field, 0); + } + }; + queryPos = newGraphSynonymQuery(queries); + } else { + Term[] terms = graph.getTerms(field, start); + assert terms.length > 0; + if (terms.length == 1) { + queryPos = usePrefix ? newPrefixQuery(field, terms[0]) : newTermQuery(terms[0]); + } else { + // We don't apply prefix on synonyms + queryPos = newSynonymQuery(terms); + } + } + if (queryPos != null) { + builder.add(queryPos, operator); + } + } + return builder.build(); + } + private Query analyzeGraphPhrase(TokenStream source, String field, Type type, int slop) throws IOException { assert type == Type.PHRASE_PREFIX || type == Type.PHRASE; @@ -615,13 +775,13 @@ private Query analyzeGraphPhrase(TokenStream source, String field, Type type, in } lastState = end; final SpanQuery queryPos; - boolean endPrefix = end == -1 && type == Type.PHRASE_PREFIX; + boolean usePrefix = end == -1 && type == Type.PHRASE_PREFIX; if (graph.hasSidePath(start)) { List queries = new ArrayList<>(); Iterator it = graph.getFiniteStrings(start, end); while (it.hasNext()) { TokenStream ts = it.next(); - SpanQuery q = createSpanQuery(ts, field, endPrefix); + SpanQuery q = createSpanQuery(ts, field, usePrefix); if (q != null) { if (queries.size() >= maxClauseCount) { throw new BooleanQuery.TooManyClauses(); @@ -640,7 +800,7 @@ private Query analyzeGraphPhrase(TokenStream source, String field, Type type, in if (terms.length >= maxClauseCount) { throw new BooleanQuery.TooManyClauses(); } - queryPos = newSpanQuery(terms, endPrefix); + queryPos = newSpanQuery(terms, usePrefix); } if (queryPos != null) { diff --git a/server/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java b/server/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java index 88fd5293392b5..667d3a3823db8 100644 --- a/server/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java +++ b/server/src/main/java/org/elasticsearch/index/search/MultiMatchQuery.java @@ -66,6 +66,7 @@ public Query parse(MultiMatchQueryBuilder.Type type, Map fieldNam case PHRASE_PREFIX: case BEST_FIELDS: case MOST_FIELDS: + case BOOL_PREFIX: queries = buildFieldQueries(type, fieldNames, value, minimumShouldMatch); break; @@ -179,10 +180,23 @@ protected Query newSynonymQuery(Term[] terms) { } @Override - public Query newTermQuery(Term term) { + protected Query newTermQuery(Term term) { return blendTerm(context, term.bytes(), commonTermsCutoff, tieBreaker, lenient, blendedFields); } + @Override + protected Query newPrefixQuery(String field, Term term) { + List disjunctions = new ArrayList<>(); + for (FieldAndBoost fieldType : blendedFields) { + Query query = fieldType.fieldType.prefixQuery(term.text(), null, context); + if (fieldType.boost != 1f) { + query = new BoostQuery(query, fieldType.boost); + } + disjunctions.add(query); + } + return new DisjunctionMaxQuery(disjunctions, tieBreaker); + } + @Override protected Query analyzePhrase(String field, TokenStream stream, int slop) throws IOException { List disjunctions = new ArrayList<>(); diff --git a/server/src/main/java/org/elasticsearch/search/SearchModule.java b/server/src/main/java/org/elasticsearch/search/SearchModule.java index de4f548f6cf08..4c6ba07c631af 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchModule.java +++ b/server/src/main/java/org/elasticsearch/search/SearchModule.java @@ -32,6 +32,7 @@ import org.elasticsearch.common.xcontent.ParseFieldRegistry; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.MatchBoolPrefixQueryBuilder; import org.elasticsearch.index.query.BoostingQueryBuilder; import org.elasticsearch.index.query.CommonTermsQueryBuilder; import org.elasticsearch.index.query.ConstantScoreQueryBuilder; @@ -786,6 +787,8 @@ private void registerQueryParsers(List plugins) { registerQuery(new QuerySpec<>(IntervalQueryBuilder.NAME, IntervalQueryBuilder::new, IntervalQueryBuilder::fromXContent)); registerQuery(new QuerySpec<>(DistanceFeatureQueryBuilder.NAME, DistanceFeatureQueryBuilder::new, DistanceFeatureQueryBuilder::fromXContent)); + registerQuery( + new QuerySpec<>(MatchBoolPrefixQueryBuilder.NAME, MatchBoolPrefixQueryBuilder::new, MatchBoolPrefixQueryBuilder::fromXContent)); if (ShapesAvailability.JTS_AVAILABLE && ShapesAvailability.SPATIAL4J_AVAILABLE) { registerQuery(new QuerySpec<>(GeoShapeQueryBuilder.NAME, GeoShapeQueryBuilder::new, GeoShapeQueryBuilder::fromXContent)); diff --git a/server/src/test/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilderTests.java new file mode 100644 index 0000000000000..b3a3a2512a5ff --- /dev/null +++ b/server/src/test/java/org/elasticsearch/index/query/MatchBoolPrefixQueryBuilderTests.java @@ -0,0 +1,284 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.query; + +import org.apache.lucene.analysis.MockSynonymAnalyzer; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; +import org.apache.lucene.search.BooleanQuery; +import org.apache.lucene.search.FuzzyQuery; +import org.apache.lucene.search.PrefixQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.SynonymQuery; +import org.apache.lucene.search.TermQuery; +import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.lucene.search.Queries; +import org.elasticsearch.index.search.MatchQuery; +import org.elasticsearch.search.internal.SearchContext; +import org.elasticsearch.test.AbstractQueryTestCase; + +import java.io.IOException; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.everyItem; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.hasSize; + +public class MatchBoolPrefixQueryBuilderTests extends AbstractQueryTestCase { + + @Override + protected MatchBoolPrefixQueryBuilder doCreateTestQueryBuilder() { + final String fieldName = randomFrom(STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME); + final Object value = IntStream.rangeClosed(0, randomIntBetween(0, 3)) + .mapToObj(i -> randomAlphaOfLengthBetween(1, 10) + " ") + .collect(Collectors.joining()) + .trim(); + + final MatchBoolPrefixQueryBuilder queryBuilder = new MatchBoolPrefixQueryBuilder(fieldName, value); + + if (randomBoolean() && isTextField(fieldName)) { + queryBuilder.analyzer(randomFrom("simple", "keyword", "whitespace")); + } + + if (randomBoolean()) { + queryBuilder.operator(randomFrom(Operator.values())); + } + + if (randomBoolean()) { + queryBuilder.minimumShouldMatch(randomMinimumShouldMatch()); + } + + if (randomBoolean()) { + queryBuilder.fuzziness(randomFuzziness(fieldName)); + } + + if (randomBoolean()) { + queryBuilder.prefixLength(randomIntBetween(0, 10)); + } + + if (randomBoolean()) { + queryBuilder.maxExpansions(randomIntBetween(1, 1000)); + } + + if (randomBoolean()) { + queryBuilder.fuzzyTranspositions(randomBoolean()); + } + + if (randomBoolean()) { + queryBuilder.fuzzyRewrite(getRandomRewriteMethod()); + } + + return queryBuilder; + } + + @Override + protected void doAssertLuceneQuery(MatchBoolPrefixQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException { + assertThat(query, notNullValue()); + assertThat(query, anyOf(instanceOf(BooleanQuery.class), instanceOf(PrefixQuery.class))); + + if (query instanceof PrefixQuery) { + final PrefixQuery prefixQuery = (PrefixQuery) query; + assertThat(prefixQuery.getPrefix().text(), equalToIgnoringCase((String) queryBuilder.value())); + } else { + assertThat(query, instanceOf(BooleanQuery.class)); + final BooleanQuery booleanQuery = (BooleanQuery) query; + // all queries except the last should be TermQuery or SynonymQuery + final Set allQueriesExceptLast = IntStream.range(0, booleanQuery.clauses().size() - 1) + .mapToObj(booleanQuery.clauses()::get) + .map(BooleanClause::getQuery) + .collect(Collectors.toSet()); + assertThat(allQueriesExceptLast, anyOf( + everyItem(instanceOf(TermQuery.class)), + everyItem(instanceOf(SynonymQuery.class)), + everyItem(instanceOf(FuzzyQuery.class)) + )); + + if (allQueriesExceptLast.stream().anyMatch(subQuery -> subQuery instanceof FuzzyQuery)) { + assertThat(queryBuilder.fuzziness(), notNullValue()); + } + allQueriesExceptLast.stream().filter(subQuery -> subQuery instanceof FuzzyQuery).forEach(subQuery -> { + final FuzzyQuery fuzzyQuery = (FuzzyQuery) subQuery; + assertThat(fuzzyQuery.getPrefixLength(), equalTo(queryBuilder.prefixLength())); + assertThat(fuzzyQuery.getTranspositions(), equalTo(queryBuilder.fuzzyTranspositions())); + }); + + // the last query should be PrefixQuery + final Query shouldBePrefixQuery = booleanQuery.clauses().get(booleanQuery.clauses().size() - 1).getQuery(); + assertThat(shouldBePrefixQuery, instanceOf(PrefixQuery.class)); + + if (queryBuilder.minimumShouldMatch() != null) { + final int optionalClauses = + (int) booleanQuery.clauses().stream().filter(clause -> clause.getOccur() == BooleanClause.Occur.SHOULD).count(); + final int expected = Queries.calculateMinShouldMatch(optionalClauses, queryBuilder.minimumShouldMatch()); + assertThat(booleanQuery.getMinimumNumberShouldMatch(), equalTo(expected)); + } + } + } + + public void testIllegalValues() { + { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new MatchBoolPrefixQueryBuilder(null, "value")); + assertEquals("[match_bool_prefix] requires fieldName", e.getMessage()); + } + + { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new MatchBoolPrefixQueryBuilder("name", null)); + assertEquals("[match_bool_prefix] requires query value", e.getMessage()); + } + + { + final MatchBoolPrefixQueryBuilder builder = new MatchBoolPrefixQueryBuilder("name", "value"); + builder.analyzer("bogusAnalyzer"); + QueryShardException e = expectThrows(QueryShardException.class, () -> builder.toQuery(createShardContext())); + assertThat(e.getMessage(), containsString("analyzer [bogusAnalyzer] not found")); + } + } + + public void testFromSimpleJson() throws IOException { + final String simple = + "{" + + "\"match_bool_prefix\": {" + + "\"fieldName\": \"fieldValue\"" + + "}" + + "}"; + final String expected = + "{" + + "\"match_bool_prefix\": {" + + "\"fieldName\": {" + + "\"query\": \"fieldValue\"," + + "\"operator\": \"OR\"," + + "\"prefix_length\": 0," + + "\"max_expansions\": 50," + + "\"fuzzy_transpositions\": true," + + "\"boost\": 1.0" + + "}" + + "}" + + "}"; + + final MatchBoolPrefixQueryBuilder builder = (MatchBoolPrefixQueryBuilder) parseQuery(simple); + checkGeneratedJson(expected, builder); + } + + public void testFromJson() throws IOException { + final String expected = + "{" + + "\"match_bool_prefix\": {" + + "\"fieldName\": {" + + "\"query\": \"fieldValue\"," + + "\"analyzer\": \"simple\"," + + "\"operator\": \"AND\"," + + "\"minimum_should_match\": \"2\"," + + "\"fuzziness\": \"1\"," + + "\"prefix_length\": 1," + + "\"max_expansions\": 10," + + "\"fuzzy_transpositions\": false," + + "\"fuzzy_rewrite\": \"constant_score\"," + + "\"boost\": 2.0" + + "}" + + "}" + + "}"; + + final MatchBoolPrefixQueryBuilder builder = (MatchBoolPrefixQueryBuilder) parseQuery(expected); + checkGeneratedJson(expected, builder); + } + + public void testParseFailsWithMultipleFields() { + { + final String json = + "{" + + "\"match_bool_prefix\" : {" + + "\"field_name_1\" : {" + + "\"query\" : \"foo\"" + + "}," + + "\"field_name_2\" : {" + + "\"query\" : \"foo\"\n" + + "}" + + "}" + + "}"; + final ParsingException e = expectThrows(ParsingException.class, () -> parseQuery(json)); + assertEquals( + "[match_bool_prefix] query doesn't support multiple fields, found [field_name_1] and [field_name_2]", e.getMessage()); + } + + { + final String simpleJson = + "{" + + "\"match_bool_prefix\" : {" + + "\"field_name_1\" : \"foo\"," + + "\"field_name_2\" : \"foo\"" + + "}" + + "}"; + final ParsingException e = expectThrows(ParsingException.class, () -> parseQuery(simpleJson)); + assertEquals( + "[match_bool_prefix] query doesn't support multiple fields, found [field_name_1] and [field_name_2]", e.getMessage()); + } + } + + public void testAnalysis() throws Exception { + final MatchBoolPrefixQueryBuilder builder = new MatchBoolPrefixQueryBuilder(STRING_FIELD_NAME, "foo bar baz"); + final Query query = builder.toQuery(createShardContext()); + + assertBooleanQuery(query, asList( + new TermQuery(new Term(STRING_FIELD_NAME, "foo")), + new TermQuery(new Term(STRING_FIELD_NAME, "bar")), + new PrefixQuery(new Term(STRING_FIELD_NAME, "baz")) + )); + } + + public void testAnalysisSynonym() throws Exception { + final MatchQuery matchQuery = new MatchQuery(createShardContext()); + matchQuery.setAnalyzer(new MockSynonymAnalyzer()); + final Query query = matchQuery.parse(MatchQuery.Type.BOOLEAN_PREFIX, STRING_FIELD_NAME, "fox dogs red"); + + assertBooleanQuery(query, asList( + new TermQuery(new Term(STRING_FIELD_NAME, "fox")), + new SynonymQuery(new Term(STRING_FIELD_NAME, "dogs"), new Term(STRING_FIELD_NAME, "dog")), + new PrefixQuery(new Term(STRING_FIELD_NAME, "red")) + )); + } + + public void testAnalysisSingleTerm() throws Exception { + final MatchBoolPrefixQueryBuilder builder = new MatchBoolPrefixQueryBuilder(STRING_FIELD_NAME, "foo"); + final Query query = builder.toQuery(createShardContext()); + assertThat(query, equalTo(new PrefixQuery(new Term(STRING_FIELD_NAME, "foo")))); + } + + private static void assertBooleanQuery(Query actual, List expectedClauseQueries) { + assertThat(actual, instanceOf(BooleanQuery.class)); + final BooleanQuery actualBooleanQuery = (BooleanQuery) actual; + assertThat(actualBooleanQuery.clauses(), hasSize(expectedClauseQueries.size())); + assertThat(actualBooleanQuery.clauses(), everyItem(hasProperty("occur", equalTo(BooleanClause.Occur.SHOULD)))); + + for (int i = 0; i < actualBooleanQuery.clauses().size(); i++) { + final Query clauseQuery = actualBooleanQuery.clauses().get(i).getQuery(); + assertThat(clauseQuery, equalTo(expectedClauseQueries.get(i))); + } + } +} diff --git a/server/src/test/java/org/elasticsearch/index/query/MatchQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/MatchQueryBuilderTests.java index c258cce6c7c50..e9f2b447da133 100644 --- a/server/src/test/java/org/elasticsearch/index/query/MatchQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/MatchQueryBuilderTests.java @@ -21,6 +21,7 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.CannedBinaryTokenStream; +import org.apache.lucene.analysis.MockSynonymAnalyzer; import org.apache.lucene.index.Term; import org.apache.lucene.queries.ExtendedCommonTermsQuery; import org.apache.lucene.search.BooleanClause; @@ -28,6 +29,7 @@ import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchNoDocsQuery; +import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PointRangeQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; @@ -394,6 +396,76 @@ public void testLenientPhraseQuery() throws Exception { containsString("field:[string_no_pos] was indexed without position data; cannot run PhraseQuery")); } + public void testAutoGenerateSynonymsPhraseQuery() throws Exception { + final MatchQuery matchQuery = new MatchQuery(createShardContext()); + matchQuery.setAnalyzer(new MockSynonymAnalyzer()); + + { + matchQuery.setAutoGenerateSynonymsPhraseQuery(false); + final Query query = matchQuery.parse(Type.BOOLEAN, STRING_FIELD_NAME, "guinea pig"); + final Query expectedQuery = new BooleanQuery.Builder() + .add(new BooleanQuery.Builder() + .add(new BooleanQuery.Builder() + .add(new TermQuery(new Term(STRING_FIELD_NAME, "guinea")), BooleanClause.Occur.MUST) + .add(new TermQuery(new Term(STRING_FIELD_NAME, "pig")), BooleanClause.Occur.MUST) + .build(), + BooleanClause.Occur.SHOULD) + .add(new TermQuery(new Term(STRING_FIELD_NAME, "cavy")), BooleanClause.Occur.SHOULD) + .build(), + BooleanClause.Occur.SHOULD).build(); + assertThat(query, equalTo(expectedQuery)); + } + + { + matchQuery.setAutoGenerateSynonymsPhraseQuery(true); + final Query query = matchQuery.parse(Type.BOOLEAN, STRING_FIELD_NAME, "guinea pig"); + final Query expectedQuery = new BooleanQuery.Builder() + .add(new BooleanQuery.Builder() + .add(new PhraseQuery.Builder() + .add(new Term(STRING_FIELD_NAME, "guinea")) + .add(new Term(STRING_FIELD_NAME, "pig")) + .build(), + BooleanClause.Occur.SHOULD) + .add(new TermQuery(new Term(STRING_FIELD_NAME, "cavy")), BooleanClause.Occur.SHOULD) + .build(), + BooleanClause.Occur.SHOULD).build(); + assertThat(query, equalTo(expectedQuery)); + } + + { + matchQuery.setAutoGenerateSynonymsPhraseQuery(false); + final Query query = matchQuery.parse(Type.BOOLEAN_PREFIX, STRING_FIELD_NAME, "guinea pig"); + final Query expectedQuery = new BooleanQuery.Builder() + .add(new BooleanQuery.Builder() + .add(new BooleanQuery.Builder() + .add(new TermQuery(new Term(STRING_FIELD_NAME, "guinea")), BooleanClause.Occur.MUST) + .add(new TermQuery(new Term(STRING_FIELD_NAME, "pig")), BooleanClause.Occur.MUST) + .build(), + BooleanClause.Occur.SHOULD) + .add(new TermQuery(new Term(STRING_FIELD_NAME, "cavy")), BooleanClause.Occur.SHOULD) + .build(), + BooleanClause.Occur.SHOULD).build(); + assertThat(query, equalTo(expectedQuery)); + } + + { + matchQuery.setAutoGenerateSynonymsPhraseQuery(true); + final Query query = matchQuery.parse(Type.BOOLEAN_PREFIX, STRING_FIELD_NAME, "guinea pig"); + final MultiPhrasePrefixQuery guineaPig = new MultiPhrasePrefixQuery(STRING_FIELD_NAME); + guineaPig.add(new Term(STRING_FIELD_NAME, "guinea")); + guineaPig.add(new Term(STRING_FIELD_NAME, "pig")); + final MultiPhrasePrefixQuery cavy = new MultiPhrasePrefixQuery(STRING_FIELD_NAME); + cavy.add(new Term(STRING_FIELD_NAME, "cavy")); + final Query expectedQuery = new BooleanQuery.Builder() + .add(new BooleanQuery.Builder() + .add(guineaPig, BooleanClause.Occur.SHOULD) + .add(cavy, BooleanClause.Occur.SHOULD) + .build(), + BooleanClause.Occur.SHOULD).build(); + assertThat(query, equalTo(expectedQuery)); + } + } + public void testMaxBooleanClause() { MatchQuery query = new MatchQuery(createShardContext()); query.setAnalyzer(new MockGraphAnalyzer(createGiantGraph(40))); diff --git a/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java index 36ba370939b17..7ca722fc31139 100644 --- a/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java @@ -31,6 +31,7 @@ import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PointRangeQuery; +import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -52,10 +53,11 @@ import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBooleanSubQuery; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertDisjunctionSubQuery; +import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.either; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase { @@ -91,10 +93,11 @@ protected MultiMatchQueryBuilder doCreateTestQueryBuilder() { // sets other parameters of the multi match query if (randomBoolean()) { - if (fieldName.equals(STRING_FIELD_NAME)) { + if (fieldName.equals(STRING_FIELD_NAME) || fieldName.equals(STRING_ALIAS_FIELD_NAME) || fieldName.equals(STRING_FIELD_NAME_2)) { query.type(randomFrom(MultiMatchQueryBuilder.Type.values())); } else { - query.type(randomValueOtherThan(MultiMatchQueryBuilder.Type.PHRASE_PREFIX, + query.type(randomValueOtherThanMany( + (type) -> type == Type.PHRASE_PREFIX || type == Type.BOOL_PREFIX, () -> randomFrom(MultiMatchQueryBuilder.Type.values()))); } } @@ -104,7 +107,7 @@ protected MultiMatchQueryBuilder doCreateTestQueryBuilder() { if (randomBoolean() && fieldName.equals(STRING_FIELD_NAME)) { query.analyzer(randomAnalyzer()); } - if (randomBoolean()) { + if (randomBoolean() && query.type() != Type.BOOL_PREFIX) { query.slop(randomIntBetween(0, 5)); } if (fieldName.equals(STRING_FIELD_NAME) && randomBoolean() && @@ -126,7 +129,7 @@ protected MultiMatchQueryBuilder doCreateTestQueryBuilder() { if (randomBoolean()) { query.tieBreaker(randomFloat()); } - if (randomBoolean()) { + if (randomBoolean() && query.type() != Type.BOOL_PREFIX) { query.cutoffFrequency((float) 10 / randomIntBetween(1, 100)); } if (randomBoolean()) { @@ -158,12 +161,21 @@ protected Map getAlternateVersions() { @Override protected void doAssertLuceneQuery(MultiMatchQueryBuilder queryBuilder, Query query, SearchContext context) throws IOException { // we rely on integration tests for deeper checks here - assertThat(query, either(instanceOf(BoostQuery.class)).or(instanceOf(TermQuery.class)) - .or(instanceOf(BooleanQuery.class)).or(instanceOf(DisjunctionMaxQuery.class)) - .or(instanceOf(FuzzyQuery.class)).or(instanceOf(MultiPhrasePrefixQuery.class)) - .or(instanceOf(MatchAllDocsQuery.class)).or(instanceOf(ExtendedCommonTermsQuery.class)) - .or(instanceOf(MatchNoDocsQuery.class)).or(instanceOf(PhraseQuery.class)) - .or(instanceOf(PointRangeQuery.class)).or(instanceOf(IndexOrDocValuesQuery.class))); + assertThat(query, anyOf(Arrays.asList( + instanceOf(BoostQuery.class), + instanceOf(TermQuery.class), + instanceOf(BooleanQuery.class), + instanceOf(DisjunctionMaxQuery.class), + instanceOf(FuzzyQuery.class), + instanceOf(MultiPhrasePrefixQuery.class), + instanceOf(MatchAllDocsQuery.class), + instanceOf(ExtendedCommonTermsQuery.class), + instanceOf(MatchNoDocsQuery.class), + instanceOf(PhraseQuery.class), + instanceOf(PointRangeQuery.class), + instanceOf(IndexOrDocValuesQuery.class), + instanceOf(PrefixQuery.class) + ))); } public void testIllegaArguments() { @@ -240,6 +252,51 @@ public void testToQueryFieldMissing() throws Exception { instanceOf(MatchNoDocsQuery.class)); } + public void testToQueryBooleanPrefixSingleField() throws IOException { + final MultiMatchQueryBuilder builder = new MultiMatchQueryBuilder("foo bar", STRING_FIELD_NAME); + builder.type(Type.BOOL_PREFIX); + final Query query = builder.toQuery(createShardContext()); + assertThat(query, instanceOf(BooleanQuery.class)); + final BooleanQuery booleanQuery = (BooleanQuery) query; + assertThat(booleanQuery.clauses(), hasSize(2)); + assertThat(assertBooleanSubQuery(booleanQuery, TermQuery.class, 0).getTerm(), equalTo(new Term(STRING_FIELD_NAME, "foo"))); + assertThat(assertBooleanSubQuery(booleanQuery, PrefixQuery.class, 1).getPrefix(), equalTo(new Term(STRING_FIELD_NAME, "bar"))); + } + + public void testToQueryBooleanPrefixMultipleFields() throws IOException { + { + final MultiMatchQueryBuilder builder = new MultiMatchQueryBuilder("foo bar", STRING_FIELD_NAME, STRING_ALIAS_FIELD_NAME); + builder.type(Type.BOOL_PREFIX); + final Query query = builder.toQuery(createShardContext()); + assertThat(query, instanceOf(DisjunctionMaxQuery.class)); + final DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) query; + assertThat(disMaxQuery.getDisjuncts(), hasSize(2)); + for (Query disjunctQuery : disMaxQuery.getDisjuncts()) { + assertThat(disjunctQuery, instanceOf(BooleanQuery.class)); + final BooleanQuery booleanQuery = (BooleanQuery) disjunctQuery; + assertThat(booleanQuery.clauses(), hasSize(2)); + assertThat(assertBooleanSubQuery(booleanQuery, TermQuery.class, 0).getTerm(), equalTo(new Term(STRING_FIELD_NAME, "foo"))); + assertThat(assertBooleanSubQuery(booleanQuery, PrefixQuery.class, 1).getPrefix(), + equalTo(new Term(STRING_FIELD_NAME, "bar"))); + } + } + + { + // STRING_FIELD_NAME_2 is a keyword field + final MultiMatchQueryBuilder queryBuilder = new MultiMatchQueryBuilder("foo bar", STRING_FIELD_NAME, STRING_FIELD_NAME_2); + queryBuilder.type(Type.BOOL_PREFIX); + final Query query = queryBuilder.toQuery(createShardContext()); + assertThat(query, instanceOf(DisjunctionMaxQuery.class)); + final DisjunctionMaxQuery disMaxQuery = (DisjunctionMaxQuery) query; + assertThat(disMaxQuery.getDisjuncts(), hasSize(2)); + final BooleanQuery firstDisjunct = assertDisjunctionSubQuery(disMaxQuery, BooleanQuery.class, 0); + assertThat(firstDisjunct.clauses(), hasSize(2)); + assertThat(assertBooleanSubQuery(firstDisjunct, TermQuery.class, 0).getTerm(), equalTo(new Term(STRING_FIELD_NAME, "foo"))); + final PrefixQuery secondDisjunct = assertDisjunctionSubQuery(disMaxQuery, PrefixQuery.class, 1); + assertThat(secondDisjunct.getPrefix(), equalTo(new Term(STRING_FIELD_NAME_2, "foo bar"))); + } + } + public void testFromJson() throws IOException { String json = "{\n" + diff --git a/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java b/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java index c8ab7cc19dcfd..b7755dd321416 100644 --- a/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java +++ b/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java @@ -321,6 +321,7 @@ public List> getRescorers() { "intervals", "match", "match_all", + "match_bool_prefix", "match_none", "match_phrase", "match_phrase_prefix", From d12eea16135ff3cde23284db41439e6752c56bf4 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Wed, 27 Mar 2019 12:11:20 -0500 Subject: [PATCH 32/77] Muting test #40368 (#40541) --- .../checkpoint/DataFrameTransformsCheckpointServiceTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/checkpoint/DataFrameTransformsCheckpointServiceTests.java b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/checkpoint/DataFrameTransformsCheckpointServiceTests.java index ea5362d184b34..0868315165cdc 100644 --- a/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/checkpoint/DataFrameTransformsCheckpointServiceTests.java +++ b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/checkpoint/DataFrameTransformsCheckpointServiceTests.java @@ -82,6 +82,7 @@ public void testExtractIndexCheckpointsLostPrimaries() { } } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40368") public void testExtractIndexCheckpointsInconsistentGlobalCheckpoints() { Map expectedCheckpoints = new HashMap<>(); Set indices = randomUserIndices(); From a8b02e8676ab653dc1ebc32f92fe2035264f82ec Mon Sep 17 00:00:00 2001 From: David Kyle Date: Wed, 27 Mar 2019 17:36:43 +0000 Subject: [PATCH 33/77] Mute DataFrameTaskFailedStateIT.testFailureStateInteraction (#40544) --- .../dataframe/integration/DataFrameTaskFailedStateIT.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameTaskFailedStateIT.java b/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameTaskFailedStateIT.java index 887279ef20fc8..de0757a30ba1b 100644 --- a/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameTaskFailedStateIT.java +++ b/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameTaskFailedStateIT.java @@ -21,6 +21,11 @@ public class DataFrameTaskFailedStateIT extends DataFrameRestTestCase { + public void testDummy() { + // remove once the awaits fix below is resolved + } + + @AwaitsFix( bugUrl = "https://github.com/elastic/elasticsearch/issues/40543") public void testFailureStateInteraction() throws Exception { createReviewsIndex(); String transformId = "failure_pivot_1"; From 730dad6e22e151ded0e1185cd0306c3a9097d6d9 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Wed, 27 Mar 2019 18:48:35 +0100 Subject: [PATCH 34/77] Use default discovery implementation for single-node discovery (#40036) Switches "discovery.type: single-node" from using a separate implementation for single-node discovery to using the existing standard discovery implementation, with two small adaptions: - auto-bootstrapping, but requiring initial_master_nodes not to be set. - not actively pinging other nodes using the Peerfinder - not allowing other nodes to join its single-node cluster (if they have e.g. been set up using regular discovery and connect to the single-disco node). --- .../coordination/ClusterBootstrapService.java | 30 +++- .../cluster/coordination/Coordinator.java | 30 +++- .../discovery/DiscoveryModule.java | 25 ++-- .../elasticsearch/discovery/PeerFinder.java | 2 +- .../discovery/single/SingleNodeDiscovery.java | 139 ------------------ .../ClusterBootstrapServiceTests.java | 50 +++++++ .../coordination/CoordinatorTests.java | 73 +++++++-- .../single/SingleNodeDiscoveryIT.java | 81 ++++++++++ .../single/SingleNodeDiscoveryTests.java | 86 ----------- .../test/InternalTestCluster.java | 12 +- 10 files changed, 258 insertions(+), 270 deletions(-) delete mode 100644 server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java delete mode 100644 server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/ClusterBootstrapService.java b/server/src/main/java/org/elasticsearch/cluster/coordination/ClusterBootstrapService.java index c4643771fb790..7271013cb363d 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/ClusterBootstrapService.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/ClusterBootstrapService.java @@ -29,10 +29,13 @@ import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.discovery.DiscoveryModule; +import org.elasticsearch.node.Node; import org.elasticsearch.threadpool.ThreadPool.Names; import org.elasticsearch.transport.TransportService; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -75,15 +78,28 @@ public class ClusterBootstrapService { public ClusterBootstrapService(Settings settings, TransportService transportService, Supplier> discoveredNodesSupplier, BooleanSupplier isBootstrappedSupplier, Consumer votingConfigurationConsumer) { - - final List initialMasterNodes = INITIAL_MASTER_NODES_SETTING.get(settings); - bootstrapRequirements = unmodifiableSet(new LinkedHashSet<>(initialMasterNodes)); - if (bootstrapRequirements.size() != initialMasterNodes.size()) { - throw new IllegalArgumentException( - "setting [" + INITIAL_MASTER_NODES_SETTING.getKey() + "] contains duplicates: " + initialMasterNodes); + if (DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE.equals(DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings))) { + if (INITIAL_MASTER_NODES_SETTING.exists(settings)) { + throw new IllegalArgumentException("setting [" + INITIAL_MASTER_NODES_SETTING.getKey() + + "] is not allowed when [" + DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey() + "] is set to [" + + DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE + "]"); + } + if (DiscoveryNode.isMasterNode(settings) == false) { + throw new IllegalArgumentException("node with [" + DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey() + "] set to [" + + DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE + "] must be master-eligible"); + } + bootstrapRequirements = Collections.singleton(Node.NODE_NAME_SETTING.get(settings)); + unconfiguredBootstrapTimeout = null; + } else { + final List initialMasterNodes = INITIAL_MASTER_NODES_SETTING.get(settings); + bootstrapRequirements = unmodifiableSet(new LinkedHashSet<>(initialMasterNodes)); + if (bootstrapRequirements.size() != initialMasterNodes.size()) { + throw new IllegalArgumentException( + "setting [" + INITIAL_MASTER_NODES_SETTING.getKey() + "] contains duplicates: " + initialMasterNodes); + } + unconfiguredBootstrapTimeout = discoveryIsConfigured(settings) ? null : UNCONFIGURED_BOOTSTRAP_TIMEOUT_SETTING.get(settings); } - unconfiguredBootstrapTimeout = discoveryIsConfigured(settings) ? null : UNCONFIGURED_BOOTSTRAP_TIMEOUT_SETTING.get(settings); this.transportService = transportService; this.discoveredNodesSupplier = discoveredNodesSupplier; this.isBootstrappedSupplier = isBootstrappedSupplier; diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java b/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java index 16bcab3cd3f22..154f4ab162d71 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/Coordinator.java @@ -51,12 +51,14 @@ import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ListenableFuture; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.discovery.Discovery; +import org.elasticsearch.discovery.DiscoveryModule; import org.elasticsearch.discovery.DiscoveryStats; import org.elasticsearch.discovery.HandshakingTransportAddressConnector; import org.elasticsearch.discovery.PeerFinder; @@ -69,6 +71,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -94,6 +97,7 @@ public class Coordinator extends AbstractLifecycleComponent implements Discovery TimeValue.timeValueMillis(30000), TimeValue.timeValueMillis(1), Setting.Property.NodeScope); private final Settings settings; + private final boolean singleNodeDiscovery; private final TransportService transportService; private final MasterService masterService; private final AllocationService allocationService; @@ -143,6 +147,7 @@ public Coordinator(String nodeName, Settings settings, ClusterSettings clusterSe this.masterService = masterService; this.allocationService = allocationService; this.onJoinValidators = JoinTaskExecutor.addBuiltInJoinValidators(onJoinValidators); + this.singleNodeDiscovery = DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE.equals(DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings)); this.joinHelper = new JoinHelper(settings, allocationService, masterService, transportService, this::getCurrentTerm, this::getStateForMasterService, this::handleJoinRequest, this::joinLeaderInTerm, this.onJoinValidators); this.persistedStateSupplier = persistedStateSupplier; @@ -424,6 +429,13 @@ private void handleJoinRequest(JoinRequest joinRequest, JoinHelper.JoinCallback assert Thread.holdsLock(mutex) == false; assert getLocalNode().isMasterNode() : getLocalNode() + " received a join but is not master-eligible"; logger.trace("handleJoinRequest: as {}, handling {}", mode, joinRequest); + + if (singleNodeDiscovery && joinRequest.getSourceNode().equals(getLocalNode()) == false) { + joinCallback.onFailure(new IllegalStateException("cannot join node with [" + DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey() + + "] set to [" + DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE + "] discovery")); + return; + } + transportService.connectToNode(joinRequest.getSourceNode()); final ClusterState stateForJoinValidation = getStateForMasterService(); @@ -636,6 +648,14 @@ protected void doStart() { coordinationState.set(new CoordinationState(settings, getLocalNode(), persistedState)); peerFinder.setCurrentTerm(getCurrentTerm()); configuredHostsResolver.start(); + VotingConfiguration votingConfiguration = coordinationState.get().getLastAcceptedState().getLastCommittedConfiguration(); + if (singleNodeDiscovery && + votingConfiguration.isEmpty() == false && + votingConfiguration.hasQuorum(Collections.singleton(getLocalNode().getId())) == false) { + throw new IllegalStateException("cannot start with [" + DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey() + "] set to [" + + DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE + "] when local node " + getLocalNode() + + " does not have quorum in voting configuration " + votingConfiguration); + } ClusterState initialState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.get(settings)) .blocks(ClusterBlocks.builder() .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK) @@ -1049,7 +1069,8 @@ private class CoordinatorPeerFinder extends PeerFinder { CoordinatorPeerFinder(Settings settings, TransportService transportService, TransportAddressConnector transportAddressConnector, ConfiguredHostsResolver configuredHostsResolver) { - super(settings, transportService, transportAddressConnector, configuredHostsResolver); + super(settings, transportService, transportAddressConnector, + singleNodeDiscovery ? hostsResolver -> Collections.emptyList() : configuredHostsResolver); } @Override @@ -1060,6 +1081,13 @@ protected void onActiveMasterFound(DiscoveryNode masterNode, long term) { } } + @Override + protected void startProbe(TransportAddress transportAddress) { + if (singleNodeDiscovery == false) { + super.startProbe(transportAddress); + } + } + @Override protected void onFoundPeersUpdated() { synchronized (mutex) { diff --git a/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java b/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java index ab95f2a430497..a14def8fa86b5 100644 --- a/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java +++ b/server/src/main/java/org/elasticsearch/discovery/DiscoveryModule.java @@ -36,7 +36,6 @@ import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; -import org.elasticsearch.discovery.single.SingleNodeDiscovery; import org.elasticsearch.gateway.GatewayMetaState; import org.elasticsearch.plugins.DiscoveryPlugin; import org.elasticsearch.threadpool.ThreadPool; @@ -50,7 +49,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.function.BiConsumer; @@ -68,6 +66,8 @@ public class DiscoveryModule { public static final String ZEN2_DISCOVERY_TYPE = "zen"; + public static final String SINGLE_NODE_DISCOVERY_TYPE = "single-node"; + public static final Setting DISCOVERY_TYPE_SETTING = new Setting<>("discovery.type", ZEN2_DISCOVERY_TYPE, Function.identity(), Property.NodeScope); public static final Setting> DISCOVERY_SEED_PROVIDERS_SETTING = @@ -114,6 +114,8 @@ public DiscoveryModule(Settings settings, ThreadPool threadPool, TransportServic List filteredSeedProviders = seedProviderNames.stream() .map(hostProviders::get).map(Supplier::get).collect(Collectors.toList()); + String discoveryType = DISCOVERY_TYPE_SETTING.get(settings); + final SeedHostsProvider seedHostsProvider = hostsResolver -> { final List addresses = new ArrayList<>(); for (SeedHostsProvider provider : filteredSeedProviders) { @@ -122,20 +124,17 @@ public DiscoveryModule(Settings settings, ThreadPool threadPool, TransportServic return Collections.unmodifiableList(addresses); }; - Map> discoveryTypes = new HashMap<>(); - discoveryTypes.put(ZEN2_DISCOVERY_TYPE, () -> new Coordinator(NODE_NAME_SETTING.get(settings), settings, clusterSettings, - transportService, namedWriteableRegistry, allocationService, masterService, - () -> gatewayMetaState.getPersistedState(settings, (ClusterApplierService) clusterApplier), seedHostsProvider, clusterApplier, - joinValidators, new Random(Randomness.get().nextLong()))); - discoveryTypes.put("single-node", () -> new SingleNodeDiscovery(settings, transportService, masterService, clusterApplier, - gatewayMetaState)); - String discoveryType = DISCOVERY_TYPE_SETTING.get(settings); - Supplier discoverySupplier = discoveryTypes.get(discoveryType); - if (discoverySupplier == null) { + if (ZEN2_DISCOVERY_TYPE.equals(discoveryType) || SINGLE_NODE_DISCOVERY_TYPE.equals(discoveryType)) { + discovery = new Coordinator(NODE_NAME_SETTING.get(settings), + settings, clusterSettings, + transportService, namedWriteableRegistry, allocationService, masterService, + () -> gatewayMetaState.getPersistedState(settings, (ClusterApplierService) clusterApplier), seedHostsProvider, + clusterApplier, joinValidators, new Random(Randomness.get().nextLong())); + } else { throw new IllegalArgumentException("Unknown discovery type [" + discoveryType + "]"); } + logger.info("using discovery type [{}] and seed hosts providers {}", discoveryType, seedProviderNames); - discovery = Objects.requireNonNull(discoverySupplier.get()); } public Discovery getDiscovery() { diff --git a/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java b/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java index 9bcdd5b544268..f3e52e8df5616 100644 --- a/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java +++ b/server/src/main/java/org/elasticsearch/discovery/PeerFinder.java @@ -287,7 +287,7 @@ public String toString() { return peersRemoved; } - private void startProbe(TransportAddress transportAddress) { + protected void startProbe(TransportAddress transportAddress) { assert holdsLock() : "PeerFinder mutex not held"; if (active == false) { logger.trace("startProbe({}) not running", transportAddress); diff --git a/server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java b/server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java deleted file mode 100644 index 2a415a74cd0cc..0000000000000 --- a/server/src/main/java/org/elasticsearch/discovery/single/SingleNodeDiscovery.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.discovery.single; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.cluster.ClusterChangedEvent; -import org.elasticsearch.cluster.ClusterName; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.block.ClusterBlocks; -import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterApplier; -import org.elasticsearch.cluster.service.ClusterApplier.ClusterApplyListener; -import org.elasticsearch.cluster.service.ClusterApplierService; -import org.elasticsearch.cluster.service.MasterService; -import org.elasticsearch.common.component.AbstractLifecycleComponent; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.discovery.Discovery; -import org.elasticsearch.discovery.DiscoveryStats; -import org.elasticsearch.gateway.GatewayMetaState; -import org.elasticsearch.transport.TransportService; - -import java.util.Objects; - -import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK; - -/** - * A discovery implementation where the only member of the cluster is the local node. - */ -public class SingleNodeDiscovery extends AbstractLifecycleComponent implements Discovery { - private static final Logger logger = LogManager.getLogger(SingleNodeDiscovery.class); - - private final ClusterName clusterName; - protected final TransportService transportService; - private final ClusterApplier clusterApplier; - private volatile ClusterState clusterState; - - public SingleNodeDiscovery(final Settings settings, final TransportService transportService, - final MasterService masterService, final ClusterApplier clusterApplier, - final GatewayMetaState gatewayMetaState) { - this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings); - this.transportService = Objects.requireNonNull(transportService); - masterService.setClusterStateSupplier(() -> clusterState); - this.clusterApplier = clusterApplier; - - if (clusterApplier instanceof ClusterApplierService) { - ((ClusterApplierService) clusterApplier).addLowPriorityApplier(gatewayMetaState); - } - } - - @Override - public synchronized void publish(final ClusterChangedEvent event, ActionListener publishListener, - final AckListener ackListener) { - clusterState = event.state(); - ackListener.onCommit(TimeValue.ZERO); - - clusterApplier.onNewClusterState("apply-locally-on-node[" + event.source() + "]", () -> clusterState, new ClusterApplyListener() { - @Override - public void onSuccess(String source) { - publishListener.onResponse(null); - ackListener.onNodeAck(transportService.getLocalNode(), null); - } - - @Override - public void onFailure(String source, Exception e) { - publishListener.onFailure(e); - ackListener.onNodeAck(transportService.getLocalNode(), e); - logger.warn(() -> new ParameterizedMessage("failed while applying cluster state locally [{}]", event.source()), e); - } - }); - } - - @Override - public DiscoveryStats stats() { - return new DiscoveryStats(null, null); - } - - @Override - public synchronized void startInitialJoin() { - if (lifecycle.started() == false) { - throw new IllegalStateException("can't start initial join when not started"); - } - // apply a fresh cluster state just so that state recovery gets triggered by GatewayService - // TODO: give discovery module control over GatewayService - clusterState = ClusterState.builder(clusterState).build(); - clusterApplier.onNewClusterState("single-node-start-initial-join", () -> clusterState, (source, e) -> {}); - } - - @Override - protected synchronized void doStart() { - // set initial state - DiscoveryNode localNode = transportService.getLocalNode(); - clusterState = createInitialState(localNode); - clusterApplier.setInitialState(clusterState); - } - - protected ClusterState createInitialState(DiscoveryNode localNode) { - ClusterState.Builder builder = ClusterState.builder(clusterName); - return builder.nodes(DiscoveryNodes.builder().add(localNode) - .localNodeId(localNode.getId()) - .masterNodeId(localNode.getId()) - .build()) - .blocks(ClusterBlocks.builder() - .addGlobalBlock(STATE_NOT_RECOVERED_BLOCK)) - .build(); - } - - @Override - protected void doStop() { - - } - - @Override - protected void doClose() { - - } - -} diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/ClusterBootstrapServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/ClusterBootstrapServiceTests.java index a2dee54b3c6fc..4dd3e3e33c39f 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/ClusterBootstrapServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/ClusterBootstrapServiceTests.java @@ -23,6 +23,8 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNode.Role; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.discovery.DiscoveryModule; +import org.elasticsearch.node.Node; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransport; import org.elasticsearch.transport.TransportRequest; @@ -461,4 +463,52 @@ public void testDoesNotIncludeExtraNodes() { deterministicTaskQueue.runAllTasks(); assertTrue(bootstrapped.get()); } + + public void testBootstrapsAutomaticallyWithSingleNodeDiscovery() { + final Settings.Builder settings = Settings.builder() + .put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE) + .put(NODE_NAME_SETTING.getKey(), localNode.getName()); + final AtomicBoolean bootstrapped = new AtomicBoolean(); + + ClusterBootstrapService clusterBootstrapService = new ClusterBootstrapService(settings.build(), + transportService, () -> emptyList(), () -> false, vc -> { + assertTrue(bootstrapped.compareAndSet(false, true)); + assertThat(vc.getNodeIds(), hasSize(1)); + assertThat(vc.getNodeIds(), hasItem(localNode.getId())); + assertTrue(vc.hasQuorum(singletonList(localNode.getId()))); + }); + + transportService.start(); + clusterBootstrapService.onFoundPeersUpdated(); + deterministicTaskQueue.runAllTasks(); + assertTrue(bootstrapped.get()); + + bootstrapped.set(false); + clusterBootstrapService.onFoundPeersUpdated(); + deterministicTaskQueue.runAllTasks(); + assertFalse(bootstrapped.get()); // should only bootstrap once + } + + public void testFailBootstrapWithBothSingleNodeDiscoveryAndInitialMasterNodes() { + final Settings.Builder settings = Settings.builder() + .put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE) + .put(NODE_NAME_SETTING.getKey(), localNode.getName()) + .put(INITIAL_MASTER_NODES_SETTING.getKey(), "test"); + + assertThat(expectThrows(IllegalArgumentException.class, () -> new ClusterBootstrapService(settings.build(), + transportService, () -> emptyList(), () -> false, vc -> fail())).getMessage(), + containsString("setting [" + INITIAL_MASTER_NODES_SETTING.getKey() + "] is not allowed when [discovery.type] is set " + + "to [single-node]")); + } + + public void testFailBootstrapNonMasterEligibleNodeWithSingleNodeDiscovery() { + final Settings.Builder settings = Settings.builder() + .put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE) + .put(NODE_NAME_SETTING.getKey(), localNode.getName()) + .put(Node.NODE_MASTER_SETTING.getKey(), false); + + assertThat(expectThrows(IllegalArgumentException.class, () -> new ClusterBootstrapService(settings.build(), + transportService, () -> emptyList(), () -> false, vc -> fail())).getMessage(), + containsString("node with [discovery.type] set to [single-node] must be master-eligible")); + } } diff --git a/server/src/test/java/org/elasticsearch/cluster/coordination/CoordinatorTests.java b/server/src/test/java/org/elasticsearch/cluster/coordination/CoordinatorTests.java index 07ae6679e4489..6f078217e4f45 100644 --- a/server/src/test/java/org/elasticsearch/cluster/coordination/CoordinatorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/coordination/CoordinatorTests.java @@ -63,6 +63,7 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.PrioritizedEsThreadPoolExecutor; import org.elasticsearch.common.util.set.Sets; +import org.elasticsearch.discovery.DiscoveryModule; import org.elasticsearch.discovery.SeedHostsProvider.HostsResolver; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.gateway.MetaStateService; @@ -204,7 +205,7 @@ public void testCanUpdateClusterStateAfterStabilisation() { } public void testDoesNotElectNonMasterNode() { - final Cluster cluster = new Cluster(randomIntBetween(1, 5), false); + final Cluster cluster = new Cluster(randomIntBetween(1, 5), false, Settings.EMPTY); cluster.runRandomly(); cluster.stabilise(); @@ -904,7 +905,7 @@ public void testIncompatibleDiffResendsFullState() { * and join the leader again. */ public void testStayCandidateAfterReceivingFollowerCheckFromKnownMaster() { - final Cluster cluster = new Cluster(2, false); + final Cluster cluster = new Cluster(2, false, Settings.EMPTY); cluster.runRandomly(); cluster.stabilise(); @@ -1029,7 +1030,7 @@ public void testCannotJoinClusterWithDifferentUUID() throws IllegalAccessExcepti final ClusterNode shiftedNode = randomFrom(cluster2.clusterNodes).restartedNode(); final ClusterNode newNode = cluster1.new ClusterNode(nextNodeIndex.getAndIncrement(), - shiftedNode.getLocalNode(), n -> shiftedNode.persistedState); + shiftedNode.getLocalNode(), n -> shiftedNode.persistedState, shiftedNode.nodeSettings); cluster1.clusterNodes.add(newNode); MockLogAppender mockAppender = new MockLogAppender(); @@ -1053,7 +1054,7 @@ public void testCannotJoinClusterWithDifferentUUID() throws IllegalAccessExcepti final ClusterNode detachedNode = newNode.restartedNode( metaData -> DetachClusterCommand.updateMetaData(metaData), - term -> DetachClusterCommand.updateCurrentTerm()); + term -> DetachClusterCommand.updateCurrentTerm(), newNode.nodeSettings); cluster1.clusterNodes.replaceAll(cn -> cn == newNode ? detachedNode : cn); cluster1.stabilise(); } @@ -1111,6 +1112,43 @@ public void testFollowerRemovedIfUnableToSendRequestsToMaster() { + DEFAULT_CLUSTER_STATE_UPDATE_DELAY); } + public void testSingleNodeDiscoveryWithoutQuorum() { + final Cluster cluster = new Cluster(3); + cluster.runRandomly(); + cluster.stabilise(); + + final ClusterNode clusterNode = cluster.getAnyNode(); + logger.debug("rebooting [{}]", clusterNode.getId()); + clusterNode.close(); + cluster.clusterNodes.forEach( + cn -> cluster.deterministicTaskQueue.scheduleNow(cn.onNode( + new Runnable() { + @Override + public void run() { + cn.transportService.disconnectFromNode(clusterNode.getLocalNode()); + } + + @Override + public String toString() { + return "disconnect from " + clusterNode.getLocalNode() + " after shutdown"; + } + }))); + IllegalStateException ise = expectThrows(IllegalStateException.class, + () -> cluster.clusterNodes.replaceAll(cn -> cn == clusterNode ? + cn.restartedNode(Function.identity(), Function.identity(), Settings.builder() + .put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE).build()) : + cn)); + assertThat(ise.getMessage(), containsString("cannot start with [discovery.type] set to [single-node] when local node")); + assertThat(ise.getMessage(), containsString("does not have quorum in voting configuration")); + } + + public void testSingleNodeDiscoveryWithQuorum() { + final Cluster cluster = new Cluster(1, randomBoolean(), Settings.builder().put(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey(), + DiscoveryModule.SINGLE_NODE_DISCOVERY_TYPE).build()); + cluster.runRandomly(); + cluster.stabilise(); + } + private static long defaultMillis(Setting setting) { return setting.get(Settings.EMPTY).millis() + Cluster.DEFAULT_DELAY_VARIABILITY; } @@ -1184,10 +1222,10 @@ class Cluster { private List seedHostsList; Cluster(int initialNodeCount) { - this(initialNodeCount, true); + this(initialNodeCount, true, Settings.EMPTY); } - Cluster(int initialNodeCount, boolean allNodesMasterEligible) { + Cluster(int initialNodeCount, boolean allNodesMasterEligible, Settings nodeSettings) { deterministicTaskQueue.setExecutionDelayVariabilityMillis(DEFAULT_DELAY_VARIABILITY); assertThat(initialNodeCount, greaterThan(0)); @@ -1196,7 +1234,7 @@ class Cluster { clusterNodes = new ArrayList<>(initialNodeCount); for (int i = 0; i < initialNodeCount; i++) { final ClusterNode clusterNode = new ClusterNode(nextNodeIndex.getAndIncrement(), - allNodesMasterEligible || i == 0 || randomBoolean()); + allNodesMasterEligible || i == 0 || randomBoolean(), nodeSettings); clusterNodes.add(clusterNode); if (clusterNode.getLocalNode().isMasterNode()) { masterEligibleNodeIds.add(clusterNode.getId()); @@ -1229,7 +1267,7 @@ List addNodes(int newNodesCount) { final List addedNodes = new ArrayList<>(); for (int i = 0; i < newNodesCount; i++) { - final ClusterNode clusterNode = new ClusterNode(nextNodeIndex.getAndIncrement(), true); + final ClusterNode clusterNode = new ClusterNode(nextNodeIndex.getAndIncrement(), true, Settings.EMPTY); addedNodes.add(clusterNode); } clusterNodes.addAll(addedNodes); @@ -1701,6 +1739,7 @@ class ClusterNode { private Coordinator coordinator; private final DiscoveryNode localNode; private final MockPersistedState persistedState; + private final Settings nodeSettings; private AckedFakeThreadPoolMasterService masterService; private DisruptableClusterApplierService clusterApplierService; private ClusterService clusterService; @@ -1708,13 +1747,15 @@ class ClusterNode { private DisruptableMockTransport mockTransport; private List> extraJoinValidators = new ArrayList<>(); - ClusterNode(int nodeIndex, boolean masterEligible) { - this(nodeIndex, createDiscoveryNode(nodeIndex, masterEligible), defaultPersistedStateSupplier); + ClusterNode(int nodeIndex, boolean masterEligible, Settings nodeSettings) { + this(nodeIndex, createDiscoveryNode(nodeIndex, masterEligible), defaultPersistedStateSupplier, nodeSettings); } - ClusterNode(int nodeIndex, DiscoveryNode localNode, Function persistedStateSupplier) { + ClusterNode(int nodeIndex, DiscoveryNode localNode, Function persistedStateSupplier, + Settings nodeSettings) { this.nodeIndex = nodeIndex; this.localNode = localNode; + this.nodeSettings = nodeSettings; persistedState = persistedStateSupplier.apply(localNode); onNodeLog(localNode, this::setUp).run(); } @@ -1738,7 +1779,8 @@ protected Optional getDisruptableMockTransport(Transpo } }; - final Settings settings = Settings.builder() + final Settings settings = nodeSettings.hasValue(DiscoveryModule.DISCOVERY_TYPE_SETTING.getKey()) ? + nodeSettings : Settings.builder().put(nodeSettings) .putList(ClusterBootstrapService.INITIAL_MASTER_NODES_SETTING.getKey(), ClusterBootstrapService.INITIAL_MASTER_NODES_SETTING.get(Settings.EMPTY)).build(); // suppress auto-bootstrap transportService = mockTransport.createTransportService( @@ -1781,17 +1823,18 @@ void close() { } ClusterNode restartedNode() { - return restartedNode(Function.identity(), Function.identity()); + return restartedNode(Function.identity(), Function.identity(), nodeSettings); } - ClusterNode restartedNode(Function adaptGlobalMetaData, Function adaptCurrentTerm) { + ClusterNode restartedNode(Function adaptGlobalMetaData, Function adaptCurrentTerm, + Settings nodeSettings) { final TransportAddress address = randomBoolean() ? buildNewFakeTransportAddress() : localNode.getAddress(); final DiscoveryNode newLocalNode = new DiscoveryNode(localNode.getName(), localNode.getId(), UUIDs.randomBase64UUID(random()), // generated deterministically for repeatable tests address.address().getHostString(), address.getAddress(), address, Collections.emptyMap(), localNode.isMasterNode() ? EnumSet.allOf(Role.class) : emptySet(), Version.CURRENT); return new ClusterNode(nodeIndex, newLocalNode, - node -> new MockPersistedState(newLocalNode, persistedState, adaptGlobalMetaData, adaptCurrentTerm)); + node -> new MockPersistedState(newLocalNode, persistedState, adaptGlobalMetaData, adaptCurrentTerm), nodeSettings); } private PersistedState getPersistedState() { diff --git a/server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java b/server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java index e16389a38471f..e0fc4a4d5392c 100644 --- a/server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java +++ b/server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryIT.java @@ -19,13 +19,22 @@ package org.elasticsearch.discovery.single; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LogEvent; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.coordination.JoinHelper; import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.node.Node; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.MockHttpTransport; +import org.elasticsearch.test.MockLogAppender; import org.elasticsearch.test.NodeConfigurationSource; +import org.elasticsearch.transport.RemoteTransportException; import org.elasticsearch.transport.TransportService; import java.io.IOException; @@ -105,6 +114,78 @@ public Path nodeConfigPath(int nodeOrdinal) { } } + public void testCannotJoinNodeWithSingleNodeDiscovery() throws Exception { + MockLogAppender mockAppender = new MockLogAppender(); + mockAppender.start(); + mockAppender.addExpectation( + new MockLogAppender.SeenEventExpectation( + "test", + JoinHelper.class.getCanonicalName(), + Level.INFO, + "failed to join") { + + @Override + public boolean innerMatch(final LogEvent event) { + return event.getThrown() != null + && event.getThrown().getClass() == RemoteTransportException.class + && event.getThrown().getCause() != null + && event.getThrown().getCause().getClass() == IllegalStateException.class + && event.getThrown().getCause().getMessage().contains( + "cannot join node with [discovery.type] set to [single-node]"); + } + }); + final TransportService service = internalCluster().getInstance(TransportService.class); + final int port = service.boundAddress().publishAddress().getPort(); + final NodeConfigurationSource configurationSource = new NodeConfigurationSource() { + @Override + public Settings nodeSettings(int nodeOrdinal) { + return Settings + .builder() + .put("discovery.type", "zen") + .put("transport.type", getTestTransportType()) + .put(Node.INITIAL_STATE_TIMEOUT_SETTING.getKey(), "0s") + /* + * We align the port ranges of the two as then with zen discovery these two + * nodes would find each other. + */ + .put("transport.port", port + "-" + (port + 5 - 1)) + .build(); + } + + @Override + public Path nodeConfigPath(int nodeOrdinal) { + return null; + } + }; + try (InternalTestCluster other = + new InternalTestCluster( + randomLong(), + createTempDir(), + false, + false, + 1, + 1, + internalCluster().getClusterName(), + configurationSource, + 0, + "other", + Arrays.asList(getTestTransportPlugin(), MockHttpTransport.TestPlugin.class), + Function.identity())) { + + Logger clusterLogger = LogManager.getLogger(JoinHelper.class); + Loggers.addAppender(clusterLogger, mockAppender); + try { + other.beforeTest(random(), 0); + final ClusterState first = internalCluster().getInstance(ClusterService.class).state(); + assertThat(first.nodes().getSize(), equalTo(1)); + assertBusy(() -> mockAppender.assertAllExpectationsMatched()); + } finally { + Loggers.removeAppender(clusterLogger, mockAppender); + mockAppender.stop(); + } + } + } + public void testStatePersistence() throws Exception { createIndex("test"); internalCluster().fullRestart(); diff --git a/server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java b/server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java deleted file mode 100644 index c3dfad2d43792..0000000000000 --- a/server/src/test/java/org/elasticsearch/discovery/single/SingleNodeDiscoveryTests.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.discovery.single; - -import org.elasticsearch.core.internal.io.IOUtils; -import org.elasticsearch.Version; -import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.service.ClusterApplier; -import org.elasticsearch.cluster.service.MasterService; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.threadpool.TestThreadPool; -import org.elasticsearch.threadpool.ThreadPool; - -import java.io.Closeable; -import java.util.Stack; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Supplier; - -import static org.elasticsearch.test.ClusterServiceUtils.createMasterService; -import static org.hamcrest.Matchers.equalTo; - -public class SingleNodeDiscoveryTests extends ESTestCase { - - public void testInitialJoin() throws Exception { - final Settings settings = Settings.EMPTY; - final Version version = Version.CURRENT; - final ThreadPool threadPool = new TestThreadPool(getClass().getName()); - final Stack stack = new Stack<>(); - try { - final MockTransportService transportService = - MockTransportService.createNewService(settings, version, threadPool, null); - stack.push(transportService); - transportService.start(); - final DiscoveryNode node = transportService.getLocalNode(); - final MasterService masterService = createMasterService(threadPool, node); - AtomicReference clusterState = new AtomicReference<>(); - final SingleNodeDiscovery discovery = - new SingleNodeDiscovery(Settings.EMPTY, transportService, - masterService, new ClusterApplier() { - @Override - public void setInitialState(ClusterState initialState) { - clusterState.set(initialState); - } - - @Override - public void onNewClusterState(String source, Supplier clusterStateSupplier, - ClusterApplyListener listener) { - clusterState.set(clusterStateSupplier.get()); - listener.onSuccess(source); - } - }, null); - discovery.start(); - discovery.startInitialJoin(); - final DiscoveryNodes nodes = clusterState.get().nodes(); - assertThat(nodes.getSize(), equalTo(1)); - assertThat(nodes.getMasterNode().getId(), equalTo(node.getId())); - } finally { - while (!stack.isEmpty()) { - IOUtils.closeWhileHandlingException(stack.pop()); - } - terminate(threadPool); - } - } - -} diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java index 10a61a748cd3f..86f012fbd6583 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java @@ -631,13 +631,9 @@ private Settings getNodeSettings(final int nodeId, final long seed, final Settin .put("node.name", name) .put(NodeEnvironment.NODE_ID_SEED_SETTING.getKey(), seed); - final String discoveryType = DISCOVERY_TYPE_SETTING.get(updatedSettings.build()); - final boolean usingSingleNodeDiscovery = discoveryType.equals("single-node"); - if (usingSingleNodeDiscovery == false) { - if (autoManageMinMasterNodes) { - assertThat("automatically managing min master nodes require nodes to complete a join cycle when starting", - updatedSettings.get(INITIAL_STATE_TIMEOUT_SETTING.getKey()), nullValue()); - } + if (autoManageMinMasterNodes) { + assertThat("automatically managing min master nodes require nodes to complete a join cycle when starting", + updatedSettings.get(INITIAL_STATE_TIMEOUT_SETTING.getKey()), nullValue()); } return updatedSettings.build(); @@ -1160,7 +1156,7 @@ private synchronized void reset(boolean wipeData) throws IOException { nextNodeId.set(newSize); assert size() == newSize; - if (newSize > 0) { + if (autoManageMinMasterNodes && newSize > 0) { validateClusterFormed(); } logger.debug("Cluster is consistent again - nodes: [{}] nextNodeId: [{}] numSharedNodes: [{}]", From 03394b862bd33a566eac1ed57b832abda7a149ce Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Wed, 27 Mar 2019 18:49:03 +0100 Subject: [PATCH 35/77] No mapper service and index caches for replicated closed indices (#40423) Replicated closed indices can't be indexed into or searched, and therefore don't need a shard with full indexing and search capabilities allocated. We can save on a lot of heap memory for those indices by not allocating a mapper service and caching infrastructure (which preallocates a constant amount per instance). Before this change, a 1GB ES instance could host 250 replicated closed metricbeat indices (each index with one shard). After this change, the same instance can host 7300 replicated closed metricbeat instances (not that this would be a recommended configuration). Most of the remaining memory is in the cluster state and the IndexSettings object. --- .../org/elasticsearch/index/IndexModule.java | 3 +- .../org/elasticsearch/index/IndexService.java | 56 +++++++++++++------ .../elasticsearch/index/shard/IndexShard.java | 9 ++- .../elasticsearch/indices/IndicesService.java | 11 ++-- .../elasticsearch/index/IndexModuleTests.java | 5 +- .../index/engine/EngineTestCase.java | 2 +- .../xpack/security/Security.java | 2 +- 7 files changed, 58 insertions(+), 30 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/IndexModule.java b/server/src/main/java/org/elasticsearch/index/IndexModule.java index 6b83d2252dec7..acec458b8b0cd 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexModule.java +++ b/server/src/main/java/org/elasticsearch/index/IndexModule.java @@ -366,6 +366,7 @@ public static Type defaultStoreType(final boolean allowMmap) { } public IndexService newIndexService( + IndexService.IndexCreationContext indexCreationContext, NodeEnvironment environment, NamedXContentRegistry xContentRegistry, IndexService.ShardStoreDeleter shardStoreDeleter, @@ -395,7 +396,7 @@ public IndexService newIndexService( } else { queryCache = new DisabledQueryCache(indexSettings); } - return new IndexService(indexSettings, environment, xContentRegistry, + return new IndexService(indexSettings, indexCreationContext, environment, xContentRegistry, new SimilarityService(indexSettings, scriptService, similarities), shardStoreDeleter, analysisRegistry, engineFactory, circuitBreakerService, bigArrays, threadPool, scriptService, client, queryCache, store, eventListener, searcherWrapperFactory, mapperRegistry, diff --git a/server/src/main/java/org/elasticsearch/index/IndexService.java b/server/src/main/java/org/elasticsearch/index/IndexService.java index ea40dd1db016d..501dbf442b00b 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexService.java +++ b/server/src/main/java/org/elasticsearch/index/IndexService.java @@ -136,6 +136,7 @@ public class IndexService extends AbstractIndexComponent implements IndicesClust public IndexService( IndexSettings indexSettings, + IndexCreationContext indexCreationContext, NodeEnvironment nodeEnv, NamedXContentRegistry xContentRegistry, SimilarityService similarityService, @@ -162,21 +163,36 @@ public IndexService( this.similarityService = similarityService; this.namedWriteableRegistry = namedWriteableRegistry; this.circuitBreakerService = circuitBreakerService; - this.mapperService = new MapperService(indexSettings, registry.build(indexSettings), xContentRegistry, similarityService, - mapperRegistry, - // we parse all percolator queries as they would be parsed on shard 0 - () -> newQueryShardContext(0, null, System::currentTimeMillis, null)); - this.indexFieldData = new IndexFieldDataService(indexSettings, indicesFieldDataCache, circuitBreakerService, mapperService); - if (indexSettings.getIndexSortConfig().hasIndexSort()) { - // we delay the actual creation of the sort order for this index because the mapping has not been merged yet. - // The sort order is validated right after the merge of the mapping later in the process. - this.indexSortSupplier = () -> indexSettings.getIndexSortConfig().buildIndexSort( - mapperService::fullName, - indexFieldData::getForField - ); - } else { + if (indexSettings.getIndexMetaData().getState() == IndexMetaData.State.CLOSE && + indexCreationContext == IndexCreationContext.CREATE_INDEX) { // metadata verification needs a mapper service + this.mapperService = null; + this.indexFieldData = null; this.indexSortSupplier = () -> null; + this.bitsetFilterCache = null; + this.warmer = null; + this.indexCache = null; + } else { + this.mapperService = new MapperService(indexSettings, registry.build(indexSettings), xContentRegistry, similarityService, + mapperRegistry, + // we parse all percolator queries as they would be parsed on shard 0 + () -> newQueryShardContext(0, null, System::currentTimeMillis, null)); + this.indexFieldData = new IndexFieldDataService(indexSettings, indicesFieldDataCache, circuitBreakerService, mapperService); + if (indexSettings.getIndexSortConfig().hasIndexSort()) { + // we delay the actual creation of the sort order for this index because the mapping has not been merged yet. + // The sort order is validated right after the merge of the mapping later in the process. + this.indexSortSupplier = () -> indexSettings.getIndexSortConfig().buildIndexSort( + mapperService::fullName, + indexFieldData::getForField + ); + } else { + this.indexSortSupplier = () -> null; + } + indexFieldData.setListener(new FieldDataCacheListener(this)); + this.bitsetFilterCache = new BitsetFilterCache(indexSettings, new BitsetCacheListener(this)); + this.warmer = new IndexWarmer(threadPool, indexFieldData, bitsetFilterCache.createListener(threadPool)); + this.indexCache = new IndexCache(indexSettings, queryCache, bitsetFilterCache); } + this.shardStoreDeleter = shardStoreDeleter; this.bigArrays = bigArrays; this.threadPool = threadPool; @@ -185,10 +201,6 @@ public IndexService( this.eventListener = eventListener; this.nodeEnv = nodeEnv; this.indexStore = indexStore; - indexFieldData.setListener(new FieldDataCacheListener(this)); - this.bitsetFilterCache = new BitsetFilterCache(indexSettings, new BitsetCacheListener(this)); - this.warmer = new IndexWarmer(threadPool, indexFieldData, bitsetFilterCache.createListener(threadPool)); - this.indexCache = new IndexCache(indexSettings, queryCache, bitsetFilterCache); this.engineFactory = Objects.requireNonNull(engineFactory); // initialize this last -- otherwise if the wrapper requires any other member to be non-null we fail with an NPE this.searcherWrapper = wrapperFactory.newWrapper(this); @@ -202,6 +214,11 @@ public IndexService( updateFsyncTaskIfNecessary(); } + public enum IndexCreationContext { + CREATE_INDEX, + META_DATA_VERIFICATION + } + public int numberOfShards() { return shards.size(); } @@ -548,7 +565,10 @@ List getSearchOperationListener() { // pkg private for @Override public boolean updateMapping(final IndexMetaData currentIndexMetaData, final IndexMetaData newIndexMetaData) throws IOException { - return mapperService().updateMapping(currentIndexMetaData, newIndexMetaData); + if (mapperService == null) { + return false; + } + return mapperService.updateMapping(currentIndexMetaData, newIndexMetaData); } private class StoreCloseListener implements Store.OnClose { diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 4efac334bde17..97d1939c1b292 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -2493,8 +2493,9 @@ private EngineConfig newEngineConfig() { Sort indexSort = indexSortSupplier.get(); return new EngineConfig(shardId, shardRouting.allocationId().getId(), threadPool, indexSettings, warmer, store, indexSettings.getMergePolicy(), - mapperService.indexAnalyzer(), similarityService.similarity(mapperService), codecService, shardEventListener, - indexCache.query(), cachingPolicy, translogConfig, + mapperService != null ? mapperService.indexAnalyzer() : null, + similarityService.similarity(mapperService), codecService, shardEventListener, + indexCache != null ? indexCache.query() : null, cachingPolicy, translogConfig, IndexingMemoryController.SHARD_INACTIVE_TIME_SETTING.get(indexSettings.getSettings()), Collections.singletonList(refreshListeners), Collections.singletonList(new RefreshMetricUpdater(refreshMetric)), @@ -3077,7 +3078,9 @@ public void afterRefresh(boolean didRefresh) throws IOException { private EngineConfig.TombstoneDocSupplier tombstoneDocSupplier() { final RootObjectMapper.Builder noopRootMapper = new RootObjectMapper.Builder("__noop"); - final DocumentMapper noopDocumentMapper = new DocumentMapper.Builder(noopRootMapper, mapperService).build(mapperService); + final DocumentMapper noopDocumentMapper = mapperService != null ? + new DocumentMapper.Builder(noopRootMapper, mapperService).build(mapperService) : + null; return new EngineConfig.TombstoneDocSupplier() { @Override public ParsedDocument newDeleteTombstoneDoc(String type, String id) { diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesService.java b/server/src/main/java/org/elasticsearch/indices/IndicesService.java index 4a12bdae6b9ea..913fb47157eda 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndicesService.java +++ b/server/src/main/java/org/elasticsearch/indices/IndicesService.java @@ -156,6 +156,8 @@ import static java.util.Collections.unmodifiableMap; import static org.elasticsearch.common.collect.MapBuilder.newMapBuilder; import static org.elasticsearch.common.util.CollectionUtils.arrayAsArrayList; +import static org.elasticsearch.index.IndexService.IndexCreationContext.CREATE_INDEX; +import static org.elasticsearch.index.IndexService.IndexCreationContext.META_DATA_VERIFICATION; import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder; public class IndicesService extends AbstractLifecycleComponent @@ -491,7 +493,7 @@ public void onStoreClosed(ShardId shardId) { finalListeners.add(oldShardsStats); final IndexService indexService = createIndexService( - "create index", + CREATE_INDEX, indexMetaData, indicesQueryCache, indicesFieldDataCache, @@ -513,7 +515,7 @@ public void onStoreClosed(ShardId shardId) { /** * This creates a new IndexService without registering it */ - private synchronized IndexService createIndexService(final String reason, + private synchronized IndexService createIndexService(IndexService.IndexCreationContext indexCreationContext, IndexMetaData indexMetaData, IndicesQueryCache indicesQueryCache, IndicesFieldDataCache indicesFieldDataCache, @@ -526,7 +528,7 @@ private synchronized IndexService createIndexService(final String reason, indexMetaData.getIndex(), idxSettings.getNumberOfShards(), idxSettings.getNumberOfReplicas(), - reason); + indexCreationContext); final IndexModule indexModule = new IndexModule(idxSettings, analysisRegistry, getEngineFactory(idxSettings), indexStoreFactories); for (IndexingOperationListener operationListener : indexingOperationListeners) { @@ -537,6 +539,7 @@ private synchronized IndexService createIndexService(final String reason, indexModule.addIndexEventListener(listener); } return indexModule.newIndexService( + indexCreationContext, nodeEnv, xContentRegistry, this, @@ -615,7 +618,7 @@ public synchronized void verifyIndexMetadata(IndexMetaData metaData, IndexMetaDa closeables.add(indicesQueryCache); // this will also fail if some plugin fails etc. which is nice since we can verify that early final IndexService service = - createIndexService("metadata verification", metaData, indicesQueryCache, indicesFieldDataCache, emptyList()); + createIndexService(META_DATA_VERIFICATION, metaData, indicesQueryCache, indicesFieldDataCache, emptyList()); closeables.add(() -> service.close("metadata verification", false)); service.mapperService().merge(metaData, MapperService.MergeReason.MAPPING_RECOVERY); if (metaData.equals(metaDataUpdate) == false) { diff --git a/server/src/test/java/org/elasticsearch/index/IndexModuleTests.java b/server/src/test/java/org/elasticsearch/index/IndexModuleTests.java index 5f6659afd7397..351cccdff4aa0 100644 --- a/server/src/test/java/org/elasticsearch/index/IndexModuleTests.java +++ b/server/src/test/java/org/elasticsearch/index/IndexModuleTests.java @@ -89,6 +89,7 @@ import java.util.function.Function; import static java.util.Collections.emptyMap; +import static org.elasticsearch.index.IndexService.IndexCreationContext.CREATE_INDEX; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.instanceOf; @@ -148,8 +149,8 @@ public void tearDown() throws Exception { } private IndexService newIndexService(IndexModule module) throws IOException { - return module.newIndexService(nodeEnvironment, xContentRegistry(), deleter, circuitBreakerService, bigArrays, threadPool, - scriptService, null, indicesQueryCache, mapperRegistry, + return module.newIndexService(CREATE_INDEX, nodeEnvironment, xContentRegistry(), deleter, circuitBreakerService, bigArrays, + threadPool, scriptService, null, indicesQueryCache, mapperRegistry, new IndicesFieldDataCache(settings, listener), writableRegistry()); } diff --git a/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java index c97d289215452..7fb2d50302c11 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java @@ -1052,7 +1052,7 @@ public static List readAllOperationsInLucene(Engine engine, * Asserts the provided engine has a consistent document history between translog and Lucene index. */ public static void assertConsistentHistoryBetweenTranslogAndLuceneIndex(Engine engine, MapperService mapper) throws IOException { - if (mapper.documentMapper() == null || engine.config().getIndexSettings().isSoftDeleteEnabled() == false + if (mapper == null || mapper.documentMapper() == null || engine.config().getIndexSettings().isSoftDeleteEnabled() == false || (engine instanceof InternalEngine) == false) { return; } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 3ac2537095c39..7b7e72fdd6b98 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -688,7 +688,7 @@ public void onIndexModule(IndexModule module) { throw new IllegalArgumentException("permission filters are not allowed to use the current timestamp"); }, null), - indexService.cache().bitsetFilterCache(), + indexService.cache() != null ? indexService.cache().bitsetFilterCache() : null, indexService.getThreadPool().getThreadContext(), getLicenseState(), indexService.getScriptService())); /* We need to forcefully overwrite the query cache implementation to use security's opt out query cache implementation. From 8c256d2c23e33b886a72e83a568f17d43480f562 Mon Sep 17 00:00:00 2001 From: Julie Tibshirani Date: Wed, 27 Mar 2019 11:13:51 -0700 Subject: [PATCH 36/77] Fix an off-by-one error in the vector field dimension limit. (#40489) Previously only vectors up to 499 dimensions were accepted, whereas the stated limit is 500. --- .../index/mapper/DenseVectorFieldMapper.java | 5 +-- .../index/mapper/SparseVectorFieldMapper.java | 5 +-- .../mapper/DenseVectorFieldMapperTests.java | 44 ++++++++++++++----- .../mapper/SparseVectorFieldMapperTests.java | 33 +++++++++++++- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java index f4a61c3ebd358..ec78420cc0b05 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java @@ -169,10 +169,9 @@ public void parse(ParseContext context) throws IOException { buf[offset+2] = (byte) (intValue >> 8); buf[offset+3] = (byte) intValue; offset += INT_BYTES; - dim++; - if (dim >= MAX_DIMS_COUNT) { + if (dim++ >= MAX_DIMS_COUNT) { throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + - "] has exceeded the maximum allowed number of dimensions of :[" + MAX_DIMS_COUNT + "]"); + "] has exceeded the maximum allowed number of dimensions of [" + MAX_DIMS_COUNT + "]"); } } BinaryDocValuesField field = new BinaryDocValuesField(fieldType().name(), new BytesRef(buf, 0, offset)); diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java index adf46d6a60d25..bfbf68c5a7662 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java @@ -178,10 +178,9 @@ public void parse(ParseContext context) throws IOException { } dims[dimCount] = dim; values[dimCount] = value; - dimCount ++; - if (dimCount >= MAX_DIMS_COUNT) { + if (dimCount++ >= MAX_DIMS_COUNT) { throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + - "] has exceeded the maximum allowed number of dimensions of :[" + MAX_DIMS_COUNT + "]"); + "] has exceeded the maximum allowed number of dimensions of [" + MAX_DIMS_COUNT + "]"); } } else { throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/DenseVectorFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/DenseVectorFieldMapperTests.java index 2239c99a310f5..cf6fc99657756 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/DenseVectorFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/DenseVectorFieldMapperTests.java @@ -30,18 +30,19 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; -import org.hamcrest.Matchers; +import org.junit.Before; +import java.io.IOException; import java.util.Collection; -public class DenseVectorFieldMapperTests extends ESSingleNodeTestCase { +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.instanceOf; - @Override - protected Collection> getPlugins() { - return pluginList(MapperExtrasPlugin.class); - } +public class DenseVectorFieldMapperTests extends ESSingleNodeTestCase { + private DocumentMapper mapper; - public void testDefaults() throws Exception { + @Before + public void setUpMapper() throws Exception { IndexService indexService = createIndex("test-index"); DocumentMapperParser parser = indexService.mapperService().documentMapperParser(); String mapping = Strings.toString(XContentFactory.jsonBuilder() @@ -53,10 +54,15 @@ public void testDefaults() throws Exception { .endObject() .endObject() .endObject()); + mapper = parser.parse("_doc", new CompressedXContent(mapping)); + } - DocumentMapper mapper = parser.parse("_doc", new CompressedXContent(mapping)); - assertEquals(mapping, mapper.mappingSource().toString()); + @Override + protected Collection> getPlugins() { + return pluginList(MapperExtrasPlugin.class); + } + public void testDefaults() throws Exception { float[] expectedArray = {-12.1f, 100.7f, -4}; ParsedDocument doc1 = mapper.parse(new SourceToParse("test-index", "_doc", "1", BytesReference .bytes(XContentFactory.jsonBuilder() @@ -66,7 +72,7 @@ public void testDefaults() throws Exception { XContentType.JSON)); IndexableField[] fields = doc1.rootDoc().getFields("my-dense-vector"); assertEquals(1, fields.length); - assertThat(fields[0], Matchers.instanceOf(BinaryDocValuesField.class)); + assertThat(fields[0], instanceOf(BinaryDocValuesField.class)); // assert that after decoding the indexed value is equal to expected BytesRef vectorBR = ((BinaryDocValuesField) fields[0]).binaryValue(); @@ -78,4 +84,22 @@ public void testDefaults() throws Exception { 0.001f ); } + + public void testDimensionLimit() throws IOException { + float[] validVector = new float[DenseVectorFieldMapper.MAX_DIMS_COUNT]; + BytesReference validDoc = BytesReference.bytes( + XContentFactory.jsonBuilder().startObject() + .array("my-dense-vector", validVector) + .endObject()); + mapper.parse(new SourceToParse("test-index", "_doc", "1", validDoc, XContentType.JSON)); + + float[] invalidVector = new float[DenseVectorFieldMapper.MAX_DIMS_COUNT + 1]; + BytesReference invalidDoc = BytesReference.bytes( + XContentFactory.jsonBuilder().startObject() + .array("my-dense-vector", invalidVector) + .endObject()); + MapperParsingException e = expectThrows(MapperParsingException.class, () -> mapper.parse( + new SourceToParse("test-index", "_doc", "1", invalidDoc, XContentType.JSON))); + assertThat(e.getDetailedMessage(), containsString("has exceeded the maximum allowed number of dimensions")); + } } diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SparseVectorFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SparseVectorFieldMapperTests.java index 06710e39592cc..754a6f1a31803 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SparseVectorFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SparseVectorFieldMapperTests.java @@ -33,7 +33,12 @@ import org.hamcrest.Matchers; import org.junit.Before; +import java.io.IOException; import java.util.Collection; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.core.IsInstanceOf.instanceOf; @@ -42,7 +47,7 @@ public class SparseVectorFieldMapperTests extends ESSingleNodeTestCase { private DocumentMapper mapper; @Before - public void setup() throws Exception { + public void setUpMapper() throws Exception { IndexService indexService = createIndex("test-index"); DocumentMapperParser parser = indexService.mapperService().documentMapperParser(); String mapping = Strings.toString(XContentFactory.jsonBuilder() @@ -100,7 +105,7 @@ public void testDefaults() throws Exception { ); } - public void testErrors() { + public void testDimensionNumberValidation() { // 1. test for an error on negative dimension MapperParsingException e = expectThrows(MapperParsingException.class, () -> { mapper.parse(new SourceToParse("test-index", "_doc", "1", BytesReference @@ -161,4 +166,28 @@ public void testErrors() { assertThat(e.getCause().getMessage(), containsString( "takes an object that maps a dimension number to a float, but got unexpected token [START_ARRAY]")); } + + public void testDimensionLimit() throws IOException { + Map validVector = IntStream.range(0, SparseVectorFieldMapper.MAX_DIMS_COUNT) + .boxed() + .collect(Collectors.toMap(String::valueOf, Function.identity())); + + BytesReference validDoc = BytesReference.bytes( + XContentFactory.jsonBuilder().startObject() + .field("my-sparse-vector", validVector) + .endObject()); + mapper.parse(new SourceToParse("test-index", "_doc", "1", validDoc, XContentType.JSON)); + + Map invalidVector = IntStream.range(0, SparseVectorFieldMapper.MAX_DIMS_COUNT + 1) + .boxed() + .collect(Collectors.toMap(String::valueOf, Function.identity())); + + BytesReference invalidDoc = BytesReference.bytes( + XContentFactory.jsonBuilder().startObject() + .field("my-sparse-vector", invalidVector) + .endObject()); + MapperParsingException e = expectThrows(MapperParsingException.class, () -> mapper.parse( + new SourceToParse("test-index", "_doc", "1", invalidDoc, XContentType.JSON))); + assertThat(e.getDetailedMessage(), containsString("has exceeded the maximum allowed number of dimensions")); + } } From a099ae140f109c654a7ca37c82ade19cf070f611 Mon Sep 17 00:00:00 2001 From: Benjamin Trent Date: Wed, 27 Mar 2019 14:52:30 -0500 Subject: [PATCH 37/77] Muting test (#40554) --- .../elasticsearch/action/search/SearchResponseMergerTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java index a7216853129a4..26a022c0d7745 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java @@ -396,6 +396,7 @@ public void testMergeAggs() throws InterruptedException { assertEquals(totalCount, bucket.getDocCount()); } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40553") public void testMergeSearchHits() throws InterruptedException { final long currentRelativeTime = randomLong(); final SearchTimeProvider timeProvider = new SearchTimeProvider(randomLong(), 0, () -> currentRelativeTime); From 540f2394940cced7bc64e7be53879dac25622b25 Mon Sep 17 00:00:00 2001 From: Armin Braun Date: Wed, 27 Mar 2019 20:59:51 +0100 Subject: [PATCH 38/77] Cleanup Duplication in Netty4 Module (#40148) * Just drying up the listener/promise handling a little --- .../http/netty4/Netty4HttpChannel.java | 33 +---------- .../http/netty4/Netty4HttpRequestHandler.java | 4 +- .../http/netty4/Netty4HttpServerChannel.java | 16 +----- .../netty4/Netty4InternalESLogger.java | 1 - .../transport/netty4/Netty4TcpChannel.java | 57 +++++++++++-------- .../netty4/Netty4TcpServerChannel.java | 15 +---- 6 files changed, 40 insertions(+), 86 deletions(-) diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpChannel.java b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpChannel.java index 73135c2a14560..69d84dfb78faf 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpChannel.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpChannel.java @@ -20,12 +20,11 @@ package org.elasticsearch.http.netty4; import io.netty.channel.Channel; -import io.netty.channel.ChannelPromise; -import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.concurrent.CompletableContext; import org.elasticsearch.http.HttpChannel; import org.elasticsearch.http.HttpResponse; +import org.elasticsearch.transport.netty4.Netty4TcpChannel; import java.net.InetSocketAddress; @@ -36,38 +35,12 @@ public class Netty4HttpChannel implements HttpChannel { Netty4HttpChannel(Channel channel) { this.channel = channel; - this.channel.closeFuture().addListener(f -> { - if (f.isSuccess()) { - closeContext.complete(null); - } else { - Throwable cause = f.cause(); - if (cause instanceof Error) { - ExceptionsHelper.maybeDieOnAnotherThread(cause); - closeContext.completeExceptionally(new Exception(cause)); - } else { - closeContext.completeExceptionally((Exception) cause); - } - } - }); + Netty4TcpChannel.addListener(this.channel.closeFuture(), closeContext); } @Override public void sendResponse(HttpResponse response, ActionListener listener) { - ChannelPromise writePromise = channel.newPromise(); - writePromise.addListener(f -> { - if (f.isSuccess()) { - listener.onResponse(null); - } else { - final Throwable cause = f.cause(); - ExceptionsHelper.maybeDieOnAnotherThread(cause); - if (cause instanceof Error) { - listener.onFailure(new Exception(cause)); - } else { - listener.onFailure((Exception) cause); - } - } - }); - channel.writeAndFlush(response, writePromise); + channel.writeAndFlush(response, Netty4TcpChannel.addPromise(listener, channel)); } @Override diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpRequestHandler.java b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpRequestHandler.java index 472e34d09fc40..cad95d2627083 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpRequestHandler.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/http/netty4/Netty4HttpRequestHandler.java @@ -38,7 +38,7 @@ class Netty4HttpRequestHandler extends SimpleChannelInboundHandler msg) throws Exception { + protected void channelRead0(ChannelHandlerContext ctx, HttpPipelinedRequest msg) { Netty4HttpChannel channel = ctx.channel().attr(Netty4HttpServerTransport.HTTP_CHANNEL_KEY).get(); FullHttpRequest request = msg.getRequest(); @@ -72,7 +72,7 @@ protected void channelRead0(ChannelHandlerContext ctx, HttpPipelinedRequest { - if (f.isSuccess()) { - closeContext.complete(null); - } else { - Throwable cause = f.cause(); - if (cause instanceof Error) { - ExceptionsHelper.maybeDieOnAnotherThread(cause); - closeContext.completeExceptionally(new Exception(cause)); - } else { - closeContext.completeExceptionally((Exception) cause); - } - } - }); + Netty4TcpChannel.addListener(this.channel.closeFuture(), closeContext); } @Override diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java index 38527151695d8..4eca1803b6381 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4InternalESLogger.java @@ -22,7 +22,6 @@ import io.netty.util.internal.logging.AbstractInternalLogger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.LogManager; import org.elasticsearch.common.SuppressLoggerChecks; @SuppressLoggerChecks(reason = "safely delegates to logger") diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpChannel.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpChannel.java index ef96f75be89ca..4c68466efc4d6 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpChannel.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpChannel.java @@ -46,33 +46,54 @@ public class Netty4TcpChannel implements TcpChannel { this.isServer = isServer; this.profile = profile; this.connectContext = new CompletableContext<>(); - this.channel.closeFuture().addListener(f -> { + addListener(this.channel.closeFuture(), closeContext); + addListener(connectFuture, connectContext); + } + + /** + * Adds a listener that completes the given {@link CompletableContext} to the given {@link ChannelFuture}. + * @param channelFuture Channel future + * @param context Context to complete + */ + public static void addListener(ChannelFuture channelFuture, CompletableContext context) { + channelFuture.addListener(f -> { if (f.isSuccess()) { - closeContext.complete(null); + context.complete(null); } else { Throwable cause = f.cause(); if (cause instanceof Error) { ExceptionsHelper.maybeDieOnAnotherThread(cause); - closeContext.completeExceptionally(new Exception(cause)); + context.completeExceptionally(new Exception(cause)); } else { - closeContext.completeExceptionally((Exception) cause); + context.completeExceptionally((Exception) cause); } } }); + } - connectFuture.addListener(f -> { + /** + * Creates a {@link ChannelPromise} for the given {@link Channel} and adds a listener that invokes the given {@link ActionListener} + * on its completion. + * @param listener lister to invoke + * @param channel channel + * @return write promise + */ + public static ChannelPromise addPromise(ActionListener listener, Channel channel) { + ChannelPromise writePromise = channel.newPromise(); + writePromise.addListener(f -> { if (f.isSuccess()) { - connectContext.complete(null); + listener.onResponse(null); } else { - Throwable cause = f.cause(); + final Throwable cause = f.cause(); + ExceptionsHelper.maybeDieOnAnotherThread(cause); if (cause instanceof Error) { - ExceptionsHelper.maybeDieOnAnotherThread(cause); - connectContext.completeExceptionally(new Exception(cause)); + listener.onFailure(new Exception(cause)); } else { - connectContext.completeExceptionally((Exception) cause); + listener.onFailure((Exception) cause); } } }); + return writePromise; } @Override @@ -122,21 +143,7 @@ public InetSocketAddress getRemoteAddress() { @Override public void sendMessage(BytesReference reference, ActionListener listener) { - ChannelPromise writePromise = channel.newPromise(); - writePromise.addListener(f -> { - if (f.isSuccess()) { - listener.onResponse(null); - } else { - final Throwable cause = f.cause(); - ExceptionsHelper.maybeDieOnAnotherThread(cause); - if (cause instanceof Error) { - listener.onFailure(new Exception(cause)); - } else { - listener.onFailure((Exception) cause); - } - } - }); - channel.writeAndFlush(Netty4Utils.toByteBuf(reference), writePromise); + channel.writeAndFlush(Netty4Utils.toByteBuf(reference), addPromise(listener, channel)); if (channel.eventLoop().isShutdown()) { listener.onFailure(new TransportException("Cannot send message, event loop is shutting down.")); diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpServerChannel.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpServerChannel.java index 9ef3f296f0601..830b0a8c203a4 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpServerChannel.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4TcpServerChannel.java @@ -20,7 +20,6 @@ package org.elasticsearch.transport.netty4; import io.netty.channel.Channel; -import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.concurrent.CompletableContext; import org.elasticsearch.transport.TcpServerChannel; @@ -36,19 +35,7 @@ public class Netty4TcpServerChannel implements TcpServerChannel { Netty4TcpServerChannel(Channel channel, String profile) { this.channel = channel; this.profile = profile; - this.channel.closeFuture().addListener(f -> { - if (f.isSuccess()) { - closeContext.complete(null); - } else { - Throwable cause = f.cause(); - if (cause instanceof Error) { - ExceptionsHelper.maybeDieOnAnotherThread(cause); - closeContext.completeExceptionally(new Exception(cause)); - } else { - closeContext.completeExceptionally((Exception) cause); - } - } - }); + Netty4TcpChannel.addListener(this.channel.closeFuture(), closeContext); } @Override From e3070615000228c283d17ce8d182b44f1450a5d5 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 28 Mar 2019 10:06:38 +0200 Subject: [PATCH 39/77] SQL: Polish behavior of SYS TABLES command (#40535) SYS TABLES meta command has been improved to better adhere to the ODBC spec in particular with regards to the handling of enumerations (and the differences between '%', null and ''(empty string)) Fix #40348 --- .../xpack/sql/jdbc/JdbcDatabaseMetaData.java | 7 +- .../xpack/sql/parser/CommandBuilder.java | 5 +- .../plan/logical/command/sys/SysTables.java | 25 ++-- .../logical/command/sys/SysTablesTests.java | 125 ++++++++++++------ 4 files changed, 102 insertions(+), 60 deletions(-) diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java index 5697453730455..e69d5b0201319 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/JdbcDatabaseMetaData.java @@ -257,8 +257,7 @@ public boolean nullPlusNonNullIsNull() throws SQLException { @Override public boolean supportsConvert() throws SQLException { - //TODO: add Convert - return false; + return true; } @Override @@ -774,14 +773,14 @@ public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLExce @Override public ResultSet getCatalogs() throws SQLException { // TABLE_CAT is the first column - Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%'", 1); + Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%' LIKE ''", 1); return memorySet(con.cfg, columnInfo("", "TABLE_CAT"), data); } @Override public ResultSet getTableTypes() throws SQLException { // TABLE_TYPE (4) - Object[][] data = queryColumn(con, "SYS TABLES TYPE '%'", 4); + Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", 4); return memorySet(con.cfg, columnInfo("", "TABLE_TYPE"), data); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java index 87709ac104e08..04935023747c3 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/CommandBuilder.java @@ -149,9 +149,8 @@ public SysTables visitSysTables(SysTablesContext ctx) { if (value != null) { // check special ODBC wildcard case if (value.equals(StringUtils.SQL_WILDCARD) && ctx.string().size() == 1) { - // convert % to enumeration - // https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/value-list-arguments?view=ssdt-18vs2017 - types.addAll(IndexType.VALID); + // treat % as null + // https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/value-list-arguments } // special case for legacy apps (like msquery) that always asks for 'TABLE' // which we manually map to all concrete tables supported diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTables.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTables.java index 5ce1e6dcc8a70..53f1e1019b753 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTables.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTables.java @@ -14,9 +14,8 @@ import org.elasticsearch.xpack.sql.session.Rows; import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; -import org.elasticsearch.xpack.sql.tree.Source; import org.elasticsearch.xpack.sql.tree.NodeInfo; -import org.elasticsearch.xpack.sql.util.CollectionUtils; +import org.elasticsearch.xpack.sql.tree.Source; import java.util.ArrayList; import java.util.Comparator; @@ -77,8 +76,11 @@ public final void execute(SqlSession session, ActionListener liste // namely one param specified with '%', everything else empty string // https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqltables-function?view=ssdt-18vs2017#comments - if (clusterPattern != null && clusterPattern.pattern().equals(SQL_WILDCARD)) { - if ((pattern == null || pattern.pattern().isEmpty()) && CollectionUtils.isEmpty(types)) { + // catalog enumeration + if (clusterPattern == null || clusterPattern.pattern().equals(SQL_WILDCARD)) { + // enumerate only if pattern is "" and no types are specified (types is null) + if (pattern != null && pattern.pattern().isEmpty() && index == null + && types == null) { Object[] enumeration = new Object[10]; // send only the cluster, everything else null enumeration[0] = cluster; @@ -87,12 +89,15 @@ public final void execute(SqlSession session, ActionListener liste } } - // if no types were specified (the parser takes care of the % case) - if (IndexType.VALID.equals(types)) { - if ((clusterPattern == null || clusterPattern.pattern().isEmpty()) - && (pattern == null || pattern.pattern().isEmpty())) { + // enumerate types + // if no types are specified (the parser takes care of the % case) + if (types == null) { + // empty string for catalog + if (clusterPattern != null && clusterPattern.pattern().isEmpty() + // empty string for table like and no index specified + && pattern != null && pattern.pattern().isEmpty() && index == null) { List> values = new ArrayList<>(); - // send only the types, everything else null + // send only the types, everything else is made of empty strings for (IndexType type : IndexType.VALID) { Object[] enumeration = new Object[10]; enumeration[3] = type.toSql(); @@ -105,7 +110,7 @@ public final void execute(SqlSession session, ActionListener liste } } - + // no enumeration pattern found, list actual tables String cRegex = clusterPattern != null ? clusterPattern.asJavaRegex() : null; // if the catalog doesn't match, don't return any results diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java index 74ecdc80c12d6..e2baeb2d8af98 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/plan/logical/command/sys/SysTablesTests.java @@ -51,20 +51,60 @@ public class SysTablesTests extends ESTestCase { private final IndexInfo index = new IndexInfo("test", IndexType.INDEX); private final IndexInfo alias = new IndexInfo("alias", IndexType.ALIAS); - public void testSysTablesEnumerateCatalog() throws Exception { - executeCommand("SYS TABLES CATALOG LIKE '%'", r -> { + // + // catalog enumeration + // + public void testSysTablesCatalogEnumeration() throws Exception { + executeCommand("SYS TABLES CATALOG LIKE '%' LIKE ''", r -> { assertEquals(1, r.size()); assertEquals(CLUSTER_NAME, r.column(0)); - }); + // everything else should be null + for (int i = 1; i < 10; i++) { + assertNull(r.column(i)); + } + }, index); + } + + // + // table types enumeration + // + public void testSysTablesTypesEnumerationWoString() throws Exception { + executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' ", r -> { + assertEquals(2, r.size()); + assertEquals("BASE TABLE", r.column(3)); + assertTrue(r.advanceRow()); + assertEquals("VIEW", r.column(3)); + }, new IndexInfo[0]); } public void testSysTablesEnumerateTypes() throws Exception { - executeCommand("SYS TABLES TYPE '%'", r -> { + executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", r -> { assertEquals(2, r.size()); assertEquals("BASE TABLE", r.column(3)); assertTrue(r.advanceRow()); assertEquals("VIEW", r.column(3)); - }); + }, alias, index); + } + + public void testSysTablesTypesEnumeration() throws Exception { + executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", r -> { + assertEquals(2, r.size()); + + Iterator it = IndexType.VALID.stream().sorted(Comparator.comparing(IndexType::toSql)).iterator(); + + for (int t = 0; t < r.size(); t++) { + assertEquals(it.next().toSql(), r.column(3)); + + // everything else should be null + for (int i = 0; i < 10; i++) { + if (i != 3) { + assertNull(r.column(i)); + } + } + + r.advanceRow(); + } + }, new IndexInfo[0]); } public void testSysTablesDifferentCatalog() throws Exception { @@ -77,17 +117,42 @@ public void testSysTablesDifferentCatalog() throws Exception { public void testSysTablesNoTypes() throws Exception { executeCommand("SYS TABLES", r -> { assertEquals(2, r.size()); + assertEquals("test", r.column(2)); + assertEquals("BASE TABLE", r.column(3)); + assertTrue(r.advanceRow()); + assertEquals("alias", r.column(2)); + assertEquals("VIEW", r.column(3)); + }, index, alias); + } + + public void testSysTablesWithLegacyTypes() throws Exception { + executeCommand("SYS TABLES TYPE 'TABLE', 'ALIAS'", r -> { + assertEquals(2, r.size()); + assertEquals("test", r.column(2)); + assertEquals("TABLE", r.column(3)); + assertTrue(r.advanceRow()); + assertEquals("alias", r.column(2)); + assertEquals("VIEW", r.column(3)); + }, index, alias); + } + + public void testSysTablesWithProperTypes() throws Exception { + executeCommand("SYS TABLES TYPE 'BASE TABLE', 'ALIAS'", r -> { + assertEquals(2, r.size()); + assertEquals("test", r.column(2)); assertEquals("BASE TABLE", r.column(3)); assertTrue(r.advanceRow()); + assertEquals("alias", r.column(2)); assertEquals("VIEW", r.column(3)); }, index, alias); } public void testSysTablesPattern() throws Exception { executeCommand("SYS TABLES LIKE '%'", r -> { + assertEquals(2, r.size()); assertEquals("test", r.column(2)); + assertEquals("BASE TABLE", r.column(3)); assertTrue(r.advanceRow()); - assertEquals(2, r.size()); assertEquals("alias", r.column(2)); }, index, alias); } @@ -130,7 +195,18 @@ public void testSysTablesOnlyIndicesInLegacyMode() throws Exception { assertEquals("test", r.column(2)); assertEquals("TABLE", r.column(3)); }, index); + } + + public void testSysTablesNoPatternWithTypesSpecifiedInLegacyMode() throws Exception { + executeCommand("SYS TABLES TYPE 'TABLE','VIEW'", r -> { + assertEquals(2, r.size()); + assertEquals("test", r.column(2)); + assertEquals("TABLE", r.column(3)); + assertTrue(r.advanceRow()); + assertEquals("alias", r.column(2)); + assertEquals("VIEW", r.column(3)); + }, index, alias); } public void testSysTablesOnlyIndicesLegacyModeParameterized() throws Exception { @@ -192,43 +268,6 @@ public void testSysTablesWithInvalidType() throws Exception { }, new IndexInfo[0]); } - public void testSysTablesCatalogEnumeration() throws Exception { - executeCommand("SYS TABLES CATALOG LIKE '%' LIKE ''", r -> { - assertEquals(1, r.size()); - assertEquals(CLUSTER_NAME, r.column(0)); - // everything else should be null - for (int i = 1; i < 10; i++) { - assertNull(r.column(i)); - } - }, new IndexInfo[0]); - } - - public void testSysTablesTypesEnumeration() throws Exception { - executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' TYPE '%'", r -> { - assertEquals(2, r.size()); - - Iterator it = IndexType.VALID.stream().sorted(Comparator.comparing(IndexType::toSql)).iterator(); - - for (int t = 0; t < r.size(); t++) { - assertEquals(it.next().toSql(), r.column(3)); - - // everything else should be null - for (int i = 0; i < 10; i++) { - if (i != 3) { - assertNull(r.column(i)); - } - } - - r.advanceRow(); - } - }, new IndexInfo[0]); - } - - public void testSysTablesTypesEnumerationWoString() throws Exception { - executeCommand("SYS TABLES CATALOG LIKE '' LIKE '' ", r -> { - assertEquals(0, r.size()); - }, new IndexInfo[0]); - } private SqlTypedParamValue param(Object value) { return new SqlTypedParamValue(DataTypes.fromJava(value).typeName, value); From c122fc6edddbb99c73ce25168d1152409c0b7892 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Thu, 28 Mar 2019 10:11:23 +0200 Subject: [PATCH 40/77] SQL: add "fuzziness" option to QUERY and MATCH function predicates (#40529) * Remove unused "locale" and "lowercase_expanded_terms" options from QUERY. --- docs/reference/sql/functions/search.asciidoc | 10 ++-- .../qa/src/main/resources/fulltext.csv-spec | 54 +++++++++++++++++++ .../xpack/sql/querydsl/query/MatchQuery.java | 10 ++-- .../sql/querydsl/query/MultiMatchQuery.java | 14 ++--- .../sql/querydsl/query/QueryStringQuery.java | 26 ++++----- 5 files changed, 86 insertions(+), 28 deletions(-) diff --git a/docs/reference/sql/functions/search.asciidoc b/docs/reference/sql/functions/search.asciidoc index 210be00e3034b..0534271caa91f 100644 --- a/docs/reference/sql/functions/search.asciidoc +++ b/docs/reference/sql/functions/search.asciidoc @@ -61,11 +61,11 @@ case of an `or` operator or all of the low frequency terms in the case of an `an <> page. NOTE: The allowed optional parameters for a single-field `MATCH()` variant (for the `match` {es} query) are: `analyzer`, `auto_generate_synonyms_phrase_query`, -`cutoff_frequency`, `lenient`, `fuzzy_transpositions`, `fuzzy_rewrite`, `minimum_should_match`, `operator`, +`cutoff_frequency`, `lenient`, `fuzziness`, `fuzzy_transpositions`, `fuzzy_rewrite`, `minimum_should_match`, `operator`, `max_expansions`, `prefix_length`. NOTE: The allowed optional parameters for a multi-field `MATCH()` variant (for the `multi_match` {es} query) are: `analyzer`, `auto_generate_synonyms_phrase_query`, -`cutoff_frequency`, `lenient`, `fuzzy_transpositions`, `fuzzy_rewrite`, `minimum_should_match`, `operator`, +`cutoff_frequency`, `lenient`, `fuzziness`, `fuzzy_transpositions`, `fuzzy_rewrite`, `minimum_should_match`, `operator`, `max_expansions`, `prefix_length`, `slop`, `tie_breaker`, `type`. @@ -115,9 +115,9 @@ include-tagged::{sql-specs}/docs/docs.csv-spec[optionalParameterQuery] NOTE: The allowed optional parameters for `QUERY()` are: `allow_leading_wildcard`, `analyze_wildcard`, `analyzer`, `auto_generate_synonyms_phrase_query`, `default_field`, `default_operator`, `enable_position_increments`, -`escape`, `fuzzy_max_expansions`, `fuzzy_prefix_length`, `fuzzy_rewrite`, `fuzzy_transpositions`, `lenient`, -`locale`, `lowercase_expanded_terms`, `max_determinized_states`, `minimum_should_match`, `phrase_slop`, `rewrite`, -`quote_analyzer`, `quote_field_suffix`, `tie_breaker`, `time_zone`, `type`. +`escape`, `fuzziness`, `fuzzy_max_expansions`, `fuzzy_prefix_length`, `fuzzy_rewrite`, `fuzzy_transpositions`, +`lenient`, `max_determinized_states`, `minimum_should_match`, `phrase_slop`, `rewrite`, `quote_analyzer`, +`quote_field_suffix`, `tie_breaker`, `time_zone`, `type`. [[sql-functions-search-score]] diff --git a/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec b/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec index 07df14d99e36b..99aa07ec91f4d 100644 --- a/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec +++ b/x-pack/plugin/sql/qa/src/main/resources/fulltext.csv-spec @@ -30,6 +30,60 @@ SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE QUERY('Man*', ' 10096 |Jayson |M |Mandell ; +matchWithFuzziness +SELECT first_name, SCORE() FROM test_emp WHERE MATCH(first_name, 'geo', 'fuzziness=6'); + + first_name:s | SCORE():f +----------------+--------------- +Gino |1.3684646 +Gao |2.7369292 +; + +matchWithFuzzinessAuto +SELECT first_name, SCORE() FROM test_emp WHERE MATCH(first_name, 'geo', 'fuzziness=AUTO:1,7;fuzzy_rewrite=scoring_boolean'); + + first_name:s | SCORE():f +----------------+--------------- +Gao |2.7369292 +; + +multiMatchWithFuzzinessAuto +SELECT first_name, last_name, SCORE() FROM test_emp WHERE MATCH('first_name^3,last_name^5', 'geo hir', 'fuzziness=AUTO:1,5;operator=or') ORDER BY first_name; + + first_name:s | last_name:s | SCORE():f +----------------+-----------------+--------------- +Gao |Dolinsky |8.210788 +Shir |McClurg |8.210788 +; + +multiMatchWithFuzziness +SELECT first_name, last_name, SCORE() FROM test_emp WHERE MATCH('first_name^3,last_name^5', 'geo hir', 'fuzziness=5;operator=or') ORDER BY first_name; + + first_name:s | last_name:s | SCORE():f +----------------+-----------------+--------------- +Gao |Dolinsky |8.210788 +Gino |Leonhardt |4.105394 +Shir |McClurg |8.210788 +Uri |Lenart |4.105394 +; + +queryWithFuzziness +SELECT first_name, SCORE() FROM test_emp WHERE QUERY('geo~', 'fuzziness=5;default_field=first_name'); + + first_name:s | SCORE():f +----------------+--------------- +Gino |1.3684646 +Gao |2.7369292 +; + +queryWithFuzzinessAuto +SELECT first_name, SCORE() FROM test_emp WHERE QUERY('geo~', 'fuzziness=AUTO:1,5;default_field=first_name'); + + first_name:s | SCORE():f +----------------+--------------- +Gao |2.7369292 +; + matchQuery SELECT emp_no, first_name, gender, last_name FROM test_emp WHERE MATCH(first_name, 'Erez'); diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java index d0fe697268d41..7bddacb86bf74 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MatchQuery.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.querydsl.query; import org.elasticsearch.common.Booleans; +import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilder; @@ -28,16 +29,17 @@ public class MatchQuery extends LeafQuery { // TODO: it'd be great if these could be constants instead of Strings, needs a core change to make the fields public first // TODO: add zero terms query support, I'm not sure the best way to parse it yet... // appliers.put("zero_terms_query", (qb, s) -> qb.zeroTermsQuery(s)); + appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); + appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); appliers.put("cutoff_frequency", (qb, s) -> qb.cutoffFrequency(Float.valueOf(s))); - appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); + appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.build(s))); appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))); appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s)); + appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); + appliers.put("max_expansions", (qb, s) -> qb.maxExpansions(Integer.valueOf(s))); appliers.put("minimum_should_match", (qb, s) -> qb.minimumShouldMatch(s)); appliers.put("operator", (qb, s) -> qb.operator(Operator.fromString(s))); - appliers.put("max_expansions", (qb, s) -> qb.maxExpansions(Integer.valueOf(s))); appliers.put("prefix_length", (qb, s) -> qb.prefixLength(Integer.valueOf(s))); - appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); - appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); BUILDER_APPLIERS = Collections.unmodifiableMap(appliers); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java index 4f0bc0720ae83..2c6b47d7bdcc3 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/MultiMatchQuery.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.querydsl.query; import org.elasticsearch.common.Booleans; +import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilder; @@ -29,18 +30,19 @@ public class MultiMatchQuery extends LeafQuery { appliers.put("slop", (qb, s) -> qb.slop(Integer.valueOf(s))); // TODO: add zero terms query support, I'm not sure the best way to parse it yet... // appliers.put("zero_terms_query", (qb, s) -> qb.zeroTermsQuery(s)); - appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); + appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); + appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); appliers.put("cutoff_frequency", (qb, s) -> qb.cutoffFrequency(Float.valueOf(s))); - appliers.put("tie_breaker", (qb, s) -> qb.tieBreaker(Float.valueOf(s))); + appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.build(s))); appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s)); + appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))); + appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); + appliers.put("max_expansions", (qb, s) -> qb.maxExpansions(Integer.valueOf(s))); appliers.put("minimum_should_match", (qb, s) -> qb.minimumShouldMatch(s)); appliers.put("operator", (qb, s) -> qb.operator(Operator.fromString(s))); - appliers.put("max_expansions", (qb, s) -> qb.maxExpansions(Integer.valueOf(s))); appliers.put("prefix_length", (qb, s) -> qb.prefixLength(Integer.valueOf(s))); - appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); + appliers.put("tie_breaker", (qb, s) -> qb.tieBreaker(Float.valueOf(s))); appliers.put("type", (qb, s) -> qb.type(s)); - appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); - appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))); BUILDER_APPLIERS = Collections.unmodifiableMap(appliers); } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java index de457ba918e7c..a6d8ff2dbf5fc 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/query/QueryStringQuery.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.sql.querydsl.query; import org.elasticsearch.common.Booleans; +import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.Operator; @@ -28,30 +29,29 @@ public class QueryStringQuery extends LeafQuery { static { HashMap> appliers = new HashMap<>(28); // TODO: it'd be great if these could be constants instead of Strings, needs a core change to make the fields public first + appliers.put("allow_leading_wildcard", (qb, s) -> qb.allowLeadingWildcard(Booleans.parseBoolean(s))); + appliers.put("analyze_wildcard", (qb, s) -> qb.analyzeWildcard(Booleans.parseBoolean(s))); + appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); + appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); appliers.put("default_field", (qb, s) -> qb.defaultField(s)); appliers.put("default_operator", (qb, s) -> qb.defaultOperator(Operator.fromString(s))); - appliers.put("analyzer", (qb, s) -> qb.analyzer(s)); - appliers.put("quote_analyzer", (qb, s) -> qb.quoteAnalyzer(s)); - appliers.put("allow_leading_wildcard", (qb, s) -> qb.allowLeadingWildcard(Booleans.parseBoolean(s))); - appliers.put("max_determinized_states", (qb, s) -> qb.maxDeterminizedStates(Integer.valueOf(s))); - appliers.put("lowercase_expanded_terms", (qb, s) -> {}); appliers.put("enable_position_increments", (qb, s) -> qb.enablePositionIncrements(Booleans.parseBoolean(s))); appliers.put("escape", (qb, s) -> qb.escape(Booleans.parseBoolean(s))); - appliers.put("fuzzy_prefix_length", (qb, s) -> qb.fuzzyPrefixLength(Integer.valueOf(s))); + appliers.put("fuzziness", (qb, s) -> qb.fuzziness(Fuzziness.build(s))); appliers.put("fuzzy_max_expansions", (qb, s) -> qb.fuzzyMaxExpansions(Integer.valueOf(s))); + appliers.put("fuzzy_prefix_length", (qb, s) -> qb.fuzzyPrefixLength(Integer.valueOf(s))); appliers.put("fuzzy_rewrite", (qb, s) -> qb.fuzzyRewrite(s)); + appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))); + appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); + appliers.put("max_determinized_states", (qb, s) -> qb.maxDeterminizedStates(Integer.valueOf(s))); + appliers.put("minimum_should_match", (qb, s) -> qb.minimumShouldMatch(s)); appliers.put("phrase_slop", (qb, s) -> qb.phraseSlop(Integer.valueOf(s))); - appliers.put("tie_breaker", (qb, s) -> qb.tieBreaker(Float.valueOf(s))); - appliers.put("analyze_wildcard", (qb, s) -> qb.analyzeWildcard(Booleans.parseBoolean(s))); appliers.put("rewrite", (qb, s) -> qb.rewrite(s)); - appliers.put("minimum_should_match", (qb, s) -> qb.minimumShouldMatch(s)); + appliers.put("quote_analyzer", (qb, s) -> qb.quoteAnalyzer(s)); appliers.put("quote_field_suffix", (qb, s) -> qb.quoteFieldSuffix(s)); - appliers.put("lenient", (qb, s) -> qb.lenient(Booleans.parseBoolean(s))); - appliers.put("locale", (qb, s) -> {}); + appliers.put("tie_breaker", (qb, s) -> qb.tieBreaker(Float.valueOf(s))); appliers.put("time_zone", (qb, s) -> qb.timeZone(s)); appliers.put("type", (qb, s) -> qb.type(MultiMatchQueryBuilder.Type.parse(s, LoggingDeprecationHandler.INSTANCE))); - appliers.put("auto_generate_synonyms_phrase_query", (qb, s) -> qb.autoGenerateSynonymsPhraseQuery(Booleans.parseBoolean(s))); - appliers.put("fuzzy_transpositions", (qb, s) -> qb.fuzzyTranspositions(Booleans.parseBoolean(s))); BUILDER_APPLIERS = Collections.unmodifiableMap(appliers); } From c03f2e641b8bbbdd2ce8d6ca72be1221196126c1 Mon Sep 17 00:00:00 2001 From: jimczi Date: Thu, 28 Mar 2019 09:55:29 +0100 Subject: [PATCH 41/77] Fix SearchResponseMerger#testMergeSearchHits This commit fixes an edge case in tests where search hits are empty after the merge but some shards returned hits. This can happen if the total number of merged hits is less than the provided `from`. Closes #40553 --- .../action/search/SearchResponseMergerTests.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java index 26a022c0d7745..ab31c44ff2d59 100644 --- a/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/SearchResponseMergerTests.java @@ -396,7 +396,6 @@ public void testMergeAggs() throws InterruptedException { assertEquals(totalCount, bucket.getDocCount()); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40553") public void testMergeSearchHits() throws InterruptedException { final long currentRelativeTime = randomLong(); final SearchTimeProvider timeProvider = new SearchTimeProvider(randomLong(), 0, () -> currentRelativeTime); @@ -442,6 +441,7 @@ public void testMergeSearchHits() throws InterruptedException { float expectedMaxScore = Float.NEGATIVE_INFINITY; int numIndices = requestedSize == 0 ? 0 : randomIntBetween(1, requestedSize); Iterator> indicesIterator = randomRealisticIndices(numIndices, numResponses).entrySet().iterator(); + boolean hasHits = false; for (int i = 0; i < numResponses; i++) { Map.Entry entry = indicesIterator.next(); String clusterAlias = entry.getKey(); @@ -465,6 +465,7 @@ public void testMergeSearchHits() throws InterruptedException { float maxScore = scoreSort ? numDocs * scoreFactor : Float.NaN; SearchHit[] hits = randomSearchHitArray(numDocs, numResponses, clusterAlias, indices, maxScore, scoreFactor, sortFields, priorityQueue); + hasHits |= hits.length > 0; expectedMaxScore = Math.max(expectedMaxScore, maxScore); Object[] collapseValues = null; @@ -514,7 +515,7 @@ public void testMergeSearchHits() throws InterruptedException { SearchHits searchHits = searchResponse.getHits(); // the sort fields and the collapse field are not returned when hits are empty - if (searchHits.getHits().length > 0) { + if (hasHits) { assertArrayEquals(sortFields, searchHits.getSortFields()); assertEquals(collapseField, searchHits.getCollapseField()); } else { @@ -540,7 +541,7 @@ public void testMergeSearchHits() throws InterruptedException { SearchHit[] hits = searchHits.getHits(); if (collapseField != null // the collapse field is not returned when hits are empty - && hits.length > 0) { + && hasHits) { assertEquals(hits.length, searchHits.getCollapseValues().length); } else { assertNull(searchHits.getCollapseValues()); From 5b64c770b208e5336e48a5d26b20a976c415580b Mon Sep 17 00:00:00 2001 From: David Kyle Date: Thu, 28 Mar 2019 10:01:02 +0000 Subject: [PATCH 42/77] [ML] Data Frame minor tidy ups (#40548) Remove Xlint-rawtypes option and remove unused request builders. Not all requests need to implement ToXContent. --- .../DeleteDataFrameTransformAction.java | 22 +----------------- .../action/GetDataFrameTransformsAction.java | 18 +-------------- .../GetDataFrameTransformsStatsAction.java | 20 +--------------- .../action/PutDataFrameTransformAction.java | 9 -------- .../action/StartDataFrameTransformAction.java | 18 +-------------- .../StartDataFrameTransformTaskAction.java | 18 +-------------- .../action/StopDataFrameTransformAction.java | 23 +------------------ ...aFrameTransformTaskActionRequestTests.java | 23 +++++++++++++++++++ ...FrameTransformTaskActionResponseTests.java | 23 +++++++++++++++++++ x-pack/plugin/data-frame/build.gradle | 3 --- ...ansportDeleteDataFrameTransformAction.java | 7 ++---- .../util/BatchedDataIteratorTests.java | 3 +-- 12 files changed, 55 insertions(+), 132 deletions(-) create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionRequestTests.java create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionResponseTests.java diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/DeleteDataFrameTransformAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/DeleteDataFrameTransformAction.java index 13e62da090c3e..0316153fbc822 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/DeleteDataFrameTransformAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/DeleteDataFrameTransformAction.java @@ -6,17 +6,14 @@ package org.elasticsearch.xpack.core.dataframe.action; import org.elasticsearch.action.Action; -import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.support.tasks.BaseTasksRequest; import org.elasticsearch.action.support.tasks.BaseTasksResponse; -import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.tasks.Task; @@ -42,16 +39,13 @@ public Response newResponse() { return new Response(); } - public static class Request extends BaseTasksRequest implements ToXContentFragment { + public static class Request extends BaseTasksRequest { private String id; public Request(String id) { this.id = ExceptionsHelper.requireNonNull(id, DataFrameField.ID.getPreferredName()); } - private Request() { - } - public Request(StreamInput in) throws IOException { super(in); id = in.readString(); @@ -77,12 +71,6 @@ public ActionRequestValidationException validate() { return null; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.field(DataFrameField.ID.getPreferredName(), id); - return builder; - } - @Override public int hashCode() { return Objects.hash(id); @@ -102,14 +90,6 @@ public boolean equals(Object obj) { } } - public static class RequestBuilder - extends ActionRequestBuilder { - - protected RequestBuilder(ElasticsearchClient client, DeleteDataFrameTransformAction action) { - super(client, action, new DeleteDataFrameTransformAction.Request()); - } - } - public static class Response extends BaseTasksResponse implements Writeable, ToXContentObject { private boolean acknowledged; public Response(StreamInput in) throws IOException { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsAction.java index ca6bf9d16e62f..ac1498c72a6da 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsAction.java @@ -8,14 +8,11 @@ import org.apache.logging.log4j.LogManager; import org.elasticsearch.action.Action; -import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.ActionRequestValidationException; -import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.xpack.core.action.AbstractGetResourcesRequest; @@ -48,7 +45,7 @@ public Response newResponse() { return new Response(); } - public static class Request extends AbstractGetResourcesRequest implements ToXContent { + public static class Request extends AbstractGetResourcesRequest { private static final int MAX_SIZE_RETURN = 1000; @@ -78,25 +75,12 @@ public ActionRequestValidationException validate() { return exception; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.field(DataFrameField.ID.getPreferredName(), getResourceId()); - return builder; - } - @Override public String getResourceIdField() { return DataFrameField.ID.getPreferredName(); } } - public static class RequestBuilder extends ActionRequestBuilder { - - protected RequestBuilder(ElasticsearchClient client, GetDataFrameTransformsAction action) { - super(client, action, new Request()); - } - } - public static class Response extends AbstractGetResourcesResponse implements Writeable, ToXContentObject { public static final String INVALID_TRANSFORMS_DEPRECATION_WARNING = "Found [{}] invalid transforms"; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsStatsAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsStatsAction.java index 47e922033072b..f0e92aa36db2f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsStatsAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/GetDataFrameTransformsStatsAction.java @@ -7,19 +7,16 @@ package org.elasticsearch.xpack.core.dataframe.action; import org.elasticsearch.action.Action; -import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.support.tasks.BaseTasksRequest; import org.elasticsearch.action.support.tasks.BaseTasksResponse; -import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.tasks.Task; @@ -44,7 +41,7 @@ public Response newResponse() { return new Response(); } - public static class Request extends BaseTasksRequest implements ToXContent { + public static class Request extends BaseTasksRequest { private String id; public Request(String id) { @@ -55,8 +52,6 @@ public Request(String id) { } } - private Request() {} - public Request(StreamInput in) throws IOException { super(in); id = in.readString(); @@ -87,12 +82,6 @@ public ActionRequestValidationException validate() { return null; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.field(DataFrameField.ID.getPreferredName(), id); - return builder; - } - @Override public int hashCode() { return Objects.hash(id); @@ -111,13 +100,6 @@ public boolean equals(Object obj) { } } - public static class RequestBuilder extends ActionRequestBuilder { - - protected RequestBuilder(ElasticsearchClient client, GetDataFrameTransformsStatsAction action) { - super(client, action, new Request()); - } - } - public static class Response extends BaseTasksResponse implements Writeable, ToXContentObject { private List transformsStateAndStats; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/PutDataFrameTransformAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/PutDataFrameTransformAction.java index 6c226003f663a..51b5e0d4ec1d6 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/PutDataFrameTransformAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/PutDataFrameTransformAction.java @@ -10,8 +10,6 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder; -import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ToXContentObject; @@ -100,13 +98,6 @@ public boolean equals(Object obj) { } } - public static class RequestBuilder extends MasterNodeOperationRequestBuilder { - - protected RequestBuilder(ElasticsearchClient client, PutDataFrameTransformAction action) { - super(client, action, new Request()); - } - } - public static class Response extends AcknowledgedResponse { public Response() { super(); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformAction.java index 161c4d7d2587a..b86a2339faa47 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformAction.java @@ -7,15 +7,12 @@ package org.elasticsearch.xpack.core.dataframe.action; import org.elasticsearch.action.Action; -import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.action.support.tasks.BaseTasksResponse; -import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.xpack.core.dataframe.DataFrameField; @@ -39,7 +36,7 @@ public Response newResponse() { return new Response(); } - public static class Request extends AcknowledgedRequest implements ToXContent { + public static class Request extends AcknowledgedRequest { private String id; private boolean force; @@ -76,12 +73,6 @@ public ActionRequestValidationException validate() { return null; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.field(DataFrameField.ID.getPreferredName(), id); - return builder; - } - @Override public int hashCode() { return Objects.hash(id); @@ -100,13 +91,6 @@ public boolean equals(Object obj) { } } - public static class RequestBuilder extends ActionRequestBuilder { - - protected RequestBuilder(ElasticsearchClient client, StartDataFrameTransformAction action) { - super(client, action, new Request()); - } - } - public static class Response extends BaseTasksResponse implements Writeable, ToXContentObject { private boolean started; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskAction.java index a51b9243c3d44..d3c96fb9cf171 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskAction.java @@ -7,15 +7,12 @@ package org.elasticsearch.xpack.core.dataframe.action; import org.elasticsearch.action.Action; -import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.tasks.BaseTasksRequest; import org.elasticsearch.action.support.tasks.BaseTasksResponse; -import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.xpack.core.dataframe.DataFrameField; @@ -39,7 +36,7 @@ public Response newResponse() { return new Response(); } - public static class Request extends BaseTasksRequest implements ToXContent { + public static class Request extends BaseTasksRequest { private String id; @@ -70,12 +67,6 @@ public ActionRequestValidationException validate() { return null; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.field(DataFrameField.ID.getPreferredName(), id); - return builder; - } - @Override public int hashCode() { return Objects.hash(id); @@ -94,13 +85,6 @@ public boolean equals(Object obj) { } } - public static class RequestBuilder extends ActionRequestBuilder { - - protected RequestBuilder(ElasticsearchClient client, StartDataFrameTransformTaskAction action) { - super(client, action, new Request()); - } - } - public static class Response extends BaseTasksResponse implements Writeable, ToXContentObject { private boolean started; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StopDataFrameTransformAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StopDataFrameTransformAction.java index 2fb14ddd013b6..7fa437bd15606 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StopDataFrameTransformAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/dataframe/action/StopDataFrameTransformAction.java @@ -6,17 +6,14 @@ package org.elasticsearch.xpack.core.dataframe.action; import org.elasticsearch.action.Action; -import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.tasks.BaseTasksRequest; import org.elasticsearch.action.support.tasks.BaseTasksResponse; -import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.tasks.Task; @@ -44,7 +41,7 @@ public Response newResponse() { return new Response(); } - public static class Request extends BaseTasksRequest implements ToXContent { + public static class Request extends BaseTasksRequest { private String id; private final boolean waitForCompletion; private final boolean force; @@ -98,17 +95,6 @@ public ActionRequestValidationException validate() { return null; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.field(DataFrameField.ID.getPreferredName(), id); - builder.field(DataFrameField.WAIT_FOR_COMPLETION.getPreferredName(), waitForCompletion); - builder.field(DataFrameField.FORCE.getPreferredName(), force); - if (this.getTimeout() != null) { - builder.field(DataFrameField.TIMEOUT.getPreferredName(), this.getTimeout()); - } - return builder; - } - @Override public int hashCode() { // the base class does not implement hashCode, therefore we need to hash timeout ourselves @@ -144,13 +130,6 @@ public boolean match(Task task) { } } - public static class RequestBuilder extends ActionRequestBuilder { - - protected RequestBuilder(ElasticsearchClient client, StopDataFrameTransformAction action) { - super(client, action, new Request()); - } - } - public static class Response extends BaseTasksResponse implements Writeable, ToXContentObject { private boolean stopped; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionRequestTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionRequestTests.java new file mode 100644 index 0000000000000..8d3d8e3ac7890 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionRequestTests.java @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.core.dataframe.action; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.test.AbstractWireSerializingTestCase; + +public class StartDataFrameTransformTaskActionRequestTests extends + AbstractWireSerializingTestCase { + @Override + protected StartDataFrameTransformTaskAction.Request createTestInstance() { + return new StartDataFrameTransformTaskAction.Request(randomAlphaOfLength(4)); + } + + @Override + protected Writeable.Reader instanceReader() { + return StartDataFrameTransformTaskAction.Request::new; + } +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionResponseTests.java new file mode 100644 index 0000000000000..62165f87968e0 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/dataframe/action/StartDataFrameTransformTaskActionResponseTests.java @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +package org.elasticsearch.xpack.core.dataframe.action; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.test.AbstractWireSerializingTestCase; + +public class StartDataFrameTransformTaskActionResponseTests extends + AbstractWireSerializingTestCase { + @Override + protected StartDataFrameTransformTaskAction.Response createTestInstance() { + return new StartDataFrameTransformTaskAction.Response(randomBoolean()); + } + + @Override + protected Writeable.Reader instanceReader() { + return StartDataFrameTransformTaskAction.Response::new; + } +} diff --git a/x-pack/plugin/data-frame/build.gradle b/x-pack/plugin/data-frame/build.gradle index bff8118bfc425..ad4d846fd160f 100644 --- a/x-pack/plugin/data-frame/build.gradle +++ b/x-pack/plugin/data-frame/build.gradle @@ -8,9 +8,6 @@ esplugin { extendedPlugins = ['x-pack-core'] } -compileJava.options.compilerArgs << "-Xlint:-rawtypes" -compileTestJava.options.compilerArgs << "-Xlint:-rawtypes" - dependencies { compileOnly "org.elasticsearch:elasticsearch:${version}" diff --git a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportDeleteDataFrameTransformAction.java b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportDeleteDataFrameTransformAction.java index 131ad690d2b66..2cdc4009e785b 100644 --- a/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportDeleteDataFrameTransformAction.java +++ b/x-pack/plugin/data-frame/src/main/java/org/elasticsearch/xpack/dataframe/action/TransportDeleteDataFrameTransformAction.java @@ -12,20 +12,18 @@ import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.tasks.TransportTasksAction; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.discovery.MasterNotDiscoveredException; import org.elasticsearch.persistent.PersistentTasksCustomMetaData; -import org.elasticsearch.persistent.PersistentTasksService; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; import org.elasticsearch.xpack.core.dataframe.action.DeleteDataFrameTransformAction; -import org.elasticsearch.xpack.core.indexing.IndexerState; import org.elasticsearch.xpack.core.dataframe.action.DeleteDataFrameTransformAction.Request; import org.elasticsearch.xpack.core.dataframe.action.DeleteDataFrameTransformAction.Response; +import org.elasticsearch.xpack.core.indexing.IndexerState; import org.elasticsearch.xpack.dataframe.persistence.DataFrameTransformsConfigManager; import org.elasticsearch.xpack.dataframe.transforms.DataFrameTransformTask; @@ -36,8 +34,7 @@ public class TransportDeleteDataFrameTransformAction extends TransportTasksActio private final DataFrameTransformsConfigManager transformsConfigManager; @Inject - public TransportDeleteDataFrameTransformAction(TransportService transportService, ThreadPool threadPool, ActionFilters actionFilters, - IndexNameExpressionResolver indexNameExpressionResolver, PersistentTasksService persistentTasksService, + public TransportDeleteDataFrameTransformAction(TransportService transportService, ActionFilters actionFilters, ClusterService clusterService, DataFrameTransformsConfigManager transformsConfigManager) { super(DeleteDataFrameTransformAction.NAME, clusterService, transportService, actionFilters, Request::new, Response::new, Response::new, ThreadPool.Names.SAME); diff --git a/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/util/BatchedDataIteratorTests.java b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/util/BatchedDataIteratorTests.java index 6a0714076a317..4ca60acac37ed 100644 --- a/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/util/BatchedDataIteratorTests.java +++ b/x-pack/plugin/data-frame/src/test/java/org/elasticsearch/xpack/dataframe/util/BatchedDataIteratorTests.java @@ -146,7 +146,7 @@ public void testQueryReturnsThreeBatches() throws Exception { testIterator.next(future); batch = future.get(); assertEquals(1, batch.size()); - assertTrue(batch.containsAll(Collections.singletonList(createJsonDoc("f")))); + assertTrue(batch.contains(createJsonDoc("f"))); assertFalse(testIterator.hasNext()); assertTrue(wasScrollCleared); @@ -183,7 +183,6 @@ private void assertSearchRequest() { SearchRequest searchRequest = searchRequests.get(0); assertThat(searchRequest.indices(), equalTo(new String[] {INDEX_NAME})); assertThat(searchRequest.scroll().keepAlive(), equalTo(TimeValue.timeValueMinutes(5))); - assertThat(searchRequest.types().length, equalTo(0)); assertThat(searchRequest.source().query(), equalTo(QueryBuilders.matchAllQuery())); assertThat(searchRequest.source().trackTotalHitsUpTo(), is(SearchContext.TRACK_TOTAL_HITS_ACCURATE)); } From adac5bdba5923a9b1aebaa9970fa18b7d454d1f5 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Thu, 28 Mar 2019 12:12:07 +0100 Subject: [PATCH 43/77] Mute WatchAckTests.testAckAllActions Relates to #35506 --- .../xpack/watcher/test/integration/WatchAckTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java index a0ef5e97d8534..0e95a15b2a35c 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/WatchAckTests.java @@ -122,6 +122,7 @@ public void testAckSingleAction() throws Exception { assertThat(throttledCount, greaterThan(0L)); } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/35506") public void testAckAllActions() throws Exception { PutWatchResponse putWatchResponse = watcherClient().preparePutWatch() .setId("_id") From c7ebfa2c51472fd1f296961209f9c8349d390d2d Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Thu, 28 Mar 2019 13:36:19 +0200 Subject: [PATCH 44/77] Use openjdk 12 in packer cache script (#40498) --- .ci/packer_cache.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/packer_cache.sh b/.ci/packer_cache.sh index 04511f81281b9..4533213920c3a 100755 --- a/.ci/packer_cache.sh +++ b/.ci/packer_cache.sh @@ -20,5 +20,5 @@ export JAVA_HOME="${HOME}"/.java/${ES_BUILD_JAVA} # We are caching BWC versions too, need these so we can build those export JAVA8_HOME="${HOME}"/.java/java8 export JAVA11_HOME="${HOME}"/.java/java11 -export JAVA12_HOME="${HOME}"/.java/java12 +export JAVA12_HOME="${HOME}"/.java/openjdk12 ./gradlew --parallel clean --scan -Porg.elasticsearch.acceptScanTOS=true -s resolveAllDependencies From b4d88aad3a40042f1c1bad6a8f1a1ec9512e0afc Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 28 Mar 2019 13:04:50 +0100 Subject: [PATCH 45/77] Move top-level pipeline aggs out of QuerySearchResult (#40319) As part of #40177 we have added top-level pipeline aggs to `InternalAggregations`. Given that `QuerySearchResult` holds an `InternalAggregations` instance, there is no need to keep on setting top-level pipeline aggs separately. Top-level pipeline aggs can then always be transported through `InternalAggregations`. Such change is made in a backwards compatible manner. --- build.gradle | 4 +- .../action/search/SearchPhaseController.java | 2 +- .../search/aggregations/AggregationPhase.java | 3 +- .../aggregations/InternalAggregations.java | 17 +-- .../search/query/QuerySearchResult.java | 37 ++++--- .../InternalAggregationsTests.java | 21 ++-- .../search/query/QuerySearchResultTests.java | 102 ++++++++++++++++++ 7 files changed, 145 insertions(+), 41 deletions(-) create mode 100644 server/src/test/java/org/elasticsearch/search/query/QuerySearchResultTests.java diff --git a/build.gradle b/build.gradle index 80ed642369ace..d452170b5fb90 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ task verifyVersions { * after the backport of the backcompat code is complete. */ -boolean bwc_tests_enabled = true -final String bwc_tests_disabled_issue = "" /* place a PR link here when committing bwc changes */ +boolean bwc_tests_enabled = false +final String bwc_tests_disabled_issue = "https://github.com/elastic/elasticsearch/pull/40319" /* place a PR link here when committing bwc changes */ if (bwc_tests_enabled == false) { if (bwc_tests_disabled_issue.isEmpty()) { throw new GradleException("bwc_tests_disabled_issue must be set when bwc_tests_enabled == false") diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchPhaseController.java b/server/src/main/java/org/elasticsearch/action/search/SearchPhaseController.java index f54f101041d1b..0125084c37099 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchPhaseController.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchPhaseController.java @@ -486,7 +486,7 @@ private ReducedQueryPhase reducedQueryPhase(Collection pipelineAggregators = context.aggregations().factories().createPipelineAggregators(); List siblingPipelineAggregators = new ArrayList<>(pipelineAggregators.size()); for (PipelineAggregator pipelineAggregator : pipelineAggregators) { @@ -144,7 +143,7 @@ public void execute(SearchContext context) { + "allowed at the top level"); } } - context.queryResult().pipelineAggregators(siblingPipelineAggregators); + context.queryResult().aggregations(new InternalAggregations(aggregations, siblingPipelineAggregators)); // disable aggregations so that they don't run on next pages in case of scrolling context.aggregations(null); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java b/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java index 187f5e3864ed1..8910ca25c337d 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregations.java @@ -77,7 +77,7 @@ public InternalAggregations(List aggregations, List getTopLevelPipelineAggregators() { + public List getTopLevelPipelineAggregators() { return topLevelPipelineAggregators; } @@ -91,20 +91,7 @@ public static InternalAggregations reduce(List aggregation if (aggregationsList.isEmpty()) { return null; } - InternalAggregations first = aggregationsList.get(0); - return reduce(aggregationsList, first.topLevelPipelineAggregators, context); - } - - /** - * Reduces the given list of aggregations as well as the provided top-level pipeline aggregators. - * Note that top-level pipeline aggregators are reduced only as part of the final reduction phase, otherwise they are left untouched. - */ - public static InternalAggregations reduce(List aggregationsList, - List topLevelPipelineAggregators, - ReduceContext context) { - if (aggregationsList.isEmpty()) { - return null; - } + List topLevelPipelineAggregators = aggregationsList.get(0).getTopLevelPipelineAggregators(); // first we collect all aggregations of the same type and list them together Map> aggByName = new HashMap<>(); diff --git a/server/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java b/server/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java index 34d3508f6bab5..9f9a2c2680a1f 100644 --- a/server/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java +++ b/server/src/main/java/org/elasticsearch/search/query/QuerySearchResult.java @@ -21,6 +21,7 @@ import org.apache.lucene.search.FieldDoc; import org.apache.lucene.search.TotalHits; +import org.elasticsearch.Version; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore; @@ -28,6 +29,7 @@ import org.elasticsearch.search.SearchPhaseResult; import org.elasticsearch.search.SearchShardTarget; import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.aggregations.pipeline.SiblingPipelineAggregator; @@ -37,7 +39,6 @@ import java.io.IOException; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.stream.Collectors; import static org.elasticsearch.common.lucene.Lucene.readTopDocs; @@ -54,7 +55,6 @@ public final class QuerySearchResult extends SearchPhaseResult { private DocValueFormat[] sortValueFormats; private InternalAggregations aggregations; private boolean hasAggs; - private List pipelineAggregators = Collections.emptyList(); private Suggest suggest; private boolean searchTimedOut; private Boolean terminatedEarly = null; @@ -198,14 +198,6 @@ public void profileResults(ProfileShardResult shardResults) { hasProfileResults = shardResults != null; } - public List pipelineAggregators() { - return pipelineAggregators; - } - - public void pipelineAggregators(List pipelineAggregators) { - this.pipelineAggregators = Objects.requireNonNull(pipelineAggregators); - } - public Suggest suggest() { return suggest; } @@ -294,8 +286,18 @@ public void readFromWithId(long id, StreamInput in) throws IOException { if (hasAggs = in.readBoolean()) { aggregations = InternalAggregations.readAggregations(in); } - pipelineAggregators = in.readNamedWriteableList(PipelineAggregator.class).stream().map(a -> (SiblingPipelineAggregator) a) - .collect(Collectors.toList()); + if (in.getVersion().before(Version.V_7_1_0)) { + List pipelineAggregators = in.readNamedWriteableList(PipelineAggregator.class).stream() + .map(a -> (SiblingPipelineAggregator) a).collect(Collectors.toList()); + if (hasAggs && pipelineAggregators.isEmpty() == false) { + List internalAggs = aggregations.asList().stream() + .map(agg -> (InternalAggregation) agg).collect(Collectors.toList()); + //Earlier versions serialize sibling pipeline aggs separately as they used to be set to QuerySearchResult directly, while + //later versions include them in InternalAggregations. Note that despite serializing sibling pipeline aggs as part of + //InternalAggregations is supported since 6.7.0, the shards set sibling pipeline aggs to InternalAggregations only from 7.1. + this.aggregations = new InternalAggregations(internalAggs, pipelineAggregators); + } + } if (in.readBoolean()) { suggest = new Suggest(in); } @@ -332,7 +334,16 @@ public void writeToNoId(StreamOutput out) throws IOException { out.writeBoolean(true); aggregations.writeTo(out); } - out.writeNamedWriteableList(pipelineAggregators); + if (out.getVersion().before(Version.V_7_1_0)) { + //Earlier versions expect sibling pipeline aggs separately as they used to be set to QuerySearchResult directly, + //while later versions expect them in InternalAggregations. Note that despite serializing sibling pipeline aggs as part of + //InternalAggregations is supported since 6.7.0, the shards set sibling pipeline aggs to InternalAggregations only from 7.1 on. + if (aggregations == null) { + out.writeNamedWriteableList(Collections.emptyList()); + } else { + out.writeNamedWriteableList(aggregations.getTopLevelPipelineAggregators()); + } + } if (suggest == null) { out.writeBoolean(false); } else { diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationsTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationsTests.java index 81626459db4f2..aa244ff7a320b 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationsTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationsTests.java @@ -50,18 +50,19 @@ public class InternalAggregationsTests extends ESTestCase { public void testReduceEmptyAggs() { List aggs = Collections.emptyList(); InternalAggregation.ReduceContext reduceContext = new InternalAggregation.ReduceContext(null, null, randomBoolean()); - assertNull(InternalAggregations.reduce(aggs, Collections.emptyList(), reduceContext)); + assertNull(InternalAggregations.reduce(aggs, reduceContext)); } public void testNonFinalReduceTopLevelPipelineAggs() { InternalAggregation terms = new StringTerms("name", BucketOrder.key(true), 10, 1, Collections.emptyList(), Collections.emptyMap(), DocValueFormat.RAW, 25, false, 10, Collections.emptyList(), 0); - List aggs = Collections.singletonList(new InternalAggregations(Collections.singletonList(terms))); List topLevelPipelineAggs = new ArrayList<>(); MaxBucketPipelineAggregationBuilder maxBucketPipelineAggregationBuilder = new MaxBucketPipelineAggregationBuilder("test", "test"); topLevelPipelineAggs.add((SiblingPipelineAggregator)maxBucketPipelineAggregationBuilder.create()); + List aggs = Collections.singletonList(new InternalAggregations(Collections.singletonList(terms), + topLevelPipelineAggs)); InternalAggregation.ReduceContext reduceContext = new InternalAggregation.ReduceContext(null, null, false); - InternalAggregations reducedAggs = InternalAggregations.reduce(aggs, topLevelPipelineAggs, reduceContext); + InternalAggregations reducedAggs = InternalAggregations.reduce(aggs, reduceContext); assertEquals(1, reducedAggs.getTopLevelPipelineAggregators().size()); assertEquals(1, reducedAggs.aggregations.size()); } @@ -79,15 +80,15 @@ public void testFinalReduceTopLevelPipelineAggs() { Collections.singletonList(siblingPipelineAggregator)); reducedAggs = InternalAggregations.reduce(Collections.singletonList(aggs), reduceContext); } else { - InternalAggregations aggs = new InternalAggregations(Collections.singletonList(terms)); - List topLevelPipelineAggs = Collections.singletonList(siblingPipelineAggregator); - reducedAggs = InternalAggregations.reduce(Collections.singletonList(aggs), topLevelPipelineAggs, reduceContext); + InternalAggregations aggs = new InternalAggregations(Collections.singletonList(terms), + Collections.singletonList(siblingPipelineAggregator)); + reducedAggs = InternalAggregations.reduce(Collections.singletonList(aggs), reduceContext); } assertEquals(0, reducedAggs.getTopLevelPipelineAggregators().size()); assertEquals(2, reducedAggs.aggregations.size()); } - public void testSerialization() throws Exception { + public static InternalAggregations createTestInstance() throws Exception { List aggsList = new ArrayList<>(); if (randomBoolean()) { StringTermsTests stringTermsTests = new StringTermsTests(); @@ -116,7 +117,11 @@ public void testSerialization() throws Exception { topLevelPipelineAggs.add((SiblingPipelineAggregator)new SumBucketPipelineAggregationBuilder("name3", "bucket3").create()); } } - InternalAggregations aggregations = new InternalAggregations(aggsList, topLevelPipelineAggs); + return new InternalAggregations(aggsList, topLevelPipelineAggs); + } + + public void testSerialization() throws Exception { + InternalAggregations aggregations = createTestInstance(); writeToAndReadFrom(aggregations, 0); } diff --git a/server/src/test/java/org/elasticsearch/search/query/QuerySearchResultTests.java b/server/src/test/java/org/elasticsearch/search/query/QuerySearchResultTests.java new file mode 100644 index 0000000000000..64712b3e417a0 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/search/query/QuerySearchResultTests.java @@ -0,0 +1,102 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.query; + +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.TotalHits; +import org.elasticsearch.Version; +import org.elasticsearch.action.OriginalIndices; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.SearchShardTarget; +import org.elasticsearch.search.aggregations.Aggregations; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.InternalAggregationsTests; +import org.elasticsearch.search.aggregations.pipeline.SiblingPipelineAggregator; +import org.elasticsearch.search.suggest.SuggestTests; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.VersionUtils; + +import java.util.List; + +import static java.util.Collections.emptyList; + +public class QuerySearchResultTests extends ESTestCase { + + private final NamedWriteableRegistry namedWriteableRegistry; + + public QuerySearchResultTests() { + SearchModule searchModule = new SearchModule(Settings.EMPTY, false, emptyList()); + this.namedWriteableRegistry = new NamedWriteableRegistry(searchModule.getNamedWriteables()); + } + + private static QuerySearchResult createTestInstance() throws Exception { + ShardId shardId = new ShardId("index", "uuid", randomInt()); + QuerySearchResult result = new QuerySearchResult(randomLong(), new SearchShardTarget("node", shardId, null, OriginalIndices.NONE)); + if (randomBoolean()) { + result.terminatedEarly(randomBoolean()); + } + TopDocs topDocs = new TopDocs(new TotalHits(randomLongBetween(0, Long.MAX_VALUE), TotalHits.Relation.EQUAL_TO), new ScoreDoc[0]); + result.topDocs(new TopDocsAndMaxScore(topDocs, randomBoolean() ? Float.NaN : randomFloat()), new DocValueFormat[0]); + result.size(randomInt()); + result.from(randomInt()); + if (randomBoolean()) { + result.suggest(SuggestTests.createTestItem()); + } + if (randomBoolean()) { + result.aggregations(InternalAggregationsTests.createTestInstance()); + } + return result; + } + + public void testSerialization() throws Exception { + QuerySearchResult querySearchResult = createTestInstance(); + Version version = VersionUtils.randomVersion(random()); + QuerySearchResult deserialized = copyStreamable(querySearchResult, namedWriteableRegistry, QuerySearchResult::new, version); + assertEquals(querySearchResult.getRequestId(), deserialized.getRequestId()); + assertNull(deserialized.getSearchShardTarget()); + assertEquals(querySearchResult.topDocs().maxScore, deserialized.topDocs().maxScore, 0f); + assertEquals(querySearchResult.topDocs().topDocs.totalHits, deserialized.topDocs().topDocs.totalHits); + assertEquals(querySearchResult.from(), deserialized.from()); + assertEquals(querySearchResult.size(), deserialized.size()); + assertEquals(querySearchResult.hasAggs(), deserialized.hasAggs()); + if (deserialized.hasAggs()) { + Aggregations aggs = querySearchResult.consumeAggs(); + Aggregations deserializedAggs = deserialized.consumeAggs(); + assertEquals(aggs.asList(), deserializedAggs.asList()); + List pipelineAggs = ((InternalAggregations) aggs).getTopLevelPipelineAggregators(); + List deserializedPipelineAggs = + ((InternalAggregations) deserializedAggs).getTopLevelPipelineAggregators(); + assertEquals(pipelineAggs.size(), deserializedPipelineAggs.size()); + for (int i = 0; i < pipelineAggs.size(); i++) { + SiblingPipelineAggregator pipelineAgg = pipelineAggs.get(i); + SiblingPipelineAggregator deserializedPipelineAgg = deserializedPipelineAggs.get(i); + assertArrayEquals(pipelineAgg.bucketsPaths(), deserializedPipelineAgg.bucketsPaths()); + assertEquals(pipelineAgg.name(), deserializedPipelineAgg.name()); + } + } + assertEquals(querySearchResult.terminatedEarly(), deserialized.terminatedEarly()); + } +} From 5ceef9e2b9bf44d1d6bdc3d75a49dd60cec69cd2 Mon Sep 17 00:00:00 2001 From: Przemyslaw Gomulka Date: Thu, 28 Mar 2019 13:07:55 +0100 Subject: [PATCH 46/77] Migrate systemd packaging tests from bats to java (#39954) * initial work * further testing * migrated * fix build * test * passing test with systemd * fix compilation * failing new test * all tests passing systemd * testcases in package test * passing all tests for systemd * limits are applied for systemd only * code review followup * is enabled return false * missing assertions * json log name --- .../packaging/test/PackageTestCase.java | 207 +++++++++++++- .../packaging/util/FileUtils.java | 27 ++ .../packaging/util/Packages.java | 20 +- .../elasticsearch/packaging/util/Shell.java | 5 + .../resources/packaging/tests/60_systemd.bats | 257 ------------------ 5 files changed, 242 insertions(+), 274 deletions(-) delete mode 100644 qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats diff --git a/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/PackageTestCase.java b/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/PackageTestCase.java index e306e7c63ce5a..458359b299e75 100644 --- a/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/PackageTestCase.java +++ b/qa/vagrant/src/main/java/org/elasticsearch/packaging/test/PackageTestCase.java @@ -20,31 +20,45 @@ package org.elasticsearch.packaging.test; import com.carrotsearch.randomizedtesting.annotations.TestCaseOrdering; +import org.apache.http.client.fluent.Request; +import org.elasticsearch.packaging.util.FileUtils; import org.elasticsearch.packaging.util.Shell; import org.elasticsearch.packaging.util.Shell.Result; +import org.hamcrest.CoreMatchers; import org.junit.Before; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.elasticsearch.packaging.util.FileUtils.append; import static org.elasticsearch.packaging.util.FileUtils.assertPathsDontExist; +import static org.elasticsearch.packaging.util.FileUtils.assertPathsExist; +import static org.elasticsearch.packaging.util.FileUtils.cp; +import static org.elasticsearch.packaging.util.FileUtils.fileWithGlobExist; +import static org.elasticsearch.packaging.util.FileUtils.mkdir; import static org.elasticsearch.packaging.util.FileUtils.mv; +import static org.elasticsearch.packaging.util.FileUtils.rm; +import static org.elasticsearch.packaging.util.FileUtils.slurp; import static org.elasticsearch.packaging.util.Packages.SYSTEMD_SERVICE; import static org.elasticsearch.packaging.util.Packages.assertInstalled; import static org.elasticsearch.packaging.util.Packages.assertRemoved; import static org.elasticsearch.packaging.util.Packages.install; import static org.elasticsearch.packaging.util.Packages.remove; +import static org.elasticsearch.packaging.util.Packages.restartElasticsearch; import static org.elasticsearch.packaging.util.Packages.startElasticsearch; import static org.elasticsearch.packaging.util.Packages.stopElasticsearch; import static org.elasticsearch.packaging.util.Packages.verifyPackageInstallation; import static org.elasticsearch.packaging.util.Platforms.getOsRelease; import static org.elasticsearch.packaging.util.Platforms.isSystemd; +import static org.elasticsearch.packaging.util.ServerUtils.makeRequest; import static org.elasticsearch.packaging.util.ServerUtils.runElasticsearchTests; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.Matchers.containsString; @@ -55,42 +69,50 @@ @TestCaseOrdering(TestCaseOrdering.AlphabeticOrder.class) public abstract class PackageTestCase extends PackagingTestCase { + private Shell sh; @Before public void onlyCompatibleDistributions() { assumeTrue("only compatible distributions", distribution().packaging.compatible); + sh = newShell(); } public void test10InstallPackage() throws IOException { assertRemoved(distribution()); installation = install(distribution()); assertInstalled(distribution()); - verifyPackageInstallation(installation, distribution(), newShell()); + verifyPackageInstallation(installation, distribution(), sh); } public void test20PluginsCommandWhenNoPlugins() { assumeThat(installation, is(notNullValue())); - assertThat(newShell().run(installation.bin("elasticsearch-plugin") + " list").stdout, isEmptyString()); + assertThat(sh.run(installation.bin("elasticsearch-plugin") + " list").stdout, isEmptyString()); } - public void test30InstallDoesNotStartServer() { + public void test30DaemonIsNotEnabledOnRestart() { + if (isSystemd()) { + sh.run("systemctl daemon-reload"); + String isEnabledOutput = sh.runIgnoreExitCode("systemctl is-enabled elasticsearch.service").stdout.trim(); + assertThat(isEnabledOutput, equalTo("disabled")); + } + } + + public void test31InstallDoesNotStartServer() { assumeThat(installation, is(notNullValue())); - assertThat(newShell().run("ps aux").stdout, not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); + assertThat(sh.run("ps aux").stdout, not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); } public void assertRunsWithJavaHome() throws IOException { - Shell sh = newShell(); - String systemJavaHome = sh.run("echo $SYSTEM_JAVA_HOME").stdout.trim(); byte[] originalEnvFile = Files.readAllBytes(installation.envFile); try { Files.write(installation.envFile, ("JAVA_HOME=" + systemJavaHome + "\n").getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND); - startElasticsearch(); + startElasticsearch(sh); runElasticsearchTests(); - stopElasticsearch(); + stopElasticsearch(sh); } finally { Files.write(installation.envFile, originalEnvFile); } @@ -99,7 +121,7 @@ public void assertRunsWithJavaHome() throws IOException { assertThat(new String(Files.readAllBytes(log), StandardCharsets.UTF_8), containsString(systemJavaHome)); } - public void test31JavaHomeOverride() throws IOException { + public void test32JavaHomeOverride() throws IOException { assumeThat(installation, is(notNullValue())); // we always run with java home when no bundled jdk is included, so this test would be repetitive assumeThat(distribution().hasJdk, is(true)); @@ -121,11 +143,20 @@ public void test42BundledJdkRemoved() throws IOException { } public void test40StartServer() throws IOException { + String start = sh.runIgnoreExitCode("date ").stdout.trim(); assumeThat(installation, is(notNullValue())); - startElasticsearch(); + startElasticsearch(sh); + + String journalEntries = sh.runIgnoreExitCode("journalctl _SYSTEMD_UNIT=elasticsearch.service " + + "--since \"" + start + "\" --output cat | wc -l").stdout.trim(); + assertThat(journalEntries, equalTo("0")); + + assertPathsExist(installation.pidDir.resolve("elasticsearch.pid")); + assertPathsExist(installation.logs.resolve("elasticsearch_server.json")); + runElasticsearchTests(); - verifyPackageInstallation(installation, distribution(), newShell()); // check startup script didn't change permissions + verifyPackageInstallation(installation, distribution(), sh); // check startup script didn't change permissions } public void test50Remove() { @@ -134,7 +165,6 @@ public void test50Remove() { remove(distribution()); // removing must stop the service - final Shell sh = newShell(); assertThat(sh.run("ps aux").stdout, not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); if (isSystemd()) { @@ -184,9 +214,160 @@ public void test60Reinstall() throws IOException { installation = install(distribution()); assertInstalled(distribution()); - verifyPackageInstallation(installation, distribution(), newShell()); + verifyPackageInstallation(installation, distribution(), sh); remove(distribution()); assertRemoved(distribution()); } + + public void test70RestartServer() throws IOException { + try { + installation = install(distribution()); + assertInstalled(distribution()); + + startElasticsearch(sh); + restartElasticsearch(sh); + runElasticsearchTests(); + stopElasticsearch(sh); + } finally { + cleanup(); + } + } + + + public void test72TestRuntimeDirectory() throws IOException { + try { + installation = install(distribution()); + FileUtils.rm(installation.pidDir); + startElasticsearch(sh); + assertPathsExist(installation.pidDir); + stopElasticsearch(sh); + } finally { + cleanup(); + } + } + + public void test73gcLogsExist() throws IOException { + installation = install(distribution()); + startElasticsearch(sh); + // it can be gc.log or gc.log.0.current + assertThat(installation.logs, fileWithGlobExist("gc.log*")); + stopElasticsearch(sh); + } + + // TEST CASES FOR SYSTEMD ONLY + + + /** + * # Simulates the behavior of a system restart: + * # the PID directory is deleted by the operating system + * # but it should not block ES from starting + * # see https://github.com/elastic/elasticsearch/issues/11594 + */ + public void test80DeletePID_DIRandRestart() throws IOException { + assumeTrue(isSystemd()); + + rm(installation.pidDir); + + sh.run("systemd-tmpfiles --create"); + + startElasticsearch(sh); + + final Path pidFile = installation.pidDir.resolve("elasticsearch.pid"); + + assertTrue(Files.exists(pidFile)); + + stopElasticsearch(sh); + } + + public void test81CustomPathConfAndJvmOptions() throws IOException { + assumeTrue(isSystemd()); + + assumeThat(installation, is(notNullValue())); + assertPathsExist(installation.envFile); + + stopElasticsearch(sh); + + // The custom config directory is not under /tmp or /var/tmp because + // systemd's private temp directory functionally means different + // processes can have different views of what's in these directories + String temp = sh.runIgnoreExitCode("mktemp -p /etc -d").stdout.trim(); + final Path tempConf = Paths.get(temp); + + try { + mkdir(tempConf); + cp(installation.config("elasticsearch.yml"), tempConf.resolve("elasticsearch.yml")); + cp(installation.config("log4j2.properties"), tempConf.resolve("log4j2.properties")); + + // we have to disable Log4j from using JMX lest it will hit a security + // manager exception before we have configured logging; this will fail + // startup since we detect usages of logging before it is configured + final String jvmOptions = + "-Xms512m\n" + + "-Xmx512m\n" + + "-Dlog4j2.disable.jmx=true\n"; + append(tempConf.resolve("jvm.options"), jvmOptions); + + sh.runIgnoreExitCode("chown -R elasticsearch:elasticsearch " + tempConf); + + final Shell serverShell = newShell(); + cp(installation.envFile, tempConf.resolve("elasticsearch.bk"));//backup + append(installation.envFile, "ES_PATH_CONF=" + tempConf + "\n"); + append(installation.envFile, "ES_JAVA_OPTS=-XX:-UseCompressedOops"); + + startElasticsearch(serverShell); + + final String nodesResponse = makeRequest(Request.Get("http://localhost:9200/_nodes")); + assertThat(nodesResponse, CoreMatchers.containsString("\"heap_init_in_bytes\":536870912")); + assertThat(nodesResponse, CoreMatchers.containsString("\"using_compressed_ordinary_object_pointers\":\"false\"")); + + stopElasticsearch(serverShell); + + } finally { + rm(installation.envFile); + cp(tempConf.resolve("elasticsearch.bk"), installation.envFile); + rm(tempConf); + cleanup(); + } + } + + public void test82SystemdMask() throws IOException { + try { + assumeTrue(isSystemd()); + + sh.run("systemctl mask systemd-sysctl.service"); + + installation = install(distribution()); + + sh.run("systemctl unmask systemd-sysctl.service"); + } finally { + cleanup(); + } + } + + public void test83serviceFileSetsLimits() throws IOException { + // Limits are changed on systemd platforms only + assumeTrue(isSystemd()); + + installation = install(distribution()); + + startElasticsearch(sh); + + final Path pidFile = installation.pidDir.resolve("elasticsearch.pid"); + assertTrue(Files.exists(pidFile)); + String pid = slurp(pidFile).trim(); + String maxFileSize = sh.run("cat /proc/%s/limits | grep \"Max file size\" | awk '{ print $4 }'", pid).stdout.trim(); + assertThat(maxFileSize, equalTo("unlimited")); + + String maxProcesses = sh.run("cat /proc/%s/limits | grep \"Max processes\" | awk '{ print $3 }'", pid).stdout.trim(); + assertThat(maxProcesses, equalTo("4096")); + + String maxOpenFiles = sh.run("cat /proc/%s/limits | grep \"Max open files\" | awk '{ print $4 }'", pid).stdout.trim(); + assertThat(maxOpenFiles, equalTo("65535")); + + String maxAddressSpace = sh.run("cat /proc/%s/limits | grep \"Max address space\" | awk '{ print $4 }'", pid).stdout.trim(); + assertThat(maxAddressSpace, equalTo("unlimited")); + + stopElasticsearch(sh); + } } diff --git a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/FileUtils.java b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/FileUtils.java index 10d1b3ee6b6de..efbf0bd74a354 100644 --- a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/FileUtils.java +++ b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/FileUtils.java @@ -20,6 +20,8 @@ package org.elasticsearch.packaging.util; import org.elasticsearch.core.internal.io.IOUtils; +import org.hamcrest.FeatureMatcher; +import org.hamcrest.Matcher; import java.io.BufferedWriter; import java.io.IOException; @@ -34,9 +36,11 @@ import java.nio.file.attribute.PosixFileAttributes; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.emptyIterable; import static org.hamcrest.core.IsNot.not; import static org.hamcrest.text.IsEmptyString.isEmptyOrNullString; import static org.junit.Assert.assertFalse; @@ -69,6 +73,15 @@ public static void rm(Path... paths) { } } + public static Path mktempDir(Path path) { + try { + return Files.createTempDirectory(path,"tmp"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Path mkdir(Path path) { try { return Files.createDirectories(path); @@ -176,6 +189,20 @@ public static void assertPathsExist(Path... paths) { Arrays.stream(paths).forEach(path -> assertTrue(path + " should exist", Files.exists(path))); } + public static Matcher fileWithGlobExist(String glob) throws IOException { + return new FeatureMatcher>(not(emptyIterable()),"File with pattern exist", "file with pattern"){ + + @Override + protected Iterable featureValueOf(Path actual) { + try { + return Files.newDirectoryStream(actual,glob); + } catch (IOException e) { + return Collections.emptyList(); + } + } + }; + } + public static void assertPathsDontExist(Path... paths) { Arrays.stream(paths).forEach(path -> assertFalse(path + " should not exist", Files.exists(path))); } diff --git a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Packages.java b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Packages.java index 7014a627a7aee..afa7e371c2c55 100644 --- a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Packages.java +++ b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Packages.java @@ -270,8 +270,7 @@ private static void verifyDefaultInstallation(Installation es) { ).forEach(configFile -> assertThat(es.config(configFile), file(File, "root", "elasticsearch", p660))); } - public static void startElasticsearch() throws IOException { - final Shell sh = new Shell(); + public static void startElasticsearch(Shell sh) throws IOException { if (isSystemd()) { sh.run("systemctl daemon-reload"); sh.run("systemctl enable elasticsearch.service"); @@ -281,6 +280,10 @@ public static void startElasticsearch() throws IOException { sh.run("service elasticsearch start"); } + assertElasticsearchStarted(sh); + } + + public static void assertElasticsearchStarted(Shell sh) throws IOException { waitForElasticsearch(); if (isSystemd()) { @@ -291,12 +294,21 @@ public static void startElasticsearch() throws IOException { } } - public static void stopElasticsearch() throws IOException { - final Shell sh = new Shell(); + public static void stopElasticsearch(Shell sh) throws IOException { if (isSystemd()) { sh.run("systemctl stop elasticsearch.service"); } else { sh.run("service elasticsearch stop"); } } + + public static void restartElasticsearch(Shell sh) throws IOException { + if (isSystemd()) { + sh.run("systemctl restart elasticsearch.service"); + } else { + sh.run("service elasticsearch restart"); + } + + waitForElasticsearch(); + } } diff --git a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Shell.java b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Shell.java index b437438130552..dc490de05b9c8 100644 --- a/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Shell.java +++ b/qa/vagrant/src/main/java/org/elasticsearch/packaging/util/Shell.java @@ -27,6 +27,7 @@ import java.io.InputStreamReader; import java.nio.file.Path; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.stream.Stream; @@ -67,6 +68,10 @@ public Result runIgnoreExitCode(String script) { return runScriptIgnoreExitCode(getScriptCommand(script)); } + public Result run( String command, Object... args) { + String formattedCommand = String.format(Locale.ROOT, command, args); + return run(formattedCommand); + } private String[] getScriptCommand(String script) { if (Platforms.WINDOWS) { return powershellCommand(script); diff --git a/qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats b/qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats deleted file mode 100644 index 8baa75f38f5bc..0000000000000 --- a/qa/vagrant/src/test/resources/packaging/tests/60_systemd.bats +++ /dev/null @@ -1,257 +0,0 @@ -#!/usr/bin/env bats - -# This file is used to test the elasticsearch Systemd setup. - -# WARNING: This testing file must be executed as root and can -# dramatically change your system. It should only be executed -# in a throw-away VM like those made by the Vagrantfile at -# the root of the Elasticsearch source code. This should -# cause the script to fail if it is executed any other way: -[ -f /etc/is_vagrant_vm ] || { - >&2 echo "must be run on a vagrant VM" - exit 1 -} - -# The test case can be executed with the Bash Automated -# Testing System tool available at https://github.com/sstephenson/bats -# Thanks to Sam Stephenson! - -# Licensed to Elasticsearch under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Load test utilities -load $BATS_UTILS/utils.bash -load $BATS_UTILS/packages.bash -load $BATS_UTILS/plugins.bash - -# Cleans everything for the 1st execution -setup() { - skip_not_systemd - skip_not_dpkg_or_rpm - export_elasticsearch_paths -} - -@test "[SYSTEMD] install elasticsearch" { - clean_before_test - install_package -} - -@test "[SYSTEMD] daemon reload after install" { - systemctl daemon-reload -} - -@test "[SYSTEMD] daemon isn't enabled on restart" { - # Rather than restart the VM we just ask systemd if it plans on starting - # elasticsearch on restart. Not as strong as a restart but much much - # faster. - run systemctl is-enabled elasticsearch.service - [ "$output" = "disabled" ] -} - -@test "[SYSTEMD] enable" { - systemctl enable elasticsearch.service - - systemctl is-enabled elasticsearch.service -} - -@test "[SYSTEMD] start" { - # Capture the current epoch in millis - run date +%s - epoch="$output" - - # The OpenJDK packaged for CentOS and OEL both override the default value (false) for the JVM option "AssumeMP". - # - # Because it is forced to "true" by default for these packages, the following warning message is printed to the - # standard output when the Vagrant box has only 1 CPU: - # OpenJDK 64-Bit Server VM warning: If the number of processors is expected to increase from one, then you should configure - # the number of parallel GC threads appropriately using -XX:ParallelGCThreads=N - # - # This message will then fail the next test where we check if no entries have been added to the journal. - # - # This message appears since with java-1.8.0-openjdk-1.8.0.111-1.b15.el7_2.x86_64 because of the commit: - # 2016-10-10 - Andrew Hughes - 1:1.8.0.111-1.b15 - Turn debug builds on for all JIT architectures. - # Always AssumeMP on RHEL. - # - Resolves: rhbz#1381990 - # - if [ -x "$(command -v lsb_release)" ]; then - # Here we set the "-XX:-AssumeMP" option to false again: - lsb_release=$(lsb_release -i) - if [[ "$lsb_release" =~ "CentOS" ]] || [[ "$lsb_release" =~ "OracleServer" ]]; then - echo "-XX:-AssumeMP" >> $ESCONFIG/jvm.options - fi - fi - - systemctl start elasticsearch.service - wait_for_elasticsearch_status - assert_file_exist "/var/run/elasticsearch/elasticsearch.pid" - assert_file_exist "/var/log/elasticsearch/elasticsearch_server.json" - - # Converts the epoch back in a human readable format - run date --date=@$epoch "+%Y-%m-%d %H:%M:%S" - since="$output" - - # Verifies that no new entries in journald have been added - # since the last start - result="$(journalctl _SYSTEMD_UNIT=elasticsearch.service --since "$since" --output cat | wc -l)" - [ "$result" -eq "0" ] || { - echo "Expected no entries in journalctl for the Elasticsearch service but found:" - journalctl _SYSTEMD_UNIT=elasticsearch.service --since "$since" - false - } -} - -@test "[SYSTEMD] start (running)" { - systemctl start elasticsearch.service -} - -@test "[SYSTEMD] is active (running)" { - run systemctl is-active elasticsearch.service - [ "$status" -eq 0 ] - [ "$output" = "active" ] -} - -@test "[SYSTEMD] status (running)" { - systemctl status elasticsearch.service -} - -################################## -# Check that Elasticsearch is working -################################## -@test "[SYSTEMD] test elasticsearch" { - run_elasticsearch_tests -} - -@test "[SYSTEMD] restart" { - systemctl restart elasticsearch.service - - wait_for_elasticsearch_status - - service elasticsearch status -} - -@test "[SYSTEMD] stop (running)" { - systemctl stop elasticsearch.service -} - -@test "[SYSTEMD] status (stopping)" { - run systemctl status elasticsearch.service - # I'm not sure why suse exits 0 here, but it does - if [ ! -e /etc/SuSE-release ]; then - [ "$status" -eq 3 ] || "Expected exit code 3 meaning stopped but got $status" - fi - echo "$output" | grep "Active:" | grep "inactive" -} - -@test "[SYSTEMD] stop (stopped)" { - systemctl stop elasticsearch.service -} - -@test "[SYSTEMD] status (stopped)" { - run systemctl status elasticsearch.service - # I'm not sure why suse exits 0 here, but it does - if [ ! -e /etc/SuSE-release ]; then - [ "$status" -eq 3 ] || "Expected exit code 3 meaning stopped but got $status" - fi - echo "$output" | grep "Active:" | grep "inactive" -} - -# Simulates the behavior of a system restart: -# the PID directory is deleted by the operating system -# but it should not block ES from starting -# see https://github.com/elastic/elasticsearch/issues/11594 -@test "[SYSTEMD] delete PID_DIR and restart" { - rm -rf /var/run/elasticsearch - - systemd-tmpfiles --create - - systemctl start elasticsearch.service - - wait_for_elasticsearch_status - - assert_file_exist "/var/run/elasticsearch/elasticsearch.pid" - - systemctl stop elasticsearch.service -} - -@test "[SYSTEMD] start Elasticsearch with custom JVM options" { - assert_file_exist $ESENVFILE - # The custom config directory is not under /tmp or /var/tmp because - # systemd's private temp directory functionally means different - # processes can have different views of what's in these directories - local temp=`mktemp -p /etc -d` - cp "$ESCONFIG"/elasticsearch.yml "$temp" - cp "$ESCONFIG"/log4j2.properties "$temp" - touch "$temp/jvm.options" - chown -R elasticsearch:elasticsearch "$temp" - echo "-Xms512m" >> "$temp/jvm.options" - echo "-Xmx512m" >> "$temp/jvm.options" - # we have to disable Log4j from using JMX lest it will hit a security - # manager exception before we have configured logging; this will fail - # startup since we detect usages of logging before it is configured - echo "-Dlog4j2.disable.jmx=true" >> "$temp/jvm.options" - cp $ESENVFILE "$temp/elasticsearch" - echo "ES_PATH_CONF=\"$temp\"" >> $ESENVFILE - echo "ES_JAVA_OPTS=\"-XX:-UseCompressedOops\"" >> $ESENVFILE - service elasticsearch start - wait_for_elasticsearch_status - curl -s -XGET localhost:9200/_nodes | fgrep '"heap_init_in_bytes":536870912' - curl -s -XGET localhost:9200/_nodes | fgrep '"using_compressed_ordinary_object_pointers":"false"' - service elasticsearch stop - cp "$temp/elasticsearch" $ESENVFILE -} - -@test "[SYSTEMD] masking systemd-sysctl" { - clean_before_test - - systemctl mask systemd-sysctl.service - install_package - - systemctl unmask systemd-sysctl.service -} - -@test "[SYSTEMD] service file sets limits" { - clean_before_test - install_package - systemctl start elasticsearch.service - wait_for_elasticsearch_status - local pid=$(cat /var/run/elasticsearch/elasticsearch.pid) - local max_file_size=$(cat /proc/$pid/limits | grep "Max file size" | awk '{ print $4 }') - [ "$max_file_size" == "unlimited" ] - local max_processes=$(cat /proc/$pid/limits | grep "Max processes" | awk '{ print $3 }') - [ "$max_processes" == "4096" ] - local max_open_files=$(cat /proc/$pid/limits | grep "Max open files" | awk '{ print $4 }') - [ "$max_open_files" == "65535" ] - local max_address_space=$(cat /proc/$pid/limits | grep "Max address space" | awk '{ print $4 }') - [ "$max_address_space" == "unlimited" ] - systemctl stop elasticsearch.service -} - -@test "[SYSTEMD] test runtime directory" { - clean_before_test - install_package - sudo rm -rf /var/run/elasticsearch - systemctl start elasticsearch.service - wait_for_elasticsearch_status - [ -d /var/run/elasticsearch ] - systemctl stop elasticsearch.service -} - -@test "[SYSTEMD] GC logs exist" { - start_elasticsearch_service - assert_file_exist /var/log/elasticsearch/gc.log.0.current - stop_elasticsearch_service -} From d696e57e5d12cf9c48435dbd3bba9669ee0d4891 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 28 Mar 2019 12:09:31 +0000 Subject: [PATCH 47/77] Add docs for cluster.remote.*.proxy setting (#40281) In #33062 we introduced the `cluster.remote.*.proxy` setting for proxied connections to remote clusters, but left it deliberately undocumented since it needed followup work so that it could work with SNI. However, since #32517 is now closed we can add this documentation and remove the comment about its lack of documentation. --- docs/reference/modules/remote-clusters.asciidoc | 10 +++++++++- .../elasticsearch/transport/RemoteClusterAware.java | 2 -- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/reference/modules/remote-clusters.asciidoc b/docs/reference/modules/remote-clusters.asciidoc index b134a626739fa..1c51cda907cf1 100644 --- a/docs/reference/modules/remote-clusters.asciidoc +++ b/docs/reference/modules/remote-clusters.asciidoc @@ -227,7 +227,7 @@ PUT _cluster/settings clusters are kept alive. If set to `-1`, application-level ping messages to this remote cluster are not sent. If unset, application-level ping messages are sent according to the global `transport.ping_schedule` setting, which - defaults to ``-1` meaning that pings are not sent. + defaults to `-1` meaning that pings are not sent. `cluster.remote.${cluster_alias}.transport.compress`:: @@ -237,6 +237,14 @@ PUT _cluster/settings Elasticsearch compresses the response. If unset, the global `transport.compress` is used as the fallback setting. +`cluster.remote.${cluster_alias}.proxy`:: + + Sets a proxy address for the specified remote cluster. By default this is not + set, meaning that Elasticsearch will connect directly to the nodes in the + remote cluster using their <>. + If this setting is set to an IP address or hostname then Elasticsearch will + connect to the nodes in the remote cluster using this address instead. + [float] [[retrieve-remote-clusters-info]] === Retrieving remote clusters info diff --git a/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java b/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java index 1bf47d1a42f94..859a96e784c33 100644 --- a/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java +++ b/server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java @@ -145,8 +145,6 @@ public String getKey(final String key) { /** * A proxy address for the remote cluster. - * NOTE: this settings is undocumented until we have at last one transport that supports passing - * on the hostname via a mechanism like SNI. */ public static final Setting.AffixSetting REMOTE_CLUSTERS_PROXY = Setting.affixKeySetting( "cluster.remote.", From f63ac13c4e9e6f77988c9b79ffbdfbbf09fd5e52 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 28 Mar 2019 12:13:25 +0000 Subject: [PATCH 48/77] Optimise rejection of out-of-range `long` values (#40325) Today if you try and insert a very large number like `1e9999999` into a long field we first construct this number as a `BigDecimal`, convert this to a `BigInteger` and then reject it because it is out of range. Unfortunately making such a large `BigInteger` is rather expensive. We can avoid this expense by performing a (weaker) range check on the `BigDecimal` representation of incoming `long`s too. Relates #26137 Closes #40323 --- .../support/AbstractXContentParser.java | 16 ++++++++++++--- .../org/elasticsearch/common/Numbers.java | 8 ++++++++ .../elasticsearch/common/NumbersTests.java | 20 +++++++++++++------ .../index/mapper/NumberFieldMapperTests.java | 8 ++++++++ 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/support/AbstractXContentParser.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/support/AbstractXContentParser.java index 51bb5c3c65f6d..fa6ffdd0407f9 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/support/AbstractXContentParser.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/support/AbstractXContentParser.java @@ -151,6 +151,12 @@ public int intValue(boolean coerce) throws IOException { protected abstract int doIntValue() throws IOException; + private static BigInteger LONG_MAX_VALUE_AS_BIGINTEGER = BigInteger.valueOf(Long.MAX_VALUE); + private static BigInteger LONG_MIN_VALUE_AS_BIGINTEGER = BigInteger.valueOf(Long.MIN_VALUE); + // weak bounds on the BigDecimal representation to allow for coercion + private static BigDecimal BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.ONE); + private static BigDecimal BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE).subtract(BigDecimal.ONE); + /** Return the long that {@code stringValue} stores or throws an exception if the * stored value cannot be converted to a long that stores the exact same * value and {@code coerce} is false. */ @@ -163,7 +169,11 @@ private static long toLong(String stringValue, boolean coerce) { final BigInteger bigIntegerValue; try { - BigDecimal bigDecimalValue = new BigDecimal(stringValue); + final BigDecimal bigDecimalValue = new BigDecimal(stringValue); + if (bigDecimalValue.compareTo(BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE) >= 0 || + bigDecimalValue.compareTo(BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE) <= 0) { + throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for a long"); + } bigIntegerValue = coerce ? bigDecimalValue.toBigInteger() : bigDecimalValue.toBigIntegerExact(); } catch (ArithmeticException e) { throw new IllegalArgumentException("Value [" + stringValue + "] has a decimal part"); @@ -171,11 +181,11 @@ private static long toLong(String stringValue, boolean coerce) { throw new IllegalArgumentException("For input string: \"" + stringValue + "\""); } - if (bigIntegerValue.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 || - bigIntegerValue.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0) { + if (bigIntegerValue.compareTo(LONG_MAX_VALUE_AS_BIGINTEGER) > 0 || bigIntegerValue.compareTo(LONG_MIN_VALUE_AS_BIGINTEGER) < 0) { throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for a long"); } + assert bigIntegerValue.longValueExact() <= Long.MAX_VALUE; // asserting that no ArithmeticException is thrown return bigIntegerValue.longValue(); } diff --git a/server/src/main/java/org/elasticsearch/common/Numbers.java b/server/src/main/java/org/elasticsearch/common/Numbers.java index 27c1dd18e97b8..51aecb5e19c9c 100644 --- a/server/src/main/java/org/elasticsearch/common/Numbers.java +++ b/server/src/main/java/org/elasticsearch/common/Numbers.java @@ -125,6 +125,10 @@ public static long toLongExact(Number n) { } } + // weak bounds on the BigDecimal representation to allow for coercion + private static BigDecimal BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE = BigDecimal.valueOf(Long.MAX_VALUE).add(BigDecimal.ONE); + private static BigDecimal BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE).subtract(BigDecimal.ONE); + /** Return the long that {@code stringValue} stores or throws an exception if the * stored value cannot be converted to a long that stores the exact same * value and {@code coerce} is false. */ @@ -138,6 +142,10 @@ public static long toLong(String stringValue, boolean coerce) { final BigInteger bigIntegerValue; try { BigDecimal bigDecimalValue = new BigDecimal(stringValue); + if (bigDecimalValue.compareTo(BIGDECIMAL_GREATER_THAN_LONG_MAX_VALUE) >= 0 || + bigDecimalValue.compareTo(BIGDECIMAL_LESS_THAN_LONG_MIN_VALUE) <= 0) { + throw new IllegalArgumentException("Value [" + stringValue + "] is out of range for a long"); + } bigIntegerValue = coerce ? bigDecimalValue.toBigInteger() : bigDecimalValue.toBigIntegerExact(); } catch (ArithmeticException e) { throw new IllegalArgumentException("Value [" + stringValue + "] has a decimal part"); diff --git a/server/src/test/java/org/elasticsearch/common/NumbersTests.java b/server/src/test/java/org/elasticsearch/common/NumbersTests.java index 46378ccc9e9fb..4cab3206b7fd0 100644 --- a/server/src/test/java/org/elasticsearch/common/NumbersTests.java +++ b/server/src/test/java/org/elasticsearch/common/NumbersTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.common; +import com.carrotsearch.randomizedtesting.annotations.Timeout; import org.elasticsearch.test.ESTestCase; import java.math.BigDecimal; @@ -27,19 +28,26 @@ public class NumbersTests extends ESTestCase { + @Timeout(millis = 10000) public void testToLong() { assertEquals(3L, Numbers.toLong("3", false)); assertEquals(3L, Numbers.toLong("3.1", true)); assertEquals(9223372036854775807L, Numbers.toLong("9223372036854775807.00", false)); assertEquals(-9223372036854775808L, Numbers.toLong("-9223372036854775808.00", false)); + assertEquals(9223372036854775807L, Numbers.toLong("9223372036854775807.00", true)); + assertEquals(-9223372036854775808L, Numbers.toLong("-9223372036854775808.00", true)); + assertEquals(9223372036854775807L, Numbers.toLong("9223372036854775807.99", true)); + assertEquals(-9223372036854775808L, Numbers.toLong("-9223372036854775808.99", true)); - IllegalArgumentException e = expectThrows(IllegalArgumentException.class, - () -> Numbers.toLong("9223372036854775808", false)); - assertEquals("Value [9223372036854775808] is out of range for a long", e.getMessage()); + assertEquals("Value [9223372036854775808] is out of range for a long", expectThrows(IllegalArgumentException.class, + () -> Numbers.toLong("9223372036854775808", false)).getMessage()); + assertEquals("Value [-9223372036854775809] is out of range for a long", expectThrows(IllegalArgumentException.class, + () -> Numbers.toLong("-9223372036854775809", false)).getMessage()); - e = expectThrows(IllegalArgumentException.class, - () -> Numbers.toLong("-9223372036854775809", false)); - assertEquals("Value [-9223372036854775809] is out of range for a long", e.getMessage()); + assertEquals("Value [1e99999999] is out of range for a long", expectThrows(IllegalArgumentException.class, + () -> Numbers.toLong("1e99999999", false)).getMessage()); + assertEquals("Value [-1e99999999] is out of range for a long", expectThrows(IllegalArgumentException.class, + () -> Numbers.toLong("-1e99999999", false)).getMessage()); } public void testToLongExact() { diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java index ba7f5d846840a..b4b9242daa456 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NumberFieldMapperTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.mapper; +import com.carrotsearch.randomizedtesting.annotations.Timeout; import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexableField; import org.elasticsearch.common.Strings; @@ -367,17 +368,20 @@ public void testEmptyName() throws IOException { } } + @Timeout(millis = 30000) public void testOutOfRangeValues() throws IOException { final List> inputs = Arrays.asList( OutOfRangeSpec.of(NumberType.BYTE, "128", "is out of range for a byte"), OutOfRangeSpec.of(NumberType.SHORT, "32768", "is out of range for a short"), OutOfRangeSpec.of(NumberType.INTEGER, "2147483648", "is out of range for an integer"), OutOfRangeSpec.of(NumberType.LONG, "9223372036854775808", "out of range for a long"), + OutOfRangeSpec.of(NumberType.LONG, "1e999999999", "out of range for a long"), OutOfRangeSpec.of(NumberType.BYTE, "-129", "is out of range for a byte"), OutOfRangeSpec.of(NumberType.SHORT, "-32769", "is out of range for a short"), OutOfRangeSpec.of(NumberType.INTEGER, "-2147483649", "is out of range for an integer"), OutOfRangeSpec.of(NumberType.LONG, "-9223372036854775809", "out of range for a long"), + OutOfRangeSpec.of(NumberType.LONG, "-1e999999999", "out of range for a long"), OutOfRangeSpec.of(NumberType.BYTE, 128, "is out of range for a byte"), OutOfRangeSpec.of(NumberType.SHORT, 32768, "out of range of Java short"), @@ -419,6 +423,10 @@ public void testOutOfRangeValues() throws IOException { e.getCause().getMessage(), containsString(item.message)); } } + + // the following two strings are in-range for a long after coercion + parseRequest(NumberType.LONG, createIndexRequest("9223372036854775807.9")); + parseRequest(NumberType.LONG, createIndexRequest("-9223372036854775808.9")); } private void parseRequest(NumberType type, BytesReference content) throws IOException { From 4034f8f31616353d2f5d09a90753d75e2870ee18 Mon Sep 17 00:00:00 2001 From: Andrei Stefan Date: Thu, 28 Mar 2019 14:28:07 +0200 Subject: [PATCH 49/77] Include functions' aliases in the list of functions (#40584) --- docs/reference/sql/functions/index.asciidoc | 201 ++++++++++---------- 1 file changed, 100 insertions(+), 101 deletions(-) diff --git a/docs/reference/sql/functions/index.asciidoc b/docs/reference/sql/functions/index.asciidoc index 4848fffebae71..6e966403ce0e9 100644 --- a/docs/reference/sql/functions/index.asciidoc +++ b/docs/reference/sql/functions/index.asciidoc @@ -7,115 +7,114 @@ * <> * <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> -** <> +** <> * <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> -** <> -** <> -** <> +** <> +** <> +** <> * <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> -** <> -** <> +** <> +** <> * <> -** <> -** <> -** <> -** <> -** <> -** <> -** <> +** <> +** <> +** <> +** <> +** <> +** <> +** <> * <> -** <> -** <> +** <> +** <> include::operators.asciidoc[] include::aggs.asciidoc[] From d32aa565c55e7ca19947e666a66cde86e4bec01a Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Thu, 28 Mar 2019 14:01:11 +0100 Subject: [PATCH 50/77] Mute testHttpInput Relates #40587 --- .../watcher/test/integration/HttpSecretsIntegrationTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java index f8ddc3065f79d..3eefa03137146 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/integration/HttpSecretsIntegrationTests.java @@ -87,6 +87,7 @@ protected Settings nodeSettings(int nodeOrdinal) { return super.nodeSettings(nodeOrdinal); } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40587") public void testHttpInput() throws Exception { WatcherClient watcherClient = watcherClient(); watcherClient.preparePutWatch("_id") From a4239246f24083984939d609a9843e59ca960de7 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Thu, 28 Mar 2019 14:03:29 +0100 Subject: [PATCH 51/77] Mute testTracerLog Relates to #40586 --- .../elasticsearch/transport/AbstractSimpleTransportTestCase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java index ea12dcedf1643..954d268fe3a65 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/AbstractSimpleTransportTestCase.java @@ -993,6 +993,7 @@ public void handleException(TransportException exp) { } @TestLogging(value = "org.elasticsearch.transport.TransportService.tracer:trace") + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40586") public void testTracerLog() throws Exception { TransportRequestHandler handler = (request, channel, task) -> channel.sendResponse(new StringMessageResponse("")); TransportRequestHandler handlerWithError = (request, channel, task) -> { From 8cf232bedcb452a72c09ec3e84d28de7ec0847f0 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Thu, 28 Mar 2019 09:21:59 -0400 Subject: [PATCH 52/77] SQL: Centralize SQL test dependencies version handling (#40551) Moves definitions of versions for csvjdbc and h2 libraries to the central build.gradle file. --- x-pack/plugin/sql/build.gradle | 4 ++++ x-pack/plugin/sql/qa/build.gradle | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/plugin/sql/build.gradle b/x-pack/plugin/sql/build.gradle index e0250f8ad9ec4..31cec8bbee39f 100644 --- a/x-pack/plugin/sql/build.gradle +++ b/x-pack/plugin/sql/build.gradle @@ -14,6 +14,10 @@ ext { // SQL dependency versions jlineVersion="3.10.0" antlrVersion="4.5.3" + + // SQL test dependency versions + csvjdbcVersion="1.0.34" + h2Version="1.4.197" } configurations { diff --git a/x-pack/plugin/sql/qa/build.gradle b/x-pack/plugin/sql/qa/build.gradle index a2c209f25787f..cf0a0dba8ee62 100644 --- a/x-pack/plugin/sql/qa/build.gradle +++ b/x-pack/plugin/sql/qa/build.gradle @@ -12,7 +12,7 @@ dependencies { compile project(path: xpackModule('sql:jdbc'), configuration: 'nodeps') compile project(path: xpackModule('sql:sql-action')) - compile "net.sourceforge.csvjdbc:csvjdbc:1.0.34" + compile "net.sourceforge.csvjdbc:csvjdbc:${csvjdbcVersion}" // CLI testing dependencies compile project(path: xpackModule('sql:sql-cli'), configuration: 'nodeps') @@ -54,8 +54,8 @@ subprojects { testCompile "org.elasticsearch.test:framework:${version}" // JDBC testing dependencies - testRuntime "net.sourceforge.csvjdbc:csvjdbc:1.0.34" - testRuntime "com.h2database:h2:1.4.197" + testRuntime "net.sourceforge.csvjdbc:csvjdbc:${csvjdbcVersion}" + testRuntime "com.h2database:h2:${h2Version}" testRuntime project(path: xpackModule('sql:jdbc'), configuration: 'nodeps') testRuntime xpackProject('plugin:sql:sql-client') From ecd49d8683386bcdc44ad4195e3ba8881becdc62 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Thu, 28 Mar 2019 08:30:16 -0600 Subject: [PATCH 53/77] Correct ILM metadata minimum compatibility version (#40569) The ILM metadata minimum compatibility version was not set correctly, which can cause issues in mixed-version clusters. --- .../xpack/core/indexlifecycle/IndexLifecycleMetadata.java | 2 +- .../xpack/indexlifecycle/IndexLifecycleMetadataTests.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleMetadata.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleMetadata.java index 7dce5e85ab75d..c0cafa8e9079e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleMetadata.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/IndexLifecycleMetadata.java @@ -106,7 +106,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public Version getMinimalSupportedVersion() { - return Version.V_7_0_0; + return Version.V_6_6_0; } @Override diff --git a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadataTests.java b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadataTests.java index 790dd5de632e6..2444cbf99fd52 100644 --- a/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadataTests.java +++ b/x-pack/plugin/ilm/src/test/java/org/elasticsearch/xpack/indexlifecycle/IndexLifecycleMetadataTests.java @@ -16,18 +16,18 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.xpack.core.indexlifecycle.FreezeAction; -import org.elasticsearch.xpack.core.indexlifecycle.OperationMode; import org.elasticsearch.test.AbstractDiffableSerializationTestCase; import org.elasticsearch.xpack.core.indexlifecycle.AllocateAction; import org.elasticsearch.xpack.core.indexlifecycle.DeleteAction; import org.elasticsearch.xpack.core.indexlifecycle.ForceMergeAction; +import org.elasticsearch.xpack.core.indexlifecycle.FreezeAction; import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata; import org.elasticsearch.xpack.core.indexlifecycle.IndexLifecycleMetadata.IndexLifecycleMetadataDiff; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleAction; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicy; import org.elasticsearch.xpack.core.indexlifecycle.LifecyclePolicyMetadata; import org.elasticsearch.xpack.core.indexlifecycle.LifecycleType; +import org.elasticsearch.xpack.core.indexlifecycle.OperationMode; import org.elasticsearch.xpack.core.indexlifecycle.Phase; import org.elasticsearch.xpack.core.indexlifecycle.ReadOnlyAction; import org.elasticsearch.xpack.core.indexlifecycle.RolloverAction; @@ -137,7 +137,7 @@ protected Reader> diffReader() { } public void testMinimumSupportedVersion() { - assertEquals(Version.V_7_0_0, createTestInstance().getMinimalSupportedVersion()); + assertEquals(Version.V_6_6_0, createTestInstance().getMinimalSupportedVersion()); } public void testcontext() { From 65e031742d29e5b2050d676c8d0c1015866318bc Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Thu, 28 Mar 2019 17:23:40 +0200 Subject: [PATCH 54/77] Test fixtures krb5 (#40297) Replaces the vagrant based kerberos fixtures with docker based test fixtures plugin. The configuration is now entirely static on the docker side and no longer driven by Gradle, also two different services are being configured since there are two different consumers of the fixture that can run in parallel and require different configurations. --- .../testfixtures/TestFixturesPlugin.java | 4 +- plugins/repository-hdfs/build.gradle | 121 +++++------------- test/fixtures/hdfs-fixture/Dockerfile | 8 ++ test/fixtures/hdfs-fixture/build.gradle | 28 ++-- test/fixtures/hdfs-fixture/docker-compose.yml | 11 ++ .../src/main/java/hdfs/MiniHDFS.java | 1 - test/fixtures/krb5kdc-fixture/Dockerfile | 9 ++ test/fixtures/krb5kdc-fixture/Vagrantfile | 53 -------- test/fixtures/krb5kdc-fixture/build.gradle | 78 ++++------- .../krb5kdc-fixture/docker-compose.yml | 24 ++++ .../src/main/resources/provision/addprinc.sh | 11 +- .../src/main/resources/provision/hdfs.sh | 11 ++ .../main/resources/provision/installkdc.sh | 30 +---- .../resources/provision/krb5.conf.template | 6 +- .../src/main/resources/provision/peppa.sh | 13 ++ x-pack/qa/kerberos-tests/build.gradle | 105 +++------------ 16 files changed, 184 insertions(+), 329 deletions(-) create mode 100644 test/fixtures/hdfs-fixture/Dockerfile create mode 100644 test/fixtures/krb5kdc-fixture/Dockerfile delete mode 100644 test/fixtures/krb5kdc-fixture/Vagrantfile create mode 100644 test/fixtures/krb5kdc-fixture/docker-compose.yml create mode 100644 test/fixtures/krb5kdc-fixture/src/main/resources/provision/hdfs.sh create mode 100644 test/fixtures/krb5kdc-fixture/src/main/resources/provision/peppa.sh diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/testfixtures/TestFixturesPlugin.java b/buildSrc/src/main/java/org/elasticsearch/gradle/testfixtures/TestFixturesPlugin.java index 59cb851974cb5..57f77d6d1a256 100644 --- a/buildSrc/src/main/java/org/elasticsearch/gradle/testfixtures/TestFixturesPlugin.java +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/testfixtures/TestFixturesPlugin.java @@ -31,7 +31,6 @@ import org.gradle.api.Task; import org.gradle.api.plugins.BasePlugin; import org.gradle.api.plugins.ExtraPropertiesExtension; -import org.gradle.api.tasks.Input; import org.gradle.api.tasks.TaskContainer; import java.lang.reflect.InvocationTargetException; @@ -104,6 +103,7 @@ public void apply(Project project) { "but none could be found so these will be skipped", project.getPath() ); disableTaskByType(tasks, getTaskClass("com.carrotsearch.gradle.junit4.RandomizedTestingTask")); + disableTaskByType(tasks, getTaskClass("org.elasticsearch.gradle.test.RestIntegTestTask")); // conventions are not honored when the tasks are disabled disableTaskByType(tasks, TestingConventionsTasks.class); disableTaskByType(tasks, ComposeUp.class); @@ -122,6 +122,7 @@ public void apply(Project project) { fixtureProject, (name, port) -> setSystemProperty(task, name, port) ); + task.dependsOn(fixtureProject.getTasks().getByName("postProcessFixture")); }) ); @@ -155,7 +156,6 @@ private void configureServiceInfoForTask(Task task, Project fixtureProject, BiCo ); } - @Input public boolean dockerComposeSupported(Project project) { if (OS.current().equals(OS.WINDOWS)) { return false; diff --git a/plugins/repository-hdfs/build.gradle b/plugins/repository-hdfs/build.gradle index 34323fb930fce..946b377491d26 100644 --- a/plugins/repository-hdfs/build.gradle +++ b/plugins/repository-hdfs/build.gradle @@ -24,18 +24,19 @@ import org.elasticsearch.gradle.test.RestIntegTestTask import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths - +apply plugin: 'elasticsearch.test.fixtures' + esplugin { description 'The HDFS repository plugin adds support for Hadoop Distributed File-System (HDFS) repositories.' classname 'org.elasticsearch.repositories.hdfs.HdfsPlugin' } -apply plugin: 'elasticsearch.vagrantsupport' - versions << [ 'hadoop2': '2.8.1' ] +testFixtures.useFixture ":test:fixtures:krb5kdc-fixture" + configurations { hdfsFixture } @@ -68,67 +69,27 @@ dependencyLicenses { mapping from: /hadoop-.*/, to: 'hadoop' } -// MIT Kerberos Vagrant Testing Fixture -String box = "krb5kdc" -Map vagrantEnvVars = [ - 'VAGRANT_CWD' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}", - 'VAGRANT_VAGRANTFILE' : 'Vagrantfile', - 'VAGRANT_PROJECT_DIR' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}" -] - -task krb5kdcUpdate(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'box' - subcommand 'update' - boxName box - environmentVars vagrantEnvVars - dependsOn "vagrantCheckVersion", "virtualboxCheckVersion" -} - -task krb5kdcFixture(type: org.elasticsearch.gradle.test.VagrantFixture) { - command 'up' - args '--provision', '--provider', 'virtualbox' - boxName box - environmentVars vagrantEnvVars - dependsOn krb5kdcUpdate -} - -task krb5AddPrincipals { - dependsOn krb5kdcFixture -} -List principals = [ "elasticsearch", "hdfs/hdfs.build.elastic.co" ] String realm = "BUILD.ELASTIC.CO" -for (String principal : principals) { - Task create = project.tasks.create("addPrincipal#${principal}".replace('/', '_'), org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'ssh' - args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh $principal" - boxName box - environmentVars vagrantEnvVars - dependsOn krb5kdcFixture - } - krb5AddPrincipals.dependsOn(create) -} // Create HDFS File System Testing Fixtures for HA/Secure combinations for (String fixtureName : ['hdfsFixture', 'haHdfsFixture', 'secureHdfsFixture', 'secureHaHdfsFixture']) { project.tasks.create(fixtureName, org.elasticsearch.gradle.test.AntFixture) { - dependsOn project.configurations.hdfsFixture + dependsOn project.configurations.hdfsFixture, project(':test:fixtures:krb5kdc-fixture').tasks.postProcessFixture executable = new File(project.runtimeJavaHome, 'bin/java') env 'CLASSPATH', "${ -> project.configurations.hdfsFixture.asPath }" waitCondition = { fixture, ant -> // the hdfs.MiniHDFS fixture writes the ports file when // it's ready, so we can just wait for the file to exist return fixture.portsFile.exists() - } + } final List miniHDFSArgs = [] // If it's a secure fixture, then depend on Kerberos Fixture and principals + add the krb5conf to the JVM options if (fixtureName.equals('secureHdfsFixture') || fixtureName.equals('secureHaHdfsFixture')) { - dependsOn krb5kdcFixture, krb5AddPrincipals - Path krb5Config = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf") - miniHDFSArgs.add("-Djava.security.krb5.conf=${krb5Config}"); + miniHDFSArgs.add("-Djava.security.krb5.conf=${project(':test:fixtures:krb5kdc-fixture').ext.krb5Conf("hdfs")}"); if (project.runtimeJavaVersion == JavaVersion.VERSION_1_9) { miniHDFSArgs.add('--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED') } @@ -145,9 +106,11 @@ for (String fixtureName : ['hdfsFixture', 'haHdfsFixture', 'secureHdfsFixture', // If it's a secure fixture, then set the principal name and keytab locations to use for auth. if (fixtureName.equals('secureHdfsFixture') || fixtureName.equals('secureHaHdfsFixture')) { - Path keytabPath = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("hdfs_hdfs.build.elastic.co.keytab") miniHDFSArgs.add("hdfs/hdfs.build.elastic.co@${realm}") - miniHDFSArgs.add("${keytabPath}") + miniHDFSArgs.add( + project(':test:fixtures:krb5kdc-fixture') + .ext.krb5Keytabs("hdfs", "hdfs_hdfs.build.elastic.co.keytab") + ) } args miniHDFSArgs.toArray() @@ -170,10 +133,11 @@ project.afterEvaluate { // If it's a secure cluster, add the keytab as an extra config, and set the krb5 conf in the JVM options. if (integTestTaskName.equals('integTestSecure') || integTestTaskName.equals('integTestSecureHa')) { - Path elasticsearchKT = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("elasticsearch.keytab").toAbsolutePath() - Path krb5conf = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf").toAbsolutePath() - - restIntegTestTask.clusterConfig.extraConfigFile("repository-hdfs/krb5.keytab", "${elasticsearchKT}") + String krb5conf = project(':test:fixtures:krb5kdc-fixture').ext.krb5Conf("hdfs") + restIntegTestTask.clusterConfig.extraConfigFile( + "repository-hdfs/krb5.keytab", + "${project(':test:fixtures:krb5kdc-fixture').ext.krb5Keytabs("hdfs", "elasticsearch.keytab")}" + ) jvmArgs = jvmArgs + " " + "-Djava.security.krb5.conf=${krb5conf}" if (project.runtimeJavaVersion == JavaVersion.VERSION_1_9) { jvmArgs = jvmArgs + " " + '--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED' @@ -189,9 +153,10 @@ project.afterEvaluate { if (project.runtimeJavaVersion == JavaVersion.VERSION_1_9) { restIntegTestTaskRunner.jvmArg '--add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED' } - - Path hdfsKT = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("hdfs_hdfs.build.elastic.co.keytab").toAbsolutePath() - restIntegTestTaskRunner.systemProperty "test.krb5.keytab.hdfs", "${hdfsKT}" + restIntegTestTaskRunner.systemProperty ( + "test.krb5.keytab.hdfs", + project(':test:fixtures:krb5kdc-fixture').ext.krb5Keytabs("hdfs","hdfs_hdfs.build.elastic.co.keytab") + ) } } @@ -269,41 +234,25 @@ if (fixtureSupported) { integTestHa.setEnabled(false) } -// Secure HDFS testing relies on the Vagrant based Kerberos fixture. -boolean secureFixtureSupported = false -if (fixtureSupported) { - secureFixtureSupported = project.rootProject.vagrantSupported -} - -if (secureFixtureSupported) { - project.check.dependsOn(integTestSecure) - project.check.dependsOn(integTestSecureHa) +check.dependsOn(integTestSecure, integTestSecureHa) - // Fixture dependencies - integTestSecureCluster.dependsOn secureHdfsFixture, krb5kdcFixture - integTestSecureHaCluster.dependsOn secureHaHdfsFixture, krb5kdcFixture +// Fixture dependencies +integTestSecureCluster.dependsOn secureHdfsFixture +integTestSecureHaCluster.dependsOn secureHaHdfsFixture - // Set the keytab files in the classpath so that we can access them from test code without the security manager - // freaking out. - Path hdfsKeytabPath = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs") - project.dependencies { - testRuntime fileTree(dir: hdfsKeytabPath.toString(), include: ['*.keytab']) - } - - // Run just the secure hdfs rest test suite. - integTestSecureRunner.systemProperty 'tests.rest.suite', 'secure_hdfs_repository' - // Ignore HA integration Tests. They are included below as part of integTestSecureHa test runner. - integTestSecureRunner.exclude('**/Ha*TestSuiteIT.class') - - // Only include the HA integration tests for the HA test task - integTestSecureHaRunner.patternSet.setIncludes(['**/Ha*TestSuiteIT.class']) -} else { - // Security tests unsupported. Don't run these tests. - integTestSecure.enabled = false - integTestSecureHa.enabled = false - testingConventions.enabled = false +// Set the keytab files in the classpath so that we can access them from test code without the security manager +// freaking out. +project.dependencies { + testRuntime fileTree(dir: project(':test:fixtures:krb5kdc-fixture').ext.krb5Keytabs("hdfs","hdfs_hdfs.build.elastic.co.keytab").parent, include: ['*.keytab']) } +// Run just the secure hdfs rest test suite. +integTestSecureRunner.systemProperty 'tests.rest.suite', 'secure_hdfs_repository' +// Ignore HA integration Tests. They are included below as part of integTestSecureHa test runner. +integTestSecureRunner.exclude('**/Ha*TestSuiteIT.class') +// Only include the HA integration tests for the HA test task +integTestSecureHaRunner.patternSet.setIncludes(['**/Ha*TestSuiteIT.class']) + thirdPartyAudit { ignoreMissingClasses() ignoreViolations ( diff --git a/test/fixtures/hdfs-fixture/Dockerfile b/test/fixtures/hdfs-fixture/Dockerfile new file mode 100644 index 0000000000000..b9d0e60b7d6d4 --- /dev/null +++ b/test/fixtures/hdfs-fixture/Dockerfile @@ -0,0 +1,8 @@ +FROM java:8-jre + +RUN apt-get update && apt-get install net-tools + +EXPOSE 9998 +EXPOSE 9999 + +CMD java -cp "/fixture:/fixture/*" hdfs.MiniHDFS /data \ No newline at end of file diff --git a/test/fixtures/hdfs-fixture/build.gradle b/test/fixtures/hdfs-fixture/build.gradle index 3f08ca7970ca7..f2aebda46b875 100644 --- a/test/fixtures/hdfs-fixture/build.gradle +++ b/test/fixtures/hdfs-fixture/build.gradle @@ -18,25 +18,23 @@ */ apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.test.fixtures' -versions << [ - 'hadoop2': '2.8.1' -] - -// we create MiniHdfsCluster with the hadoop artifact dependencies { - compile "org.apache.hadoop:hadoop-minicluster:${versions.hadoop2}" + compile "org.apache.hadoop:hadoop-minicluster:2.8.1" +} + +task syncClasses(type: Sync) { + from sourceSets.test.runtimeClasspath + into "${buildDir}/fixture" } -// for testing, until fixtures are actually debuggable. -// gradle hides *EVERYTHING* so you have no clue what went wrong. -task hdfs(type: JavaExec) { - classpath = sourceSets.test.compileClasspath + sourceSets.test.output - main = "hdfs.MiniHDFS" - args = [ 'build/fixtures/hdfsFixture' ] +preProcessFixture { + dependsOn syncClasses + + doLast { + file("${buildDir}/shared").mkdirs() + } } -// just a test fixture: we aren't using jars in releases -thirdPartyAudit.enabled = false -// TODO: add a simple HDFS client test for this fixture unitTest.enabled = false diff --git a/test/fixtures/hdfs-fixture/docker-compose.yml b/test/fixtures/hdfs-fixture/docker-compose.yml index e69de29bb2d1d..5bdc40b1f7246 100644 --- a/test/fixtures/hdfs-fixture/docker-compose.yml +++ b/test/fixtures/hdfs-fixture/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3' +services: + hdfs: + hostname: hdfs.build.elastic.co + build: + context: . + dockerfile: Dockerfile + volumes: + - ./build/fixture:/fixture + ports: + - "9999:9999" diff --git a/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java b/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java index ce7401fe25cae..01315cdab01ca 100644 --- a/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java +++ b/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java @@ -98,7 +98,6 @@ public static void main(String[] args) throws Exception { UserGroupInformation.setConfiguration(cfg); - // TODO: remove hardcoded port! MiniDFSCluster.Builder builder = new MiniDFSCluster.Builder(cfg); if (secure) { builder.nameNodePort(9998); diff --git a/test/fixtures/krb5kdc-fixture/Dockerfile b/test/fixtures/krb5kdc-fixture/Dockerfile new file mode 100644 index 0000000000000..50de6334b9c78 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/Dockerfile @@ -0,0 +1,9 @@ +FROM ubuntu:14.04 +ADD . /fixture +RUN echo kerberos.build.elastic.co > /etc/hostname && echo "127.0.0.1 kerberos.build.elastic.co" >> /etc/hosts +RUN bash /fixture/src/main/resources/provision/installkdc.sh + +EXPOSE 88 +EXPOSE 88/udp + +CMD sleep infinity \ No newline at end of file diff --git a/test/fixtures/krb5kdc-fixture/Vagrantfile b/test/fixtures/krb5kdc-fixture/Vagrantfile deleted file mode 100644 index 72be4dad9cbe5..0000000000000 --- a/test/fixtures/krb5kdc-fixture/Vagrantfile +++ /dev/null @@ -1,53 +0,0 @@ -# -*- mode: ruby -*- -# vi: set ft=ruby : - -# Licensed to Elasticsearch under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# This Vagrantfile exists to define a virtual machine running MIT's Kerberos 5 -# for usage as a testing fixture for the build process. -# -# In order to connect to the KDC process on this virtual machine, find and use -# the rendered krb5.conf file in the build output directory (build/conf). -# -# In order to provision principals in the KDC, use the provided addprinc.sh -# script with vagrant's ssh facility: -# -# vagrant ssh -c /vagrant/src/main/resources/provision/addprinc.sh principal -# -# You will find the newly created principal's keytab file in the build output -# directory (build/keytabs). Principal creation is idempotent, and will recopy -# existing user keytabs from the KDC if they already exist. - -Vagrant.configure("2") do |config| - - config.vm.define "krb5kdc" do |config| - config.vm.box = "elastic/ubuntu-14.04-x86_64" - end - - config.vm.hostname = "kerberos.build.elastic.co" - - if Vagrant.has_plugin?("vagrant-cachier") - config.cache.scope = :box - end - - config.vm.network "forwarded_port", guest: 88, host: 60088, protocol: "tcp" - config.vm.network "forwarded_port", guest: 88, host: 60088, protocol: "udp" - - config.vm.provision "shell", path: "src/main/resources/provision/installkdc.sh" - -end diff --git a/test/fixtures/krb5kdc-fixture/build.gradle b/test/fixtures/krb5kdc-fixture/build.gradle index 685483d534771..a3ca8d41bc4d9 100644 --- a/test/fixtures/krb5kdc-fixture/build.gradle +++ b/test/fixtures/krb5kdc-fixture/build.gradle @@ -16,68 +16,38 @@ * specific language governing permissions and limitations * under the License. */ +apply plugin: 'elasticsearch.test.fixtures' -apply plugin: 'elasticsearch.build' - -Map vagrantEnvVars = [ - 'VAGRANT_CWD' : "${project.projectDir.absolutePath}", - 'VAGRANT_VAGRANTFILE' : 'Vagrantfile', - 'VAGRANT_PROJECT_DIR' : "${project.projectDir.absolutePath}" -] - -String box = "krb5kdc" - -List defaultPrincipals = [ "elasticsearch" ] - -task update(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'box' - subcommand 'update' - boxName box - environmentVars vagrantEnvVars +// installKDC uses tabs in it for the Kerberos ACL file. +// Ignore it for pattern checking. +forbiddenPatterns { + exclude "**/installkdc.sh" } -task up(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'up' - args '--provision', '--provider', 'virtualbox' - boxName box - environmentVars vagrantEnvVars - dependsOn update -} +List services = ["peppa", "hdfs"] -task addDefaultPrincipals { - dependsOn up +preProcessFixture.doLast { + // We need to create these up-front because if docker creates them they will be owned by root and we won't be + // able to clean them up + services.each { file("${buildDir}/shared/${it}").mkdirs() } } -for (String principal : defaultPrincipals) { - Task addTask = project.tasks.create("addPrincipal#${principal}", org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'ssh' - args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh $principal" - boxName box - environmentVars vagrantEnvVars - dependsOn up +postProcessFixture { + inputs.dir("${buildDir}/shared") + services.each { service -> + File confTemplate = file("${buildDir}/shared/${service}/krb5.conf.template") + File confFile = file("${buildDir}/shared/${service}/krb5.conf") + outputs.file(confFile) + doLast { + assert confTemplate.exists() + String confContents = confTemplate.text + .replace("\${MAPPED_PORT}", "${ext."test.fixtures.${service}.udp.88"}") + confFile.text = confContents + } } - addDefaultPrincipals.dependsOn(addTask) } -task halt(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'halt' - boxName box - environmentVars vagrantEnvVars -} - -task destroy(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'destroy' - args '-f' - boxName box - environmentVars vagrantEnvVars - dependsOn halt -} +project.ext.krb5Conf = { service -> file("$buildDir/shared/${service}/krb5.conf") } +project.ext.krb5Keytabs = { service, fileName -> file("$buildDir/shared/${service}/keytabs/${fileName}") } -thirdPartyAudit.enabled = false unitTest.enabled = false - -// installKDC uses tabs in it for the Kerberos ACL file. -// Ignore it for pattern checking. -forbiddenPatterns { - exclude "**/installkdc.sh" -} diff --git a/test/fixtures/krb5kdc-fixture/docker-compose.yml b/test/fixtures/krb5kdc-fixture/docker-compose.yml new file mode 100644 index 0000000000000..4d018dd6c3e08 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3' +services: + peppa: + hostname: kerberos.build.elastic.co + build: + context: . + dockerfile: Dockerfile + command: "bash /fixture/src/main/resources/provision/peppa.sh" + volumes: + - ./build/shared/peppa:/fixture/build + ports: + - "4444" + - "88/udp" + hdfs: + hostname: kerberos.build.elastic.co + build: + context: . + dockerfile: Dockerfile + command: "bash /fixture/src/main/resources/provision/hdfs.sh" + volumes: + - ./build/shared/hdfs:/fixture/build + ports: + - "4444" + - "88/udp" diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh index d0d1570ae299a..9fc2a0735d666 100755 --- a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/addprinc.sh @@ -19,6 +19,9 @@ set -e +krb5kdc +kadmind + if [[ $# -lt 1 ]]; then echo 'Usage: addprinc.sh principalName [password]' echo ' principalName user principal name without realm' @@ -30,7 +33,7 @@ PRINC="$1" PASSWD="$2" USER=$(echo $PRINC | tr "/" "_") -VDIR=/vagrant +VDIR=/fixture RESOURCES=$VDIR/src/main/resources PROV_DIR=$RESOURCES/provision ENVPROP_FILE=$RESOURCES/env.properties @@ -64,3 +67,9 @@ else sudo kadmin -p $ADMIN_PRIN -kt $ADMIN_KTAB -q "addprinc -pw $PASSWD $PRINC" fi fi + +echo "Copying conf to local" +# make the configuration available externally +cp -v $LOCALSTATEDIR/krb5.conf $BUILD_DIR/krb5.conf.template +# We are running as root in the container, allow non root users running the container to be able to clean these up +chmod -R 777 $BUILD_DIR \ No newline at end of file diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/hdfs.sh b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/hdfs.sh new file mode 100644 index 0000000000000..ef5bba076444c --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/hdfs.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +addprinc.sh "elasticsearch" +addprinc.sh "hdfs/hdfs.build.elastic.co" + +# Use this as a signal that setup is complete +python3 -m http.server 4444 & + +sleep infinity \ No newline at end of file diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh index 2dc8ed92c9462..51af7984ce476 100755 --- a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/installkdc.sh @@ -22,32 +22,15 @@ set -e # KDC installation steps and considerations based on https://web.mit.edu/kerberos/krb5-latest/doc/admin/install_kdc.html # and helpful input from https://help.ubuntu.com/community/Kerberos -VDIR=/vagrant +VDIR=/fixture RESOURCES=$VDIR/src/main/resources PROV_DIR=$RESOURCES/provision ENVPROP_FILE=$RESOURCES/env.properties -BUILD_DIR=$VDIR/build -CONF_DIR=$BUILD_DIR/conf -KEYTAB_DIR=$BUILD_DIR/keytabs LOCALSTATEDIR=/etc LOGDIR=/var/log/krb5 MARKER_FILE=/etc/marker -# Output location for our rendered configuration files and keytabs -mkdir -p $BUILD_DIR -rm -rf $BUILD_DIR/* -mkdir -p $CONF_DIR -mkdir -p $KEYTAB_DIR - -if [ -f $MARKER_FILE ]; then - echo "Already provisioned..." - echo "Recopying configuration files..." - cp $LOCALSTATEDIR/krb5.conf $CONF_DIR/krb5.conf - cp $LOCALSTATEDIR/krb5kdc/kdc.conf $CONF_DIR/kdc.conf - exit 0; -fi - # Pull environment information REALM_NAME=$(cat $ENVPROP_FILE | grep realm= | cut -d '=' -f 2) KDC_NAME=$(cat $ENVPROP_FILE | grep kdc= | cut -d '=' -f 2) @@ -60,7 +43,7 @@ sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $LOCALSTATEDIR/krb5.conf sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $LOCALSTATEDIR/krb5.conf sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $LOCALSTATEDIR/krb5.conf sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf -cp $LOCALSTATEDIR/krb5.conf $CONF_DIR/krb5.conf + # Transfer and interpolate the kdc.conf mkdir -p $LOCALSTATEDIR/krb5kdc @@ -69,7 +52,6 @@ sed -i 's/${REALM_NAME}/'$REALM_NAME'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf sed -i 's/${KDC_NAME}/'$KDC_NAME'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf sed -i 's/${BUILD_ZONE}/'$BUILD_ZONE'/g' $LOCALSTATEDIR/krb5kdc/kdc.conf sed -i 's/${ELASTIC_ZONE}/'$ELASTIC_ZONE'/g' $LOCALSTATEDIR/krb5.conf -cp $LOCALSTATEDIR/krb5kdc/kdc.conf $CONF_DIR/kdc.conf # Touch logging locations mkdir -p $LOGDIR @@ -112,9 +94,5 @@ EOF kadmin.local -q "addprinc -pw elastic admin/admin@$REALM_NAME" kadmin.local -q "ktadd -k /etc/admin.keytab admin/admin@$REALM_NAME" -# Start Kerberos Services -krb5kdc -kadmind - -# Mark that the vm is already provisioned -touch $MARKER_FILE \ No newline at end of file +# Create a link so addprinc.sh is on path +ln -s $PROV_DIR/addprinc.sh /usr/bin/ \ No newline at end of file diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template index e572c12e70957..9504b49bc7301 100644 --- a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/krb5.conf.template @@ -32,12 +32,8 @@ [realms] ${REALM_NAME} = { - kdc = ${KDC_NAME}:88 - kdc = ${KDC_NAME}:60088 - kdc = localhost:60088 - kdc = localhost:88 - kdc = 127.0.0.1:60088 kdc = 127.0.0.1:88 + kdc = 127.0.0.1:${MAPPED_PORT} admin_server = ${KDC_NAME}:749 default_domain = ${BUILD_ZONE} } diff --git a/test/fixtures/krb5kdc-fixture/src/main/resources/provision/peppa.sh b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/peppa.sh new file mode 100644 index 0000000000000..815a9e94e8cb5 --- /dev/null +++ b/test/fixtures/krb5kdc-fixture/src/main/resources/provision/peppa.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e + +addprinc.sh elasticsearch +addprinc.sh HTTP/localhost +addprinc.sh peppa +addprinc.sh george dino + +# Use this as a signal that setup is complete +python3 -m http.server 4444 & + +sleep infinity \ No newline at end of file diff --git a/x-pack/qa/kerberos-tests/build.gradle b/x-pack/qa/kerberos-tests/build.gradle index a59becbfe6b54..50b709f77dca5 100644 --- a/x-pack/qa/kerberos-tests/build.gradle +++ b/x-pack/qa/kerberos-tests/build.gradle @@ -2,9 +2,11 @@ import java.nio.file.Path import java.nio.file.Paths import java.nio.file.Files -apply plugin: 'elasticsearch.vagrantsupport' apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.rest-test' +apply plugin: 'elasticsearch.test.fixtures' + +testFixtures.useFixture ":test:fixtures:krb5kdc-fixture" dependencies { testCompile "org.elasticsearch.plugin:x-pack-core:${version}" @@ -12,75 +14,6 @@ dependencies { testCompile project(path: xpackModule('security'), configuration: 'testArtifacts') } -// MIT Kerberos Vagrant Testing Fixture -String box = "krb5kdc" -Map vagrantEnvVars = [ - 'VAGRANT_CWD' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}", - 'VAGRANT_VAGRANTFILE' : 'Vagrantfile', - 'VAGRANT_PROJECT_DIR' : "${project(':test:fixtures:krb5kdc-fixture').projectDir}" -] - -task krb5kdcUpdate(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'box' - subcommand 'update' - boxName box - environmentVars vagrantEnvVars - dependsOn "vagrantCheckVersion", "virtualboxCheckVersion" -} - -task krb5kdcFixture(type: org.elasticsearch.gradle.test.VagrantFixture) { - command 'up' - args '--provision', '--provider', 'virtualbox' - boxName box - environmentVars vagrantEnvVars - dependsOn krb5kdcUpdate -} - -// lazily resolve to avoid any slowdowns from DNS lookups prior to when we need this value -Object httpPrincipal = new Object() { - @Override - String toString() { - InetAddress resolvedAddress = InetAddress.getByName('127.0.0.1') - return "HTTP/" + resolvedAddress.getCanonicalHostName() - } -} - -String realm = "BUILD.ELASTIC.CO" - -task 'addPrincipal#peppa'(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'ssh' - args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh peppa " - boxName box - environmentVars vagrantEnvVars - dependsOn krb5kdcFixture -} - -task 'addPrincipal#george'(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'ssh' - args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh george dino" - boxName box - environmentVars vagrantEnvVars - dependsOn krb5kdcFixture -} - -task 'addPrincipal#HTTP'(type: org.elasticsearch.gradle.vagrant.VagrantCommandTask) { - command 'ssh' - args '--command', "sudo bash /vagrant/src/main/resources/provision/addprinc.sh $httpPrincipal" - boxName box - environmentVars vagrantEnvVars - dependsOn krb5kdcFixture -} - -task krb5AddPrincipals { dependsOn krb5kdcFixture, 'addPrincipal#peppa', 'addPrincipal#george', 'addPrincipal#HTTP' } - -def generatedResources = "$buildDir/generated-resources/keytabs" -task copyKeytabToGeneratedResources(type: Copy) { - Path peppaKeytab = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs").resolve("peppa.keytab").toAbsolutePath() - from peppaKeytab; - into generatedResources - dependsOn krb5AddPrincipals -} - integTestCluster { // force localhost IPv4 otherwise it is a chicken and egg problem where we need the keytab for the hostname when starting the cluster // but do not know the exact address that is first in the http ports file @@ -96,12 +29,10 @@ integTestCluster { setting 'xpack.security.authc.realms.kerberos.kerberos.krb.debug', 'true' setting 'xpack.security.authc.realms.kerberos.kerberos.remove_realm_name', 'false' - Path krb5conf = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf").toAbsolutePath() - String jvmArgsStr = " -Djava.security.krb5.conf=${krb5conf}" + " -Dsun.security.krb5.debug=true" - jvmArgs jvmArgsStr - Path esKeytab = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("keytabs") - .resolve("$httpPrincipal".replace('/', '_') + ".keytab").toAbsolutePath() - extraConfigFile("es.keytab", "${esKeytab}") + jvmArgs += " -Djava.security.krb5.conf=${project(':test:fixtures:krb5kdc-fixture').ext.krb5Conf("peppa")}" + jvmArgs += " -Dsun.security.krb5.debug=true" + + extraConfigFile("es.keytab", project(':test:fixtures:krb5kdc-fixture').ext.krb5Keytabs("peppa", "HTTP_localhost.keytab")) setupCommand 'setupTestAdmin', 'bin/elasticsearch-users', 'useradd', "test_admin", '-p', 'x-pack-test-password', '-r', "superuser" @@ -119,6 +50,7 @@ integTestCluster { } +String realm = "BUILD.ELASTIC.CO" integTestRunner { Path peppaKeytab = Paths.get("${project.buildDir}", "generated-resources", "keytabs", "peppa.keytab") systemProperty 'test.userkt', "peppa@${realm}" @@ -126,16 +58,17 @@ integTestRunner { systemProperty 'test.userpwd', "george@${realm}" systemProperty 'test.userpwd.password', "dino" systemProperty 'tests.security.manager', 'true' - Path krb5conf = project(':test:fixtures:krb5kdc-fixture').buildDir.toPath().resolve("conf").resolve("krb5.conf").toAbsolutePath() - List jvmargs = ["-Djava.security.krb5.conf=${krb5conf}","-Dsun.security.krb5.debug=true"] - jvmArgs jvmargs + jvmArgs([ + "-Djava.security.krb5.conf=${project(':test:fixtures:krb5kdc-fixture').ext.krb5Conf("peppa")}", + "-Dsun.security.krb5.debug=true" + ]) } -if (project.rootProject.vagrantSupported == false) { - integTest.enabled = false - testingConventions.enabled = false -} else { - project.sourceSets.test.output.dir(generatedResources) - integTestCluster.dependsOn krb5AddPrincipals, krb5kdcFixture, copyKeytabToGeneratedResources - integTest.finalizedBy project(':test:fixtures:krb5kdc-fixture').halt +def generatedResources = "$buildDir/generated-resources/keytabs" +task copyKeytabToGeneratedResources(type: Copy) { + from project(':test:fixtures:krb5kdc-fixture').ext.krb5Keytabs("peppa", "peppa.keytab") + into generatedResources + dependsOn project(':test:fixtures:krb5kdc-fixture').postProcessFixture } +project.sourceSets.test.output.dir(generatedResources, builtBy:copyKeytabToGeneratedResources) + From 459715dba781b00e07ec1ad205e9bf3b5a6e9799 Mon Sep 17 00:00:00 2001 From: Mayya Sharipova Date: Thu, 28 Mar 2019 11:29:29 -0400 Subject: [PATCH 55/77] Add randomScore function in script_score query (#40186) To make script_score query to have the same features as function_score query, we need to add randomScore function. This function produces different random scores on different index shards. It is also able to produce random scores based on the internal Lucene Document Ids. --- .../query-dsl/script-score-query.asciidoc | 60 +++---- .../painless/spi/org.elasticsearch.score.txt | 7 +- .../test/painless/80_script_score.yml | 55 ------- .../painless/85_script_score_random_score.yml | 146 ++++++++++++++++++ .../search/function/ScriptScoreFunction.java | 15 ++ .../ScriptScoreFunctionBuilder.java | 2 +- .../org/elasticsearch/script/ScoreScript.java | 72 +++++++++ .../script/ScoreScriptUtils.java | 58 ++++--- 8 files changed, 301 insertions(+), 114 deletions(-) create mode 100644 modules/lang-painless/src/test/resources/rest-api-spec/test/painless/85_script_score_random_score.yml diff --git a/docs/reference/query-dsl/script-score-query.asciidoc b/docs/reference/query-dsl/script-score-query.asciidoc index ee68d3e40fe13..56c4f7c41b8ee 100644 --- a/docs/reference/query-dsl/script-score-query.asciidoc +++ b/docs/reference/query-dsl/script-score-query.asciidoc @@ -182,60 +182,44 @@ different from the query's vector, 0 is used for missing dimensions in the calculations of vector functions. -[[random-functions]] -===== Random functions -There are two predefined ways to produce random values: -`randomNotReproducible` and `randomReproducible`. +[[random-score-function]] +===== Random score function +`random_score` function generates scores that are uniformly distributed +from 0 up to but not including 1. -`randomNotReproducible()` uses `java.util.Random` class -to generate a random value of the type `long`. -The generated values are not reproducible between requests' invocations. +`randomScore` function has the following syntax: +`randomScore(, )`. +It has a required parameter - `seed` as an integer value, +and an optional parameter - `fieldName` as a string value. [source,js] -------------------------------------------------- "script" : { - "source" : "randomNotReproducible()" + "source" : "randomScore(100, '_seq_no')" } -------------------------------------------------- // NOTCONSOLE - -`randomReproducible(String seedValue, int seed)` produces -reproducible random values of type `long`. This function requires -more computational time and memory than the non-reproducible version. - -A good candidate for the `seedValue` is document field values that -are unique across documents and already pre-calculated and preloaded -in the memory. For example, values of the document's `_seq_no` field -is a good candidate, as documents on the same shard have unique values -for the `_seq_no` field. +If the `fieldName` parameter is omitted, the internal Lucene +document ids will be used as a source of randomness. This is very efficient, +but unfortunately not reproducible since documents might be renumbered +by merges. [source,js] -------------------------------------------------- "script" : { - "source" : "randomReproducible(Long.toString(doc['_seq_no'].value), 100)" + "source" : "randomScore(100)" } -------------------------------------------------- // NOTCONSOLE -A drawback of using `_seq_no` is that generated values change if -documents are updated. Another drawback is not absolute uniqueness, as -documents from different shards with the same sequence numbers -generate the same random values. - -If you need random values to be distinct across different shards, -you can use a field with unique values across shards, -such as `_id`, but watch out for the memory usage as all -these unique values need to be loaded into memory. - -[source,js] --------------------------------------------------- -"script" : { - "source" : "randomReproducible(doc['_id'].value, 100)" -} --------------------------------------------------- -// NOTCONSOLE +Note that documents that are within the same shard and have the +same value for field will get the same score, so it is usually desirable +to use a field that has unique values for all documents across a shard. +A good default choice might be to use the `_seq_no` +field, whose only drawback is that scores will change if the document is +updated since update operations also update the value of the `_seq_no` field. [[decay-functions]] @@ -349,8 +333,8 @@ the following script: ===== `random_score` -Use `randomReproducible` and `randomNotReproducible` functions -as described in <>. +Use `randomScore` function +as described in <>. ===== `field_value_factor` diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.score.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.score.txt index 3d7b29826c747..03ec9275aa8b7 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.score.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.score.txt @@ -19,11 +19,14 @@ # This file contains a whitelist for functions to be used in Score context +class org.elasticsearch.script.ScoreScript no_import { +} + static_import { double saturation(double, double) from_class org.elasticsearch.script.ScoreScriptUtils double sigmoid(double, double, double) from_class org.elasticsearch.script.ScoreScriptUtils - double randomReproducible(String, int) from_class org.elasticsearch.script.ScoreScriptUtils - double randomNotReproducible() bound_to org.elasticsearch.script.ScoreScriptUtils$RandomNotReproducible + double randomScore(org.elasticsearch.script.ScoreScript, int, String) bound_to org.elasticsearch.script.ScoreScriptUtils$RandomScoreField + double randomScore(org.elasticsearch.script.ScoreScript, int) bound_to org.elasticsearch.script.ScoreScriptUtils$RandomScoreDoc double decayGeoLinear(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoLinear double decayGeoExp(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoExp double decayGeoGauss(String, String, String, double, GeoPoint) bound_to org.elasticsearch.script.ScoreScriptUtils$DecayGeoGauss diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/80_script_score.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/80_script_score.yml index a3135777c952c..cf55810058d92 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/80_script_score.yml +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/80_script_score.yml @@ -72,61 +72,6 @@ setup: - match: { hits.hits.1._id: d2 } - match: { hits.hits.2._id: d1 } ---- -"Random functions": - - do: - indices.create: - index: test - body: - settings: - number_of_shards: 2 - mappings: - properties: - f1: - type: keyword - - do: - index: - index: test - id: 1 - body: {"f1": "v1"} - - do: - index: - index: test - id: 2 - body: {"f1": "v2"} - - do: - index: - index: test - id: 3 - body: {"f1": "v3"} - - - do: - indices.refresh: {} - - - do: - search: - rest_total_hits_as_int: true - index: test - body: - query: - script_score: - query: {match_all: {} } - script: - source: "randomReproducible(Long.toString(doc['_seq_no'].value), 100)" - - match: { hits.total: 3 } - - - do: - search: - rest_total_hits_as_int: true - index: test - body: - query: - script_score: - query: {match_all: {} } - script: - source: "randomNotReproducible()" - - match: { hits.total: 3 } - --- "Decay geo functions": - do: diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/85_script_score_random_score.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/85_script_score_random_score.yml new file mode 100644 index 0000000000000..2879d50fedebc --- /dev/null +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/85_script_score_random_score.yml @@ -0,0 +1,146 @@ +# Integration tests for ScriptScoreQuery using Painless + +setup: +- skip: + version: " - 7.99.99" # correct to 7.09.99 after backporting to 7.1 + reason: "random score function of script score was added in 7.1" + +--- +"Random score function with _seq_no field": + - do: + indices.create: + index: test + body: + settings: + number_of_shards: 2 + mappings: + properties: + f1: + type: keyword + + - do: + bulk: + refresh: true + body: + - '{"index": {"_index": "test"}}' + - '{"f1": "v0"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v1"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v2"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v3"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v4"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v5"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v6"}' + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + script_score: + query: {match_all: {} } + script: + source: "randomScore(100, '_seq_no')" + # stash ids to check for reproducibility of ranking + - set: { hits.hits.0._id: id0 } + - set: { hits.hits.1._id: id1 } + - set: { hits.hits.2._id: id2 } + - set: { hits.hits.3._id: id3 } + - set: { hits.hits.4._id: id4 } + - set: { hits.hits.5._id: id5 } + - set: { hits.hits.6._id: id6 } + + # check that ranking is reproducible + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + script_score: + query: {match_all: {} } + script: + source: "randomScore(100, '_seq_no')" + - match: { hits.hits.0._id: $id0 } + - match: { hits.hits.1._id: $id1 } + - match: { hits.hits.2._id: $id2 } + - match: { hits.hits.3._id: $id3 } + - match: { hits.hits.4._id: $id4 } + - match: { hits.hits.5._id: $id5 } + - match: { hits.hits.6._id: $id6 } + +--- +"Random score function with internal doc Ids": + - do: + indices.create: + index: test + body: + settings: + number_of_shards: 1 + mappings: + properties: + f1: + type: keyword + + - do: + bulk: + refresh: true + body: + - '{"index": {"_index": "test"}}' + - '{"f1": "v0"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v1"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v2"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v3"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v4"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v5"}' + - '{"index": {"_index": "test"}}' + - '{"f1": "v6"}' + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + script_score: + query: {match_all: {} } + script: + source: "randomScore(100)" + # stash ids to check for reproducibility of ranking + - set: { hits.hits.0._id: id0 } + - set: { hits.hits.1._id: id1 } + - set: { hits.hits.2._id: id2 } + - set: { hits.hits.3._id: id3 } + - set: { hits.hits.4._id: id4 } + - set: { hits.hits.5._id: id5 } + - set: { hits.hits.6._id: id6 } + + # check that ranking is reproducible + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + script_score: + query: {match_all: {} } + script: + source: "randomScore(100)" + - match: { hits.hits.0._id: $id0 } + - match: { hits.hits.1._id: $id1 } + - match: { hits.hits.2._id: $id2 } + - match: { hits.hits.3._id: $id3 } + - match: { hits.hits.4._id: $id4 } + - match: { hits.hits.5._id: $id5 } + - match: { hits.hits.6._id: $id6 } diff --git a/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java b/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java index 8e51bc5951d59..960df44a62514 100644 --- a/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java +++ b/server/src/main/java/org/elasticsearch/common/lucene/search/function/ScriptScoreFunction.java @@ -50,11 +50,24 @@ public float score() { private final ScoreScript.LeafFactory script; + private final int shardId; + private final String indexName; + public ScriptScoreFunction(Script sScript, ScoreScript.LeafFactory script) { super(CombineFunction.REPLACE); this.sScript = sScript; this.script = script; + this.indexName = null; + this.shardId = -1; + } + + public ScriptScoreFunction(Script sScript, ScoreScript.LeafFactory script, String indexName, int shardId) { + super(CombineFunction.REPLACE); + this.sScript = sScript; + this.script = script; + this.indexName = indexName; + this.shardId = shardId; } @Override @@ -62,6 +75,8 @@ public LeafScoreFunction getLeafScoreFunction(LeafReaderContext ctx) throws IOEx final ScoreScript leafScript = script.newInstance(ctx); final CannedScorer scorer = new CannedScorer(); leafScript.setScorer(scorer); + leafScript._setIndexName(indexName); + leafScript._setShard(shardId); return new LeafScoreFunction() { @Override public double score(int docId, float subQueryScore) throws IOException { diff --git a/server/src/main/java/org/elasticsearch/index/query/functionscore/ScriptScoreFunctionBuilder.java b/server/src/main/java/org/elasticsearch/index/query/functionscore/ScriptScoreFunctionBuilder.java index a860bd19d7c5f..accfd2f656999 100644 --- a/server/src/main/java/org/elasticsearch/index/query/functionscore/ScriptScoreFunctionBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/functionscore/ScriptScoreFunctionBuilder.java @@ -94,7 +94,7 @@ protected ScoreFunction doToFunction(QueryShardContext context) { try { ScoreScript.Factory factory = context.getScriptService().compile(script, ScoreScript.CONTEXT); ScoreScript.LeafFactory searchScript = factory.newFactory(script.getParams(), context.lookup()); - return new ScriptScoreFunction(script, searchScript); + return new ScriptScoreFunction(script, searchScript, context.index().getName(), context.getShardId()); } catch (Exception e) { throw new QueryShardException(context, "script_score: the script could not be loaded", e); } diff --git a/server/src/main/java/org/elasticsearch/script/ScoreScript.java b/server/src/main/java/org/elasticsearch/script/ScoreScript.java index 6ac5935826bf7..f31af4c008c74 100644 --- a/server/src/main/java/org/elasticsearch/script/ScoreScript.java +++ b/server/src/main/java/org/elasticsearch/script/ScoreScript.java @@ -62,6 +62,11 @@ public abstract class ScoreScript { private DoubleSupplier scoreSupplier = () -> 0.0; + private final int docBase; + private int docId; + private int shardId = -1; + private String indexName = null; + public ScoreScript(Map params, SearchLookup lookup, LeafReaderContext leafContext) { // null check needed b/c of expression engine subclass if (lookup == null) { @@ -69,11 +74,13 @@ public ScoreScript(Map params, SearchLookup lookup, LeafReaderCo assert leafContext == null; this.params = null; this.leafLookup = null; + this.docBase = 0; } else { this.leafLookup = lookup.getLeafSearchLookup(leafContext); params = new HashMap<>(params); params.putAll(leafLookup.asMap()); this.params = new DeprecationMap(params, DEPRECATIONS, "score-script"); + this.docBase = leafContext.docBase; } } @@ -91,6 +98,7 @@ public final Map> getDoc() { /** Set the current document to run the script on next. */ public void setDocument(int docid) { + this.docId = docid; leafLookup.setDocument(docid); } @@ -104,10 +112,74 @@ public void setScorer(Scorable scorer) { }; } + /** + * Accessed as _score in the painless script + * @return the score of the inner query + */ public double get_score() { return scoreSupplier.getAsDouble(); } + + /** + * Starting a name with underscore, so that the user cannot access this function directly through a script + * It is only used within predefined painless functions. + * @return the internal document ID + */ + public int _getDocId() { + return docId; + } + + /** + * Starting a name with underscore, so that the user cannot access this function directly through a script + * It is only used within predefined painless functions. + * @return the internal document ID with the base + */ + public int _getDocBaseId() { + return docBase + docId; + } + + /** + * Starting a name with underscore, so that the user cannot access this function directly through a script + * It is only used within predefined painless functions. + * @return shard id or throws an exception if shard is not set up for this script instance + */ + public int _getShardId() { + if (shardId > -1) { + return shardId; + } else { + throw new IllegalArgumentException("shard id can not be looked up!"); + } + } + + /** + * Starting a name with underscore, so that the user cannot access this function directly through a script + * It is only used within predefined painless functions. + * @return index name or throws an exception if the index name is not set up for this script instance + */ + public String _getIndex() { + if (indexName != null) { + return indexName; + } else { + throw new IllegalArgumentException("index name can not be looked up!"); + } + } + + /** + * Starting a name with underscore, so that the user cannot access this function directly through a script + */ + public void _setShard(int shardId) { + this.shardId = shardId; + } + + /** + * Starting a name with underscore, so that the user cannot access this function directly through a script + */ + public void _setIndexName(String indexName) { + this.indexName = indexName; + } + + /** A factory to construct {@link ScoreScript} instances. */ public interface LeafFactory { diff --git a/server/src/main/java/org/elasticsearch/script/ScoreScriptUtils.java b/server/src/main/java/org/elasticsearch/script/ScoreScriptUtils.java index 273b8fcf8559d..c7d6e889397ff 100644 --- a/server/src/main/java/org/elasticsearch/script/ScoreScriptUtils.java +++ b/server/src/main/java/org/elasticsearch/script/ScoreScriptUtils.java @@ -21,22 +21,20 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.StringHelper; -import org.elasticsearch.common.Randomness; +import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.time.DateMathParser; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.mapper.DateFieldMapper; import java.time.ZoneId; -import java.util.Random; -/** - * ScoringScriptImpl can be used as {@link ScoreScript} - * to run a previously compiled Painless script. - */ +import static com.carrotsearch.hppc.BitMixer.mix32; + public final class ScoreScriptUtils { /****** STATIC FUNCTIONS that can be used by users for score calculations **/ @@ -53,26 +51,50 @@ public static double sigmoid(double value, double k, double a){ return Math.pow(value,a) / (Math.pow(k,a) + Math.pow(value,a)); } + // random score based on the documents' values of the given field + public static final class RandomScoreField { + private final ScoreScript scoreScript; + private final ScriptDocValues docValues; + private final int saltedSeed; - // reproducible random - public static double randomReproducible(String seedValue, int seed) { - int hash = StringHelper.murmurhash3_x86_32(new BytesRef(seedValue), seed); - return (hash & 0x00FFFFFF) / (float)(1 << 24); // only use the lower 24 bits to construct a float from 0.0-1.0 - } - // not reproducible random - public static final class RandomNotReproducible { - private final Random rnd; + public RandomScoreField(ScoreScript scoreScript, int seed, String fieldName) { + this.scoreScript = scoreScript; + this.docValues = scoreScript.getDoc().get(fieldName); + int salt = (scoreScript._getIndex().hashCode() << 10) | scoreScript._getShardId(); + this.saltedSeed = mix32(salt ^ seed); - public RandomNotReproducible() { - this.rnd = Randomness.get(); } - public double randomNotReproducible() { - return rnd.nextDouble(); + public double randomScore() { + try { + docValues.setNextDocId(scoreScript._getDocId()); + String seedValue = String.valueOf(docValues.get(0)); + int hash = StringHelper.murmurhash3_x86_32(new BytesRef(seedValue), saltedSeed); + return (hash & 0x00FFFFFF) / (float)(1 << 24); // only use the lower 24 bits to construct a float from 0.0-1.0 + } catch (Exception e) { + throw ExceptionsHelper.convertToElastic(e); + } } } + // random score based on the internal Lucene document Ids + public static final class RandomScoreDoc { + private final ScoreScript scoreScript; + private final int saltedSeed; + + public RandomScoreDoc(ScoreScript scoreScript, int seed) { + this.scoreScript = scoreScript; + int salt = (scoreScript._getIndex().hashCode() << 10) | scoreScript._getShardId(); + this.saltedSeed = mix32(salt ^ seed); + } + + public double randomScore() { + String seedValue = Integer.toString(scoreScript._getDocBaseId()); + int hash = StringHelper.murmurhash3_x86_32(new BytesRef(seedValue), saltedSeed); + return (hash & 0x00FFFFFF) / (float)(1 << 24); // only use the lower 24 bits to construct a float from 0.0-1.0 + } + } // **** Decay functions on geo field public static final class DecayGeoLinear { From f732a04b863c6baeb252387b6e7d5935f23ebd30 Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Thu, 28 Mar 2019 17:35:33 +0200 Subject: [PATCH 56/77] Disable integTest when Docker is not available (#40585) * Disable integTest when Docker is not available Resolves: #40549 --- x-pack/qa/saml-idp-tests/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/qa/saml-idp-tests/build.gradle b/x-pack/qa/saml-idp-tests/build.gradle index 44a28278636a9..7b76321fe9d4f 100644 --- a/x-pack/qa/saml-idp-tests/build.gradle +++ b/x-pack/qa/saml-idp-tests/build.gradle @@ -38,6 +38,9 @@ task setupPorts { idpMetaFile.write(content.toString(), "UTF-8") } } +// Don't attempt to get ephemeral ports when Docker is not available +setupPorts.onlyIf { idpFixtureProject.postProcessFixture.enabled } + integTestCluster.dependsOn setupPorts integTestCluster { From c6b9868192f1fb96a50e5d1c823b3302080556fd Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Thu, 28 Mar 2019 16:53:20 +0100 Subject: [PATCH 57/77] Mute DataFrameAuditorIT#testAuditorWritesAudits Relates to #40594 --- .../xpack/dataframe/integration/DataFrameAuditorIT.java | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameAuditorIT.java b/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameAuditorIT.java index 750faf8dade51..2367e255cd9ba 100644 --- a/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameAuditorIT.java +++ b/x-pack/plugin/data-frame/qa/single-node-tests/src/test/java/org/elasticsearch/xpack/dataframe/integration/DataFrameAuditorIT.java @@ -49,6 +49,7 @@ public void createIndexes() throws IOException { setupUser(TEST_USER_NAME, Arrays.asList("data_frame_transforms_admin", DATA_ACCESS_ROLE)); } + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/40594") @SuppressWarnings("unchecked") public void testAuditorWritesAudits() throws Exception { String transformId = "simplePivotForAudit"; From fc404e59f5245411cc3ca702d3064b5436a88198 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Thu, 28 Mar 2019 09:41:56 -0700 Subject: [PATCH 58/77] [DOCS] Adds anchors for ruby client (#39867) --- docs/ruby/client.asciidoc | 1 + docs/ruby/copyright.asciidoc | 1 + docs/ruby/model.asciidoc | 1 + docs/ruby/persistence.asciidoc | 1 + docs/ruby/rails.asciidoc | 1 + 5 files changed, 5 insertions(+) diff --git a/docs/ruby/client.asciidoc b/docs/ruby/client.asciidoc index 2037ae1a0b280..074c77d41b03b 100644 --- a/docs/ruby/client.asciidoc +++ b/docs/ruby/client.asciidoc @@ -1,3 +1,4 @@ +[[ruby_client]] == The Ruby Client The `elasticsearch` http://rubygems.org/gems/elasticsearch[Rubygem] provides a low-level client diff --git a/docs/ruby/copyright.asciidoc b/docs/ruby/copyright.asciidoc index 3747cc572e40f..8a84be27636f4 100644 --- a/docs/ruby/copyright.asciidoc +++ b/docs/ruby/copyright.asciidoc @@ -1,3 +1,4 @@ +[[copyright]] == Copyright and License This software is Copyright (c) 2013-2018 by Elasticsearch BV. diff --git a/docs/ruby/model.asciidoc b/docs/ruby/model.asciidoc index 0b0be45708fa8..62339bb239149 100644 --- a/docs/ruby/model.asciidoc +++ b/docs/ruby/model.asciidoc @@ -1,3 +1,4 @@ +[[activemodel_activerecord]] == ActiveModel / ActiveRecord The `elasticsearch-model` http://rubygems.org/gems/elasticsearch-model[Rubygem] diff --git a/docs/ruby/persistence.asciidoc b/docs/ruby/persistence.asciidoc index 7d361978ee703..5306dae47c661 100644 --- a/docs/ruby/persistence.asciidoc +++ b/docs/ruby/persistence.asciidoc @@ -1,3 +1,4 @@ +[[persistence]] == Persistence The `elasticsearch-persistence` http://rubygems.org/gems/elasticsearch-persistence[Rubygem] diff --git a/docs/ruby/rails.asciidoc b/docs/ruby/rails.asciidoc index 1fef3f42381a6..213258c7e2266 100644 --- a/docs/ruby/rails.asciidoc +++ b/docs/ruby/rails.asciidoc @@ -1,3 +1,4 @@ +[[ruby_on_rails]] == Ruby On Rails The `elasticsearch-rails` http://rubygems.org/gems/elasticsearch-rails[Rubygem] From 9ff6bbce1d78812bcc254dbd0dfb6583f7d14221 Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Thu, 28 Mar 2019 10:42:28 -0600 Subject: [PATCH 59/77] Handle null retention leases in WaitForNoFollowersStep (#40477) In some cases the retention leases can return null, causing a `NullPointerException` when waiting for no followers. This wraps those so that no NPE is thrown. Here is an example failure: ``` [2019-03-26T09:24:01,368][ERROR][o.e.x.i.IndexLifecycleRunner] [node-0] policy [deletePolicy] for index [ilm-00001] failed on step [{"phase":"delete","action":"delete","name":"wait-for-shard-history-leases"}]. Moving to ERROR step java.lang.NullPointerException: null at org.elasticsearch.xpack.core.indexlifecycle.WaitForNoFollowersStep.lambda$evaluateCondition$0(WaitForNoFollowersStep.java:60) ~[?:?] at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:267) ~[?:1.8.0_191] at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[?:1.8.0_191] at java.util.Spliterators$ArraySpliterator.tryAdvance(Spliterators.java:958) ~[?:1.8.0_191] at java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126) ~[?:1.8.0_191] at java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:498) ~[?:1.8.0_191] at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:485) ~[?:1.8.0_191] at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) ~[?:1.8.0_191] at java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230) ~[?:1.8.0_191] at java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196) ~[?:1.8.0_191] at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:1.8.0_191] at java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:449) ~[?:1.8.0_191] at org.elasticsearch.xpack.core.indexlifecycle.WaitForNoFollowersStep.lambda$evaluateCondition$2(WaitForNoFollowersStep.java:61) ~[?:?] at org.elasticsearch.action.ActionListener$1.onResponse(ActionListener.java:62) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.ContextPreservingActionListener.onResponse(ContextPreservingActionListener.java:43) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.TransportAction$1.onResponse(TransportAction.java:68) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.TransportAction$1.onResponse(TransportAction.java:64) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.ContextPreservingActionListener.onResponse(ContextPreservingActionListener.java:43) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.broadcast.node.TransportBroadcastByNodeAction$AsyncAction.onCompletion(TransportBroadcastByNodeAction.java:383) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.broadcast.node.TransportBroadcastByNodeAction$AsyncAction.onNodeResponse(TransportBroadcastByNodeAction.java:352) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.broadcast.node.TransportBroadcastByNodeAction$AsyncAction$1.handleResponse(TransportBroadcastByNodeAction.java:324) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.action.support.broadcast.node.TransportBroadcastByNodeAction$AsyncAction$1.handleResponse(TransportBroadcastByNodeAction.java:314) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.transport.TransportService$ContextRestoreResponseHandler.handleResponse(TransportService.java:1095) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] at org.elasticsearch.transport.TransportService$DirectResponseChannel.processResponse(TransportService.java:1176) ~[elasticsearch-8.0.0-SNAPSHOT.jar:8.0.0-SNAPSHOT] ... ``` --- .../WaitForNoFollowersStep.java | 9 +++-- .../WaitForNoFollowersStepTests.java | 36 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStep.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStep.java index 3cfaeba048d5f..958120b99b879 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStep.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStep.java @@ -20,7 +20,9 @@ import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.Objects; +import java.util.Optional; /** * A step that waits until the index it's used on is no longer a leader index. @@ -57,8 +59,11 @@ public void evaluateCondition(IndexMetaData indexMetaData, Listener listener) { boolean isCurrentlyLeaderIndex = Arrays.stream(indexStats.getShards()) .map(ShardStats::getRetentionLeaseStats) - .flatMap(retentionLeaseStats -> retentionLeaseStats.retentionLeases().leases().stream()) - .anyMatch(lease -> CCR_LEASE_KEY.equals(lease.source())); + .map(Optional::ofNullable) + .map(o -> o.flatMap(stats -> Optional.ofNullable(stats.retentionLeases()))) + .map(o -> o.flatMap(leases -> Optional.ofNullable(leases.leases()))) + .map(o -> o.map(Collection::stream)) + .anyMatch(lease -> lease.isPresent() && lease.get().anyMatch(l -> CCR_LEASE_KEY.equals(l.source()))); if (isCurrentlyLeaderIndex) { listener.onResponse(false, new Info()); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStepTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStepTests.java index f1f3c053e2345..6953455489d1a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStepTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/indexlifecycle/WaitForNoFollowersStepTests.java @@ -132,6 +132,42 @@ public void onFailure(Exception e) { containsString("this index is a leader index; waiting for all following indices to cease following before proceeding")); } + public void testNoShardStats() { + WaitForNoFollowersStep step = createRandomInstance(); + + String indexName = randomAlphaOfLengthBetween(5,10); + + int numberOfShards = randomIntBetween(1, 100); + final IndexMetaData indexMetaData = IndexMetaData.builder(indexName) + .settings(settings(Version.CURRENT)) + .numberOfShards(numberOfShards) + .numberOfReplicas(randomIntBetween(1, 10)) + .build(); + + ShardStats sStats = new ShardStats(null, mockShardPath(), null, null, null, null); + ShardStats[] shardStats = new ShardStats[1]; + shardStats[0] = sStats; + mockIndexStatsCall(step.getClient(), indexName, new IndexStats(indexName, "uuid", shardStats)); + + final SetOnce conditionMetHolder = new SetOnce<>(); + final SetOnce stepInfoHolder = new SetOnce<>(); + step.evaluateCondition(indexMetaData, new AsyncWaitStep.Listener() { + @Override + public void onResponse(boolean conditionMet, ToXContentObject infomationContext) { + conditionMetHolder.set(conditionMet); + stepInfoHolder.set(infomationContext); + } + + @Override + public void onFailure(Exception e) { + fail("onFailure should not be called in this test, called with exception: " + e.getMessage()); + } + }); + + assertTrue(conditionMetHolder.get()); + assertNull(stepInfoHolder.get()); + } + public void testFailure() { WaitForNoFollowersStep step = createRandomInstance(); From bd9e9b3acf0b08ba3bc1c647fbffc22e2d305002 Mon Sep 17 00:00:00 2001 From: Henning Andersen <33268011+henningandersen@users.noreply.github.com> Date: Thu, 28 Mar 2019 18:55:31 +0100 Subject: [PATCH 60/77] Geo Point parse error fix (#40447) When geo point parsing threw a parse exception, it did not consume remaining tokens from the parser. This in turn meant that indexing documents with malformed geo points into mappings with ignore_malformed=true would fail in some cases, since DocumentParser expects geo_point parsing to end on the END_OBJECT token. Related to #17617 --- .../common/xcontent/XContentSubParser.java | 6 +- .../common/xcontent/XContentParserTests.java | 48 +++++++- .../elasticsearch/common/geo/GeoUtils.java | 104 +++++++++--------- .../mapper/GeoPointFieldMapperTests.java | 10 ++ .../index/search/geo/GeoUtilsTests.java | 32 ++++++ 5 files changed, 145 insertions(+), 55 deletions(-) diff --git a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentSubParser.java b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentSubParser.java index e02f9f176246e..adcbf6ef1bee0 100644 --- a/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentSubParser.java +++ b/libs/x-content/src/main/java/org/elasticsearch/common/xcontent/XContentSubParser.java @@ -25,7 +25,7 @@ import java.util.Map; /** - * Wrapper for a XContentParser that makes a single object to look like a complete document. + * Wrapper for a XContentParser that makes a single object/array look like a complete document. * * The wrapper prevents the parsing logic to consume tokens outside of the wrapped object as well * as skipping to the end of the object in case of a parsing error. The wrapper is intended to be @@ -39,8 +39,8 @@ public class XContentSubParser implements XContentParser { public XContentSubParser(XContentParser parser) { this.parser = parser; - if (parser.currentToken() != Token.START_OBJECT) { - throw new IllegalStateException("The sub parser has to be created on the start of an object"); + if (parser.currentToken() != Token.START_OBJECT && parser.currentToken() != Token.START_ARRAY) { + throw new IllegalStateException("The sub parser has to be created on the start of an object or array"); } level = 1; } diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java index 5dbe7be40f312..e98f1e3d58510 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java @@ -329,7 +329,7 @@ public void testNestedMapInList() throws IOException { } } - public void testSubParser() throws IOException { + public void testSubParserObject() throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); int numberOfTokens; numberOfTokens = generateRandomObjectForMarking(builder); @@ -354,6 +354,7 @@ public void testSubParser() throws IOException { // And sometimes skipping children subParser.skipChildren(); } + } finally { assertFalse(subParser.isClosed()); subParser.close(); @@ -367,6 +368,49 @@ public void testSubParser() throws IOException { } } + public void testSubParserArray() throws IOException { + XContentBuilder builder = XContentFactory.jsonBuilder(); + int numberOfArrayElements = randomInt(10); + builder.startObject(); + builder.field("array"); + builder.startArray(); + int numberOfTokens = 0; + for (int i = 0; i < numberOfArrayElements; ++i) { + numberOfTokens += generateRandomObjectForMarking(builder); + } + builder.endArray(); + builder.endObject(); + + String content = Strings.toString(builder); + + try (XContentParser parser = createParser(JsonXContent.jsonXContent, content)) { + assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken()); + assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); // array field + assertEquals("array", parser.currentName()); + assertEquals(XContentParser.Token.START_ARRAY, parser.nextToken()); // [ + XContentParser subParser = new XContentSubParser(parser); + try { + int tokensToSkip = randomInt(numberOfTokens - 1); + for (int i = 0; i < tokensToSkip; i++) { + // Simulate incomplete parsing + assertNotNull(subParser.nextToken()); + } + if (randomBoolean()) { + // And sometimes skipping children + subParser.skipChildren(); + } + + } finally { + assertFalse(subParser.isClosed()); + subParser.close(); + assertTrue(subParser.isClosed()); + } + assertEquals(XContentParser.Token.END_ARRAY, parser.currentToken()); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + } + public void testCreateSubParserAtAWrongPlace() throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); generateRandomObjectForMarking(builder); @@ -377,7 +421,7 @@ public void testCreateSubParserAtAWrongPlace() throws IOException { assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken()); // first field assertEquals("first_field", parser.currentName()); IllegalStateException exception = expectThrows(IllegalStateException.class, () -> new XContentSubParser(parser)); - assertEquals("The sub parser has to be created on the start of an object", exception.getMessage()); + assertEquals("The sub parser has to be created on the start of an object or array", exception.getMessage()); } } diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java b/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java index a45667b908d74..6dcaaaa7d6a29 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.common.xcontent.XContentSubParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.fielddata.FieldData; @@ -435,51 +436,52 @@ public static GeoPoint parseGeoPoint(XContentParser parser, GeoPoint point, fina NumberFormatException numberFormatException = null; if(parser.currentToken() == Token.START_OBJECT) { - while(parser.nextToken() != Token.END_OBJECT) { - if(parser.currentToken() == Token.FIELD_NAME) { - String field = parser.currentName(); - if(LATITUDE.equals(field)) { - parser.nextToken(); - switch (parser.currentToken()) { - case VALUE_NUMBER: - case VALUE_STRING: - try { - lat = parser.doubleValue(true); - } catch (NumberFormatException e) { - numberFormatException = e; - } - break; - default: - throw new ElasticsearchParseException("latitude must be a number"); - } - } else if (LONGITUDE.equals(field)) { - parser.nextToken(); - switch (parser.currentToken()) { - case VALUE_NUMBER: - case VALUE_STRING: - try { - lon = parser.doubleValue(true); - } catch (NumberFormatException e) { - numberFormatException = e; - } - break; - default: - throw new ElasticsearchParseException("longitude must be a number"); - } - } else if (GEOHASH.equals(field)) { - if(parser.nextToken() == Token.VALUE_STRING) { - geohash = parser.text(); + try (XContentSubParser subParser = new XContentSubParser(parser)) { + while (subParser.nextToken() != Token.END_OBJECT) { + if (subParser.currentToken() == Token.FIELD_NAME) { + String field = subParser.currentName(); + if (LATITUDE.equals(field)) { + subParser.nextToken(); + switch (subParser.currentToken()) { + case VALUE_NUMBER: + case VALUE_STRING: + try { + lat = subParser.doubleValue(true); + } catch (NumberFormatException e) { + numberFormatException = e; + } + break; + default: + throw new ElasticsearchParseException("latitude must be a number"); + } + } else if (LONGITUDE.equals(field)) { + subParser.nextToken(); + switch (subParser.currentToken()) { + case VALUE_NUMBER: + case VALUE_STRING: + try { + lon = subParser.doubleValue(true); + } catch (NumberFormatException e) { + numberFormatException = e; + } + break; + default: + throw new ElasticsearchParseException("longitude must be a number"); + } + } else if (GEOHASH.equals(field)) { + if (subParser.nextToken() == Token.VALUE_STRING) { + geohash = subParser.text(); + } else { + throw new ElasticsearchParseException("geohash must be a string"); + } } else { - throw new ElasticsearchParseException("geohash must be a string"); + throw new ElasticsearchParseException("field must be either [{}], [{}] or [{}]", LATITUDE, LONGITUDE, GEOHASH); } } else { - throw new ElasticsearchParseException("field must be either [{}], [{}] or [{}]", LATITUDE, LONGITUDE, GEOHASH); + throw new ElasticsearchParseException("token [{}] not allowed", subParser.currentToken()); } - } else { - throw new ElasticsearchParseException("token [{}] not allowed", parser.currentToken()); } } - if (geohash != null) { if(!Double.isNaN(lat) || !Double.isNaN(lon)) { throw new ElasticsearchParseException("field must be either lat/lon or geohash"); @@ -498,19 +500,21 @@ public static GeoPoint parseGeoPoint(XContentParser parser, GeoPoint point, fina } } else if(parser.currentToken() == Token.START_ARRAY) { - int element = 0; - while(parser.nextToken() != Token.END_ARRAY) { - if(parser.currentToken() == Token.VALUE_NUMBER) { - element++; - if(element == 1) { - lon = parser.doubleValue(); - } else if(element == 2) { - lat = parser.doubleValue(); + try (XContentSubParser subParser = new XContentSubParser(parser)) { + int element = 0; + while (subParser.nextToken() != Token.END_ARRAY) { + if (subParser.currentToken() == Token.VALUE_NUMBER) { + element++; + if (element == 1) { + lon = subParser.doubleValue(); + } else if (element == 2) { + lat = subParser.doubleValue(); + } else { + GeoPoint.assertZValue(ignoreZValue, subParser.doubleValue()); + } } else { - GeoPoint.assertZValue(ignoreZValue, parser.doubleValue()); + throw new ElasticsearchParseException("numeric value expected"); } - } else { - throw new ElasticsearchParseException("numeric value expected"); } } return point.reset(lat, lon); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java index f5597ecb1f443..2142fca565c9b 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldMapperTests.java @@ -523,5 +523,15 @@ public void testInvalidGeopointValuesIgnored() throws Exception { BytesReference.bytes(XContentFactory.jsonBuilder() .startObject().field("location", "NaN,12").endObject() ), XContentType.JSON)).rootDoc().getField("location"), nullValue()); + + assertThat(defaultMapper.parse(new SourceToParse("test", "type", "1", + BytesReference.bytes(XContentFactory.jsonBuilder() + .startObject().startObject("location").nullField("lat").field("lon", 1).endObject().endObject() + ), XContentType.JSON)).rootDoc().getField("location"), nullValue()); + + assertThat(defaultMapper.parse(new SourceToParse("test", "type", "1", + BytesReference.bytes(XContentFactory.jsonBuilder() + .startObject().startObject("location").nullField("lat").nullField("lon").endObject().endObject() + ), XContentType.JSON)).rootDoc().getField("location"), nullValue()); } } diff --git a/server/src/test/java/org/elasticsearch/index/search/geo/GeoUtilsTests.java b/server/src/test/java/org/elasticsearch/index/search/geo/GeoUtilsTests.java index ee916dd4c47dd..1a85e29f02090 100644 --- a/server/src/test/java/org/elasticsearch/index/search/geo/GeoUtilsTests.java +++ b/server/src/test/java/org/elasticsearch/index/search/geo/GeoUtilsTests.java @@ -397,6 +397,8 @@ public void testParseGeoPoint() throws IOException { parser.nextToken(); GeoPoint point = GeoUtils.parseGeoPoint(parser); assertThat(point, equalTo(new GeoPoint(lat, lon))); + assertThat(parser.currentToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } json = jsonBuilder().startObject().field("lat", String.valueOf(lat)).field("lon", String.valueOf(lon)).endObject(); try (XContentParser parser = createParser(json)) { @@ -438,6 +440,21 @@ public void testParseGeoPointStringZValueError() throws IOException { } } + public void testParseGeoPointArrayZValueError() throws IOException { + double lat = randomDouble() * 180 - 90 + randomIntBetween(-1000, 1000) * 180; + double lon = randomDouble() * 360 - 180 + randomIntBetween(-1000, 1000) * 360; + double alt = randomDouble() * 1000; + XContentBuilder json = jsonBuilder().startArray().value(lat).value(lon).value(alt).endArray(); + try (XContentParser parser = createParser(json)) { + parser.nextToken(); + Exception e = expectThrows(ElasticsearchParseException.class, + () -> GeoUtils.parseGeoPoint(parser, new GeoPoint(), false)); + assertThat(e.getMessage(), containsString("but [ignore_z_value] parameter is [false]")); + assertThat(parser.currentToken(), is(Token.END_ARRAY)); + assertNull(parser.nextToken()); + } + } + public void testParseGeoPointGeohash() throws IOException { for (int i = 0; i < 100; i++) { int geoHashLength = randomIntBetween(1, GeoHashUtils.PRECISION); @@ -451,6 +468,8 @@ public void testParseGeoPointGeohash() throws IOException { GeoPoint point = GeoUtils.parseGeoPoint(parser); assertThat(point.lat(), allOf(lessThanOrEqualTo(90.0), greaterThanOrEqualTo(-90.0))); assertThat(point.lon(), allOf(lessThanOrEqualTo(180.0), greaterThanOrEqualTo(-180.0))); + assertThat(parser.currentToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } json = jsonBuilder().startObject().field("geohash", geohashBuilder.toString()).endObject(); try (XContentParser parser = createParser(json)) { @@ -470,6 +489,8 @@ public void testParseGeoPointGeohashWrongType() throws IOException { parser.nextToken(); Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser)); assertThat(e.getMessage(), containsString("geohash must be a string")); + assertThat(parser.currentToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } } @@ -480,6 +501,8 @@ public void testParseGeoPointLatNoLon() throws IOException { parser.nextToken(); Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser)); assertThat(e.getMessage(), is("field [lon] missing")); + assertThat(parser.currentToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } } @@ -490,6 +513,8 @@ public void testParseGeoPointLonNoLat() throws IOException { parser.nextToken(); Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser)); assertThat(e.getMessage(), is("field [lat] missing")); + assertThat(parser.currentToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } } @@ -500,6 +525,8 @@ public void testParseGeoPointLonWrongType() throws IOException { parser.nextToken(); Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser)); assertThat(e.getMessage(), is("longitude must be a number")); + assertThat(parser.currentToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } } @@ -510,6 +537,8 @@ public void testParseGeoPointLatWrongType() throws IOException { parser.nextToken(); Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser)); assertThat(e.getMessage(), is("latitude must be a number")); + assertThat(parser.currentToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } } @@ -578,6 +607,9 @@ public void testParseGeoPointArrayWrongType() throws IOException { } Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser)); assertThat(e.getMessage(), is("numeric value expected")); + assertThat(parser.currentToken(), is(Token.END_ARRAY)); + assertThat(parser.nextToken(), is(Token.END_OBJECT)); + assertNull(parser.nextToken()); } } From 3fffae6714fe8054a4d0fda3c97b8bb37e857bee Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Thu, 28 Mar 2019 12:06:18 -0600 Subject: [PATCH 61/77] Remove with(out)-system-key tests (#40547) This change removes the variants of the rolling upgrade and full cluster restart tests that use or do not use a system key. These tests were added during 5.x when the system key was still used for security and now the system key is only used as the watcher encryption key so duplicating rolling upgrade and full cluster restarts is not needed. The change here removes the subprojects for testing these scenarios and defaults to always run with the watcher sensitive values encrypted for these tests. --- x-pack/qa/full-cluster-restart/build.gradle | 275 +++++-------- .../with-system-key/build.gradle | 0 .../without-system-key/build.gradle | 0 x-pack/qa/rolling-upgrade/build.gradle | 388 +++++++----------- .../with-system-key/build.gradle | 1 - .../without-system-key/build.gradle | 1 - 6 files changed, 262 insertions(+), 403 deletions(-) delete mode 100644 x-pack/qa/full-cluster-restart/with-system-key/build.gradle delete mode 100644 x-pack/qa/full-cluster-restart/without-system-key/build.gradle delete mode 100644 x-pack/qa/rolling-upgrade/with-system-key/build.gradle delete mode 100644 x-pack/qa/rolling-upgrade/without-system-key/build.gradle diff --git a/x-pack/qa/full-cluster-restart/build.gradle b/x-pack/qa/full-cluster-restart/build.gradle index 4c2ecd2b7b4ca..40dca76abc913 100644 --- a/x-pack/qa/full-cluster-restart/build.gradle +++ b/x-pack/qa/full-cluster-restart/build.gradle @@ -3,10 +3,10 @@ import org.elasticsearch.gradle.test.RestIntegTestTask import org.elasticsearch.gradle.Version import java.nio.charset.StandardCharsets -import java.util.regex.Matcher // Apply the java plugin to this project so the sources can be edited in an IDE -apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.standalone-test' + unitTest.enabled = false dependencies { @@ -70,8 +70,6 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant -> return tmpFile.exists() } -Project mainProject = project - String coreFullClusterRestartPath = project(':qa:full-cluster-restart').projectDir.toPath().resolve('src/test/java').toString() sourceSets { test { @@ -89,224 +87,157 @@ forbiddenPatterns { exclude '**/system_key' } -// tests are pushed down to subprojects -testingConventions.enabled = false - -/** - * Subdirectories of this project are test rolling upgrades with various - * configuration options based on their name. - */ -subprojects { - Matcher m = project.name =~ /with(out)?-system-key/ - if (false == m.matches()) { - throw new InvalidUserDataException("Invalid project name [${project.name}]") - } - boolean withSystemKey = m.group(1) == null - - apply plugin: 'elasticsearch.standalone-test' +String outputDir = "${buildDir}/generated-resources/${project.name}" - // Use resources from the rolling-upgrade project in subdirectories - sourceSets { - test { - java { - srcDirs = ["${mainProject.projectDir}/src/test/java", coreFullClusterRestartPath] - } - resources { - srcDirs = ["${mainProject.projectDir}/src/test/resources"] - } - } - } - - licenseHeaders { - approvedLicenses << 'Apache' - } - - forbiddenPatterns { - exclude '**/system_key' - } - - String outputDir = "${buildDir}/generated-resources/${project.name}" - - // This is a top level task which we will add dependencies to below. - // It is a single task that can be used to backcompat tests against all versions. - task bwcTest { +// This is a top level task which we will add dependencies to below. +// It is a single task that can be used to backcompat tests against all versions. +task bwcTest { description = 'Runs backwards compatibility tests.' group = 'verification' - } +} - String output = "${buildDir}/generated-resources/${project.name}" - task copyTestNodeKeyMaterial(type: Copy) { +task copyTestNodeKeyMaterial(type: Copy) { from project(':x-pack:plugin:core').files('src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem', - 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt', - 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks') + 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt', + 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks') into outputDir - } +} - for (Version version : bwcVersions.indexCompatible) { +for (Version version : bwcVersions.indexCompatible) { String baseName = "v${version}" Task oldClusterTest = tasks.create(name: "${baseName}#oldClusterTest", type: RestIntegTestTask) { - mustRunAfter(precommit) + mustRunAfter(precommit) } Object extension = extensions.findByName("${baseName}#oldClusterTestCluster") configure(extensions.findByName("${baseName}#oldClusterTestCluster")) { - dependsOn copyTestNodeKeyMaterial - if (version.before('6.3.0')) { - String depVersion = version; - if (project.bwcVersions.unreleased.contains(version)) { - depVersion += "-SNAPSHOT" - } - mavenPlugin 'x-pack', "org.elasticsearch.plugin:x-pack:${depVersion}" - - } - bwcVersion = version - numBwcNodes = 2 - numNodes = 2 - clusterName = 'full-cluster-restart' - String usersCli = version.before('6.3.0') ? 'bin/x-pack/users' : 'bin/elasticsearch-users' - setupCommand 'setupTestUser', usersCli, 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' - waitCondition = waitWithAuth - - // some tests rely on the translog not being flushed - setting 'indices.memory.shard_inactive_time', '20m' - - setting 'xpack.security.enabled', 'true' - setting 'xpack.security.transport.ssl.enabled', 'true' - if (project.inFipsJvm) { - setting 'xpack.security.transport.ssl.key', 'testnode.pem' - setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' - keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' - } else { - setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' - setting 'xpack.security.transport.ssl.keystore.password', 'testnode' - } - setting 'xpack.license.self_generated.type', 'trial' - dependsOn copyTestNodeKeyMaterial - extraConfigFile 'testnode.pem', new File(outputDir + '/testnode.pem') - extraConfigFile 'testnode.crt', new File(outputDir + '/testnode.crt') - extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') - if (withSystemKey) { - if (version.onOrAfter('5.1.0') && version.before('6.0.0')) { - // The setting didn't exist until 5.1.0 - setting 'xpack.security.system_key.required', 'true' + dependsOn copyTestNodeKeyMaterial + if (version.before('6.3.0')) { + String depVersion = version; + if (project.bwcVersions.unreleased.contains(version)) { + depVersion += "-SNAPSHOT" + } + mavenPlugin 'x-pack', "org.elasticsearch.plugin:x-pack:${depVersion}" + } - if (version.onOrAfter('6.0.0')) { - keystoreFile 'xpack.watcher.encryption_key', "${mainProject.projectDir}/src/test/resources/system_key" + bwcVersion = version + numBwcNodes = 2 + numNodes = 2 + clusterName = 'full-cluster-restart' + String usersCli = version.before('6.3.0') ? 'bin/x-pack/users' : 'bin/elasticsearch-users' + setupCommand 'setupTestUser', usersCli, 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' + waitCondition = waitWithAuth + + // some tests rely on the translog not being flushed + setting 'indices.memory.shard_inactive_time', '20m' + + setting 'xpack.security.enabled', 'true' + setting 'xpack.security.transport.ssl.enabled', 'true' + if (project.inFipsJvm) { + setting 'xpack.security.transport.ssl.key', 'testnode.pem' + setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' + keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' } else { - extraConfigFile 'x-pack/system_key', "${mainProject.projectDir}/src/test/resources/system_key" + setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' + setting 'xpack.security.transport.ssl.keystore.password', 'testnode' } + setting 'xpack.license.self_generated.type', 'trial' + dependsOn copyTestNodeKeyMaterial + extraConfigFile 'testnode.pem', new File(outputDir + '/testnode.pem') + extraConfigFile 'testnode.crt', new File(outputDir + '/testnode.crt') + extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') + + keystoreFile 'xpack.watcher.encryption_key', "${project.projectDir}/src/test/resources/system_key" setting 'xpack.watcher.encrypt_sensitive_data', 'true' - } } Task oldClusterTestRunner = tasks.getByName("${baseName}#oldClusterTestRunner") oldClusterTestRunner.configure { - systemProperty 'tests.is_old_cluster', 'true' - systemProperty 'tests.old_cluster_version', version.toString().minus("-SNAPSHOT") - systemProperty 'tests.path.repo', new File(buildDir, "cluster/shared/repo") - exclude 'org/elasticsearch/upgrades/FullClusterRestartIT.class' - exclude 'org/elasticsearch/upgrades/FullClusterRestartSettingsUpgradeIT.class' - exclude 'org/elasticsearch/upgrades/QueryBuilderBWCIT.class' + systemProperty 'tests.is_old_cluster', 'true' + systemProperty 'tests.old_cluster_version', version.toString().minus("-SNAPSHOT") + systemProperty 'tests.path.repo', new File(buildDir, "cluster/shared/repo") + exclude 'org/elasticsearch/upgrades/FullClusterRestartIT.class' + exclude 'org/elasticsearch/upgrades/FullClusterRestartSettingsUpgradeIT.class' + exclude 'org/elasticsearch/upgrades/QueryBuilderBWCIT.class' } Task upgradedClusterTest = tasks.create(name: "${baseName}#upgradedClusterTest", type: RestIntegTestTask) configure(extensions.findByName("${baseName}#upgradedClusterTestCluster")) { - dependsOn oldClusterTestRunner, - "${baseName}#oldClusterTestCluster#node0.stop", - "${baseName}#oldClusterTestCluster#node1.stop" - numNodes = 2 - clusterName = 'full-cluster-restart' - dataDir = { nodeNum -> oldClusterTest.nodes[nodeNum].dataDir } - cleanShared = false // We want to keep snapshots made by the old cluster! - setupCommand 'setupTestUser', 'bin/elasticsearch-users', 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' - waitCondition = waitWithAuth - - // some tests rely on the translog not being flushed - setting 'indices.memory.shard_inactive_time', '20m' - setting 'xpack.security.enabled', 'true' - if (project.inFipsJvm) { - setting 'xpack.security.transport.ssl.key', 'testnode.pem' - setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' - keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' - } else { - setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' - setting 'xpack.security.transport.ssl.keystore.password', 'testnode' - } - setting 'xpack.license.self_generated.type', 'trial' - dependsOn copyTestNodeKeyMaterial - extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') - extraConfigFile 'testnode.pem', new File(outputDir + '/testnode.pem') - extraConfigFile 'testnode.crt', new File(outputDir + '/testnode.crt') - if (withSystemKey) { - setting 'xpack.watcher.encrypt_sensitive_data', 'true' - keystoreFile 'xpack.watcher.encryption_key', "${mainProject.projectDir}/src/test/resources/system_key" - } + dependsOn oldClusterTestRunner, + "${baseName}#oldClusterTestCluster#node0.stop", + "${baseName}#oldClusterTestCluster#node1.stop" + numNodes = 2 + clusterName = 'full-cluster-restart' + dataDir = { nodeNum -> oldClusterTest.nodes[nodeNum].dataDir } + cleanShared = false // We want to keep snapshots made by the old cluster! + setupCommand 'setupTestUser', 'bin/elasticsearch-users', 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' + waitCondition = waitWithAuth + + // some tests rely on the translog not being flushed + setting 'indices.memory.shard_inactive_time', '20m' + setting 'xpack.security.enabled', 'true' + if (project.inFipsJvm) { + setting 'xpack.security.transport.ssl.key', 'testnode.pem' + setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' + keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' + } else { + setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' + setting 'xpack.security.transport.ssl.keystore.password', 'testnode' + } + setting 'xpack.license.self_generated.type', 'trial' + dependsOn copyTestNodeKeyMaterial + extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') + extraConfigFile 'testnode.pem', new File(outputDir + '/testnode.pem') + extraConfigFile 'testnode.crt', new File(outputDir + '/testnode.crt') + + setting 'xpack.watcher.encrypt_sensitive_data', 'true' + keystoreFile 'xpack.watcher.encryption_key', "${project.projectDir}/src/test/resources/system_key" } Task upgradedClusterTestRunner = tasks.getByName("${baseName}#upgradedClusterTestRunner") upgradedClusterTestRunner.configure { - systemProperty 'tests.is_old_cluster', 'false' - systemProperty 'tests.old_cluster_version', version.toString().minus("-SNAPSHOT") - systemProperty 'tests.path.repo', new File(buildDir, "cluster/shared/repo") - exclude 'org/elasticsearch/upgrades/FullClusterRestartIT.class' - exclude 'org/elasticsearch/upgrades/FullClusterRestartSettingsUpgradeIT.class' - exclude 'org/elasticsearch/upgrades/QueryBuilderBWCIT.class' + systemProperty 'tests.is_old_cluster', 'false' + systemProperty 'tests.old_cluster_version', version.toString().minus("-SNAPSHOT") + systemProperty 'tests.path.repo', new File(buildDir, "cluster/shared/repo") + exclude 'org/elasticsearch/upgrades/FullClusterRestartIT.class' + exclude 'org/elasticsearch/upgrades/FullClusterRestartSettingsUpgradeIT.class' + exclude 'org/elasticsearch/upgrades/QueryBuilderBWCIT.class' } Task versionBwcTest = tasks.create(name: "${baseName}#bwcTest") { - dependsOn = [upgradedClusterTest] + dependsOn = [upgradedClusterTest] } if (project.bwc_tests_enabled) { - bwcTest.dependsOn(versionBwcTest) + bwcTest.dependsOn(versionBwcTest) } - } - - unitTest.enabled = false // no unit tests for full cluster restarts, only the rest integration test +} - // basic integ tests includes testing bwc against the most recent version - task bwcTestSnapshots { +// basic integ tests includes testing bwc against the most recent version +task bwcTestSnapshots { if (project.bwc_tests_enabled) { - for (final def version : bwcVersions.unreleasedIndexCompatible) { - dependsOn "v${version}#bwcTest" - } + for (final def version : bwcVersions.unreleasedIndexCompatible) { + dependsOn "v${version}#bwcTest" + } } - } - - check.dependsOn(bwcTestSnapshots) +} - dependencies { - // "org.elasticsearch.plugin:x-pack-core:${version}" doesn't work with idea because the testArtifacts are also here - testCompile project(path: xpackModule('core'), configuration: 'default') - testCompile project(path: xpackModule('watcher'), configuration: 'runtime') - testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') - testCompile project(path: xpackModule('security'), configuration: 'testArtifacts') - } +check.dependsOn(bwcTestSnapshots) - // copy x-pack plugin info so it is on the classpath and security manager has the right permissions - task copyXPackRestSpec(type: Copy) { +// copy x-pack plugin info so it is on the classpath and security manager has the right permissions +task copyXPackRestSpec(type: Copy) { dependsOn(project.configurations.restSpec, 'processTestResources') from project(xpackModule('core')).sourceSets.test.resources include 'rest-api-spec/api/**' into project.sourceSets.test.output.resourcesDir - } +} - task copyXPackPluginProps(type: Copy) { +task copyXPackPluginProps(type: Copy) { dependsOn(copyXPackRestSpec) from project(xpackModule('core')).file('src/main/plugin-metadata') from project(xpackModule('core')).tasks.pluginProperties into outputDir - } - project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps) - - repositories { - maven { - url "https://artifacts.elastic.co/maven" - } - maven { - url "https://snapshots.elastic.co/maven" - } - } } +project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps) diff --git a/x-pack/qa/full-cluster-restart/with-system-key/build.gradle b/x-pack/qa/full-cluster-restart/with-system-key/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/x-pack/qa/full-cluster-restart/without-system-key/build.gradle b/x-pack/qa/full-cluster-restart/without-system-key/build.gradle deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/x-pack/qa/rolling-upgrade/build.gradle b/x-pack/qa/rolling-upgrade/build.gradle index f8222669b218e..f689573a61437 100644 --- a/x-pack/qa/rolling-upgrade/build.gradle +++ b/x-pack/qa/rolling-upgrade/build.gradle @@ -3,10 +3,10 @@ import org.elasticsearch.gradle.test.RestIntegTestTask import org.elasticsearch.gradle.Version import java.nio.charset.StandardCharsets -import java.util.regex.Matcher // Apply the java plugin to this project so the sources can be edited in an IDE -apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.standalone-test' + unitTest.enabled = false dependencies { @@ -68,161 +68,50 @@ Closure waitWithAuth = { NodeInfo node, AntBuilder ant -> return tmpFile.exists() } -Project mainProject = project - compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-try,-unchecked" forbiddenPatterns { exclude '**/system_key' } -// Tests are pushed down to subprojects -testingConventions.enabled = false - -/** - * Subdirectories of this project are test rolling upgrades with various - * configuration options based on their name. - */ -subprojects { - Matcher m = project.name =~ /with(out)?-system-key/ - if (false == m.matches()) { - throw new InvalidUserDataException("Invalid project name [${project.name}]") - } - boolean withSystemKey = m.group(1) == null - - apply plugin: 'elasticsearch.standalone-test' - - // Use resources from the rolling-upgrade project in subdirectories - sourceSets { - test { - java { - srcDirs = ["${mainProject.projectDir}/src/test/java"] - } - resources { - srcDirs = ["${mainProject.projectDir}/src/test/resources"] - } - } - } - - forbiddenPatterns { - exclude '**/system_key' - } +String outputDir = "${buildDir}/generated-resources/${project.name}" - String outputDir = "${buildDir}/generated-resources/${project.name}" - - // This is a top level task which we will add dependencies to below. - // It is a single task that can be used to backcompat tests against all versions. - task bwcTest { +// This is a top level task which we will add dependencies to below. +// It is a single task that can be used to backcompat tests against all versions. +task bwcTest { description = 'Runs backwards compatibility tests.' group = 'verification' - } +} - String output = "${buildDir}/generated-resources/${project.name}" - task copyTestNodeKeyMaterial(type: Copy) { +task copyTestNodeKeyMaterial(type: Copy) { from project(':x-pack:plugin:core').files('src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.pem', - 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt', - 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks') + 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.crt', + 'src/test/resources/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode.jks') into outputDir - } +} - for (Version version : bwcVersions.wireCompatible) { +for (Version version : bwcVersions.wireCompatible) { String baseName = "v${version}" Task oldClusterTest = tasks.create(name: "${baseName}#oldClusterTest", type: RestIntegTestTask) { - mustRunAfter(precommit) + mustRunAfter(precommit) } configure(extensions.findByName("${baseName}#oldClusterTestCluster")) { - dependsOn copyTestNodeKeyMaterial - if (version.before('6.3.0')) { - String depVersion = version; - if (project.bwcVersions.unreleased.contains(version)) { - depVersion += "-SNAPSHOT" - } - mavenPlugin 'x-pack', "org.elasticsearch.plugin:x-pack:${depVersion}" - } - String usersCli = version.before('6.3.0') ? 'bin/x-pack/users' : 'bin/elasticsearch-users' - setupCommand 'setupTestUser', usersCli, 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' - bwcVersion = version - numBwcNodes = 3 - numNodes = 3 - clusterName = 'rolling-upgrade' - waitCondition = waitWithAuth - setting 'xpack.monitoring.exporters._http.type', 'http' - setting 'xpack.monitoring.exporters._http.enabled', 'false' - setting 'xpack.monitoring.exporters._http.auth.username', 'test_user' - setting 'xpack.monitoring.exporters._http.auth.password', 'x-pack-test-password' - setting 'xpack.license.self_generated.type', 'trial' - setting 'xpack.security.enabled', 'true' - setting 'xpack.security.transport.ssl.enabled', 'true' - setting 'xpack.security.authc.token.enabled', 'true' - setting 'xpack.security.audit.enabled', 'true' - if (project.inFipsJvm) { - setting 'xpack.security.transport.ssl.key', 'testnode.pem' - setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' - keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' - } else { - setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' - setting 'xpack.security.transport.ssl.keystore.password', 'testnode' - } - dependsOn copyTestNodeKeyMaterial - extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') - extraConfigFile 'testnode.pem', new File(outputDir + '/testnode.pem') - extraConfigFile 'testnode.crt', new File(outputDir + '/testnode.crt') - if (version.onOrAfter('7.0.0')) { - setting 'xpack.security.authc.realms.file.file1.order', '0' - setting 'xpack.security.authc.realms.native.native1.order', '1' - } else { - setting 'xpack.security.authc.realms.file1.type', 'file' - setting 'xpack.security.authc.realms.file1.order', '0' - setting 'xpack.security.authc.realms.native1.type', 'native' - setting 'xpack.security.authc.realms.native1.order', '1' - } - - if (withSystemKey) { - if (version.onOrAfter('5.1.0') && version.before('6.0.0')) { - // The setting didn't exist until 5.1.0 - setting 'xpack.security.system_key.required', 'true' - } - if (version.onOrAfter('6.0.0')) { - keystoreFile 'xpack.watcher.encryption_key', "${mainProject.projectDir}/src/test/resources/system_key" - } else { - String systemKeyFile = version.before('6.3.0') ? 'x-pack/system_key' : 'system_key' - extraConfigFile systemKeyFile, "${mainProject.projectDir}/src/test/resources/system_key" - keystoreSetting 'xpack.security.authc.token.passphrase', 'token passphrase' + dependsOn copyTestNodeKeyMaterial + if (version.before('6.3.0')) { + String depVersion = version; + if (project.bwcVersions.unreleased.contains(version)) { + depVersion += "-SNAPSHOT" + } + mavenPlugin 'x-pack', "org.elasticsearch.plugin:x-pack:${depVersion}" } - setting 'xpack.watcher.encrypt_sensitive_data', 'true' - } - - if (version.onOrAfter('6.6.0')) { - setting 'ccr.auto_follow.wait_for_metadata_timeout', '1s' - } - - // Old versions of the code contain an invalid assertion that trips - // during tests. Versions 5.6.9 and 6.2.4 have been fixed by removing - // the assertion, but this is impossible for released versions. - // However, released versions run without assertions, so end users won't - // be suffering the effects. This argument effectively removes the - // incorrect assertion from the older versions used in the BWC tests. - if (version.before('5.6.9') || (version.onOrAfter('6.0.0') && version.before('6.2.4'))) { - jvmArgs '-da:org.elasticsearch.xpack.monitoring.exporter.http.HttpExportBulk' - } - } - - Task oldClusterTestRunner = tasks.getByName("${baseName}#oldClusterTestRunner") - oldClusterTestRunner.configure { - systemProperty 'tests.rest.suite', 'old_cluster' - } - - Closure configureUpgradeCluster = {String name, Task lastRunner, int stopNode, Closure getOtherUnicastHostAddresses -> - configure(extensions.findByName("${baseName}#${name}")) { - dependsOn lastRunner, "${baseName}#oldClusterTestCluster#node${stopNode}.stop" - setupCommand 'setupTestUser', 'bin/elasticsearch-users', 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' + String usersCli = version.before('6.3.0') ? 'bin/x-pack/users' : 'bin/elasticsearch-users' + setupCommand 'setupTestUser', usersCli, 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' + bwcVersion = version + numBwcNodes = 3 + numNodes = 3 clusterName = 'rolling-upgrade' - otherUnicastHostAddresses = { getOtherUnicastHostAddresses() } - /* Override the data directory so the new node always gets the node we - * just stopped's data directory. */ - dataDir = { nodeNumber -> oldClusterTest.nodes[stopNode].dataDir } waitCondition = waitWithAuth setting 'xpack.monitoring.exporters._http.type', 'http' setting 'xpack.monitoring.exporters._http.enabled', 'false' @@ -231,154 +120,195 @@ subprojects { setting 'xpack.license.self_generated.type', 'trial' setting 'xpack.security.enabled', 'true' setting 'xpack.security.transport.ssl.enabled', 'true' + setting 'xpack.security.authc.token.enabled', 'true' + setting 'xpack.security.audit.enabled', 'true' if (project.inFipsJvm) { - setting 'xpack.security.transport.ssl.key', 'testnode.pem' - setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' - keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' + setting 'xpack.security.transport.ssl.key', 'testnode.pem' + setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' + keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' } else { - setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' - setting 'xpack.security.transport.ssl.keystore.password', 'testnode' + setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' + setting 'xpack.security.transport.ssl.keystore.password', 'testnode' } - setting 'node.attr.upgraded', 'true' - setting 'xpack.security.authc.token.enabled', 'true' - setting 'xpack.security.audit.enabled', 'true' - setting 'node.name', "upgraded-node-${stopNode}" dependsOn copyTestNodeKeyMaterial extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') extraConfigFile 'testnode.pem', new File(outputDir + '/testnode.pem') extraConfigFile 'testnode.crt', new File(outputDir + '/testnode.crt') if (version.onOrAfter('7.0.0')) { - setting 'xpack.security.authc.realms.file.file1.order', '0' - setting 'xpack.security.authc.realms.native.native1.order', '1' + setting 'xpack.security.authc.realms.file.file1.order', '0' + setting 'xpack.security.authc.realms.native.native1.order', '1' } else { - setting 'xpack.security.authc.realms.file1.type', 'file' - setting 'xpack.security.authc.realms.file1.order', '0' - setting 'xpack.security.authc.realms.native1.type', 'native' - setting 'xpack.security.authc.realms.native1.order', '1' + setting 'xpack.security.authc.realms.file1.type', 'file' + setting 'xpack.security.authc.realms.file1.order', '0' + setting 'xpack.security.authc.realms.native1.type', 'native' + setting 'xpack.security.authc.realms.native1.order', '1' + } + + keystoreFile 'xpack.watcher.encryption_key', "${project.projectDir}/src/test/resources/system_key" + setting 'xpack.watcher.encrypt_sensitive_data', 'true' + + if (version.onOrAfter('6.6.0')) { + setting 'ccr.auto_follow.wait_for_metadata_timeout', '1s' } - if (withSystemKey) { - setting 'xpack.watcher.encrypt_sensitive_data', 'true' - keystoreFile 'xpack.watcher.encryption_key', "${mainProject.projectDir}/src/test/resources/system_key" + + // Old versions of the code contain an invalid assertion that trips + // during tests. Versions 5.6.9 and 6.2.4 have been fixed by removing + // the assertion, but this is impossible for released versions. + // However, released versions run without assertions, so end users won't + // be suffering the effects. This argument effectively removes the + // incorrect assertion from the older versions used in the BWC tests. + if (version.before('5.6.9') || (version.onOrAfter('6.0.0') && version.before('6.2.4'))) { + jvmArgs '-da:org.elasticsearch.xpack.monitoring.exporter.http.HttpExportBulk' } - if (version.before('6.0.0')) { - keystoreSetting 'xpack.security.authc.token.passphrase', 'token passphrase' + } + + Task oldClusterTestRunner = tasks.getByName("${baseName}#oldClusterTestRunner") + oldClusterTestRunner.configure { + systemProperty 'tests.rest.suite', 'old_cluster' + } + + Closure configureUpgradeCluster = {String name, Task lastRunner, int stopNode, Closure getOtherUnicastHostAddresses -> + configure(extensions.findByName("${baseName}#${name}")) { + dependsOn lastRunner, "${baseName}#oldClusterTestCluster#node${stopNode}.stop" + setupCommand 'setupTestUser', 'bin/elasticsearch-users', 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' + clusterName = 'rolling-upgrade' + otherUnicastHostAddresses = { getOtherUnicastHostAddresses() } + /* Override the data directory so the new node always gets the node we + * just stopped's data directory. */ + dataDir = { nodeNumber -> oldClusterTest.nodes[stopNode].dataDir } + waitCondition = waitWithAuth + setting 'xpack.monitoring.exporters._http.type', 'http' + setting 'xpack.monitoring.exporters._http.enabled', 'false' + setting 'xpack.monitoring.exporters._http.auth.username', 'test_user' + setting 'xpack.monitoring.exporters._http.auth.password', 'x-pack-test-password' + setting 'xpack.license.self_generated.type', 'trial' + setting 'xpack.security.enabled', 'true' + setting 'xpack.security.transport.ssl.enabled', 'true' + if (project.inFipsJvm) { + setting 'xpack.security.transport.ssl.key', 'testnode.pem' + setting 'xpack.security.transport.ssl.certificate', 'testnode.crt' + keystoreSetting 'xpack.security.transport.ssl.secure_key_passphrase', 'testnode' + } else { + setting 'xpack.security.transport.ssl.keystore.path', 'testnode.jks' + setting 'xpack.security.transport.ssl.keystore.password', 'testnode' + } + setting 'node.attr.upgraded', 'true' + setting 'xpack.security.authc.token.enabled', 'true' + setting 'xpack.security.audit.enabled', 'true' + setting 'node.name', "upgraded-node-${stopNode}" + dependsOn copyTestNodeKeyMaterial + extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') + extraConfigFile 'testnode.pem', new File(outputDir + '/testnode.pem') + extraConfigFile 'testnode.crt', new File(outputDir + '/testnode.crt') + if (version.onOrAfter('7.0.0')) { + setting 'xpack.security.authc.realms.file.file1.order', '0' + setting 'xpack.security.authc.realms.native.native1.order', '1' + } else { + setting 'xpack.security.authc.realms.file1.type', 'file' + setting 'xpack.security.authc.realms.file1.order', '0' + setting 'xpack.security.authc.realms.native1.type', 'native' + setting 'xpack.security.authc.realms.native1.order', '1' + } + setting 'xpack.watcher.encrypt_sensitive_data', 'true' + keystoreFile 'xpack.watcher.encryption_key', "${project.projectDir}/src/test/resources/system_key" + if (version.before('6.0.0')) { + keystoreSetting 'xpack.security.authc.token.passphrase', 'token passphrase' + } } - } } Task oneThirdUpgradedTest = tasks.create(name: "${baseName}#oneThirdUpgradedTest", type: RestIntegTestTask) configureUpgradeCluster("oneThirdUpgradedTestCluster", oldClusterTestRunner, 0, - // Use all running nodes as seed nodes so there is no race between pinging and the tests - { [oldClusterTest.nodes.get(1).transportUri(), oldClusterTest.nodes.get(2).transportUri()] }) + // Use all running nodes as seed nodes so there is no race between pinging and the tests + { [oldClusterTest.nodes.get(1).transportUri(), oldClusterTest.nodes.get(2).transportUri()] }) Task oneThirdUpgradedTestRunner = tasks.getByName("${baseName}#oneThirdUpgradedTestRunner") oneThirdUpgradedTestRunner.configure { - systemProperty 'tests.rest.suite', 'mixed_cluster' - systemProperty 'tests.first_round', 'true' - // We only need to run these tests once so we may as well do it when we're two thirds upgraded - systemProperty 'tests.rest.blacklist', [ - 'mixed_cluster/10_basic/Start scroll in mixed cluster on upgraded node that we will continue after upgrade', - 'mixed_cluster/30_ml_jobs_crud/Create a job in the mixed cluster and write some data', - 'mixed_cluster/40_ml_datafeed_crud/Put job and datafeed in mixed cluster', + systemProperty 'tests.rest.suite', 'mixed_cluster' + systemProperty 'tests.first_round', 'true' + // We only need to run these tests once so we may as well do it when we're two thirds upgraded + systemProperty 'tests.rest.blacklist', [ + 'mixed_cluster/10_basic/Start scroll in mixed cluster on upgraded node that we will continue after upgrade', + 'mixed_cluster/30_ml_jobs_crud/Create a job in the mixed cluster and write some data', + 'mixed_cluster/40_ml_datafeed_crud/Put job and datafeed in mixed cluster', ].join(',') - finalizedBy "${baseName}#oldClusterTestCluster#node1.stop" + finalizedBy "${baseName}#oldClusterTestCluster#node1.stop" } Task twoThirdsUpgradedTest = tasks.create(name: "${baseName}#twoThirdsUpgradedTest", type: RestIntegTestTask) configureUpgradeCluster("twoThirdsUpgradedTestCluster", oneThirdUpgradedTestRunner, 1, - // Use all running nodes as seed nodes so there is no race between pinging and the tests - { [oldClusterTest.nodes.get(2).transportUri(), oneThirdUpgradedTest.nodes.get(0).transportUri()] }) + // Use all running nodes as seed nodes so there is no race between pinging and the tests + { [oldClusterTest.nodes.get(2).transportUri(), oneThirdUpgradedTest.nodes.get(0).transportUri()] }) Task twoThirdsUpgradedTestRunner = tasks.getByName("${baseName}#twoThirdsUpgradedTestRunner") twoThirdsUpgradedTestRunner.configure { - systemProperty 'tests.rest.suite', 'mixed_cluster' - systemProperty 'tests.first_round', 'false' - finalizedBy "${baseName}#oldClusterTestCluster#node2.stop" + systemProperty 'tests.rest.suite', 'mixed_cluster' + systemProperty 'tests.first_round', 'false' + finalizedBy "${baseName}#oldClusterTestCluster#node2.stop" } Task upgradedClusterTest = tasks.create(name: "${baseName}#upgradedClusterTest", type: RestIntegTestTask) configureUpgradeCluster("upgradedClusterTestCluster", twoThirdsUpgradedTestRunner, 2, - // Use all running nodes as seed nodes so there is no race between pinging and the tests - { [oneThirdUpgradedTest.nodes.get(0).transportUri(), twoThirdsUpgradedTest.nodes.get(0).transportUri()] }) + // Use all running nodes as seed nodes so there is no race between pinging and the tests + { [oneThirdUpgradedTest.nodes.get(0).transportUri(), twoThirdsUpgradedTest.nodes.get(0).transportUri()] }) Task upgradedClusterTestRunner = tasks.getByName("${baseName}#upgradedClusterTestRunner") upgradedClusterTestRunner.configure { - systemProperty 'tests.rest.suite', 'upgraded_cluster' - /* - * Force stopping all the upgraded nodes after the test runner - * so they are alive during the test. - */ - finalizedBy "${baseName}#oneThirdUpgradedTestCluster#stop" - finalizedBy "${baseName}#twoThirdsUpgradedTestCluster#stop" - - // migration tests should only run when the original/old cluster nodes where versions < 5.2.0. - // this stinks but we do the check here since our rest tests do not support conditionals - // otherwise we could check the index created version - String versionStr = project.extensions.findByName("${baseName}#oldClusterTestCluster").properties.get('bwcVersion') - String[] versionParts = versionStr.split('\\.') - if (versionParts[0].equals("5")) { - Integer minor = Integer.parseInt(versionParts[1]) - if (minor >= 2) { - systemProperty 'tests.rest.blacklist', '/20_security/Verify default password migration results in upgraded cluster' - } - } + systemProperty 'tests.rest.suite', 'upgraded_cluster' + /* + * Force stopping all the upgraded nodes after the test runner + * so they are alive during the test. + */ + finalizedBy "${baseName}#oneThirdUpgradedTestCluster#stop" + finalizedBy "${baseName}#twoThirdsUpgradedTestCluster#stop" + + // migration tests should only run when the original/old cluster nodes where versions < 5.2.0. + // this stinks but we do the check here since our rest tests do not support conditionals + // otherwise we could check the index created version + String versionStr = project.extensions.findByName("${baseName}#oldClusterTestCluster").properties.get('bwcVersion') + String[] versionParts = versionStr.split('\\.') + if (versionParts[0].equals("5")) { + Integer minor = Integer.parseInt(versionParts[1]) + if (minor >= 2) { + systemProperty 'tests.rest.blacklist', '/20_security/Verify default password migration results in upgraded cluster' + } + } } Task versionBwcTest = tasks.create(name: "${baseName}#bwcTest") { - dependsOn = [upgradedClusterTest] + dependsOn = [upgradedClusterTest] } if (project.bwc_tests_enabled) { - bwcTest.dependsOn(versionBwcTest) + bwcTest.dependsOn(versionBwcTest) } - } - - unitTest.enabled = false // no unit tests for rolling upgrades, only the rest integration test +} - // basic integ tests includes testing bwc against the most recent version - task bwcTestSnapshots { +// basic integ tests includes testing bwc against the most recent version +task bwcTestSnapshots { if (project.bwc_tests_enabled) { - for (final def version : bwcVersions.unreleasedWireCompatible) { - dependsOn "v${version}#bwcTest" - } + for (final def version : bwcVersions.unreleasedWireCompatible) { + dependsOn "v${version}#bwcTest" + } } - } - check.dependsOn(bwcTestSnapshots) - - dependencies { - // "org.elasticsearch.plugin:x-pack-core:${version}" doesn't work with idea because the testArtifacts are also here - testCompile project(path: xpackModule('core'), configuration: 'default') - testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') - testCompile project(path: xpackModule('watcher')) - } - - compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-try,-unchecked" +} +check.dependsOn(bwcTestSnapshots) - // copy x-pack plugin info so it is on the classpath and security manager has the right permissions - task copyXPackRestSpec(type: Copy) { +// copy x-pack plugin info so it is on the classpath and security manager has the right permissions +task copyXPackRestSpec(type: Copy) { dependsOn(project.configurations.restSpec, 'processTestResources') from project(xpackProject('plugin').path).sourceSets.test.resources include 'rest-api-spec/api/**' into project.sourceSets.test.output.resourcesDir - } +} - task copyXPackPluginProps(type: Copy) { +task copyXPackPluginProps(type: Copy) { dependsOn(copyXPackRestSpec) from project(xpackModule('core')).file('src/main/plugin-metadata') from project(xpackModule('core')).tasks.pluginProperties into outputDir - } - project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps) - - repositories { - maven { - url "https://artifacts.elastic.co/maven" - } - maven { - url "https://snapshots.elastic.co/maven" - } - } } +project.sourceSets.test.output.dir(outputDir, builtBy: copyXPackPluginProps) diff --git a/x-pack/qa/rolling-upgrade/with-system-key/build.gradle b/x-pack/qa/rolling-upgrade/with-system-key/build.gradle deleted file mode 100644 index 03505e01dedd8..0000000000000 --- a/x-pack/qa/rolling-upgrade/with-system-key/build.gradle +++ /dev/null @@ -1 +0,0 @@ -group = "${group}.x-pack.qa.rolling-upgrade.with-system-key" diff --git a/x-pack/qa/rolling-upgrade/without-system-key/build.gradle b/x-pack/qa/rolling-upgrade/without-system-key/build.gradle deleted file mode 100644 index aa7ac502eb3e6..0000000000000 --- a/x-pack/qa/rolling-upgrade/without-system-key/build.gradle +++ /dev/null @@ -1 +0,0 @@ -group = "${group}.x-pack.qa.rolling-upgrade.without-system-key" From a58a829edef5ad3d1920f2b9f810ecf8fec89f41 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Thu, 28 Mar 2019 14:39:34 -0400 Subject: [PATCH 62/77] [DOCS] Document common settings for snapshot repository plugins (#40475) --- docs/plugins/repository-azure.asciidoc | 4 +--- docs/plugins/repository-gcs.asciidoc | 2 ++ docs/plugins/repository-hdfs.asciidoc | 2 ++ docs/plugins/repository-s3.asciidoc | 2 ++ docs/plugins/repository-shared-settings.asciidoc | 11 +++++++++++ 5 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 docs/plugins/repository-shared-settings.asciidoc diff --git a/docs/plugins/repository-azure.asciidoc b/docs/plugins/repository-azure.asciidoc index 9249efd5d1744..61dcadd6e10d6 100644 --- a/docs/plugins/repository-azure.asciidoc +++ b/docs/plugins/repository-azure.asciidoc @@ -126,9 +126,7 @@ The Azure repository supports following settings: setting doesn't affect index files that are already compressed by default. Defaults to `true`. -`readonly`:: - - Makes repository read-only. Defaults to `false`. +include::repository-shared-settings.asciidoc[] `location_mode`:: diff --git a/docs/plugins/repository-gcs.asciidoc b/docs/plugins/repository-gcs.asciidoc index f655d29307074..b34c9456a9306 100644 --- a/docs/plugins/repository-gcs.asciidoc +++ b/docs/plugins/repository-gcs.asciidoc @@ -240,6 +240,8 @@ The following settings are supported: setting doesn't affect index files that are already compressed by default. Defaults to `true`. +include::repository-shared-settings.asciidoc[] + `application_name`:: deprecated[7.0.0, This setting is now defined in the <>] diff --git a/docs/plugins/repository-hdfs.asciidoc b/docs/plugins/repository-hdfs.asciidoc index e798682a38699..bedb0e7e1ef87 100644 --- a/docs/plugins/repository-hdfs.asciidoc +++ b/docs/plugins/repository-hdfs.asciidoc @@ -64,6 +64,8 @@ The following settings are supported: Whether to compress the metadata or not. (Enabled by default) +include::repository-shared-settings.asciidoc[] + `chunk_size`:: Override the chunk size. (Disabled by default) diff --git a/docs/plugins/repository-s3.asciidoc b/docs/plugins/repository-s3.asciidoc index 7c4e763a3b04a..084d67f236472 100644 --- a/docs/plugins/repository-s3.asciidoc +++ b/docs/plugins/repository-s3.asciidoc @@ -213,6 +213,8 @@ The following settings are supported: setting doesn't affect index files that are already compressed by default. Defaults to `true`. +include::repository-shared-settings.asciidoc[] + `server_side_encryption`:: When set to `true` files are encrypted on server side using AES256 diff --git a/docs/plugins/repository-shared-settings.asciidoc b/docs/plugins/repository-shared-settings.asciidoc new file mode 100644 index 0000000000000..ca9345e0ffc2c --- /dev/null +++ b/docs/plugins/repository-shared-settings.asciidoc @@ -0,0 +1,11 @@ +`max_restore_bytes_per_sec`:: + + Throttles per node restore rate. Defaults to `40mb` per second. + +`max_snapshot_bytes_per_sec`:: + + Throttles per node snapshot rate. Defaults to `40mb` per second. + +`readonly`:: + + Makes repository read-only. Defaults to `false`. \ No newline at end of file From dd8b4bbc34a11c92a622b742b00cf1c34235e8e9 Mon Sep 17 00:00:00 2001 From: James Rodewig Date: Thu, 28 Mar 2019 15:05:36 -0400 Subject: [PATCH 63/77] [DOCS] Correct keystore commands for Email and Jira actions in Watcher (#40417) --- x-pack/docs/en/watcher/actions/email.asciidoc | 6 +++--- x-pack/docs/en/watcher/actions/jira.asciidoc | 11 +++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/x-pack/docs/en/watcher/actions/email.asciidoc b/x-pack/docs/en/watcher/actions/email.asciidoc index 61b0fa8cf9e7f..1f8de38319100 100644 --- a/x-pack/docs/en/watcher/actions/email.asciidoc +++ b/x-pack/docs/en/watcher/actions/email.asciidoc @@ -325,7 +325,7 @@ In order to store the account SMTP password, use the keystore command [source,yaml] -------------------------------------------------- -bin/elasticsearch-keystore xpack.notification.email.account.gmail_account.smtp.secure_password +bin/elasticsearch-keystore add xpack.notification.email.account.gmail_account.smtp.secure_password -------------------------------------------------- If you get an authentication error that indicates that you need to continue the @@ -363,7 +363,7 @@ In order to store the account SMTP password, use the keystore command [source,yaml] -------------------------------------------------- -bin/elasticsearch-keystore xpack.notification.email.account.outlook_account.smtp.secure_password +bin/elasticsearch-keystore add xpack.notification.email.account.outlook_account.smtp.secure_password -------------------------------------------------- @@ -400,7 +400,7 @@ In order to store the account SMTP password, use the keystore command [source,yaml] -------------------------------------------------- -bin/elasticsearch-keystore xpack.notification.email.account.ses_account.smtp.secure_password +bin/elasticsearch-keystore add xpack.notification.email.account.ses_account.smtp.secure_password -------------------------------------------------- NOTE: You need to use your Amazon SES SMTP credentials to send email through diff --git a/x-pack/docs/en/watcher/actions/jira.asciidoc b/x-pack/docs/en/watcher/actions/jira.asciidoc index f0b9c714181b8..4608ee6ab1af5 100644 --- a/x-pack/docs/en/watcher/actions/jira.asciidoc +++ b/x-pack/docs/en/watcher/actions/jira.asciidoc @@ -109,12 +109,15 @@ Jira account you need to specify (see {ref}/secure-settings.html[secure settings [source,yaml] -------------------------------------------------- -bin/elasticsearch-keystore xpack.notification.jira.account.monitoring.secure_url -bin/elasticsearch-keystore xpack.notification.jira.account.monitoring.secure_user -bin/elasticsearch-keystore xpack.notification.jira.account.monitoring.secure_password +bin/elasticsearch-keystore add xpack.notification.jira.account.monitoring.secure_url +bin/elasticsearch-keystore add xpack.notification.jira.account.monitoring.secure_user +bin/elasticsearch-keystore add xpack.notification.jira.account.monitoring.secure_password -------------------------------------------------- -deprecated[The insecure way of storing sensitive data (`url`, `user` and `password`) in the configuration file or the cluster settings is deprecated] +[WARNING] +====== +Storing sensitive data (`url`, `user` and `password`) in the configuration file or the cluster settings is insecure and has been deprecated. Please use {es}'s secure {ref}/secure-settings.html[keystore] method instead. +====== To avoid credentials that transit in clear text over the network, {watcher} will reject `url` settings like `http://internal-jira.elastic.co` that are based on From 8be33d1ec773623785b75720e5de6679820a9a5e Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Thu, 28 Mar 2019 16:22:59 -0400 Subject: [PATCH 64/77] Add start and stop time to cat recovery API (#40378) The cat recovery API is incredibly useful. Yet it is missing the start and stop time as an option from the output. This commit adds these as options to the cat recovery API. We elect to make these not visible by default to avoid breaking the output that users might rely on. --- .../elasticsearch/action/ActionModule.java | 3 +- ...Action.java => RestCatRecoveryAction.java} | 13 +- .../action/cat/RestRecoveryActionTests.java | 132 ++++++++++-------- 3 files changed, 90 insertions(+), 58 deletions(-) rename server/src/main/java/org/elasticsearch/rest/action/cat/{RestRecoveryAction.java => RestCatRecoveryAction.java} (89%) diff --git a/server/src/main/java/org/elasticsearch/action/ActionModule.java b/server/src/main/java/org/elasticsearch/action/ActionModule.java index 83e1e01614435..2cfe66372115f 100644 --- a/server/src/main/java/org/elasticsearch/action/ActionModule.java +++ b/server/src/main/java/org/elasticsearch/action/ActionModule.java @@ -289,6 +289,7 @@ import org.elasticsearch.rest.action.cat.RestAliasAction; import org.elasticsearch.rest.action.cat.RestAllocationAction; import org.elasticsearch.rest.action.cat.RestCatAction; +import org.elasticsearch.rest.action.cat.RestCatRecoveryAction; import org.elasticsearch.rest.action.cat.RestFielddataAction; import org.elasticsearch.rest.action.cat.RestHealthAction; import org.elasticsearch.rest.action.cat.RestIndicesAction; @@ -665,7 +666,7 @@ public void initRestHandlers(Supplier nodesInCluster) { // Fully qualified to prevent interference with rest.action.count.RestCountAction registerHandler.accept(new org.elasticsearch.rest.action.cat.RestCountAction(settings, restController)); // Fully qualified to prevent interference with rest.action.indices.RestRecoveryAction - registerHandler.accept(new org.elasticsearch.rest.action.cat.RestRecoveryAction(settings, restController)); + registerHandler.accept(new RestCatRecoveryAction(settings, restController)); registerHandler.accept(new RestHealthAction(settings, restController)); registerHandler.accept(new org.elasticsearch.rest.action.cat.RestPendingClusterTasksAction(settings, restController)); registerHandler.accept(new RestAliasAction(settings, restController)); diff --git a/server/src/main/java/org/elasticsearch/rest/action/cat/RestRecoveryAction.java b/server/src/main/java/org/elasticsearch/rest/action/cat/RestCatRecoveryAction.java similarity index 89% rename from server/src/main/java/org/elasticsearch/rest/action/cat/RestRecoveryAction.java rename to server/src/main/java/org/elasticsearch/rest/action/cat/RestCatRecoveryAction.java index a66741c2d9411..0cea93e4e7ee7 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/cat/RestRecoveryAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/cat/RestCatRecoveryAction.java @@ -30,6 +30,7 @@ import org.elasticsearch.common.Table; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentElasticsearchExtension; import org.elasticsearch.indices.recovery.RecoveryState; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -47,8 +48,8 @@ * in a string format, designed to be used at the command line. An Index can * be specified to limit output to a particular index or indices. */ -public class RestRecoveryAction extends AbstractCatAction { - public RestRecoveryAction(Settings settings, RestController restController) { +public class RestCatRecoveryAction extends AbstractCatAction { + public RestCatRecoveryAction(Settings settings, RestController restController) { super(settings); restController.registerHandler(GET, "/_cat/recovery", this); restController.registerHandler(GET, "/_cat/recovery/{index}", this); @@ -86,6 +87,10 @@ protected Table getTableWithHeader(RestRequest request) { t.startHeaders() .addCell("index", "alias:i,idx;desc:index name") .addCell("shard", "alias:s,sh;desc:shard name") + .addCell("start_time", "default:false;alias:start;desc:recovery start time") + .addCell("start_time_millis", "default:false;alias:start_millis;desc:recovery start time in epoch milliseconds") + .addCell("stop_time", "default:false;alias:stop;desc:recovery stop time") + .addCell("stop_time_millis", "default:false;alias:stop_millis;desc:recovery stop time in epoch milliseconds") .addCell("time", "alias:t,ti;desc:recovery time") .addCell("type", "alias:ty;desc:recovery type") .addCell("stage", "alias:st;desc:recovery stage") @@ -149,6 +154,10 @@ public int compare(RecoveryState o1, RecoveryState o2) { t.startRow(); t.addCell(index); t.addCell(state.getShardId().id()); + t.addCell(XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().startTime())); + t.addCell(state.getTimer().startTime()); + t.addCell(XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().stopTime())); + t.addCell(state.getTimer().stopTime()); t.addCell(new TimeValue(state.getTimer().time())); t.addCell(state.getRecoverySource().getType().toString().toLowerCase(Locale.ROOT)); t.addCell(state.getStage().toString().toLowerCase(Locale.ROOT)); diff --git a/server/src/test/java/org/elasticsearch/rest/action/cat/RestRecoveryActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/cat/RestRecoveryActionTests.java index 25f04532ac8ce..7bfa50ff2b724 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/cat/RestRecoveryActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/cat/RestRecoveryActionTests.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.Table; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentElasticsearchExtension; import org.elasticsearch.index.Index; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.recovery.RecoveryState; @@ -37,7 +38,9 @@ import org.elasticsearch.usage.UsageService; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -53,7 +56,7 @@ public void testRestRecoveryAction() { final Settings settings = Settings.EMPTY; UsageService usageService = new UsageService(); final RestController restController = new RestController(Collections.emptySet(), null, null, null, usageService); - final RestRecoveryAction action = new RestRecoveryAction(settings, restController); + final RestCatRecoveryAction action = new RestCatRecoveryAction(settings, restController); final int totalShards = randomIntBetween(1, 32); final int successfulShards = Math.max(0, totalShards - randomIntBetween(1, 2)); final int failedShards = totalShards - successfulShards; @@ -64,7 +67,11 @@ public void testRestRecoveryAction() { final RecoveryState state = mock(RecoveryState.class); when(state.getShardId()).thenReturn(new ShardId(new Index("index", "_na_"), i)); final RecoveryState.Timer timer = mock(RecoveryState.Timer.class); - when(timer.time()).thenReturn((long)randomIntBetween(1000000, 10 * 1000000)); + final long startTime = randomLongBetween(0, new Date().getTime()); + when(timer.startTime()).thenReturn(startTime); + final long time = randomLongBetween(1000000, 10 * 1000000); + when(timer.time()).thenReturn(time); + when(timer.stopTime()).thenReturn(startTime + time); when(state.getTimer()).thenReturn(timer); when(state.getRecoverySource()).thenReturn(TestShardRouting.randomRecoverySource()); when(state.getStage()).thenReturn(randomFrom(RecoveryState.Stage.values())); @@ -122,63 +129,78 @@ public void testRestRecoveryAction() { List headers = table.getHeaders(); - assertThat(headers.get(0).value, equalTo("index")); - assertThat(headers.get(1).value, equalTo("shard")); - assertThat(headers.get(2).value, equalTo("time")); - assertThat(headers.get(3).value, equalTo("type")); - assertThat(headers.get(4).value, equalTo("stage")); - assertThat(headers.get(5).value, equalTo("source_host")); - assertThat(headers.get(6).value, equalTo("source_node")); - assertThat(headers.get(7).value, equalTo("target_host")); - assertThat(headers.get(8).value, equalTo("target_node")); - assertThat(headers.get(9).value, equalTo("repository")); - assertThat(headers.get(10).value, equalTo("snapshot")); - assertThat(headers.get(11).value, equalTo("files")); - assertThat(headers.get(12).value, equalTo("files_recovered")); - assertThat(headers.get(13).value, equalTo("files_percent")); - assertThat(headers.get(14).value, equalTo("files_total")); - assertThat(headers.get(15).value, equalTo("bytes")); - assertThat(headers.get(16).value, equalTo("bytes_recovered")); - assertThat(headers.get(17).value, equalTo("bytes_percent")); - assertThat(headers.get(18).value, equalTo("bytes_total")); - assertThat(headers.get(19).value, equalTo("translog_ops")); - assertThat(headers.get(20).value, equalTo("translog_ops_recovered")); - assertThat(headers.get(21).value, equalTo("translog_ops_percent")); + final List expectedHeaders = Arrays.asList( + "index", + "shard", + "start_time", + "start_time_millis", + "stop_time", + "stop_time_millis", + "time", + "type", + "stage", + "source_host", + "source_node", + "target_host", + "target_node", + "repository", + "snapshot", + "files", + "files_recovered", + "files_percent", + "files_total", + "bytes", + "bytes_recovered", + "bytes_percent", + "bytes_total", + "translog_ops", + "translog_ops_recovered", + "translog_ops_percent"); + + for (int i = 0; i < expectedHeaders.size(); i++) { + assertThat(headers.get(i).value, equalTo(expectedHeaders.get(i))); + } assertThat(table.getRows().size(), equalTo(successfulShards)); + for (int i = 0; i < successfulShards; i++) { final RecoveryState state = recoveryStates.get(i); - List cells = table.getRows().get(i); - assertThat(cells.get(0).value, equalTo("index")); - assertThat(cells.get(1).value, equalTo(i)); - assertThat(cells.get(2).value, equalTo(new TimeValue(state.getTimer().time()))); - assertThat(cells.get(3).value, equalTo(state.getRecoverySource().getType().name().toLowerCase(Locale.ROOT))); - assertThat(cells.get(4).value, equalTo(state.getStage().name().toLowerCase(Locale.ROOT))); - assertThat(cells.get(5).value, equalTo(state.getSourceNode() == null ? "n/a" : state.getSourceNode().getHostName())); - assertThat(cells.get(6).value, equalTo(state.getSourceNode() == null ? "n/a" : state.getSourceNode().getName())); - assertThat(cells.get(7).value, equalTo(state.getTargetNode().getHostName())); - assertThat(cells.get(8).value, equalTo(state.getTargetNode().getName())); - assertThat( - cells.get(9).value, - equalTo(state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ? - "n/a" : - ((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getRepository())); - assertThat( - cells.get(10).value, - equalTo(state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ? - "n/a" : - ((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getSnapshotId().getName())); - assertThat(cells.get(11).value, equalTo(state.getIndex().totalRecoverFiles())); - assertThat(cells.get(12).value, equalTo(state.getIndex().recoveredFileCount())); - assertThat(cells.get(13).value, equalTo(percent(state.getIndex().recoveredFilesPercent()))); - assertThat(cells.get(14).value, equalTo(state.getIndex().totalFileCount())); - assertThat(cells.get(15).value, equalTo(state.getIndex().totalRecoverBytes())); - assertThat(cells.get(16).value, equalTo(state.getIndex().recoveredBytes())); - assertThat(cells.get(17).value, equalTo(percent(state.getIndex().recoveredBytesPercent()))); - assertThat(cells.get(18).value, equalTo(state.getIndex().totalBytes())); - assertThat(cells.get(19).value, equalTo(state.getTranslog().totalOperations())); - assertThat(cells.get(20).value, equalTo(state.getTranslog().recoveredOperations())); - assertThat(cells.get(21).value, equalTo(percent(state.getTranslog().recoveredPercent()))); + final List expectedValues = Arrays.asList( + "index", + i, + XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().startTime()), + state.getTimer().startTime(), + XContentElasticsearchExtension.DEFAULT_DATE_PRINTER.print(state.getTimer().stopTime()), + state.getTimer().stopTime(), + new TimeValue(state.getTimer().time()), + state.getRecoverySource().getType().name().toLowerCase(Locale.ROOT), + state.getStage().name().toLowerCase(Locale.ROOT), + state.getSourceNode() == null ? "n/a" : state.getSourceNode().getHostName(), + state.getSourceNode() == null ? "n/a" : state.getSourceNode().getName(), + state.getTargetNode().getHostName(), + state.getTargetNode().getName(), + state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ? + "n/a" : + ((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getRepository(), + state.getRecoverySource() == null || state.getRecoverySource().getType() != RecoverySource.Type.SNAPSHOT ? + "n/a" : + ((SnapshotRecoverySource) state.getRecoverySource()).snapshot().getSnapshotId().getName(), + state.getIndex().totalRecoverFiles(), + state.getIndex().recoveredFileCount(), + percent(state.getIndex().recoveredFilesPercent()), + state.getIndex().totalFileCount(), + state.getIndex().totalRecoverBytes(), + state.getIndex().recoveredBytes(), + percent(state.getIndex().recoveredBytesPercent()), + state.getIndex().totalBytes(), + state.getTranslog().totalOperations(), + state.getTranslog().recoveredOperations(), + percent(state.getTranslog().recoveredPercent())); + + final List cells = table.getRows().get(i); + for (int j = 0; j < expectedValues.size(); j++) { + assertThat(cells.get(j).value, equalTo(expectedValues.get(j))); + } } } From 0dd2fdfef2086d17e0ab0bcbf7fc1d662cdae9ca Mon Sep 17 00:00:00 2001 From: Jeff Hajewski Date: Thu, 28 Mar 2019 16:07:03 -0500 Subject: [PATCH 65/77] Update max dims for vectors to 1024. (#40597) --- .../org/elasticsearch/index/mapper/DenseVectorFieldMapper.java | 2 +- .../org/elasticsearch/index/mapper/SparseVectorFieldMapper.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java index ec78420cc0b05..d48a457ba08cd 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/DenseVectorFieldMapper.java @@ -46,7 +46,7 @@ public class DenseVectorFieldMapper extends FieldMapper implements ArrayValueMapperParser { public static final String CONTENT_TYPE = "dense_vector"; - public static short MAX_DIMS_COUNT = 500; //maximum allowed number of dimensions + public static short MAX_DIMS_COUNT = 1024; //maximum allowed number of dimensions private static final byte INT_BYTES = 4; public static class Defaults { diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java index bfbf68c5a7662..931e27bc1c19f 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SparseVectorFieldMapper.java @@ -46,7 +46,7 @@ public class SparseVectorFieldMapper extends FieldMapper { public static final String CONTENT_TYPE = "sparse_vector"; - public static short MAX_DIMS_COUNT = 500; //maximum allowed number of dimensions + public static short MAX_DIMS_COUNT = 1024; //maximum allowed number of dimensions public static int MAX_DIMS_NUMBER = 65535; //maximum allowed dimension's number public static class Defaults { From 0355bc1f0f502e9bae5fc26b2e96a29add8b38e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 28 Mar 2019 22:11:46 +0100 Subject: [PATCH 66/77] Fixing typo in test error message (#40611) --- .../xpack/ccr/action/TransportResumeFollowActionTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowActionTests.java b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowActionTests.java index b9a3c7ed021f4..44f8583bb9b5a 100644 --- a/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowActionTests.java +++ b/x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowActionTests.java @@ -238,7 +238,7 @@ public void testDynamicIndexSettingsAreClassified() { if (setting.isDynamic()) { boolean notReplicated = TransportResumeFollowAction.NON_REPLICATED_SETTINGS.contains(setting); boolean replicated = replicatedSettings.contains(setting); - assertThat("setting [" + setting.getKey() + "] is not classified as replicated xor not replicated", + assertThat("setting [" + setting.getKey() + "] is not classified as replicated or not replicated", notReplicated ^ replicated, is(true)); } } From e08881b54e79903aff0b07aceb112e0e38949afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BCscher?= Date: Thu, 28 Mar 2019 22:52:49 +0100 Subject: [PATCH 67/77] Muting XContentParserTests#testSubParserArray --- .../org/elasticsearch/common/xcontent/XContentParserTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java index e98f1e3d58510..0fe8a2b9f91fb 100644 --- a/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java +++ b/libs/x-content/src/test/java/org/elasticsearch/common/xcontent/XContentParserTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.common.xcontent; import com.fasterxml.jackson.core.JsonParseException; + import org.elasticsearch.common.CheckedSupplier; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; @@ -368,6 +369,7 @@ public void testSubParserObject() throws IOException { } } + @AwaitsFix(bugUrl="https://github.com/elastic/elasticsearch/issues/40617") public void testSubParserArray() throws IOException { XContentBuilder builder = XContentFactory.jsonBuilder(); int numberOfArrayElements = randomInt(10); From d0acf6285cd7845e554fe988e279d101d5631758 Mon Sep 17 00:00:00 2001 From: Andy Bristol Date: Thu, 28 Mar 2019 16:06:06 -0700 Subject: [PATCH 68/77] lower bwc skip for search as you type (#40599) --- .../rest-api-spec/test/search-as-you-type/10_basic.yml | 2 +- .../rest-api-spec/test/search-as-you-type/20_highlighting.yml | 2 +- .../rest-api-spec/test/search/310_match_bool_prefix.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml index f9b76a7399a37..3ddcd89347fcb 100644 --- a/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml +++ b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/10_basic.yml @@ -1,6 +1,6 @@ setup: - skip: - version: " - 7.1.0" + version: " - 7.0.99" reason: "added in 7.1.0" - do: diff --git a/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml index 5a96a11a47586..82a599ce686c2 100644 --- a/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml +++ b/modules/mapper-extras/src/test/resources/rest-api-spec/test/search-as-you-type/20_highlighting.yml @@ -1,6 +1,6 @@ setup: - skip: - version: " - 7.1.0" + version: " - 7.0.99" reason: "added in 7.1.0" - do: diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml index 957d26036b4a8..bcc28c7853425 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search/310_match_bool_prefix.yml @@ -1,6 +1,6 @@ setup: - skip: - version: " - 7.1.0" + version: " - 7.0.99" reason: "added in 7.1.0" - do: From 80b88cff3e3a73d30e4c11460ade6927efeac6be Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Fri, 29 Mar 2019 08:01:06 +0200 Subject: [PATCH 69/77] Fix 3rd pary S3 tests (#40588) * Fix 3rd pary S3 tests This is allready excluded on line 186, by doing this again here, the other exclusion from arround that line are removed causing the tests to fail. * Fix blacklisting with the fixture --- plugins/repository-s3/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/repository-s3/build.gradle b/plugins/repository-s3/build.gradle index d27afb0861a98..8a2edeb78c507 100644 --- a/plugins/repository-s3/build.gradle +++ b/plugins/repository-s3/build.gradle @@ -177,6 +177,10 @@ if (useFixture) { } plugin file(tasks.bundlePlugin.archiveFile) } + + integTest.runner { + systemProperty 'tests.rest.blacklist', 'repository_s3/50_repository_ecs_credentials/*' + } } else { integTest.runner { systemProperty 'tests.rest.blacklist', @@ -259,10 +263,6 @@ testClusters.integTest { } } -integTest.runner { - systemProperty 'tests.rest.blacklist', 'repository_s3/50_repository_ecs_credentials/*' -} - if (useFixture) { task integTestECS(type: RestIntegTestTask.class) { description = "Runs tests using the ECS repository." From e48fe8b33d72e6f14e1d7f9fc392664e5d6faa48 Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Fri, 29 Mar 2019 08:08:04 +0200 Subject: [PATCH 70/77] Run the build integ test in parallel (#39788) * Run the build integ test in parallel Because the randomized runner lives in buildSrc, we run these tests with the Gradle runner, and had no parallelism configured so far. * Handle Windows and "auto" better --- buildSrc/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 9f658c91ab394..be54b2c68f639 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -232,6 +232,11 @@ if (project != rootProject) { if (isLuceneSnapshot) { systemProperty 'test.lucene-snapshot-revision', isLuceneSnapshot[0][1] } + String defaultParallel = System.getProperty('tests.jvms', project.rootProject.ext.defaultParallel) + if (defaultParallel == "auto") { + defaultParallel = Math.max(Runtime.getRuntime().availableProcessors(), 4) + } + maxParallelForks defaultParallel as Integer } check.dependsOn(integTest) From 33af548df7c3fccef38eb8a0b34ee2a808800840 Mon Sep 17 00:00:00 2001 From: Alpar Torok Date: Fri, 29 Mar 2019 08:10:14 +0200 Subject: [PATCH 71/77] Remove Gradle deprecation warnings (#40449) * Remove Gradle deprecation warnings Turns out that without the explicit project, some how the deprecated properties were being red. With this change we have no Gradle deprecation warnings. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d452170b5fb90..f6c3222a4074a 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,7 @@ plugins { id 'com.gradle.build-scan' version '2.0.2' id 'base' } -if (properties.get("org.elasticsearch.acceptScanTOS", "false") == "true") { +if (Boolean.valueOf(project.findProperty('org.elasticsearch.acceptScanTOS') ?: "false")) { buildScan { termsOfServiceUrl = 'https://gradle.com/terms-of-service' termsOfServiceAgree = 'yes' From 7b6e7146eaacee36d3ec7932b85761ed01dd5ca8 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Fri, 29 Mar 2019 18:04:49 +1100 Subject: [PATCH 72/77] Remove obsolete security settings (#40496) Removes the deprecated accept_default_password setting. This setting become redundant when default passwords were removed from 6.0, but the setting was kept for BWC. Removes native role store cache settings. These have been unused since 5.2 but were kept for BWC. --- docs/reference/migration/migrate_8_0.asciidoc | 2 ++ .../migration/migrate_8_0/security.asciidoc | 18 ++++++++++++++++++ .../elasticsearch/xpack/security/Security.java | 1 - .../security/authc/esnative/ReservedRealm.java | 5 ----- .../security/authz/store/NativeRolesStore.java | 14 -------------- 5 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 docs/reference/migration/migrate_8_0/security.asciidoc diff --git a/docs/reference/migration/migrate_8_0.asciidoc b/docs/reference/migration/migrate_8_0.asciidoc index 789a2d5fa0ce4..0c695a3b2bb47 100644 --- a/docs/reference/migration/migrate_8_0.asciidoc +++ b/docs/reference/migration/migrate_8_0.asciidoc @@ -15,6 +15,7 @@ coming[8.0.0] * <> * <> * <> +* <> //NOTE: The notable-breaking-changes tagged regions are re-used in the //Installation and Upgrade Guide @@ -41,3 +42,4 @@ include::migrate_8_0/analysis.asciidoc[] include::migrate_8_0/discovery.asciidoc[] include::migrate_8_0/mappings.asciidoc[] include::migrate_8_0/snapshots.asciidoc[] +include::migrate_8_0/security.asciidoc[] diff --git a/docs/reference/migration/migrate_8_0/security.asciidoc b/docs/reference/migration/migrate_8_0/security.asciidoc new file mode 100644 index 0000000000000..e09d21764f740 --- /dev/null +++ b/docs/reference/migration/migrate_8_0/security.asciidoc @@ -0,0 +1,18 @@ +[float] +[[breaking_80_security_changes]] +=== Security changes + +[float] +==== The `accept_default_password` setting has been removed + +The `xpack.security.authc.accept_default_password` setting has not had any affect +since the 6.0 release of {es}. It has been removed and cannot be used. + +[float] +==== The `roles.index.cache.*` settings have been removed + +The `xpack.security.authz.store.roles.index.cache.max_size` and +`xpack.security.authz.store.roles.index.cache.ttl` settings have +been removed. These settings have been redundant and deprecated +since the 5.2 release of {es}. + diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 7b7e72fdd6b98..c6f269b1edd4d 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -618,7 +618,6 @@ public static List> getSettings(boolean transportClientMode, List ACCEPT_DEFAULT_PASSWORD_SETTING = Setting.boolSetting( - SecurityField.setting("authc.accept_default_password"), true, Setting.Property.NodeScope, Setting.Property.Filtered, - Setting.Property.Deprecated); public static final Setting BOOTSTRAP_ELASTIC_PASSWORD = SecureSetting.secureString("bootstrap.password", KeyStoreWrapper.SEED_SETTING); @@ -250,7 +246,6 @@ private Version getDefinedVersion(String username) { } public static void addSettings(List> settingsList) { - settingsList.add(ACCEPT_DEFAULT_PASSWORD_SETTING); settingsList.add(BOOTSTRAP_ELASTIC_PASSWORD); } } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java index b82bf7f3c7fc2..c0ec72277d870 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java @@ -26,10 +26,7 @@ import org.elasticsearch.client.Client; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.settings.Setting; -import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -69,7 +66,6 @@ import static org.elasticsearch.xpack.core.ClientHelper.SECURITY_ORIGIN; import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; import static org.elasticsearch.xpack.core.ClientHelper.stashWithOrigin; -import static org.elasticsearch.xpack.core.security.SecurityField.setting; import static org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ROLE_TYPE; import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_INDEX_NAME; @@ -83,11 +79,6 @@ */ public class NativeRolesStore implements BiConsumer, ActionListener> { - // these are no longer used, but leave them around for users upgrading - private static final Setting CACHE_SIZE_SETTING = - Setting.intSetting(setting("authz.store.roles.index.cache.max_size"), 10000, Property.NodeScope, Property.Deprecated); - private static final Setting CACHE_TTL_SETTING = Setting.timeSetting(setting("authz.store.roles.index.cache.ttl"), - TimeValue.timeValueMinutes(20), Property.NodeScope, Property.Deprecated); private static final Logger logger = LogManager.getLogger(NativeRolesStore.class); private final Settings settings; @@ -413,11 +404,6 @@ static RoleDescriptor transformRole(String id, BytesReference sourceBytes, Logge } } - public static void addSettings(List> settings) { - settings.add(CACHE_SIZE_SETTING); - settings.add(CACHE_TTL_SETTING); - } - /** * Gets the document's id field for the given role name. */ From 1f392a7d63d74cb65b67c9441be0c45c54ede411 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Fri, 29 Mar 2019 18:06:47 +1100 Subject: [PATCH 73/77] Support roles with application privileges against wildcard applications (#40398) This commit introduces 2 changes to application privileges: - The validation rules now accept a wildcard in the "suffix" of an application name. Wildcards were always accepted in the application name, but the "valid filename" check for the suffix incorrectly prevented the use of wildcards there. - A role may now be defined against a wildcard application (e.g. kibana-*) and this will be correctly treated as granting the named privileges against all named applications. This does not allow wildcard application names in the body of a "has-privileges" check, but the "has-privileges" check can test concrete application names against roles with wildcards. --- .../permission/ApplicationPermission.java | 21 ++-- .../authz/privilege/ApplicationPrivilege.java | 41 +++++-- .../ApplicationPrivilegeDescriptor.java | 7 ++ .../ApplicationPermissionTests.java | 33 +++++- .../privilege/ApplicationPrivilegeTests.java | 34 ++++-- .../authz/store/CompositeRolesStore.java | 4 +- .../authz/store/NativePrivilegeStore.java | 58 +++++++++- .../store/NativePrivilegeStoreTests.java | 39 +++++++ .../privileges/20_has_application_privs.yml | 103 ++++++++++++++++++ 9 files changed, 302 insertions(+), 38 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermission.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermission.java index 0cd4e8a8b0ddc..da6af4ec7cbdc 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermission.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermission.java @@ -104,15 +104,18 @@ public ResourcePrivilegesMap checkResourcePrivileges(final String applicationNam for (String checkResource : checkForResources) { for (String checkPrivilegeName : checkForPrivilegeNames) { final Set nameSet = Collections.singleton(checkPrivilegeName); - final ApplicationPrivilege checkPrivilege = ApplicationPrivilege.get(applicationName, nameSet, storedPrivileges); - assert checkPrivilege.getApplication().equals(applicationName) : "Privilege " + checkPrivilege + " should have application " - + applicationName; - assert checkPrivilege.name().equals(nameSet) : "Privilege " + checkPrivilege + " should have name " + nameSet; - - if (grants(checkPrivilege, checkResource)) { - resourcePrivilegesMapBuilder.addResourcePrivilege(checkResource, checkPrivilegeName, Boolean.TRUE); - } else { - resourcePrivilegesMapBuilder.addResourcePrivilege(checkResource, checkPrivilegeName, Boolean.FALSE); + final Set checkPrivileges = ApplicationPrivilege.get(applicationName, nameSet, storedPrivileges); + logger.trace("Resolved privileges [{}] for [{},{}]", checkPrivileges, applicationName, nameSet); + for (ApplicationPrivilege checkPrivilege : checkPrivileges) { + assert Automatons.predicate(applicationName).test(checkPrivilege.getApplication()) : "Privilege " + checkPrivilege + + " should have application " + applicationName; + assert checkPrivilege.name().equals(nameSet) : "Privilege " + checkPrivilege + " should have name " + nameSet; + + if (grants(checkPrivilege, checkResource)) { + resourcePrivilegesMapBuilder.addResourcePrivilege(checkResource, checkPrivilegeName, Boolean.TRUE); + } else { + resourcePrivilegesMapBuilder.addResourcePrivilege(checkResource, checkPrivilegeName, Boolean.FALSE); + } } } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilege.java index 13db17a63bb0d..c4460b000e6d8 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilege.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.core.security.authz.privilege; import org.elasticsearch.common.Strings; +import org.elasticsearch.xpack.core.security.support.Automatons; import java.util.Arrays; import java.util.Collection; @@ -15,6 +16,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -101,7 +103,7 @@ private static void validateApplicationName(String application, boolean allowWil if (allowWildcard == false) { throw new IllegalArgumentException("Application names may not contain '*' (found '" + application + "')"); } - if(application.equals("*")) { + if (application.equals("*")) { // this is allowed and short-circuiting here makes the later validation simpler return; } @@ -128,7 +130,10 @@ private static void validateApplicationName(String application, boolean allowWil } if (parts.length > 1) { - final String suffix = parts[1]; + String suffix = parts[1]; + if (allowWildcard && suffix.endsWith("*")) { + suffix = suffix.substring(0, suffix.length() - 1); + } if (Strings.validFileName(suffix) == false) { throw new IllegalArgumentException("An application name suffix may not contain any of the characters '" + Strings.collectionToDelimitedString(Strings.INVALID_FILENAME_CHARS, "") + "' (found '" + suffix + "')"); @@ -165,20 +170,38 @@ public static void validatePrivilegeOrActionName(String name) { } /** - * Finds or creates an application privileges with the provided names. + * Finds or creates a collection of application privileges with the provided names. + * If application is a wildcard, it will be expanded to all matching application names in {@code stored} * Each element in {@code name} may be the name of a stored privilege (to be resolved from {@code stored}, or a bespoke action pattern. */ - public static ApplicationPrivilege get(String application, Set name, Collection stored) { + public static Set get(String application, Set name, Collection stored) { if (name.isEmpty()) { - return NONE.apply(application); + return Collections.singleton(NONE.apply(application)); + } else if (application.contains("*")) { + Predicate predicate = Automatons.predicate(application); + final Set result = stored.stream() + .map(ApplicationPrivilegeDescriptor::getApplication) + .filter(predicate) + .distinct() + .map(appName -> resolve(appName, name, stored)) + .collect(Collectors.toSet()); + if (result.isEmpty()) { + return Collections.singleton(resolve(application, name, Collections.emptyMap())); + } else { + return result; + } } else { - Map lookup = stored.stream() - .filter(apd -> apd.getApplication().equals(application)) - .collect(Collectors.toMap(ApplicationPrivilegeDescriptor::getName, Function.identity())); - return resolve(application, name, lookup); + return Collections.singleton(resolve(application, name, stored)); } } + private static ApplicationPrivilege resolve(String application, Set name, Collection stored) { + final Map lookup = stored.stream() + .filter(apd -> apd.getApplication().equals(application)) + .collect(Collectors.toMap(ApplicationPrivilegeDescriptor::getName, Function.identity())); + return resolve(application, name, lookup); + } + private static ApplicationPrivilege resolve(String application, Set names, Map lookup) { final int size = names.size(); if (size == 0) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeDescriptor.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeDescriptor.java index 85d6aad3e3560..613f64f93b54a 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeDescriptor.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeDescriptor.java @@ -23,6 +23,8 @@ import java.util.Objects; import java.util.Set; +import static org.elasticsearch.common.Strings.collectionToCommaDelimitedString; + /** * An {@code ApplicationPrivilegeDescriptor} is a representation of a stored {@link ApplicationPrivilege}. * A user (via a role) can be granted an application privilege by name (e.g. ("myapp", "read"). @@ -104,6 +106,11 @@ public XContentBuilder toXContent(XContentBuilder builder, boolean includeTypeFi return builder.endObject(); } + @Override + public String toString() { + return getClass().getSimpleName() + "{[" + application + "],[" + name + "],[" + collectionToCommaDelimitedString(actions) + "]}"; + } + /** * Construct a new {@link ApplicationPrivilegeDescriptor} from XContent. * diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermissionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermissionTests.java index 992ca8db1b083..0f81b872b86d4 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermissionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/ApplicationPermissionTests.java @@ -13,15 +13,16 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import static java.util.Collections.singletonList; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; public class ApplicationPermissionTests extends ESTestCase { @@ -34,6 +35,7 @@ public class ApplicationPermissionTests extends ESTestCase { private ApplicationPrivilege app1Delete = storePrivilege("app1", "delete", "write/delete"); private ApplicationPrivilege app1Create = storePrivilege("app1", "create", "write/create"); private ApplicationPrivilege app2Read = storePrivilege("app2", "read", "read/*"); + private ApplicationPrivilege otherAppRead = storePrivilege("other-app", "read", "read/*"); private ApplicationPrivilege storePrivilege(String app, String name, String... patterns) { store.add(new ApplicationPrivilegeDescriptor(app, name, Sets.newHashSet(patterns), Collections.emptyMap())); @@ -104,6 +106,16 @@ public void testDoesNotMatchAcrossApplications() { assertThat(buildPermission(app1All, "*").grants(app2Read, "123"), equalTo(false)); } + public void testMatchingWithWildcardApplicationNames() { + final Set readAllApp = ApplicationPrivilege.get("app*", Collections.singleton("read"), store); + assertThat(buildPermission(readAllApp, "*").grants(app1Read, "123"), equalTo(true)); + assertThat(buildPermission(readAllApp, "foo/*").grants(app2Read, "foo/bar"), equalTo(true)); + + assertThat(buildPermission(readAllApp, "*").grants(app1Write, "123"), equalTo(false)); + assertThat(buildPermission(readAllApp, "foo/*").grants(app2Read, "bar/baz"), equalTo(false)); + assertThat(buildPermission(readAllApp, "*").grants(otherAppRead, "abc"), equalTo(false)); + } + public void testMergedPermissionChecking() { final ApplicationPrivilege app1ReadWrite = compositePrivilege("app1", app1Read, app1Write); final ApplicationPermission hasPermission = buildPermission(app1ReadWrite, "allow/*"); @@ -138,16 +150,27 @@ public void testInspectPermissionContents() { } private ApplicationPrivilege actionPrivilege(String appName, String... actions) { - return ApplicationPrivilege.get(appName, Sets.newHashSet(actions), Collections.emptyList()); + final Set privileges = ApplicationPrivilege.get(appName, Sets.newHashSet(actions), Collections.emptyList()); + assertThat(privileges, hasSize(1)); + return privileges.iterator().next(); } private ApplicationPrivilege compositePrivilege(String application, ApplicationPrivilege... children) { Set names = Stream.of(children).map(ApplicationPrivilege::name).flatMap(Set::stream).collect(Collectors.toSet()); - return ApplicationPrivilege.get(application, names, store); + final Set privileges = ApplicationPrivilege.get(application, names, store); + assertThat(privileges, hasSize(1)); + return privileges.iterator().next(); } - private ApplicationPermission buildPermission(ApplicationPrivilege privilege, String... resources) { - return new ApplicationPermission(singletonList(new Tuple<>(privilege, Sets.newHashSet(resources)))); + return buildPermission(Collections.singleton(privilege), resources); + } + + private ApplicationPermission buildPermission(Collection privileges, String... resources) { + final Set resourceSet = Sets.newHashSet(resources); + final List>> privilegesAndResources = privileges.stream() + .map(p -> new Tuple<>(p, resourceSet)) + .collect(Collectors.toList()); + return new ApplicationPermission(privilegesAndResources); } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeTests.java index c65f06f05f957..cd917ed81f16e 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ApplicationPrivilegeTests.java @@ -10,6 +10,7 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; +import org.hamcrest.Matchers; import org.junit.Assert; import java.util.Arrays; @@ -22,9 +23,11 @@ import static org.elasticsearch.common.Strings.collectionToCommaDelimitedString; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.iterableWithSize; public class ApplicationPrivilegeTests extends ESTestCase { @@ -59,6 +62,12 @@ public void testValidationOfApplicationName() { assertNoException(app, () -> ApplicationPrivilege.validateApplicationName(app)); assertNoException(app, () -> ApplicationPrivilege.validateApplicationNameOrWildcard(app)); } + + // wildcards in the suffix + for (String app : Arrays.asList("app1-*", "app1-foo*", "app1-.*", "app1-.foo.*", appNameWithSpecialChars + "*")) { + assertValidationFailure(app, "application name", () -> ApplicationPrivilege.validateApplicationName(app)); + assertNoException(app, () -> ApplicationPrivilege.validateApplicationNameOrWildcard(app)); + } } public void testValidationOfPrivilegeName() { @@ -101,16 +110,23 @@ public void testNonePrivilege() { } public void testGetPrivilegeByName() { - final ApplicationPrivilegeDescriptor descriptor = descriptor("my-app", "read", "data:read/*", "action:login"); + final ApplicationPrivilegeDescriptor myRead = descriptor("my-app", "read", "data:read/*", "action:login"); final ApplicationPrivilegeDescriptor myWrite = descriptor("my-app", "write", "data:write/*", "action:login"); final ApplicationPrivilegeDescriptor myAdmin = descriptor("my-app", "admin", "data:read/*", "action:*"); final ApplicationPrivilegeDescriptor yourRead = descriptor("your-app", "read", "data:read/*", "action:login"); - final Set stored = Sets.newHashSet(descriptor, myWrite, myAdmin, yourRead); + final Set stored = Sets.newHashSet(myRead, myWrite, myAdmin, yourRead); + + final Set myAppRead = ApplicationPrivilege.get("my-app", Collections.singleton("read"), stored); + assertThat(myAppRead, iterableWithSize(1)); + assertPrivilegeEquals(myAppRead.iterator().next(), myRead); - assertEqual(ApplicationPrivilege.get("my-app", Collections.singleton("read"), stored), descriptor); - assertEqual(ApplicationPrivilege.get("my-app", Collections.singleton("write"), stored), myWrite); + final Set myAppWrite = ApplicationPrivilege.get("my-app", Collections.singleton("write"), stored); + assertThat(myAppWrite, iterableWithSize(1)); + assertPrivilegeEquals(myAppWrite.iterator().next(), myWrite); - final ApplicationPrivilege readWrite = ApplicationPrivilege.get("my-app", Sets.newHashSet("read", "write"), stored); + final Set myReadWrite = ApplicationPrivilege.get("my-app", Sets.newHashSet("read", "write"), stored); + assertThat(myReadWrite, Matchers.hasSize(1)); + final ApplicationPrivilege readWrite = myReadWrite.iterator().next(); assertThat(readWrite.getApplication(), equalTo("my-app")); assertThat(readWrite.name(), containsInAnyOrder("read", "write")); assertThat(readWrite.getPatterns(), arrayContainingInAnyOrder("data:read/*", "data:write/*", "action:login")); @@ -124,10 +140,10 @@ public void testGetPrivilegeByName() { } } - private void assertEqual(ApplicationPrivilege myReadPriv, ApplicationPrivilegeDescriptor myRead) { - assertThat(myReadPriv.getApplication(), equalTo(myRead.getApplication())); - assertThat(getPrivilegeName(myReadPriv), equalTo(myRead.getName())); - assertThat(Sets.newHashSet(myReadPriv.getPatterns()), equalTo(myRead.getActions())); + private void assertPrivilegeEquals(ApplicationPrivilege privilege, ApplicationPrivilegeDescriptor descriptor) { + assertThat(privilege.getApplication(), equalTo(descriptor.getApplication())); + assertThat(privilege.name(), contains(descriptor.getName())); + assertThat(Sets.newHashSet(privilege.getPatterns()), equalTo(descriptor.getActions())); } private ApplicationPrivilegeDescriptor descriptor(String application, String name, String... actions) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java index 2d1d4a98b4ba6..48659b8968661 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java @@ -402,8 +402,8 @@ public static void buildRoleFromDescriptors(Collection roleDescr .flatMap(Collection::stream) .collect(Collectors.toSet()); privilegeStore.getPrivileges(applicationNames, applicationPrivilegeNames, ActionListener.wrap(appPrivileges -> { - applicationPrivilegesMap.forEach((key, names) -> - builder.addApplicationPrivilege(ApplicationPrivilege.get(key.v1(), names, appPrivileges), key.v2())); + applicationPrivilegesMap.forEach((key, names) -> ApplicationPrivilege.get(key.v1(), names, appPrivileges) + .forEach(priv -> builder.addApplicationPrivilege(priv, key.v2()))); listener.onResponse(builder.build()); }, listener::onFailure)); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStore.java index 09c89752f8314..19694bb003314 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStore.java @@ -34,9 +34,11 @@ import org.elasticsearch.common.xcontent.XContentParseException; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.index.query.TermsQueryBuilder; import org.elasticsearch.xpack.core.ClientHelper; import org.elasticsearch.xpack.core.security.ScrollHelper; import org.elasticsearch.xpack.core.security.action.role.ClearRolesCacheRequest; @@ -46,6 +48,7 @@ import org.elasticsearch.xpack.security.support.SecurityIndexManager; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -62,6 +65,7 @@ import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; import static org.elasticsearch.xpack.core.ClientHelper.stashWithOrigin; import static org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor.DOC_TYPE_VALUE; +import static org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor.Fields.APPLICATION; import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECURITY_INDEX_NAME; /** @@ -97,7 +101,7 @@ public void getPrivileges(Collection applications, Collection na listener.onResponse(Collections.emptyList()); } else if (frozenSecurityIndex.isAvailable() == false) { listener.onFailure(frozenSecurityIndex.getUnavailableReason()); - } else if (applications != null && applications.size() == 1 && names != null && names.size() == 1) { + } else if (isSinglePrivilegeMatch(applications, names)) { getPrivilege(Objects.requireNonNull(Iterables.get(applications, 0)), Objects.requireNonNull(Iterables.get(names, 0)), ActionListener.wrap(privilege -> listener.onResponse(privilege == null ? Collections.emptyList() : Collections.singletonList(privilege)), @@ -110,11 +114,14 @@ public void getPrivileges(Collection applications, Collection na if (isEmpty(applications) && isEmpty(names)) { query = typeQuery; } else if (isEmpty(names)) { - query = QueryBuilders.boolQuery().filter(typeQuery).filter( - QueryBuilders.termsQuery(ApplicationPrivilegeDescriptor.Fields.APPLICATION.getPreferredName(), applications)); + query = QueryBuilders.boolQuery().filter(typeQuery).filter(getApplicationNameQuery(applications)); } else if (isEmpty(applications)) { query = QueryBuilders.boolQuery().filter(typeQuery) - .filter(QueryBuilders.termsQuery(ApplicationPrivilegeDescriptor.Fields.NAME.getPreferredName(), names)); + .filter(getPrivilegeNameQuery(names)); + } else if (hasWildcard(applications)) { + query = QueryBuilders.boolQuery().filter(typeQuery) + .filter(getApplicationNameQuery(applications)) + .filter(getPrivilegeNameQuery(names)); } else { final String[] docIds = applications.stream() .flatMap(a -> names.stream().map(n -> toDocId(a, n))) @@ -139,6 +146,49 @@ public void getPrivileges(Collection applications, Collection na } } + private boolean isSinglePrivilegeMatch(Collection applications, Collection names) { + return applications != null && applications.size() == 1 && hasWildcard(applications) == false && names != null && names.size() == 1; + } + + private boolean hasWildcard(Collection applications) { + return applications.stream().anyMatch(n -> n.endsWith("*")); + } + + private QueryBuilder getPrivilegeNameQuery(Collection names) { + return QueryBuilders.termsQuery(ApplicationPrivilegeDescriptor.Fields.NAME.getPreferredName(), names); + } + + private QueryBuilder getApplicationNameQuery(Collection applications) { + if (applications.contains("*")) { + return QueryBuilders.existsQuery(APPLICATION.getPreferredName()); + } + final List rawNames = new ArrayList<>(applications.size()); + final List wildcardNames = new ArrayList<>(applications.size()); + for (String name : applications) { + if (name.endsWith("*")) { + wildcardNames.add(name); + } else { + rawNames.add(name); + } + } + + assert rawNames.isEmpty() == false || wildcardNames.isEmpty() == false; + + TermsQueryBuilder termsQuery = rawNames.isEmpty() ? null : QueryBuilders.termsQuery(APPLICATION.getPreferredName(), rawNames); + if (wildcardNames.isEmpty()) { + return termsQuery; + } + final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); + if (termsQuery != null) { + boolQuery.filter(termsQuery); + } + for (String wildcard : wildcardNames) { + final String prefix = wildcard.substring(0, wildcard.length() - 1); + boolQuery.filter(QueryBuilders.prefixQuery(APPLICATION.getPreferredName(), prefix)); + } + return boolQuery; + } + private static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java index 8f60b1d30523f..a833748854943 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/NativePrivilegeStoreTests.java @@ -181,6 +181,45 @@ public void testGetPrivilegesByApplicationName() throws Exception { assertResult(sourcePrivileges, future); } + public void testGetPrivilegesByWildcardApplicationName() throws Exception { + final PlainActionFuture> future = new PlainActionFuture<>(); + store.getPrivileges(Arrays.asList("myapp-*", "yourapp"), null, future); + assertThat(requests, iterableWithSize(1)); + assertThat(requests.get(0), instanceOf(SearchRequest.class)); + SearchRequest request = (SearchRequest) requests.get(0); + assertThat(request.indices(), arrayContaining(SecurityIndexManager.SECURITY_INDEX_NAME)); + + final String query = Strings.toString(request.source().query()); + assertThat(query, containsString("{\"bool\":{\"filter\":[{\"terms\":{\"application\":[\"yourapp\"]")); + assertThat(query, containsString("{\"prefix\":{\"application\":{\"value\":\"myapp-\"")); + assertThat(query, containsString("{\"term\":{\"type\":{\"value\":\"application-privilege\"")); + + final SearchHit[] hits = new SearchHit[0]; + listener.get().onResponse(new SearchResponse(new SearchResponseSections( + new SearchHits(hits, new TotalHits(hits.length, TotalHits.Relation.EQUAL_TO), 0f), + null, null, false, false, null, 1), + "_scrollId1", 1, 1, 0, 1, null, null)); + } + + public void testGetPrivilegesByStarApplicationName() throws Exception { + final PlainActionFuture> future = new PlainActionFuture<>(); + store.getPrivileges(Arrays.asList("*", "anything"), null, future); + assertThat(requests, iterableWithSize(1)); + assertThat(requests.get(0), instanceOf(SearchRequest.class)); + SearchRequest request = (SearchRequest) requests.get(0); + assertThat(request.indices(), arrayContaining(SecurityIndexManager.SECURITY_INDEX_NAME)); + + final String query = Strings.toString(request.source().query()); + assertThat(query, containsString("{\"exists\":{\"field\":\"application\"")); + assertThat(query, containsString("{\"term\":{\"type\":{\"value\":\"application-privilege\"")); + + final SearchHit[] hits = new SearchHit[0]; + listener.get().onResponse(new SearchResponse(new SearchResponseSections( + new SearchHits(hits, new TotalHits(hits.length, TotalHits.Relation.EQUAL_TO), 0f), + null, null, false, false, null, 1), + "_scrollId1", 1, 1, 0, 1, null, null)); + } + public void testGetAllPrivileges() throws Exception { final List sourcePrivileges = Arrays.asList( new ApplicationPrivilegeDescriptor("app1", "admin", newHashSet("action:admin/*", "action:login", "data:read/*"), emptyMap()), diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/20_has_application_privs.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/20_has_application_privs.yml index 85ac286c3f025..eb92cc252b560 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/20_has_application_privs.yml +++ b/x-pack/plugin/src/test/resources/rest-api-spec/test/privileges/20_has_application_privs.yml @@ -28,6 +28,16 @@ setup: "name": "write", "actions": [ "data:write/*" ] } + }, + "yourapp-v1" : { + "read": { + "actions": [ "data:read/*" ] + } + }, + "yourapp-v2" : { + "read": { + "actions": [ "data:read/*" ] + } } } @@ -83,6 +93,21 @@ setup: } ] } + - do: + security.put_role: + name: "yourapp_read_config" + body: > + { + "cluster": [], + "indices": [], + "applications": [ + { + "application": "yourapp-*", + "privileges": ["read"], + "resources": ["settings/*"] + } + ] + } # And a user for each role - do: @@ -101,6 +126,14 @@ setup: "password": "p@ssw0rd", "roles" : [ "myapp_engineering_write" ] } + - do: + security.put_user: + username: "your_read" + body: > + { + "password": "p@ssw0rd", + "roles" : [ "yourapp_read_config" ] + } --- teardown: @@ -109,6 +142,16 @@ teardown: application: myapp name: "user,read,write" ignore: 404 + - do: + security.delete_privileges: + application: yourapp-v1 + name: "read" + ignore: 404 + - do: + security.delete_privileges: + application: yourapp-v2 + name: "read" + ignore: 404 - do: security.delete_user: @@ -120,6 +163,11 @@ teardown: username: "eng_write" ignore: 404 + - do: + security.delete_user: + username: "your_read" + ignore: 404 + - do: security.delete_role: name: "myapp_engineering_read" @@ -129,6 +177,11 @@ teardown: security.delete_role: name: "myapp_engineering_write" ignore: 404 + + - do: + security.delete_role: + name: "yourapp_read_config" + ignore: 404 --- "Test has_privileges with application-privileges": - do: @@ -188,3 +241,53 @@ teardown: } } } + - do: + headers: { Authorization: "Basic eW91cl9yZWFkOnBAc3N3MHJk" } # your_read + security.has_privileges: + user: null + body: > + { + "application": [ + { + "application" : "yourapp-v1", + "resources" : [ "settings/host", "settings/port", "system/key" ], + "privileges" : [ "data:read/settings", "data:write/settings", "read", "write" ] + }, + { + "application" : "yourapp-v2", + "resources" : [ "settings/host" ], + "privileges" : [ "data:read/settings", "data:write/settings" ] + } + ] + } + + - match: { "username" : "your_read" } + - match: { "has_all_requested" : false } + - match: { "application": { + "yourapp-v1": { + "settings/host": { + "data:read/settings": true, + "data:write/settings": false, + "read": true, + "write": false + }, + "settings/port": { + "data:read/settings": true, + "data:write/settings": false, + "read": true, + "write": false + }, + "system/key": { + "data:read/settings": false, + "data:write/settings": false, + "read": false, + "write": false + } + }, + "yourapp-v2": { + "settings/host": { + "data:read/settings": true, + "data:write/settings": false, + } + } + } } From ae569a286dd0acb85152723eb207f7974bd27ea2 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Fri, 29 Mar 2019 09:01:36 +0100 Subject: [PATCH 74/77] Fix merging of search_as_you_type field mapper (#40593) The merge of the `search_as_you_type` field mapper uses the wrong prefix field and does not update the underlying field types. --- .../mapper/SearchAsYouTypeFieldMapper.java | 16 +++++- .../SearchAsYouTypeFieldMapperTests.java | 55 +++++++++++++++++++ 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java index 69948bf98a6ac..867e975e9f51c 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapper.java @@ -516,7 +516,7 @@ protected void parseCreateField(ParseContext context, List field @Override protected String contentType() { - return CONTENT_TYPE; + return "shingle"; } } @@ -663,6 +663,16 @@ public SearchAsYouTypeFieldMapper(String simpleName, this.maxShingleSize = maxShingleSize; } + @Override + public FieldMapper updateFieldType(Map fullNameToFieldType) { + SearchAsYouTypeFieldMapper fieldMapper = (SearchAsYouTypeFieldMapper) super.updateFieldType(fullNameToFieldType); + fieldMapper.prefixField = (PrefixFieldMapper) fieldMapper.prefixField.updateFieldType(fullNameToFieldType); + for (int i = 0; i < fieldMapper.shingleFields.length; i++) { + fieldMapper.shingleFields[i] = (ShingleFieldMapper) fieldMapper.shingleFields[i].updateFieldType(fullNameToFieldType); + } + return fieldMapper; + } + @Override protected void parseCreateField(ParseContext context, List fields) throws IOException { final String value = context.externalValueSet() ? context.externalValue().toString() : context.parser().textOrNull(); @@ -692,10 +702,10 @@ protected void doMerge(Mapper mergeWith) { super.doMerge(mergeWith); SearchAsYouTypeFieldMapper mw = (SearchAsYouTypeFieldMapper) mergeWith; if (mw.maxShingleSize != maxShingleSize) { - throw new IllegalArgumentException("mapper [" + name() + "] has different maxShingleSize setting, current [" + throw new IllegalArgumentException("mapper [" + name() + "] has different [max_shingle_size] setting, current [" + this.maxShingleSize + "], merged [" + mw.maxShingleSize + "]"); } - this.prefixField = (PrefixFieldMapper) this.prefixField.merge(mw); + this.prefixField = (PrefixFieldMapper) this.prefixField.merge(mw.prefixField); ShingleFieldMapper[] shingleFieldMappers = new ShingleFieldMapper[mw.shingleFields.length]; for (int i = 0; i < shingleFieldMappers.length; i++) { diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java index 9ed43a9505624..4622b34ea1514 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java @@ -71,6 +71,7 @@ import static java.util.Arrays.asList; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.hasProperty; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.notNullValue; @@ -180,6 +181,60 @@ public void testConfiguration() throws IOException { getShingleFieldMapper(defaultMapper, "a_field._4gram").fieldType(), 4, analyzerName, prefixFieldMapper.fieldType()); } + public void testSimpleMerge() throws IOException { + MapperService mapperService = createIndex("test").mapperService(); + { + String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("analyzer", "standard") + .endObject() + .endObject() + .endObject().endObject()); + DocumentMapper mapper = mapperService.merge("_doc", + new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); + } + + { + String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("analyzer", "standard") + .endObject() + .startObject("b_field") + .field("type", "text") + .endObject() + .endObject() + .endObject().endObject()); + DocumentMapper mapper = mapperService.merge("_doc", + new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE); + } + + { + String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject() + .startObject("_doc") + .startObject("properties") + .startObject("a_field") + .field("type", "search_as_you_type") + .field("analyzer", "standard") + .field("max_shingle_size", "4") + .endObject() + .startObject("b_field") + .field("type", "text") + .endObject() + .endObject() + .endObject().endObject()); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> mapperService.merge("_doc", + new CompressedXContent(mapping), MapperService.MergeReason.MAPPING_UPDATE)); + assertThat(e.getMessage(), containsString("different [max_shingle_size]")); + } + } + public void testIndexOptions() throws IOException { final String mapping = Strings.toString(XContentFactory.jsonBuilder() .startObject() From 53df53ada80cd2249e20bf00a18defbf16aaa8b3 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Fri, 29 Mar 2019 09:34:51 +0100 Subject: [PATCH 75/77] Update docs for the DFR similarity (#40579) The basic models `b, de, p` and the after effect `no` are not available anymore in Lucene 8 but they are still listed in the >7x documentation. This change removes these references that should also be listed in the breaking change of es 7.0. Closes #40264 --- .../index-modules/similarity.asciidoc | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/docs/reference/index-modules/similarity.asciidoc b/docs/reference/index-modules/similarity.asciidoc index 014923d463cbd..ee6cf3958375b 100644 --- a/docs/reference/index-modules/similarity.asciidoc +++ b/docs/reference/index-modules/similarity.asciidoc @@ -92,22 +92,14 @@ from randomness] framework. This similarity has the following options: [horizontal] `basic_model`:: - Possible values: {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelG.html[`be`], - {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelD.html[`d`], - {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelG.html[`g`], + Possible values: {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelG.html[`g`], {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelIF.html[`if`], - {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelIn.html[`in`], - {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelIne.html[`ine`] and - {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelP.html[`p`]. - -`be`, `d` and `p` should be avoided in practice as they might return scores that -are equal to 0 or infinite with terms that do not meet the expected random -distribution. + {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelIn.html[`in`] and + {lucene-core-javadoc}/org/apache/lucene/search/similarities/BasicModelIne.html[`ine`]. `after_effect`:: - Possible values: {lucene-core-javadoc}/org/apache/lucene/search/similarities/AfterEffect.NoAfterEffect.html[`no`], - {lucene-core-javadoc}/org/apache/lucene/search/similarities/AfterEffectB.html[`b`] and - {lucene-core-javadoc}/org/apache/lucene/search/similarities/AfterEffectL.html[`l`]. + Possible values: {lucene-core-javadoc}/org/apache/lucene/search/similarities/AfterEffectB.html[`b`] and + {lucene-core-javadoc}/org/apache/lucene/search/similarities/AfterEffectB.html[`l`]. `normalization`:: Possible values: {lucene-core-javadoc}/org/apache/lucene/search/similarities/Normalization.NoNormalization.html[`no`], From c106c783a0cc3d4b57386cd4e83ae7ad74ef13de Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Fri, 29 Mar 2019 09:42:59 +0100 Subject: [PATCH 76/77] Remove -Xlint exclusions in the ingest-common module. (#40505) Fix the generics in processors extending AbstractStringProcessor and its factory. Relates to #40366 --- modules/ingest-common/build.gradle | 3 --- .../ingest/common/AbstractStringProcessor.java | 8 ++++---- .../org/elasticsearch/ingest/common/BytesProcessor.java | 2 +- .../org/elasticsearch/ingest/common/GsubProcessor.java | 6 +++--- .../elasticsearch/ingest/common/LowercaseProcessor.java | 2 +- .../org/elasticsearch/ingest/common/TrimProcessor.java | 2 +- .../elasticsearch/ingest/common/URLDecodeProcessor.java | 2 +- .../elasticsearch/ingest/common/UppercaseProcessor.java | 2 +- .../common/AbstractStringProcessorFactoryTestCase.java | 8 ++++---- .../ingest/common/AbstractStringProcessorTestCase.java | 6 +++--- .../elasticsearch/ingest/common/BytesProcessorTests.java | 4 ++-- .../ingest/common/GsubProcessorFactoryTests.java | 2 +- .../elasticsearch/ingest/common/GsubProcessorTests.java | 4 ++-- .../ingest/common/LowercaseProcessorTests.java | 4 ++-- .../elasticsearch/ingest/common/TrimProcessorTests.java | 4 ++-- .../ingest/common/URLDecodeProcessorTests.java | 4 ++-- .../ingest/common/UppercaseProcessorTests.java | 4 ++-- 17 files changed, 32 insertions(+), 35 deletions(-) diff --git a/modules/ingest-common/build.gradle b/modules/ingest-common/build.gradle index 1681258e7c7ee..b6179eb852ae9 100644 --- a/modules/ingest-common/build.gradle +++ b/modules/ingest-common/build.gradle @@ -29,9 +29,6 @@ dependencies { compile project(':libs:dissect') } -compileJava.options.compilerArgs << "-Xlint:-unchecked,-rawtypes" -compileTestJava.options.compilerArgs << "-Xlint:-unchecked,-rawtypes" - integTestCluster { module project(':modules:lang-painless') } \ No newline at end of file diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AbstractStringProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AbstractStringProcessor.java index 792e5e4ebed2d..546519aa5f606 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AbstractStringProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/AbstractStringProcessor.java @@ -80,8 +80,8 @@ protected Factory(String processorType) { } @Override - public AbstractStringProcessor create(Map registry, String tag, - Map config) throws Exception { + public AbstractStringProcessor create(Map registry, String tag, + Map config) throws Exception { String field = ConfigurationUtils.readStringProperty(processorType, tag, config, "field"); boolean ignoreMissing = ConfigurationUtils.readBooleanProperty(processorType, tag, config, "ignore_missing", false); String targetField = ConfigurationUtils.readStringProperty(processorType, tag, config, "target_field", field); @@ -89,7 +89,7 @@ public AbstractStringProcessor create(Map registry, S return newProcessor(tag, config, field, ignoreMissing, targetField); } - protected abstract AbstractStringProcessor newProcessor(String processorTag, Map config, String field, - boolean ignoreMissing, String targetField); + protected abstract AbstractStringProcessor newProcessor(String processorTag, Map config, String field, + boolean ignoreMissing, String targetField); } } diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/BytesProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/BytesProcessor.java index d07b56e1b3df5..8de75878f5fe5 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/BytesProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/BytesProcessor.java @@ -27,7 +27,7 @@ * Processor that converts the content of string fields to the byte value. * Throws exception is the field is not of type string or can not convert to the numeric byte value */ -public final class BytesProcessor extends AbstractStringProcessor { +public final class BytesProcessor extends AbstractStringProcessor { public static final String TYPE = "bytes"; diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GsubProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GsubProcessor.java index 39553910692fc..9f3e656bba4b6 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GsubProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GsubProcessor.java @@ -29,7 +29,7 @@ * Processor that allows to search for patterns in field content and replace them with corresponding string replacement. * Support fields of string type only, throws exception if a field is of a different type. */ -public final class GsubProcessor extends AbstractStringProcessor { +public final class GsubProcessor extends AbstractStringProcessor { public static final String TYPE = "gsub"; @@ -67,8 +67,8 @@ public Factory() { } @Override - protected AbstractStringProcessor newProcessor(String processorTag, Map config, String field, - boolean ignoreMissing, String targetField) { + protected GsubProcessor newProcessor(String processorTag, Map config, String field, + boolean ignoreMissing, String targetField) { String pattern = readStringProperty(TYPE, processorTag, config, "pattern"); String replacement = readStringProperty(TYPE, processorTag, config, "replacement"); Pattern searchPattern; diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/LowercaseProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/LowercaseProcessor.java index 4269cb05257f5..6c14dbdabba78 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/LowercaseProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/LowercaseProcessor.java @@ -27,7 +27,7 @@ * Throws exception is the field is not of type string. */ -public final class LowercaseProcessor extends AbstractStringProcessor { +public final class LowercaseProcessor extends AbstractStringProcessor { public static final String TYPE = "lowercase"; diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/TrimProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/TrimProcessor.java index 98fe1223e5391..d1b3c87785424 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/TrimProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/TrimProcessor.java @@ -25,7 +25,7 @@ * Processor that trims the content of string fields. * Throws exception is the field is not of type string. */ -public final class TrimProcessor extends AbstractStringProcessor { +public final class TrimProcessor extends AbstractStringProcessor { public static final String TYPE = "trim"; diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/URLDecodeProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/URLDecodeProcessor.java index fb6c5acf98b24..fa9d377714ee9 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/URLDecodeProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/URLDecodeProcessor.java @@ -26,7 +26,7 @@ /** * Processor that URL-decodes a string */ -public final class URLDecodeProcessor extends AbstractStringProcessor { +public final class URLDecodeProcessor extends AbstractStringProcessor { public static final String TYPE = "urldecode"; diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/UppercaseProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/UppercaseProcessor.java index 6c428627c7d72..4503bfc02f71e 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/UppercaseProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/UppercaseProcessor.java @@ -26,7 +26,7 @@ * Processor that converts the content of string fields to uppercase. * Throws exception is the field is not of type string. */ -public final class UppercaseProcessor extends AbstractStringProcessor { +public final class UppercaseProcessor extends AbstractStringProcessor { public static final String TYPE = "uppercase"; diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorFactoryTestCase.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorFactoryTestCase.java index 0465e24902842..ba6a2be73465e 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorFactoryTestCase.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorFactoryTestCase.java @@ -37,7 +37,7 @@ protected Map modifyConfig(Map config) { return config; } - protected void assertProcessor(AbstractStringProcessor processor) {} + protected void assertProcessor(AbstractStringProcessor processor) {} public void testCreate() throws Exception { AbstractStringProcessor.Factory factory = newFactory(); @@ -47,7 +47,7 @@ public void testCreate() throws Exception { Map config = new HashMap<>(); config.put("field", fieldName); - AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config)); + AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config)); assertThat(processor.getTag(), equalTo(processorTag)); assertThat(processor.getField(), equalTo(fieldName)); assertThat(processor.isIgnoreMissing(), is(false)); @@ -64,7 +64,7 @@ public void testCreateWithIgnoreMissing() throws Exception { config.put("field", fieldName); config.put("ignore_missing", true); - AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config)); + AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config)); assertThat(processor.getTag(), equalTo(processorTag)); assertThat(processor.getField(), equalTo(fieldName)); assertThat(processor.isIgnoreMissing(), is(true)); @@ -82,7 +82,7 @@ public void testCreateWithTargetField() throws Exception { config.put("field", fieldName); config.put("target_field", targetFieldName); - AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config)); + AbstractStringProcessor processor = factory.create(null, processorTag, modifyConfig(config)); assertThat(processor.getTag(), equalTo(processorTag)); assertThat(processor.getField(), equalTo(fieldName)); assertThat(processor.isIgnoreMissing(), is(false)); diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorTestCase.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorTestCase.java index 4e4182bfdc891..f667f84e5d7b1 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorTestCase.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AbstractStringProcessorTestCase.java @@ -33,7 +33,7 @@ public abstract class AbstractStringProcessorTestCase extends ESTestCase { - protected abstract AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField); + protected abstract AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField); protected String modifyInput(String input) { return input; @@ -41,8 +41,8 @@ protected String modifyInput(String input) { protected abstract T expectedResult(String input); - protected Class expectedResultType(){ - return (Class) String.class; // most results types are Strings + protected Class expectedResultType(){ + return String.class; // most results types are Strings } public void testProcessor() throws Exception { diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/BytesProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/BytesProcessorTests.java index 788340a455a42..2520f3e5ad17f 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/BytesProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/BytesProcessorTests.java @@ -29,12 +29,12 @@ import static org.hamcrest.Matchers.equalTo; -public class BytesProcessorTests extends AbstractStringProcessorTestCase { +public class BytesProcessorTests extends AbstractStringProcessorTestCase { private String modifiedInput; @Override - protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { + protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { return new BytesProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField); } diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorFactoryTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorFactoryTests.java index 4a70b4686e0a6..0dadefbb4ee64 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorFactoryTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorFactoryTests.java @@ -42,7 +42,7 @@ protected Map modifyConfig(Map config) { } @Override - protected void assertProcessor(AbstractStringProcessor processor) { + protected void assertProcessor(AbstractStringProcessor processor) { GsubProcessor gsubProcessor = (GsubProcessor) processor; assertThat(gsubProcessor.getPattern().toString(), equalTo("\\.")); assertThat(gsubProcessor.getReplacement(), equalTo("-")); diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorTests.java index 38d0202d3a1e2..9c003356c3dff 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GsubProcessorTests.java @@ -21,10 +21,10 @@ import java.util.regex.Pattern; -public class GsubProcessorTests extends AbstractStringProcessorTestCase { +public class GsubProcessorTests extends AbstractStringProcessorTestCase { @Override - protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { + protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { return new GsubProcessor(randomAlphaOfLength(10), field, Pattern.compile("\\."), "-", ignoreMissing, targetField); } diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorTests.java index 67a73669c0387..b804d3a0221c2 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/LowercaseProcessorTests.java @@ -21,9 +21,9 @@ import java.util.Locale; -public class LowercaseProcessorTests extends AbstractStringProcessorTestCase { +public class LowercaseProcessorTests extends AbstractStringProcessorTestCase { @Override - protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { + protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { return new LowercaseProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField); } diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorTests.java index f0ae554f5cad1..abd7cae12fe91 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/TrimProcessorTests.java @@ -19,10 +19,10 @@ package org.elasticsearch.ingest.common; -public class TrimProcessorTests extends AbstractStringProcessorTestCase { +public class TrimProcessorTests extends AbstractStringProcessorTestCase { @Override - protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { + protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { return new TrimProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField); } diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/URLDecodeProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/URLDecodeProcessorTests.java index 7697f1fcba3d4..150d594afd9af 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/URLDecodeProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/URLDecodeProcessorTests.java @@ -22,14 +22,14 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; -public class URLDecodeProcessorTests extends AbstractStringProcessorTestCase { +public class URLDecodeProcessorTests extends AbstractStringProcessorTestCase { @Override protected String modifyInput(String input) { return "Hello%20G%C3%BCnter" + input; } @Override - protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { + protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { return new URLDecodeProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField); } diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorTests.java index 76459f8116890..1b027c4380837 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/UppercaseProcessorTests.java @@ -21,10 +21,10 @@ import java.util.Locale; -public class UppercaseProcessorTests extends AbstractStringProcessorTestCase { +public class UppercaseProcessorTests extends AbstractStringProcessorTestCase { @Override - protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { + protected AbstractStringProcessor newProcessor(String field, boolean ignoreMissing, String targetField) { return new UppercaseProcessor(randomAlphaOfLength(10), field, ignoreMissing, targetField); } From 963c9b8092a6ba95d85c7d97b8a2d43169832367 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Fri, 29 Mar 2019 09:44:42 +0100 Subject: [PATCH 77/77] Update ingest jdocs that a null return value will drop the current document. (#40359) --- server/src/main/java/org/elasticsearch/ingest/Pipeline.java | 3 +++ server/src/main/java/org/elasticsearch/ingest/Processor.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/ingest/Pipeline.java b/server/src/main/java/org/elasticsearch/ingest/Pipeline.java index fc5311be5cbde..218713383227e 100644 --- a/server/src/main/java/org/elasticsearch/ingest/Pipeline.java +++ b/server/src/main/java/org/elasticsearch/ingest/Pipeline.java @@ -89,6 +89,9 @@ public static Pipeline create(String id, Map config, /** * Modifies the data of a document to be indexed based on the processor this pipeline holds + * + * If null is returned then this document will be dropped and not indexed, otherwise + * this document will be kept and indexed. */ public IngestDocument execute(IngestDocument ingestDocument) throws Exception { long startTimeInNanos = relativeTimeProvider.getAsLong(); diff --git a/server/src/main/java/org/elasticsearch/ingest/Processor.java b/server/src/main/java/org/elasticsearch/ingest/Processor.java index 92b08bba77bf7..c064ddb35a129 100644 --- a/server/src/main/java/org/elasticsearch/ingest/Processor.java +++ b/server/src/main/java/org/elasticsearch/ingest/Processor.java @@ -39,6 +39,9 @@ public interface Processor { /** * Introspect and potentially modify the incoming data. + * + * @return If null is returned then the current document will be dropped and not be indexed, + * otherwise this document will be kept and indexed */ IngestDocument execute(IngestDocument ingestDocument) throws Exception;