From 827bb58c9e9256ec947d3a3114cbd583d090f059 Mon Sep 17 00:00:00 2001 From: Andy Bristol Date: Sun, 10 Jun 2018 08:12:46 -0700 Subject: [PATCH 01/71] [test] add fix for rare virtualbox error (#31212) See the vagrant issue mentioned in this commit for details. This error has happened a couple times in packaging test CI builds with workers using virtualbox 5.2.10r122088 --- Vagrantfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Vagrantfile b/Vagrantfile index 313cb351d8bc5..1ed75b223725b 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -31,6 +31,9 @@ Vagrant.configure(2) do |config| # Give the box more memory and cpu because our tests are beasts! vbox.memory = Integer(ENV['VAGRANT_MEMORY'] || 8192) vbox.cpus = Integer(ENV['VAGRANT_CPUS'] || 4) + + # see https://github.com/hashicorp/vagrant/issues/9524 + vbox.customize ["modifyvm", :id, "--audio", "none"] end # Switch the default share for the project root from /vagrant to From 49695b01c0ace5581cd9265431f39c392b8151cd Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Tue, 5 Jun 2018 09:02:13 -0400 Subject: [PATCH 02/71] TEST: Retry synced-flush if ongoing ops on primary (#30978) When the last indexing operation is completed, we will fire a global checkpoint sync. Since a global checkpoint sync request is a replication request, it will acquire an index shard permit on the primary when executing. If this happens at the same time while we are issuing the synced-flush, the synced-flush request will fail as it thinks there are in-flight operations. We can avoid such situation by retrying another synced-flush if the current request fails due to ongoing operations on the primary. Closes #29392 --- .../indices/flush/SyncedFlushService.java | 12 ------ .../elasticsearch/indices/flush/FlushIT.java | 22 +---------- .../indices/flush/SyncedFlushUtil.java | 37 +++++++++++++------ 3 files changed, 26 insertions(+), 45 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/indices/flush/SyncedFlushService.java b/server/src/main/java/org/elasticsearch/indices/flush/SyncedFlushService.java index 52e0ac8ab860f..6ef6c1546d152 100644 --- a/server/src/main/java/org/elasticsearch/indices/flush/SyncedFlushService.java +++ b/server/src/main/java/org/elasticsearch/indices/flush/SyncedFlushService.java @@ -19,7 +19,6 @@ package org.elasticsearch.indices.flush; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.elasticsearch.Assertions; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; @@ -502,18 +501,7 @@ private InFlightOpsResponse performInFlightOps(InFlightOpsRequest request) { if (indexShard.routingEntry().primary() == false) { throw new IllegalStateException("[" + request.shardId() +"] expected a primary shard"); } - if (Assertions.ENABLED) { - if (logger.isTraceEnabled()) { - logger.trace("in flight operations {}, acquirers {}", indexShard.getActiveOperationsCount(), indexShard.getActiveOperations()); - } - } int opCount = indexShard.getActiveOperationsCount(); - // Need to snapshot the debug info twice as it's updated concurrently with the permit count. - if (Assertions.ENABLED) { - if (logger.isTraceEnabled()) { - logger.trace("in flight operations {}, acquirers {}", indexShard.getActiveOperationsCount(), indexShard.getActiveOperations()); - } - } return new InFlightOpsResponse(opCount); } diff --git a/server/src/test/java/org/elasticsearch/indices/flush/FlushIT.java b/server/src/test/java/org/elasticsearch/indices/flush/FlushIT.java index 94bd8e80898db..a543e87adcb46 100644 --- a/server/src/test/java/org/elasticsearch/indices/flush/FlushIT.java +++ b/server/src/test/java/org/elasticsearch/indices/flush/FlushIT.java @@ -46,16 +46,13 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.IndicesService; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.junit.annotations.TestLogging; import java.io.IOException; import java.util.Arrays; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -103,7 +100,7 @@ public void onFailure(Exception e) { } } - public void testSyncedFlush() throws ExecutionException, InterruptedException, IOException { + public void testSyncedFlush() throws Exception { internalCluster().ensureAtLeastNumDataNodes(2); prepareCreate("test").setSettings(Settings.builder().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)).get(); ensureGreen(); @@ -246,16 +243,6 @@ private void indexDoc(Engine engine, String id) throws IOException { assertThat(indexResult.getFailure(), nullValue()); } - private String syncedFlushDescription(ShardsSyncedFlushResult result) { - String detail = result.shardResponses().entrySet().stream() - .map(e -> "Shard [" + e.getKey() + "], result [" + e.getValue() + "]") - .collect(Collectors.joining(",")); - return String.format(Locale.ROOT, "Total shards: [%d], failed: [%s], reason: [%s], detail: [%s]", - result.totalShards(), result.failed(), result.failureReason(), detail); - } - - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/29392") - @TestLogging("_root:DEBUG,org.elasticsearch.indices.flush:TRACE") public void testSyncedFlushSkipOutOfSyncReplicas() throws Exception { internalCluster().ensureAtLeastNumDataNodes(between(2, 3)); final int numberOfReplicas = internalCluster().numDataNodes() - 1; @@ -281,7 +268,6 @@ public void testSyncedFlushSkipOutOfSyncReplicas() throws Exception { indexDoc(IndexShardTestCase.getEngine(outOfSyncReplica), "extra_" + i); } final ShardsSyncedFlushResult partialResult = SyncedFlushUtil.attemptSyncedFlush(logger, internalCluster(), shardId); - logger.info("Partial seal: {}", syncedFlushDescription(partialResult)); assertThat(partialResult.totalShards(), equalTo(numberOfReplicas + 1)); assertThat(partialResult.successfulShards(), equalTo(numberOfReplicas)); assertThat(partialResult.shardResponses().get(outOfSyncReplica.routingEntry()).failureReason, equalTo( @@ -297,8 +283,6 @@ public void testSyncedFlushSkipOutOfSyncReplicas() throws Exception { assertThat(fullResult.successfulShards(), equalTo(numberOfReplicas + 1)); } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/29392") - @TestLogging("_root:DEBUG,org.elasticsearch.indices.flush:TRACE") public void testDoNotRenewSyncedFlushWhenAllSealed() throws Exception { internalCluster().ensureAtLeastNumDataNodes(between(2, 3)); final int numberOfReplicas = internalCluster().numDataNodes() - 1; @@ -315,11 +299,9 @@ public void testDoNotRenewSyncedFlushWhenAllSealed() throws Exception { index("test", "doc", Integer.toString(i)); } final ShardsSyncedFlushResult firstSeal = SyncedFlushUtil.attemptSyncedFlush(logger, internalCluster(), shardId); - logger.info("First seal: {}", syncedFlushDescription(firstSeal)); assertThat(firstSeal.successfulShards(), equalTo(numberOfReplicas + 1)); // Do not renew synced-flush final ShardsSyncedFlushResult secondSeal = SyncedFlushUtil.attemptSyncedFlush(logger, internalCluster(), shardId); - logger.info("Second seal: {}", syncedFlushDescription(secondSeal)); assertThat(secondSeal.successfulShards(), equalTo(numberOfReplicas + 1)); assertThat(secondSeal.syncId(), equalTo(firstSeal.syncId())); // Shards were updated, renew synced flush. @@ -328,7 +310,6 @@ public void testDoNotRenewSyncedFlushWhenAllSealed() throws Exception { index("test", "doc", Integer.toString(i)); } final ShardsSyncedFlushResult thirdSeal = SyncedFlushUtil.attemptSyncedFlush(logger, internalCluster(), shardId); - logger.info("Third seal: {}", syncedFlushDescription(thirdSeal)); assertThat(thirdSeal.successfulShards(), equalTo(numberOfReplicas + 1)); assertThat(thirdSeal.syncId(), not(equalTo(firstSeal.syncId()))); // Manually remove or change sync-id, renew synced flush. @@ -344,7 +325,6 @@ public void testDoNotRenewSyncedFlushWhenAllSealed() throws Exception { assertThat(shard.commitStats().syncId(), nullValue()); } final ShardsSyncedFlushResult forthSeal = SyncedFlushUtil.attemptSyncedFlush(logger, internalCluster(), shardId); - logger.info("Forth seal: {}", syncedFlushDescription(forthSeal)); assertThat(forthSeal.successfulShards(), equalTo(numberOfReplicas + 1)); assertThat(forthSeal.syncId(), not(equalTo(thirdSeal.syncId()))); } diff --git a/server/src/test/java/org/elasticsearch/indices/flush/SyncedFlushUtil.java b/server/src/test/java/org/elasticsearch/indices/flush/SyncedFlushUtil.java index 987f69b65878a..8a8d57295a502 100644 --- a/server/src/test/java/org/elasticsearch/indices/flush/SyncedFlushUtil.java +++ b/server/src/test/java/org/elasticsearch/indices/flush/SyncedFlushUtil.java @@ -29,6 +29,9 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import static org.elasticsearch.test.ESTestCase.assertBusy; /** Utils for SyncedFlush */ public class SyncedFlushUtil { @@ -40,21 +43,31 @@ private SyncedFlushUtil() { /** * Blocking version of {@link SyncedFlushService#attemptSyncedFlush(ShardId, ActionListener)} */ - public static ShardsSyncedFlushResult attemptSyncedFlush(Logger logger, InternalTestCluster cluster, ShardId shardId) { + public static ShardsSyncedFlushResult attemptSyncedFlush(Logger logger, InternalTestCluster cluster, ShardId shardId) throws Exception { + /* + * When the last indexing operation is completed, we will fire a global checkpoint sync. + * Since a global checkpoint sync request is a replication request, it will acquire an index + * shard permit on the primary when executing. If this happens at the same time while we are + * issuing the synced-flush, the synced-flush request will fail as it thinks there are + * in-flight operations. We can avoid such situation by continuing issuing another synced-flush + * if the synced-flush failed due to the ongoing operations on the primary. + */ SyncedFlushService service = cluster.getInstance(SyncedFlushService.class); - logger.debug("Issue synced-flush on node [{}], shard [{}], cluster state [{}]", - service.nodeName(), shardId, cluster.clusterService(service.nodeName()).state()); - LatchedListener listener = new LatchedListener<>(); - service.attemptSyncedFlush(shardId, listener); - try { + AtomicReference> listenerHolder = new AtomicReference<>(); + assertBusy(() -> { + LatchedListener listener = new LatchedListener<>(); + listenerHolder.set(listener); + service.attemptSyncedFlush(shardId, listener); listener.latch.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + if (listener.result != null && listener.result.failureReason() != null + && listener.result.failureReason().contains("ongoing operations on primary")) { + throw new AssertionError(listener.result.failureReason()); // cause the assert busy to retry + } + }); + if (listenerHolder.get().error != null) { + throw ExceptionsHelper.convertToElastic(listenerHolder.get().error); } - if (listener.error != null) { - throw ExceptionsHelper.convertToElastic(listener.error); - } - return listener.result; + return listenerHolder.get().result; } public static final class LatchedListener implements ActionListener { From 3797ddabca35e1923b843a5b5346e4fead9156e3 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 11 Jun 2018 02:03:21 +0200 Subject: [PATCH 03/71] Move java version checker back to its own jar (#30708) The java version checker requires being written with java 7 APIs. In order to use java 8 apis in other launcher utilities, this commit moves the java version checker back to its own jar. --- distribution/build.gradle | 1 + distribution/src/bin/elasticsearch-env | 2 +- distribution/src/bin/elasticsearch-env.bat | 2 +- .../tools/java-version-checker/build.gradle | 14 ++++++++++ .../java_version_checker}/JavaVersion.java | 8 +++--- .../JavaVersionChecker.java | 28 ++++++++++++++++--- .../SuppressForbidden.java | 5 ++-- distribution/tools/launchers/build.gradle | 17 ++++------- .../tools/launchers/JvmOptionsParser.java | 2 ++ .../tools/launchers/Launchers.java | 2 ++ settings.gradle | 1 + 11 files changed, 59 insertions(+), 23 deletions(-) create mode 100644 distribution/tools/java-version-checker/build.gradle rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker}/JavaVersion.java (87%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker}/JavaVersionChecker.java (74%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker}/SuppressForbidden.java (92%) diff --git a/distribution/build.gradle b/distribution/build.gradle index 068c8da480f11..ff3a06b4dc577 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -231,6 +231,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { from { project(':server').jar } from { project(':server').configurations.runtime } from { project(':libs:plugin-classloader').jar } + from { project(':distribution:tools:java-version-checker').jar } from { project(':distribution:tools:launchers').jar } into('tools/plugin-cli') { from { project(':distribution:tools:plugin-cli').jar } diff --git a/distribution/src/bin/elasticsearch-env b/distribution/src/bin/elasticsearch-env index 9d58d88e7aaf1..cc16f710345e4 100644 --- a/distribution/src/bin/elasticsearch-env +++ b/distribution/src/bin/elasticsearch-env @@ -63,7 +63,7 @@ if [ ! -z "$JAVA_OPTS" ]; then fi # check the Java version -"$JAVA" -cp "$ES_CLASSPATH" org.elasticsearch.tools.launchers.JavaVersionChecker +"$JAVA" -cp "$ES_CLASSPATH" org.elasticsearch.tools.java_version_checker.JavaVersionChecker export HOSTNAME=$HOSTNAME diff --git a/distribution/src/bin/elasticsearch-env.bat b/distribution/src/bin/elasticsearch-env.bat index 8bd5f24864e44..b990767092092 100644 --- a/distribution/src/bin/elasticsearch-env.bat +++ b/distribution/src/bin/elasticsearch-env.bat @@ -42,7 +42,7 @@ if defined JAVA_OPTS ( ) rem check the Java version -%JAVA% -cp "%ES_CLASSPATH%" "org.elasticsearch.tools.launchers.JavaVersionChecker" || exit /b 1 +%JAVA% -cp "%ES_CLASSPATH%" "org.elasticsearch.tools.java_version_checker.JavaVersionChecker" || exit /b 1 set HOSTNAME=%COMPUTERNAME% diff --git a/distribution/tools/java-version-checker/build.gradle b/distribution/tools/java-version-checker/build.gradle new file mode 100644 index 0000000000000..ad9b56fec0502 --- /dev/null +++ b/distribution/tools/java-version-checker/build.gradle @@ -0,0 +1,14 @@ +import org.elasticsearch.gradle.precommit.PrecommitTasks + +apply plugin: 'elasticsearch.build' + +targetCompatibility = JavaVersion.VERSION_1_7 + +// java_version_checker do not depend on core so only JDK signatures should be checked +forbiddenApisMain.signaturesURLs = [PrecommitTasks.getResource('/forbidden/jdk-signatures.txt')] + +test.enabled = false +namingConventions.enabled = false +javadoc.enabled = false +loggerUsageCheck.enabled = false +jarHell.enabled = false diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JavaVersion.java b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersion.java similarity index 87% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JavaVersion.java rename to distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersion.java index 30ca7a4a2a7d0..a06579d84254a 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JavaVersion.java +++ b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersion.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.tools.java_version_checker; import java.util.ArrayList; import java.util.List; @@ -25,8 +25,8 @@ public class JavaVersion { - static final List CURRENT = parse(System.getProperty("java.specification.version")); - static final List JAVA_8 = parse("1.8"); + public static final List CURRENT = parse(System.getProperty("java.specification.version")); + public static final List JAVA_8 = parse("1.8"); static List parse(final String value) { if (!value.matches("^0*[0-9]+(\\.[0-9]+)*$")) { @@ -41,7 +41,7 @@ static List parse(final String value) { return version; } - static int majorVersion(final List javaVersion) { + public static int majorVersion(final List javaVersion) { Objects.requireNonNull(javaVersion); if (javaVersion.get(0) > 1) { return javaVersion.get(0); diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JavaVersionChecker.java b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java similarity index 74% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JavaVersionChecker.java rename to distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java index ed632d060a577..b61c5856084ff 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JavaVersionChecker.java +++ b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java @@ -17,7 +17,7 @@ * under the License. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.tools.java_version_checker; import java.util.Arrays; import java.util.Locale; @@ -45,10 +45,30 @@ public static void main(final String[] args) { Locale.ROOT, "the minimum required Java version is 8; your Java version from [%s] does not meet this requirement", System.getProperty("java.home")); - Launchers.errPrintln(message); - Launchers.exit(1); + errPrintln(message); + exit(1); } - Launchers.exit(0); + exit(0); + } + + /** + * Prints a string and terminates the line on standard error. + * + * @param message the message to print + */ + @SuppressForbidden(reason = "System#err") + static void errPrintln(final String message) { + System.err.println(message); + } + + /** + * Exit the VM with the specified status. + * + * @param status the status + */ + @SuppressForbidden(reason = "System#exit") + static void exit(final int status) { + System.exit(status); } } diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SuppressForbidden.java b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/SuppressForbidden.java similarity index 92% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SuppressForbidden.java rename to distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/SuppressForbidden.java index 9e44c1497b781..d1299cbff9511 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SuppressForbidden.java +++ b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/SuppressForbidden.java @@ -17,17 +17,18 @@ * under the License. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.tools.java_version_checker; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; + /** * Annotation to suppress forbidden-apis errors inside a whole class, a method, or a field. */ @Retention(RetentionPolicy.CLASS) @Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE }) -@interface SuppressForbidden { +public @interface SuppressForbidden { String reason(); } diff --git a/distribution/tools/launchers/build.gradle b/distribution/tools/launchers/build.gradle index ff0f4c473a49e..a774691b2eb17 100644 --- a/distribution/tools/launchers/build.gradle +++ b/distribution/tools/launchers/build.gradle @@ -22,10 +22,8 @@ import org.gradle.api.JavaVersion apply plugin: 'elasticsearch.build' -sourceCompatibility = JavaVersion.VERSION_1_7 -targetCompatibility = JavaVersion.VERSION_1_7 - dependencies { + compile parent.project('java-version-checker') testCompile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" testCompile "junit:junit:${versions.junit}" testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}" @@ -33,13 +31,10 @@ dependencies { archivesBaseName = 'elasticsearch-launchers' -// launchers do not depend on core so only JDK signatures should be checked -forbiddenApisMain { - signaturesURLs = [PrecommitTasks.getResource('/forbidden/jdk-signatures.txt')] -} -forbiddenApisTest { - signaturesURLs = [PrecommitTasks.getResource('/forbidden/jdk-signatures.txt')] -} +// java_version_checker do not depend on core so only JDK signatures should be checked +List jdkSignatures = [PrecommitTasks.getResource('/forbidden/jdk-signatures.txt')] +forbiddenApisMain.signaturesURLs = jdkSignatures +forbiddenApisTest.signaturesURLs = jdkSignatures namingConventions { testClass = 'org.elasticsearch.tools.launchers.LaunchersTestCase' @@ -48,4 +43,4 @@ namingConventions { javadoc.enabled = false loggerUsageCheck.enabled = false -jarHell.enabled=false +jarHell.enabled = false diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOptionsParser.java b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOptionsParser.java index 8cd401e53229d..7f612132d8c59 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOptionsParser.java +++ b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOptionsParser.java @@ -38,6 +38,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.elasticsearch.tools.java_version_checker.JavaVersion; + /** * Parses JVM options from a file and prints a single line with all JVM options to standard output. */ diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java index 349227c2b6d1b..6c9a1ef9473c3 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java +++ b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java @@ -19,6 +19,8 @@ package org.elasticsearch.tools.launchers; +import org.elasticsearch.tools.java_version_checker.SuppressForbidden; + /** * Utility methods for launchers. */ diff --git a/settings.gradle b/settings.gradle index ca20adcc56642..9ec0ccb9d7b3e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,6 +28,7 @@ List projects = [ 'distribution:bwc:staged-minor-snapshot', 'distribution:bwc:next-bugfix-snapshot', 'distribution:bwc:maintenance-bugfix-snapshot', + 'distribution:tools:java-version-checker', 'distribution:tools:launchers', 'distribution:tools:plugin-cli', 'server', From 50dbd7d2fb1dd2d4b0950c62b28a6d4db6d41e60 Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Fri, 8 Jun 2018 20:36:31 +0300 Subject: [PATCH 04/71] Compliant SAML Response destination check (#31175) Make SAML Response Destination check compliant Only validate the Destination element of an incoming SAML Response if Destination is present and the SAML Response is signed. The standard [1] - 3.5.5.2 and [2] - 3.2.2 does mention that the Destination element is optional and should only be verified when the SAML Response is signed. Some Identity Provider implementations are known to not set a Destination XML Attribute in their SAML responses when those are not signed, so this change also aims to enhance interoperability. [1] https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf [2] https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf --- .../authc/saml/SamlAuthenticator.java | 4 +- .../authc/saml/SamlAuthenticatorTests.java | 48 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java index f8826bebcac71..61e451150cd08 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java @@ -159,8 +159,10 @@ private String getSessionIndex(Assertion assertion) { private void checkResponseDestination(Response response) { final String asc = getSpConfiguration().getAscUrl(); if (asc.equals(response.getDestination()) == false) { - throw samlException("SAML response " + response.getID() + " is for destination " + response.getDestination() + if (response.isSigned() || Strings.hasText(response.getDestination())) { + throw samlException("SAML response " + response.getID() + " is for destination " + response.getDestination() + " but this realm uses " + asc); + } } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java index 8bb9890151ff0..913258cf45c5d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java @@ -523,13 +523,59 @@ public void testIncorrectDestinationIsRejected() throws Exception { "" + "" + ""; - SamlToken token = token(signDoc(xml)); + SamlToken token = randomBoolean() ? token(signDoc(xml)) : token(signAssertions(xml, idpSigningCertificatePair)); final ElasticsearchSecurityException exception = expectSamlException(() -> authenticator.authenticate(token)); assertThat(exception.getMessage(), containsString("destination")); assertThat(exception.getCause(), nullValue()); assertThat(SamlUtils.isSamlException(exception), is(true)); } + public void testMissingDestinationIsNotRejectedForNotSignedResponse() throws Exception { + Instant now = clock.instant(); + Instant validUntil = now.plusSeconds(30); + String sessionindex = randomId(); + final String xml = "\n" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "randomopaquestring" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + PASSWORD_AUTHN_CTX + "" + + "" + + "" + + "" + + "daredevil" + + "" + + "" + + ""; + SamlToken token = token(signAssertions(xml, idpSigningCertificatePair)); + final SamlAttributes attributes = authenticator.authenticate(token); + assertThat(attributes, notNullValue()); + assertThat(attributes.attributes(), iterableWithSize(1)); + final List uid = attributes.getAttributeValues("urn:oid:0.9.2342.19200300.100.1.1"); + assertThat(uid, contains("daredevil")); + assertThat(uid, iterableWithSize(1)); + assertThat(attributes.name(), notNullValue()); + assertThat(attributes.name().format, equalTo(TRANSIENT)); + } + public void testIncorrectRequestIdIsRejected() throws Exception { Instant now = clock.instant(); Instant validUntil = now.plusSeconds(30); From 9be3ebd030750b5dc05869564b814d49e740ceb6 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Mon, 11 Jun 2018 09:09:23 +0200 Subject: [PATCH 05/71] Don't swallow exceptions on replication (#31179) Swallowing these exceptions is dangerous as they can result in replicas going out-of-sync with the primary. Follow-up to #28571 --- .../action/bulk/TransportShardBulkAction.java | 44 ++++++++----------- .../TransportResyncReplicationAction.java | 17 +++---- .../replication/TransportWriteAction.java | 6 +-- 3 files changed, 24 insertions(+), 43 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java b/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java index 1d70fdf454faf..99d3f825247a9 100644 --- a/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java +++ b/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java @@ -487,32 +487,24 @@ public static Translog.Location performOnReplica(BulkShardRequest request, Index BulkItemRequest item = request.items()[i]; final Engine.Result operationResult; DocWriteRequest docWriteRequest = item.request(); - try { - switch (replicaItemExecutionMode(item, i)) { - case NORMAL: - final DocWriteResponse primaryResponse = item.getPrimaryResponse().getResponse(); - operationResult = performOpOnReplica(primaryResponse, docWriteRequest, replica); - assert operationResult != null : "operation result must never be null when primary response has no failure"; - location = syncOperationResultOrThrow(operationResult, location); - break; - case NOOP: - break; - case FAILURE: - final BulkItemResponse.Failure failure = item.getPrimaryResponse().getFailure(); - assert failure.getSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO : "seq no must be assigned"; - operationResult = replica.markSeqNoAsNoop(failure.getSeqNo(), failure.getMessage()); - assert operationResult != null : "operation result must never be null when primary response has no failure"; - location = syncOperationResultOrThrow(operationResult, location); - break; - default: - throw new IllegalStateException("illegal replica item execution mode for: " + docWriteRequest); - } - } catch (Exception e) { - // if its not an ignore replica failure, we need to make sure to bubble up the failure - // so we will fail the shard - if (!TransportActions.isShardNotAvailableException(e)) { - throw e; - } + switch (replicaItemExecutionMode(item, i)) { + case NORMAL: + final DocWriteResponse primaryResponse = item.getPrimaryResponse().getResponse(); + operationResult = performOpOnReplica(primaryResponse, docWriteRequest, replica); + assert operationResult != null : "operation result must never be null when primary response has no failure"; + location = syncOperationResultOrThrow(operationResult, location); + break; + case NOOP: + break; + case FAILURE: + final BulkItemResponse.Failure failure = item.getPrimaryResponse().getFailure(); + assert failure.getSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO : "seq no must be assigned"; + operationResult = replica.markSeqNoAsNoop(failure.getSeqNo(), failure.getMessage()); + assert operationResult != null : "operation result must never be null when primary response has no failure"; + location = syncOperationResultOrThrow(operationResult, location); + break; + default: + throw new IllegalStateException("illegal replica item execution mode for: " + docWriteRequest); } } return location; diff --git a/server/src/main/java/org/elasticsearch/action/resync/TransportResyncReplicationAction.java b/server/src/main/java/org/elasticsearch/action/resync/TransportResyncReplicationAction.java index 78c1e835d4087..f8ad58b9cac8c 100644 --- a/server/src/main/java/org/elasticsearch/action/resync/TransportResyncReplicationAction.java +++ b/server/src/main/java/org/elasticsearch/action/resync/TransportResyncReplicationAction.java @@ -121,19 +121,12 @@ protected WriteReplicaResult shardOperationOnReplica(ResyncReplicationRequest re public static Translog.Location performOnReplica(ResyncReplicationRequest request, IndexShard replica) throws Exception { Translog.Location location = null; for (Translog.Operation operation : request.getOperations()) { - try { - final Engine.Result operationResult = replica.applyTranslogOperation(operation, Engine.Operation.Origin.REPLICA); - if (operationResult.getResultType() == Engine.Result.Type.MAPPING_UPDATE_REQUIRED) { - throw new TransportReplicationAction.RetryOnReplicaException(replica.shardId(), - "Mappings are not available on the replica yet, triggered update: " + operationResult.getRequiredMappingUpdate()); - } - location = syncOperationResultOrThrow(operationResult, location); - } catch (Exception e) { - // if its not a failure to be ignored, let it bubble up - if (!TransportActions.isShardNotAvailableException(e)) { - throw e; - } + final Engine.Result operationResult = replica.applyTranslogOperation(operation, Engine.Operation.Origin.REPLICA); + if (operationResult.getResultType() == Engine.Result.Type.MAPPING_UPDATE_REQUIRED) { + throw new TransportReplicationAction.RetryOnReplicaException(replica.shardId(), + "Mappings are not available on the replica yet, triggered update: " + operationResult.getRequiredMappingUpdate()); } + location = syncOperationResultOrThrow(operationResult, location); } if (request.getTrimAboveSeqNo() != SequenceNumbers.UNASSIGNED_SEQ_NO) { replica.trimOperationOfPreviousPrimaryTerms(request.getTrimAboveSeqNo()); diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java b/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java index b14fd156b735d..ca91a32a17a3a 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/TransportWriteAction.java @@ -76,11 +76,7 @@ protected static Location syncOperationResultOrThrow(final Engine.Result operati // check if any transient write operation failures should be bubbled up Exception failure = operationResult.getFailure(); assert failure instanceof MapperParsingException : "expected mapper parsing failures. got " + failure; - if (!TransportActions.isShardNotAvailableException(failure)) { - throw failure; - } else { - location = currentLocation; - } + throw failure; } else { location = locationToSync(currentLocation, operationResult.getTranslogLocation()); } From 277143ee2aea54141298f1aff3374b3280151831 Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Mon, 11 Jun 2018 09:12:44 +0200 Subject: [PATCH 06/71] Use stronger write-once semantics for Azure repository (#30437) There's no need for an extra blobExists() call when writing a blob to the Azure service. Azure provides an option (with stronger consistency guarantees) on the upload method that guarantees that the blob that's uploaded does not already exist. This saves one network roundtrip. Relates to #19749 --- .../azure/AzureStorageTestServer.java | 11 ++++++++++- .../azure/blobstore/AzureBlobContainer.java | 4 +--- .../cloud/azure/blobstore/AzureBlobStore.java | 4 +++- .../azure/storage/AzureStorageService.java | 3 ++- .../azure/storage/AzureStorageServiceImpl.java | 17 +++++++++++++++-- .../azure/storage/AzureStorageServiceMock.java | 6 +++++- 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java b/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java index b1450b79fabe8..8183ee5043ec8 100644 --- a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java +++ b/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java @@ -164,7 +164,12 @@ private static PathTrie defaultHandlers(final String endpoint, f if (destContainer == null) { return newContainerNotFoundError(requestId); } - destContainer.objects.put(destBlobName, body); + + byte[] existingBytes = destContainer.objects.putIfAbsent(destBlobName, body); + if (existingBytes != null) { + return newBlobAlreadyExistsError(requestId); + } + return new Response(RestStatus.CREATED, emptyMap(), "text/plain", EMPTY_BYTE); }) ); @@ -363,6 +368,10 @@ private static Response newBlobNotFoundError(final long requestId) { return newError(requestId, RestStatus.NOT_FOUND, "BlobNotFound", "The specified blob does not exist"); } + private static Response newBlobAlreadyExistsError(final long requestId) { + return newError(requestId, RestStatus.CONFLICT, "BlobAlreadyExists", "The specified blob already exists"); + } + private static Response newInternalError(final long requestId) { return newError(requestId, RestStatus.INTERNAL_SERVER_ERROR, "InternalError", "The server encountered an internal error"); } diff --git a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java index 5bae0d4014951..8cb0c8c803df5 100644 --- a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java +++ b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java @@ -88,10 +88,8 @@ public InputStream readBlob(String blobName) throws IOException { @Override public void writeBlob(String blobName, InputStream inputStream, long blobSize) throws IOException { - if (blobExists(blobName)) { - throw new FileAlreadyExistsException("blob [" + blobName + "] already exists, cannot overwrite"); - } logger.trace("writeBlob({}, stream, {})", buildKey(blobName), blobSize); + try { blobStore.writeBlob(buildKey(blobName), inputStream, blobSize); } catch (URISyntaxException|StorageException e) { diff --git a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobStore.java b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobStore.java index b6e5af6af4ecd..12a5adecf06e9 100644 --- a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobStore.java +++ b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobStore.java @@ -34,6 +34,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; +import java.nio.file.FileAlreadyExistsException; import java.util.Locale; import java.util.Map; @@ -115,7 +116,8 @@ public Map listBlobsByPrefix(String keyPath, String prefix return this.client.listBlobsByPrefix(this.clientName, this.locMode, container, keyPath, prefix); } - public void writeBlob(String blobName, InputStream inputStream, long blobSize) throws URISyntaxException, StorageException { + public void writeBlob(String blobName, InputStream inputStream, long blobSize) throws URISyntaxException, StorageException, + FileAlreadyExistsException { this.client.writeBlob(this.clientName, this.locMode, container, blobName, inputStream, blobSize); } } diff --git a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageService.java b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageService.java index b1bfe11dded79..934533f81c99c 100644 --- a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageService.java +++ b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageService.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; +import java.nio.file.FileAlreadyExistsException; import java.util.Map; /** @@ -79,7 +80,7 @@ Map listBlobsByPrefix(String account, LocationMode mode, St throws URISyntaxException, StorageException; void writeBlob(String account, LocationMode mode, String container, String blobName, InputStream inputStream, long blobSize) throws - URISyntaxException, StorageException; + URISyntaxException, StorageException, FileAlreadyExistsException; static InputStream giveSocketPermissionsToStream(InputStream stream) { return new InputStream() { diff --git a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceImpl.java b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceImpl.java index 43ffb425e3b16..597c9813b6e92 100644 --- a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceImpl.java +++ b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceImpl.java @@ -19,11 +19,13 @@ package org.elasticsearch.cloud.azure.storage; +import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.CloudStorageAccount; import com.microsoft.azure.storage.LocationMode; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.RetryExponentialRetry; import com.microsoft.azure.storage.RetryPolicy; +import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.blob.BlobInputStream; import com.microsoft.azure.storage.blob.BlobListingDetails; @@ -46,8 +48,10 @@ import org.elasticsearch.repositories.RepositoryException; import java.io.InputStream; +import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.FileAlreadyExistsException; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; @@ -327,12 +331,21 @@ enumBlobListingDetails, null, generateOperationContext(account))) { @Override public void writeBlob(String account, LocationMode mode, String container, String blobName, InputStream inputStream, long blobSize) - throws URISyntaxException, StorageException { + throws URISyntaxException, StorageException, FileAlreadyExistsException { logger.trace("writeBlob({}, stream, {})", blobName, blobSize); CloudBlobClient client = this.getSelectedClient(account, mode); CloudBlobContainer blobContainer = client.getContainerReference(container); CloudBlockBlob blob = blobContainer.getBlockBlobReference(blobName); - SocketAccess.doPrivilegedVoidException(() -> blob.upload(inputStream, blobSize, null, null, generateOperationContext(account))); + try { + SocketAccess.doPrivilegedVoidException(() -> blob.upload(inputStream, blobSize, AccessCondition.generateIfNotExistsCondition(), + null, generateOperationContext(account))); + } catch (StorageException se) { + if (se.getHttpStatusCode() == HttpURLConnection.HTTP_CONFLICT && + StorageErrorCodeStrings.BLOB_ALREADY_EXISTS.equals(se.getErrorCode())) { + throw new FileAlreadyExistsException(blobName, null, se.getMessage()); + } + throw se; + } logger.trace("writeBlob({}, stream, {}) - done", blobName, blobSize); } } diff --git a/plugins/repository-azure/src/test/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceMock.java b/plugins/repository-azure/src/test/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceMock.java index 6b57ac59618f8..cdfb94b0aadbd 100644 --- a/plugins/repository-azure/src/test/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceMock.java +++ b/plugins/repository-azure/src/test/java/org/elasticsearch/cloud/azure/storage/AzureStorageServiceMock.java @@ -34,6 +34,7 @@ import java.io.InputStream; import java.net.SocketPermission; import java.net.URISyntaxException; +import java.nio.file.FileAlreadyExistsException; import java.nio.file.NoSuchFileException; import java.security.AccessController; import java.util.Locale; @@ -106,7 +107,10 @@ public Map listBlobsByPrefix(String account, LocationMode @Override public void writeBlob(String account, LocationMode mode, String container, String blobName, InputStream inputStream, long blobSize) - throws URISyntaxException, StorageException { + throws URISyntaxException, StorageException, FileAlreadyExistsException { + if (blobs.containsKey(blobName)) { + throw new FileAlreadyExistsException(blobName); + } try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { blobs.put(blobName, outputStream); Streams.copy(inputStream, outputStream); From 0e14141e7df8fc45cacf7d709d6d84c08ce07e17 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 11 Jun 2018 09:32:12 +0200 Subject: [PATCH 07/71] [Docs] Remove mention pattern files in Grok processor (#31170) Pattern files have been removed in 16fa3e546e172d3d194c2711654824d4851d69f8 --- docs/reference/ingest/ingest-node.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/ingest/ingest-node.asciidoc b/docs/reference/ingest/ingest-node.asciidoc index f606a4d013f44..25065ee2eb19c 100644 --- a/docs/reference/ingest/ingest-node.asciidoc +++ b/docs/reference/ingest/ingest-node.asciidoc @@ -1325,7 +1325,7 @@ This pipeline will insert these named captures as new fields within the document // NOTCONSOLE [[custom-patterns]] -==== Custom Patterns and Pattern Files +==== Custom Patterns The Grok processor comes pre-packaged with a base set of pattern. These patterns may not always have what you are looking for. Pattern have a very basic format. Each entry describes has a name and the pattern itself. From ac923c51c71af615a117b2116a48efe62b684aee Mon Sep 17 00:00:00 2001 From: Yannick Welsch Date: Mon, 11 Jun 2018 11:09:45 +0200 Subject: [PATCH 08/71] [TEST] Fix testRecoveryAfterPrimaryPromotion This test was failing from time to time due to a ConcurrentModificationException, which was triggered due to the primary-replica resync running concurrently with shards being removed. Closes #30767 --- .../index/replication/ESIndexLevelReplicationTestCase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/server/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index 84fff6f3b303f..e32cea1fb182e 100644 --- a/server/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/server/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -81,6 +81,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.atomic.AtomicInteger; @@ -147,7 +148,7 @@ protected class ReplicationGroup implements AutoCloseable, Iterable ReplicationGroup(final IndexMetaData indexMetaData) throws IOException { final ShardRouting primaryRouting = this.createShardRouting("s0", true); primary = newShard(primaryRouting, indexMetaData, null, getEngineFactory(primaryRouting), () -> {}); - replicas = new ArrayList<>(); + replicas = new CopyOnWriteArrayList<>(); this.indexMetaData = indexMetaData; updateAllocationIDsOnPrimary(); for (int i = 0; i < indexMetaData.getNumberOfReplicas(); i++) { From d728f16de9e6d7b00d7d106d40530b2bb7e3dc14 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Mon, 11 Jun 2018 15:12:12 +0200 Subject: [PATCH 09/71] Remove all unused imports and fix CRLF (#31207) The X-Pack opening and the recent other refactorings left a lot of unused imports in the codebase. This commit removes them all. --- .../client/benchmark/AbstractBenchmark.java | 2 -- .../client/RequestConvertersTests.java | 2 -- .../org/elasticsearch/client/SnapshotIT.java | 3 --- .../org/elasticsearch/client/RequestTests.java | 3 --- .../plugins/ListPluginsCommand.java | 4 ---- .../org/elasticsearch/plugins/PluginCli.java | 1 - .../elasticsearch/common/unit/TimeValue.java | 1 - .../common/xcontent/XContentParserTests.java | 1 - .../analysis/common/SnowballAnalyzer.java | 1 - .../StandardHtmlStripAnalyzerProvider.java | 2 -- .../common/StemmerTokenFilterFactory.java | 1 - .../common/ShingleTokenFilterTests.java | 17 ----------------- .../ingest/common/DateIndexNameProcessor.java | 1 - .../ingest/common/JsonProcessor.java | 7 ------- .../ingest/common/KeyValueProcessor.java | 1 - .../ingest/common/URLDecodeProcessor.java | 2 -- .../common/AppendProcessorFactoryTests.java | 2 -- .../ingest/common/AppendProcessorTests.java | 1 - .../ingest/common/IngestRestartIT.java | 2 -- .../common/JoinProcessorFactoryTests.java | 1 - .../ingest/common/SetProcessorTests.java | 1 - .../mustache/RestMultiSearchTemplateAction.java | 2 -- .../mustache/RestSearchTemplateAction.java | 7 ------- .../mustache/CustomMustacheFactoryTests.java | 1 - .../org/elasticsearch/painless/Definition.java | 1 - .../elasticsearch/painless/ScriptClassInfo.java | 2 -- .../painless/SimpleChecksAdapter.java | 1 - .../painless/antlr/EnhancedPainlessLexer.java | 3 --- .../painless/antlr/PainlessLexer.java | 3 --- .../painless/antlr/PainlessParser.java | 3 --- .../elasticsearch/painless/node/ECallLocal.java | 1 - .../elasticsearch/painless/node/ENewObj.java | 1 - .../elasticsearch/painless/node/EVariable.java | 1 - .../painless/node/PSubCallInvoke.java | 1 - .../painless/node/PSubShortcut.java | 1 - .../org/elasticsearch/painless/node/SCatch.java | 1 - .../painless/node/SDeclaration.java | 1 - .../org/elasticsearch/painless/node/SEach.java | 1 - .../elasticsearch/painless/node/SReturn.java | 1 - .../ChildrenAggregationBuilder.java | 1 - .../reindex/AbstractBaseReindexRestHandler.java | 1 - .../documentation/ReindexDocumentationIT.java | 1 - .../ReindexFromRemoteBuildRestClientTests.java | 2 -- .../index/reindex/SimpleExecutableScript.java | 2 -- .../transport/netty4/ESLoggingHandler.java | 2 -- .../netty4/ByteBufBytesReferenceTests.java | 2 -- .../analysis/PhoneticTokenFilterFactory.java | 1 - .../pl/PolishStemTokenFilterFactory.java | 5 ----- .../discovery/ec2/AwsEc2Service.java | 1 - .../discovery/ec2/AwsEc2ServiceImpl.java | 1 - .../discovery/ec2/AwsEc2ServiceImplTests.java | 4 ---- .../cloud/gce/GceInstancesService.java | 1 - .../cloud/gce/GceMetadataService.java | 4 ---- .../cloud/gce/network/GceNameResolver.java | 4 ---- .../gce/RetryHttpInitializerWrapper.java | 3 --- .../repositories/s3/S3BlobContainer.java | 1 - .../repositories/s3/S3Repository.java | 1 - .../repositories/s3/AwsS3ServiceImplTests.java | 2 -- .../repositories/s3/TestAwsS3Service.java | 1 - .../elasticsearch/RestDieWithDignityAction.java | 3 --- .../bootstrap/EvilElasticsearchCliTests.java | 4 ---- .../threadpool/EvilThreadPoolTests.java | 1 - .../transport/TransportClientProducer.java | 3 --- .../vectorhighlight/CustomFieldQuery.java | 1 - .../action/TransportActionNodeProxy.java | 1 - .../node/tasks/cancel/CancelTasksResponse.java | 2 -- .../node/tasks/list/ListTasksResponse.java | 1 - .../get/GetRepositoriesResponse.java | 2 -- .../storedscripts/PutStoredScriptRequest.java | 1 - .../delete/TransportDeleteIndexAction.java | 1 - .../admin/indices/flush/FlushRequest.java | 1 - .../admin/indices/flush/SyncedFlushRequest.java | 1 - .../flush/TransportShardFlushAction.java | 1 - .../mapping/get/GetMappingsResponse.java | 4 ---- .../refresh/TransportShardRefreshAction.java | 1 - .../settings/get/GetSettingsResponse.java | 4 ---- .../get/TransportGetSettingsAction.java | 1 - .../indices/shrink/TransportResizeAction.java | 1 - .../action/get/TransportMultiGetAction.java | 1 - .../action/index/IndexRequest.java | 1 - .../action/ingest/DeletePipelineRequest.java | 1 - .../action/search/SearchPhaseController.java | 2 -- .../action/search/SearchTransportService.java | 1 - .../support/master/AcknowledgedRequest.java | 1 - .../support/master/MasterNodeRequest.java | 1 - .../replication/ReplicationOperation.java | 2 -- .../replication/TransportReplicationAction.java | 1 - .../TransportInstanceSingleOperationAction.java | 1 - .../org/elasticsearch/bootstrap/ESPolicy.java | 1 - .../client/ClusterAdminClient.java | 1 - .../client/transport/TransportClient.java | 1 - .../cluster/ClusterStateTaskExecutor.java | 1 - .../org/elasticsearch/cluster/Diffable.java | 2 -- .../cluster/InternalClusterInfoService.java | 3 --- .../action/index/NodeMappingRefreshAction.java | 2 -- .../cluster/metadata/AliasMetaData.java | 1 - .../cluster/metadata/IndexMetaData.java | 1 - .../cluster/routing/IndexShardRoutingTable.java | 1 - .../decider/ResizeAllocationDecider.java | 4 ---- .../common/RandomBasedUUIDGenerator.java | 1 - .../common/compress/Compressor.java | 1 - .../common/document/DocumentField.java | 2 -- .../common/geo/builders/MultiPointBuilder.java | 2 -- .../common/geo/builders/PointBuilder.java | 1 - .../common/geo/builders/PolygonBuilder.java | 1 - .../common/geo/parsers/ShapeParser.java | 1 - .../common/inject/AbstractModule.java | 1 - .../elasticsearch/common/inject/Injector.java | 1 - .../common/inject/InjectorBuilder.java | 1 - .../common/inject/InjectorImpl.java | 2 -- .../common/inject/PrivateModule.java | 5 ----- .../inject/assistedinject/FactoryProvider.java | 8 -------- .../common/inject/assistedinject/Parameter.java | 1 - .../org/elasticsearch/common/io/Channels.java | 1 - .../common/logging/DeprecationLogger.java | 1 - .../common/lucene/search/Queries.java | 1 - .../org/elasticsearch/common/path/PathTrie.java | 2 -- .../common/settings/AddFileKeyStoreCommand.java | 6 ------ .../common/settings/IndexScopedSettings.java | 1 - .../common/settings/KeyStoreCli.java | 1 - .../org/elasticsearch/common/text/Text.java | 1 - .../common/transport/TransportAddress.java | 1 - .../common/unit/ByteSizeValue.java | 1 - .../elasticsearch/common/util/BigArrays.java | 1 - .../QueueResizingEsThreadPoolExecutor.java | 1 - .../common/util/concurrent/ThreadContext.java | 2 -- .../elasticsearch/discovery/DiscoveryStats.java | 1 - .../java/org/elasticsearch/env/ESFileStore.java | 2 -- .../java/org/elasticsearch/env/ShardLock.java | 1 - .../elasticsearch/gateway/GatewayModule.java | 1 - .../gateway/LocalAllocateDangledIndices.java | 1 - .../gateway/PrimaryShardAllocator.java | 1 - .../analysis/StandardAnalyzerProvider.java | 2 -- .../index/engine/EngineSearcher.java | 2 -- .../index/fielddata/IndexFieldData.java | 1 - .../plain/LatLonPointDVAtomicFieldData.java | 2 -- .../plain/PagedBytesIndexFieldData.java | 1 - .../index/mapper/DocumentMapper.java | 1 - .../index/query/BoolQueryBuilder.java | 1 - .../index/query/MatchPhraseQueryBuilder.java | 1 - .../index/query/MultiMatchQueryBuilder.java | 1 - .../index/query/SpanMultiTermQueryBuilder.java | 2 -- .../index/query/WildcardQueryBuilder.java | 1 - .../ScriptScoreFunctionBuilder.java | 1 - .../reindex/LeaderBulkByScrollTaskState.java | 1 - .../org/elasticsearch/index/shard/ShardId.java | 1 - .../index/shard/ShardSplittingQuery.java | 1 - .../index/store/StoreFileMetaData.java | 1 - .../translog/BufferedChecksumStreamInput.java | 1 - .../index/translog/TranslogToolCli.java | 1 - .../index/translog/TruncateTranslogCommand.java | 2 -- .../indices/IndexCreationException.java | 1 - .../elasticsearch/indices/IndicesService.java | 1 - .../ingest/PipelineConfiguration.java | 2 -- .../node/AdaptiveSelectionStats.java | 1 - .../node/NodeValidationException.java | 1 - .../node/ResponseCollectorService.java | 3 --- .../org/elasticsearch/plugins/PluginInfo.java | 1 - .../elasticsearch/plugins/PluginsService.java | 2 -- .../org/elasticsearch/plugins/SearchPlugin.java | 2 -- .../blobstore/ChecksumBlobStoreFormat.java | 1 - .../cluster/RestGetRepositoriesAction.java | 1 - .../admin/indices/RestGetAllAliasesAction.java | 4 ---- .../admin/indices/RestUpdateSettingsAction.java | 3 --- .../action/admin/indices/RestUpgradeAction.java | 1 - .../action/search/RestMultiSearchAction.java | 6 ------ .../org/elasticsearch/script/FilterScript.java | 1 - .../org/elasticsearch/search/SearchHit.java | 1 - .../org/elasticsearch/search/SearchModule.java | 1 - .../search/aggregations/AggregationBuilder.java | 1 - .../search/aggregations/Aggregator.java | 1 - .../aggregations/InternalAggregation.java | 1 - .../bucket/composite/CompositeAggregator.java | 1 - .../filter/FiltersAggregationBuilder.java | 1 - .../geogrid/GeoGridAggregationBuilder.java | 2 -- .../bucket/global/GlobalAggregationBuilder.java | 1 - .../bucket/histogram/DateHistogramInterval.java | 1 - .../histogram/HistogramAggregationBuilder.java | 1 - .../missing/MissingAggregationBuilder.java | 1 - .../bucket/nested/NestedAggregationBuilder.java | 1 - .../nested/ReverseNestedAggregationBuilder.java | 1 - .../bucket/range/AbstractRangeBuilder.java | 1 - .../range/DateRangeAggregationBuilder.java | 1 - .../range/GeoDistanceAggregationBuilder.java | 2 -- .../bucket/range/IpRangeAggregationBuilder.java | 1 - .../bucket/range/RangeAggregationBuilder.java | 1 - .../sampler/DiversifiedAggregationBuilder.java | 1 - .../sampler/SamplerAggregationBuilder.java | 1 - .../SignificantTermsAggregationBuilder.java | 2 -- .../SignificantTextAggregationBuilder.java | 2 -- .../significant/SignificantTextAggregator.java | 4 ---- .../bucket/terms/InternalMappedTerms.java | 1 - .../bucket/terms/TermsAggregationBuilder.java | 1 - .../metrics/avg/AvgAggregationBuilder.java | 1 - .../CardinalityAggregationBuilder.java | 1 - .../geobounds/GeoBoundsAggregationBuilder.java | 1 - .../GeoCentroidAggregationBuilder.java | 1 - .../metrics/max/MaxAggregationBuilder.java | 1 - .../metrics/min/MinAggregationBuilder.java | 1 - .../PercentileRanksAggregationBuilder.java | 3 --- .../ScriptedMetricAggregationBuilder.java | 1 - .../metrics/stats/StatsAggregationBuilder.java | 1 - .../ExtendedStatsAggregationBuilder.java | 1 - .../metrics/sum/SumAggregationBuilder.java | 1 - .../tophits/TopHitsAggregationBuilder.java | 1 - .../MovAvgPipelineAggregationBuilder.java | 1 - .../movavg/models/HoltWintersModel.java | 2 -- .../pipeline/movfn/MovingFunctionScript.java | 1 - .../pipeline/movfn/MovingFunctions.java | 1 - .../support/ValuesSourceAggregationBuilder.java | 1 - .../support/ValuesSourceConfig.java | 2 -- .../support/ValuesSourceParserHelper.java | 1 - .../search/collapse/CollapseContext.java | 1 - .../elasticsearch/search/fetch/FetchPhase.java | 1 - .../highlight/SimpleFragmentsBuilder.java | 1 - .../search/internal/ShardSearchRequest.java | 1 - .../search/query/TopDocsCollectorContext.java | 1 - .../search/rescore/RescorePhase.java | 2 -- .../context/CategoryContextMapping.java | 1 - .../phrase/MultiCandidateGeneratorWrapper.java | 1 - .../suggest/phrase/PhraseSuggestionContext.java | 2 -- .../CompressibleBytesOutputStream.java | 1 - .../transport/TcpTransportChannel.java | 1 - .../java/org/elasticsearch/VersionTests.java | 2 -- .../cluster/node/tasks/TaskManagerTestCase.java | 1 - .../admin/cluster/node/tasks/TasksIT.java | 1 - .../put/PutRepositoryResponseTests.java | 3 --- .../cluster/settings/SettingsUpdaterTests.java | 2 -- .../admin/indices/alias/AliasActionsTests.java | 1 - .../admin/indices/flush/FlushBlocksIT.java | 1 - .../settings/get/GetSettingsResponseTests.java | 1 - .../shards/IndicesShardStoreRequestIT.java | 1 - .../FieldCapabilitiesResponseTests.java | 6 ------ .../fieldcaps/FieldCapabilitiesTests.java | 1 - .../action/get/MultiGetResponseTests.java | 1 - .../action/search/MultiSearchRequestTests.java | 3 --- .../TransportReplicationActionTests.java | 1 - .../replication/TransportWriteActionTests.java | 1 - .../bootstrap/ElasticsearchCliTests.java | 2 -- ...hardFailedClusterStateTaskExecutorTests.java | 1 - .../cluster/metadata/IndexMetaDataTests.java | 1 - .../metadata/TemplateUpgradeServiceTests.java | 2 -- .../allocation/AllocationCommandsTests.java | 1 - .../allocation/DiskThresholdMonitorTests.java | 1 - .../service/ClusterApplierServiceTests.java | 1 - .../cluster/settings/SettingsFilteringIT.java | 1 - .../org/elasticsearch/common/BooleansTests.java | 1 - .../common/bytes/PagedBytesReferenceTests.java | 9 --------- .../elasticsearch/common/geo/GeoHashTests.java | 1 - .../common/unit/DistanceUnitTests.java | 1 - .../common/unit/FuzzinessTests.java | 1 - .../common/util/CollectionUtilsTests.java | 1 - .../common/xcontent/BaseXContentTestCase.java | 1 - .../discovery/zen/ZenDiscoveryUnitTests.java | 1 - .../org/elasticsearch/env/ESFileStoreTests.java | 3 --- .../gateway/GatewayIndexStateIT.java | 1 - .../gateway/MetaDataStateFormatTests.java | 1 - .../gateway/MetaStateServiceTests.java | 1 - .../elasticsearch/index/SearchSlowLogTests.java | 3 --- .../index/analysis/CustomNormalizerTests.java | 1 - .../fielddata/ordinals/MultiOrdinalsTests.java | 1 - .../fielddata/ordinals/SingleOrdinalsTests.java | 1 - .../index/mapper/ExternalMapperPlugin.java | 1 - .../index/mapper/ExternalMetadataMapper.java | 1 - .../query/GeoPolygonQueryBuilderTests.java | 2 -- .../query/SimpleQueryStringBuilderTests.java | 1 - .../index/query/SpanGapQueryBuilderTests.java | 2 -- .../reindex/BulkByScrollTaskStatusTests.java | 1 - .../index/reindex/BulkByScrollTaskTests.java | 2 -- .../index/search/MultiMatchQueryTests.java | 2 -- .../index/search/nested/NestedSortingTests.java | 1 - .../index/shard/StoreRecoveryTests.java | 2 -- .../index/store/CorruptedFileIT.java | 1 - .../elasticsearch/indexing/IndexActionIT.java | 1 - .../indexlifecycle/IndexLifecycleActionIT.java | 1 - .../indices/analyze/AnalyzeActionIT.java | 3 --- .../indices/settings/GetSettingsBlocksIT.java | 1 - .../indices/state/RareClusterStateIT.java | 9 --------- .../store/IndicesStoreIntegrationIT.java | 1 - .../indices/store/IndicesStoreTests.java | 1 - .../ingest/IngestDocumentTests.java | 1 - .../ingest/IngestServiceTests.java | 1 - .../PersistentTasksExecutorResponseTests.java | 1 - .../PersistentTasksNodeServiceTests.java | 1 - .../recovery/TruncatedRecoveryIT.java | 4 ---- .../repositories/RepositoryDataTests.java | 3 --- .../admin/indices/RestResizeHandlerTests.java | 2 -- .../script/ScriptMetaDataTests.java | 2 -- .../script/ScriptServiceTests.java | 1 - .../elasticsearch/search/SearchModuleTests.java | 1 - .../aggregations/AggregationsIntegrationIT.java | 1 - .../bucket/GlobalAggregatorTests.java | 1 - .../search/aggregations/bucket/SamplerIT.java | 1 - .../bucket/TermsDocCountErrorIT.java | 1 - .../histogram/InternalHistogramTests.java | 1 - .../bucket/sampler/SamplerAggregatorTests.java | 1 - .../significant/SignificanceHeuristicTests.java | 1 - .../SignificantTextAggregatorTests.java | 9 --------- .../terms/TermsAggregatorFactoryTests.java | 1 - .../aggregations/metrics/PercentilesTests.java | 1 - .../aggregations/metrics/ScriptedMetricIT.java | 1 - .../hdr/HDRPercentileRanksAggregatorTests.java | 1 - .../TDigestPercentileRanksAggregatorTests.java | 1 - .../pipeline/movfn/MovFnUnitTests.java | 1 - .../search/query/SimpleQueryStringIT.java | 12 ------------ .../sort/GeoDistanceSortBuilderTests.java | 1 - .../search/suggest/SuggestBuilderTests.java | 1 - .../completion/CategoryContextMappingTests.java | 1 - .../completion/CompletionSuggestionTests.java | 1 - .../search/suggest/term/SortByTests.java | 1 - .../transport/ConnectionProfileTests.java | 1 - .../src/main/java/hdfs/MiniHDFS.java | 1 - .../org/elasticsearch/cli/MockTerminal.java | 2 -- .../search/aggregations/AggregatorTestCase.java | 1 - .../aggregations/BaseAggregationTestCase.java | 15 --------------- .../test/InternalAggregationTestCase.java | 1 - .../org/elasticsearch/test/MockLogAppender.java | 1 - .../elasticsearch/test/TestCustomMetaData.java | 4 ---- .../test/engine/MockInternalEngine.java | 1 - .../test/hamcrest/ElasticsearchAssertions.java | 4 ---- .../elasticsearch/test/rest/ESRestTestCase.java | 1 - .../rest/yaml/section/ClientYamlTestSuite.java | 1 - .../transport/MockTcpTransportPlugin.java | 2 -- .../org/elasticsearch/node/MockNodeTests.java | 1 - .../elasticsearch/test/VersionUtilsTests.java | 1 - .../licensor/tools/KeyPairGeneratorTool.java | 1 - .../license/ExpirationCallback.java | 2 +- .../elasticsearch/license/LicensingClient.java | 1 - .../license/OperationModeFileWatcher.java | 2 -- .../license/StartTrialClusterTask.java | 3 --- .../xpack/core/ml/action/OpenJobAction.java | 1 - .../autodetect/state/ModelSizeStats.java | 1 - .../xpack/core/rollup/action/RollupJobCaps.java | 6 ------ .../rollup/action/StartRollupJobAction.java | 1 - .../core/rollup/action/StopRollupJobAction.java | 1 - .../xpack/core/rollup/job/MetricConfig.java | 1 - .../xpack/core/scheduler/SchedulerEngine.java | 1 - .../action/saml/SamlAuthenticateResponse.java | 1 - .../expressiondsl/RoleMapperExpression.java | 2 +- .../core/security/authz/RoleDescriptor.java | 1 - .../xpack/core/ssl/PEMKeyConfig.java | 3 --- .../support/xcontent/XContentSource.java | 1 - .../ml/integration/MlRestTestStateCleaner.java | 1 - .../core/ml/job/config/AnalysisLimitsTests.java | 1 - .../ml/job/config/DataDescriptionTests.java | 1 - .../xpack/core/ml/job/config/MlFilterTests.java | 1 - .../autodetect/state/ModelSnapshotTests.java | 5 ----- .../core/ml/job/results/AnomalyRecordTests.java | 1 - .../core/rollup/RollupRestTestStateCleaner.java | 1 - .../job/TermsGroupConfigSerializingTests.java | 2 -- .../xpack/core/ssl/CertParsingUtilsTests.java | 2 -- .../xpack/core/ssl/SSLServiceTests.java | 6 ------ .../xpack/logstash/LogstashFeatureSet.java | 4 ---- .../ml/action/TransportUpdateJobAction.java | 1 - .../xpack/ml/job/persistence/JobProvider.java | 1 - .../elasticsearch/xpack/ml/MlMetadataTests.java | 1 - .../xpack/ml/support/BaseMlIntegTestCase.java | 2 -- .../exporter/http/HttpHostBuilder.java | 2 +- .../xpack/monitoring/MultiNodesStatsTests.java | 1 - .../cleaner/AbstractIndicesCleanerTestCase.java | 1 - .../indices/IndexStatsCollectorTests.java | 1 - .../monitoring/exporter/ExportersTests.java | 3 --- ...AbstractPublishableHttpResourceTestCase.java | 1 - .../xpack/rollup/RollupResponseTranslator.java | 4 ---- .../action/TransportStopRollupAction.java | 3 --- .../xpack/rollup/job/IndexerUtils.java | 2 -- .../xpack/rollup/action/SearchActionTests.java | 1 - .../xpack/rollup/job/IndexerUtilsTests.java | 1 - .../xpack/rollup/job/RollupJobTaskTests.java | 3 --- .../xpack/security/PkiRealmBootstrapCheck.java | 1 - .../security/authc/saml/SamlRequestHandler.java | 1 - .../security/crypto/tool/SystemKeyTool.java | 1 - .../action/saml/RestSamlAuthenticateAction.java | 1 - .../saml/RestSamlInvalidateSessionAction.java | 1 - .../RestSamlPrepareAuthenticationAction.java | 1 - .../transport/filter/SecurityIpFilterRule.java | 3 --- .../test/SecuritySettingsSource.java | 1 - .../xpack/security/TemplateUpgraderTests.java | 1 - .../TransportGetRoleMappingsActionTests.java | 1 - .../audit/index/IndexAuditTrailTests.java | 3 --- .../authc/esnative/NativeRealmTests.java | 2 -- .../authc/file/FileUserRolesStoreTests.java | 1 - .../authc/ldap/ActiveDirectoryRealmTests.java | 1 - .../security/authc/ldap/LdapRealmTests.java | 1 - .../authc/ldap/LdapSessionFactoryTests.java | 1 - .../authc/ldap/support/LdapTestCase.java | 1 - .../accesscontrol/IndicesPermissionTests.java | 1 - .../authz/store/CompositeRolesStoreTests.java | 2 -- .../authz/store/FileRolesStoreTests.java | 1 - .../ServerTransportFilterIntegrationTests.java | 1 - .../transport/ssl/EllipticCurveSSLTests.java | 2 -- .../transport/ssl/SslIntegrationTests.java | 1 - .../sql/jdbc/jdbc/JdbcPreparedStatement.java | 1 - .../xpack/sql/jdbc/jdbcx/JdbcDataSource.java | 2 -- .../xpack/sql/cli/CliSessionTests.java | 1 - .../xpack/sql/cli/JLineTerminalTests.java | 2 -- .../cli/command/ServerInfoCliCommandTests.java | 1 - .../sql/plugin/AbstractSqlQueryRequest.java | 2 -- .../xpack/sql/analysis/analyzer/Analyzer.java | 1 - .../xpack/sql/expression/Attribute.java | 1 - .../xpack/sql/expression/Exists.java | 1 - .../xpack/sql/expression/Foldables.java | 1 - .../expression/function/FunctionRegistry.java | 1 - .../xpack/sql/expression/function/Score.java | 1 - .../sql/expression/function/ScoreAttribute.java | 1 - .../expression/function/aggregate/Count.java | 1 - .../function/aggregate/NumericAggregate.java | 1 - .../scalar/ScalarFunctionAttribute.java | 1 - .../scalar/datetime/DateTimeFunction.java | 1 - .../function/scalar/datetime/HourOfDay.java | 1 - .../function/scalar/script/ScriptTemplate.java | 1 - .../xpack/sql/expression/predicate/Equals.java | 1 - .../xpack/sql/expression/predicate/In.java | 1 - .../predicate/fulltext/FullTextPredicate.java | 1 - .../xpack/sql/expression/regex/Like.java | 1 - .../xpack/sql/expression/regex/LikePattern.java | 1 - .../xpack/sql/expression/regex/RLike.java | 1 - .../xpack/sql/parser/ParsingException.java | 2 -- .../xpack/sql/parser/SqlBaseLexer.java | 3 --- .../xpack/sql/parser/SqlBaseParser.java | 3 --- .../xpack/sql/plan/logical/Join.java | 1 - .../xpack/sql/plan/physical/OrderExec.java | 1 - .../xpack/sql/plan/physical/Unexecutable.java | 1 - .../xpack/sql/planner/PlanningException.java | 1 - .../xpack/sql/planner/QueryFolder.java | 1 - .../xpack/sql/querydsl/agg/AndAggFilter.java | 1 - .../xpack/sql/querydsl/agg/OrAggFilter.java | 1 - .../xpack/sql/session/Configuration.java | 1 - .../org/elasticsearch/xpack/sql/type/Types.java | 1 - .../sql/querydsl/query/BoolQueryTests.java | 5 ----- .../xpack/sql/tree/LocationTests.java | 1 - .../upgrade/IndexUpgradeIntegTestCase.java | 2 -- .../xpack/upgrade/InternalIndexReindexerIT.java | 1 - .../EncryptSensitiveDataBootstrapCheck.java | 1 - .../xpack/watcher/WatcherFeatureSet.java | 1 - .../xpack/watcher/common/http/HttpProxy.java | 7 ------- .../xpack/watcher/notification/email/Email.java | 1 - .../rest/action/RestExecuteWatchAction.java | 2 -- .../watcher/rest/action/RestGetWatchAction.java | 1 - .../stats/TransportWatcherStatsAction.java | 1 - .../hipchat/HipChatActionFactoryTests.java | 1 - .../pagerduty/PagerDutyActionFactoryTests.java | 1 - .../actions/slack/SlackActionFactoryTests.java | 1 - .../watcher/common/http/HttpRequestTests.java | 1 - .../notification/email/AccountTests.java | 1 - .../notification/email/AccountsTests.java | 1 - .../email/EmailSecretsIntegrationTests.java | 2 -- .../notification/email/EmailServiceTests.java | 3 --- .../notification/email/HtmlSanitizerTests.java | 2 -- .../notification/email/ProfileTests.java | 1 - .../ReportingAttachmentParserTests.java | 1 - .../hipchat/HipChatAccountsTests.java | 1 - .../hipchat/HipChatServiceTests.java | 2 -- .../slack/message/SlackMessageTests.java | 2 -- .../trigger/ScheduleTriggerEngineMock.java | 1 - .../ml/integration/DeleteExpiredDataIT.java | 1 - .../xpack/ml/integration/ModelPlotsIT.java | 1 - .../xpack/ml/integration/OverallBucketsIT.java | 1 - .../xpack/security/ReindexWithSecurityIT.java | 5 ----- .../core/ssl/CertificateGenerateToolTests.java | 2 -- .../xpack/core/ssl/CertificateToolTests.java | 2 -- .../authc/file/tool/UsersToolTests.java | 1 - .../crypto/tool/SystemKeyToolTests.java | 1 - .../qa/sql/multinode/RestSqlMultinodeIT.java | 5 ----- .../qa/sql/security/SqlSecurityTestCase.java | 2 -- .../qa/sql/cli/CliIntegrationTestCase.java | 2 -- .../xpack/qa/sql/cli/ErrorsTestCase.java | 2 -- .../xpack/qa/sql/cli/FetchSizeTestCase.java | 2 -- .../xpack/qa/sql/jdbc/DataLoader.java | 4 ---- .../xpack/qa/sql/jdbc/ErrorsTestCase.java | 2 -- .../xpack/qa/sql/jdbc/FetchSizeTestCase.java | 2 -- .../qa/sql/jdbc/JdbcIntegrationTestCase.java | 4 ---- .../ActiveDirectorySessionFactoryTests.java | 1 - .../test/feature_aware/FeatureAwareCheck.java | 1 - 474 files changed, 3 insertions(+), 800 deletions(-) diff --git a/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/AbstractBenchmark.java b/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/AbstractBenchmark.java index 23cb29563b5e3..fe38c180ce541 100644 --- a/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/AbstractBenchmark.java +++ b/client/benchmark/src/main/java/org/elasticsearch/client/benchmark/AbstractBenchmark.java @@ -28,8 +28,6 @@ import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index c09da06995599..91532b7f2b7e1 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -30,7 +30,6 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.DocWriteRequest; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; -import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; @@ -99,7 +98,6 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.RandomCreateIndexGenerator; import org.elasticsearch.index.VersionType; import org.elasticsearch.index.query.TermQueryBuilder; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java index aaba5da820613..3b27c2631f4d3 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SnapshotIT.java @@ -19,8 +19,6 @@ package org.elasticsearch.client; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryResponse; @@ -35,7 +33,6 @@ import org.elasticsearch.rest.RestStatus; import java.io.IOException; -import java.util.Collections; import static org.hamcrest.Matchers.equalTo; diff --git a/client/rest/src/test/java/org/elasticsearch/client/RequestTests.java b/client/rest/src/test/java/org/elasticsearch/client/RequestTests.java index a7cf625b61d68..2353c9230ebf6 100644 --- a/client/rest/src/test/java/org/elasticsearch/client/RequestTests.java +++ b/client/rest/src/test/java/org/elasticsearch/client/RequestTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.client; -import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; @@ -29,9 +28,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java index 6015d9da14307..ab16ce7524e01 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/ListPluginsCommand.java @@ -23,7 +23,6 @@ import org.elasticsearch.Version; import org.elasticsearch.cli.EnvironmentAwareCommand; import org.elasticsearch.cli.Terminal; -import org.elasticsearch.common.Nullable; import org.elasticsearch.env.Environment; import java.io.IOException; @@ -31,11 +30,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; /** * A command for the plugin cli to list plugins installed in elasticsearch. diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/PluginCli.java index 9d8ccd4438d8a..687dad482645a 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/PluginCli.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/PluginCli.java @@ -22,7 +22,6 @@ import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.LoggingAwareMultiCommand; -import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; import java.io.IOException; diff --git a/libs/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java b/libs/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java index 56cdc09b34e8c..f40fbbe73a9dc 100644 --- a/libs/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java +++ b/libs/core/src/main/java/org/elasticsearch/common/unit/TimeValue.java @@ -19,7 +19,6 @@ package org.elasticsearch.common.unit; -import java.io.IOException; import java.util.Locale; import java.util.Objects; import java.util.concurrent.TimeUnit; 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 b6164c2696735..6fb4f207de0e9 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,7 +20,6 @@ package org.elasticsearch.common.xcontent; import com.fasterxml.jackson.core.JsonParseException; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.json.JsonXContent; diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/SnowballAnalyzer.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/SnowballAnalyzer.java index 5dbe902fe1500..bc4b9a763fd68 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/SnowballAnalyzer.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/SnowballAnalyzer.java @@ -30,7 +30,6 @@ import org.apache.lucene.analysis.standard.StandardFilter; import org.apache.lucene.analysis.standard.StandardTokenizer; import org.apache.lucene.analysis.tr.TurkishLowerCaseFilter; -import org.apache.lucene.util.Version; /** Filters {@link StandardTokenizer} with {@link StandardFilter}, {@link * LowerCaseFilter}, {@link StopFilter} and {@link SnowballFilter}. diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StandardHtmlStripAnalyzerProvider.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StandardHtmlStripAnalyzerProvider.java index 2cc2a29ad6a07..24b0c4e714ff6 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StandardHtmlStripAnalyzerProvider.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StandardHtmlStripAnalyzerProvider.java @@ -20,8 +20,6 @@ package org.elasticsearch.analysis.common; import org.apache.lucene.analysis.CharArraySet; -import org.apache.lucene.analysis.core.StopAnalyzer; -import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StemmerTokenFilterFactory.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StemmerTokenFilterFactory.java index 630a6a6ebeca4..67895e82e61c6 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StemmerTokenFilterFactory.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/StemmerTokenFilterFactory.java @@ -76,7 +76,6 @@ import org.tartarus.snowball.ext.LithuanianStemmer; import org.tartarus.snowball.ext.LovinsStemmer; import org.tartarus.snowball.ext.NorwegianStemmer; -import org.tartarus.snowball.ext.PorterStemmer; import org.tartarus.snowball.ext.PortugueseStemmer; import org.tartarus.snowball.ext.RomanianStemmer; import org.tartarus.snowball.ext.RussianStemmer; diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/ShingleTokenFilterTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/ShingleTokenFilterTests.java index 44f61c5fc101f..02aeb7e41bccd 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/ShingleTokenFilterTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/ShingleTokenFilterTests.java @@ -23,31 +23,14 @@ import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.core.WhitespaceTokenizer; import org.apache.lucene.analysis.miscellaneous.DisableGraphAttribute; -import org.elasticsearch.Version; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; -import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.AnalysisTestsHelper; -import org.elasticsearch.index.analysis.IndexAnalyzers; -import org.elasticsearch.index.analysis.NamedAnalyzer; import org.elasticsearch.index.analysis.TokenFilterFactory; -import org.elasticsearch.index.query.Operator; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTokenStreamTestCase; -import org.elasticsearch.test.IndexSettingsModule; import java.io.StringReader; -import java.util.Arrays; -import java.util.Collection; - -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; -import static org.elasticsearch.test.ESTestCase.createTestAnalysis; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; public class ShingleTokenFilterTests extends ESTokenStreamTestCase { public void testPreConfiguredShingleFilterDisableGraphAttribute() throws Exception { diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateIndexNameProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateIndexNameProcessor.java index 311f30513c119..b44eaa3bfa3ca 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateIndexNameProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/DateIndexNameProcessor.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.function.Function; import org.elasticsearch.ExceptionsHelper; diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java index edcc4bf3e0cff..2f217735df2ff 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/JsonProcessor.java @@ -19,19 +19,12 @@ package org.elasticsearch.ingest.common; -import com.fasterxml.jackson.core.JsonParseException; -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentParserUtils; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; -import org.elasticsearch.common.xcontent.json.JsonXContentParser; import org.elasticsearch.ingest.AbstractProcessor; import org.elasticsearch.ingest.ConfigurationUtils; import org.elasticsearch.ingest.IngestDocument; diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/KeyValueProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/KeyValueProcessor.java index fb856e28c31a0..6ed065926d60f 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/KeyValueProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/KeyValueProcessor.java @@ -27,7 +27,6 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; 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 549c0ebcb9bbb..945419499ad43 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 @@ -21,8 +21,6 @@ import java.io.UnsupportedEncodingException; import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; -import java.util.Locale; import java.util.Map; /** diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorFactoryTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorFactoryTests.java index 0d600283b1dc5..39a7bfd9a20b2 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorFactoryTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorFactoryTests.java @@ -22,7 +22,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ingest.TestTemplateService; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.test.ESTestCase; import org.junit.Before; @@ -32,7 +31,6 @@ import java.util.Map; import static org.hamcrest.CoreMatchers.equalTo; -import static org.mockito.Mockito.mock; public class AppendProcessorFactoryTests extends ESTestCase { diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorTests.java index 66cddd43e6583..aefb5d5273113 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/AppendProcessorTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.ingest.common; -import org.elasticsearch.index.VersionType; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.IngestDocument.MetaData; import org.elasticsearch.ingest.Processor; diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/IngestRestartIT.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/IngestRestartIT.java index a8ca20485c451..9658637f16444 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/IngestRestartIT.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/IngestRestartIT.java @@ -19,9 +19,7 @@ package org.elasticsearch.ingest.common; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.support.WriteRequest; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorFactoryTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorFactoryTests.java index 6506b5f450a5b..9078dc484a90f 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorFactoryTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/JoinProcessorFactoryTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.ingest.common; import org.elasticsearch.ElasticsearchParseException; -import org.elasticsearch.ingest.RandomDocumentPicks; import org.elasticsearch.test.ESTestCase; import java.util.HashMap; diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SetProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SetProcessorTests.java index 6fec977e6c268..9d0a520c5c4f9 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SetProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/SetProcessorTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.ingest.common; -import org.elasticsearch.index.VersionType; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.IngestDocument.MetaData; import org.elasticsearch.ingest.Processor; diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java index 9969e6b38e54a..3aef9c32dc3d7 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestMultiSearchTemplateAction.java @@ -19,8 +19,6 @@ package org.elasticsearch.script.mustache; -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.rest.BaseRestHandler; diff --git a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestSearchTemplateAction.java b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestSearchTemplateAction.java index f42afcc19b80f..e9bbc7aeeecf2 100644 --- a/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestSearchTemplateAction.java +++ b/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/RestSearchTemplateAction.java @@ -21,20 +21,13 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.client.node.NodeClient; -import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.ParsingException; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.ObjectParser; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.action.RestStatusToXContentListener; import org.elasticsearch.rest.action.search.RestSearchAction; -import org.elasticsearch.script.ScriptType; import java.io.IOException; import java.util.Collections; diff --git a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java index 287265fef7654..28c536d788e6a 100644 --- a/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java +++ b/modules/lang-mustache/src/test/java/org/elasticsearch/script/mustache/CustomMustacheFactoryTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.script.mustache; -import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptEngine; import org.elasticsearch.script.TemplateScript; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java index 95032acabef9b..f97df128f15e5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Definition.java @@ -19,7 +19,6 @@ package org.elasticsearch.painless; -import org.apache.lucene.util.SetOnce; import org.elasticsearch.painless.spi.Whitelist; import java.lang.invoke.MethodHandle; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java index df83471c37125..60ce1d033532a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/ScriptClassInfo.java @@ -23,9 +23,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; import java.util.function.Function; import static java.util.Collections.unmodifiableList; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/SimpleChecksAdapter.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/SimpleChecksAdapter.java index aa6d121945b88..6f3b4d296ede5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/SimpleChecksAdapter.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/SimpleChecksAdapter.java @@ -22,7 +22,6 @@ import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; import org.objectweb.asm.util.CheckClassAdapter; import org.objectweb.asm.util.CheckMethodAdapter; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java index adef4d3642571..cf24a47386603 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/EnhancedPainlessLexer.java @@ -20,12 +20,9 @@ package org.elasticsearch.painless.antlr; import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.LexerNoViableAltException; import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenSource; import org.antlr.v4.runtime.misc.Interval; -import org.antlr.v4.runtime.misc.Pair; import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Location; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java index 734089c384a5f..dd62701b86e4d 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessLexer.java @@ -2,12 +2,9 @@ package org.elasticsearch.painless.antlr; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) abstract class PainlessLexer extends Lexer { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java index bba53d650ad32..9cd3334aa51da 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/antlr/PainlessParser.java @@ -3,11 +3,8 @@ import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.misc.*; import org.antlr.v4.runtime.tree.*; import java.util.List; -import java.util.Iterator; -import java.util.ArrayList; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) class PainlessParser extends Parser { diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java index e7b6b8ca7eaec..4c977fa66e89a 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ECallLocal.java @@ -19,7 +19,6 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.MethodKey; import org.elasticsearch.painless.Globals; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java index feaf7474874a5..2a96d68bcb417 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/ENewObj.java @@ -22,7 +22,6 @@ import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Definition.Struct; -import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EVariable.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EVariable.java index 32e8fb3aa7f58..3dd3e73ac79d4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EVariable.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/EVariable.java @@ -19,7 +19,6 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java index 32d2bdbff557e..e13fe0d85c143 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubCallInvoke.java @@ -19,7 +19,6 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java index 695e999edba6f..ff88f0018556c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/PSubShortcut.java @@ -19,7 +19,6 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Definition.Method; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java index 9f5f012c6d98f..535ad5235b07c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SCatch.java @@ -20,7 +20,6 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java index b6b97e8d1e97b..f00db583ceae4 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SDeclaration.java @@ -20,7 +20,6 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Locals.Variable; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java index ade4ddef97d0d..04de0c0696e96 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SEach.java @@ -20,7 +20,6 @@ package org.elasticsearch.painless.node; import org.elasticsearch.painless.Definition; -import org.elasticsearch.painless.Definition.Type; import org.elasticsearch.painless.Definition.def; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java index 5e7b30cacb966..8f9e7771ea8ae 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/SReturn.java @@ -19,7 +19,6 @@ package org.elasticsearch.painless.node; -import org.elasticsearch.painless.Definition; import org.elasticsearch.painless.Globals; import org.elasticsearch.painless.Locals; import org.elasticsearch.painless.Location; diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java index a14a7a35c85ec..5b67a31f18862 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/aggregations/ChildrenAggregationBuilder.java @@ -32,7 +32,6 @@ import org.elasticsearch.join.mapper.ParentIdFieldMapper; import org.elasticsearch.join.mapper.ParentJoinFieldMapper; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.FieldContext; diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java index 4ea2592801db8..de0c39b8f65ba 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractBaseReindexRestHandler.java @@ -29,7 +29,6 @@ import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.tasks.LoggingTaskListener; import org.elasticsearch.tasks.Task; diff --git a/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java b/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java index 1f99f062d25af..5707bede69e40 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java +++ b/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java @@ -28,7 +28,6 @@ import org.elasticsearch.index.reindex.BulkByScrollTask; import org.elasticsearch.index.reindex.DeleteByQueryAction; import org.elasticsearch.index.reindex.ReindexAction; -import org.elasticsearch.index.reindex.ReindexRequest; import org.elasticsearch.index.reindex.ReindexRequestBuilder; import org.elasticsearch.index.reindex.RethrottleAction; import org.elasticsearch.index.reindex.UpdateByQueryAction; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java index c5957ef8be5a7..14e3142d226c9 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java @@ -22,10 +22,8 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilderTestCase; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java index 7437a5512dfd0..be661282df765 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/SimpleExecutableScript.java @@ -21,11 +21,9 @@ import org.elasticsearch.script.ExecutableScript; -import java.util.HashMap; import java.util.Map; import java.util.function.Consumer; -import static org.elasticsearch.test.ESTestCase.randomBoolean; public class SimpleExecutableScript implements ExecutableScript { private final Consumer> script; diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/ESLoggingHandler.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/ESLoggingHandler.java index 5ee79834740d7..5c275f63be885 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/ESLoggingHandler.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/ESLoggingHandler.java @@ -23,7 +23,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; -import io.netty.util.internal.StringUtil; import org.elasticsearch.Version; import org.elasticsearch.common.compress.Compressor; import org.elasticsearch.common.compress.CompressorFactory; @@ -35,7 +34,6 @@ import org.elasticsearch.transport.TransportStatus; import java.io.IOException; -import java.io.UncheckedIOException; final class ESLoggingHandler extends LoggingHandler { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java index 7a6768010eb16..4a41aaec952a0 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java @@ -25,9 +25,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.ReleasableBytesStreamOutput; -import javax.net.ssl.SSLEngine; import java.io.IOException; -import java.nio.ByteBuffer; public class ByteBufBytesReferenceTests extends AbstractBytesReferenceTestCase { diff --git a/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/PhoneticTokenFilterFactory.java b/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/PhoneticTokenFilterFactory.java index 36b4ab9ca58f4..8caf4ce411898 100644 --- a/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/PhoneticTokenFilterFactory.java +++ b/plugins/analysis-phonetic/src/main/java/org/elasticsearch/index/analysis/PhoneticTokenFilterFactory.java @@ -23,7 +23,6 @@ import org.apache.commons.codec.language.Caverphone1; import org.apache.commons.codec.language.Caverphone2; import org.apache.commons.codec.language.ColognePhonetic; -import org.apache.commons.codec.language.DaitchMokotoffSoundex; import org.apache.commons.codec.language.Metaphone; import org.apache.commons.codec.language.RefinedSoundex; import org.apache.commons.codec.language.Soundex; diff --git a/plugins/analysis-stempel/src/main/java/org/elasticsearch/index/analysis/pl/PolishStemTokenFilterFactory.java b/plugins/analysis-stempel/src/main/java/org/elasticsearch/index/analysis/pl/PolishStemTokenFilterFactory.java index aa3194c5831b7..e66ee3a7ba9fb 100644 --- a/plugins/analysis-stempel/src/main/java/org/elasticsearch/index/analysis/pl/PolishStemTokenFilterFactory.java +++ b/plugins/analysis-stempel/src/main/java/org/elasticsearch/index/analysis/pl/PolishStemTokenFilterFactory.java @@ -23,16 +23,11 @@ import org.apache.lucene.analysis.pl.PolishAnalyzer; import org.apache.lucene.analysis.stempel.StempelFilter; import org.apache.lucene.analysis.stempel.StempelStemmer; -import org.egothor.stemmer.Trie; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.AbstractTokenFilterFactory; -import java.io.IOException; - - - public class PolishStemTokenFilterFactory extends AbstractTokenFilterFactory { public PolishStemTokenFilterFactory(IndexSettings indexSettings, Environment environment, String name, Settings settings) { diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Service.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Service.java index 880be6c037323..db3164fe9007a 100644 --- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Service.java +++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2Service.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.settings.SecureString; 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 java.util.ArrayList; diff --git a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImpl.java b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImpl.java index 3b5b955260e6d..b53dc7a876301 100644 --- a/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImpl.java +++ b/plugins/discovery-ec2/src/main/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImpl.java @@ -36,7 +36,6 @@ import com.amazonaws.services.ec2.AmazonEC2Client; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.Randomness; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; diff --git a/plugins/discovery-ec2/src/test/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImplTests.java b/plugins/discovery-ec2/src/test/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImplTests.java index 06693bbff11ac..f157cd6d44b17 100644 --- a/plugins/discovery-ec2/src/test/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImplTests.java +++ b/plugins/discovery-ec2/src/test/java/org/elasticsearch/discovery/ec2/AwsEc2ServiceImplTests.java @@ -25,13 +25,9 @@ import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import org.elasticsearch.common.settings.MockSecureSettings; -import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.discovery.ec2.AwsEc2Service; -import org.elasticsearch.discovery.ec2.AwsEc2ServiceImpl; import org.elasticsearch.test.ESTestCase; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesService.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesService.java index 7e8fb7cf396d0..d05f15344bc83 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesService.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceInstancesService.java @@ -20,7 +20,6 @@ package org.elasticsearch.cloud.gce; import com.google.api.services.compute.model.Instance; -import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.unit.TimeValue; diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java index f17126328dba8..c736862d426de 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/GceMetadataService.java @@ -22,10 +22,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; -import java.security.AccessController; import java.security.GeneralSecurityException; -import java.security.PrivilegedAction; -import java.security.PrivilegedExceptionAction; import java.util.function.Function; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; @@ -33,7 +30,6 @@ import com.google.api.client.http.HttpHeaders; import com.google.api.client.http.HttpResponse; import com.google.api.client.http.HttpTransport; -import org.elasticsearch.SpecialPermission; import org.elasticsearch.cloud.gce.util.Access; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.settings.Setting; diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/network/GceNameResolver.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/network/GceNameResolver.java index 10d3fd0e7723c..46c4ac7bac547 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/network/GceNameResolver.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/cloud/gce/network/GceNameResolver.java @@ -28,10 +28,6 @@ import java.io.IOException; import java.net.InetAddress; -import java.net.URISyntaxException; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; /** *

Resolves certain GCE related 'meta' hostnames into an actual hostname diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/discovery/gce/RetryHttpInitializerWrapper.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/discovery/gce/RetryHttpInitializerWrapper.java index 68cceab76d339..dc7f8bf8596af 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/discovery/gce/RetryHttpInitializerWrapper.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/discovery/gce/RetryHttpInitializerWrapper.java @@ -30,14 +30,11 @@ import com.google.api.client.util.ExponentialBackOff; import com.google.api.client.util.Sleeper; import org.apache.logging.log4j.Logger; -import org.elasticsearch.SpecialPermission; import org.elasticsearch.cloud.gce.util.Access; import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.unit.TimeValue; import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Objects; public class RetryHttpInitializerWrapper implements HttpRequestInitializer { diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java index 173609b7cfe9c..222802ae30437 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3BlobContainer.java @@ -46,7 +46,6 @@ import java.io.IOException; import java.io.InputStream; -import java.nio.file.FileAlreadyExistsException; import java.nio.file.NoSuchFileException; import java.security.AccessController; import java.security.PrivilegedAction; diff --git a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java index cb4f977bae77b..c185027d67f26 100644 --- a/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java +++ b/plugins/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Repository.java @@ -35,7 +35,6 @@ import org.elasticsearch.repositories.RepositoryException; import org.elasticsearch.repositories.blobstore.BlobStoreRepository; -import java.io.IOException; /** * Shared file system implementation of the BlobStoreRepository diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java index 18c701f5fc1a6..353de31fa1873 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/AwsS3ServiceImplTests.java @@ -24,8 +24,6 @@ import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import org.elasticsearch.common.settings.MockSecureSettings; -import org.elasticsearch.common.settings.SecureSetting; -import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; diff --git a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/TestAwsS3Service.java b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/TestAwsS3Service.java index 522ca06614c00..c5012d9c68bc7 100644 --- a/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/TestAwsS3Service.java +++ b/plugins/repository-s3/src/test/java/org/elasticsearch/repositories/s3/TestAwsS3Service.java @@ -23,7 +23,6 @@ import com.amazonaws.services.s3.AmazonS3; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.cluster.metadata.RepositoryMetaData; import org.elasticsearch.common.settings.Settings; public class TestAwsS3Service extends InternalAwsS3Service { diff --git a/qa/die-with-dignity/src/main/java/org/elasticsearch/RestDieWithDignityAction.java b/qa/die-with-dignity/src/main/java/org/elasticsearch/RestDieWithDignityAction.java index 6aa56aa30be97..a3876599b7e30 100644 --- a/qa/die-with-dignity/src/main/java/org/elasticsearch/RestDieWithDignityAction.java +++ b/qa/die-with-dignity/src/main/java/org/elasticsearch/RestDieWithDignityAction.java @@ -21,12 +21,9 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.http.HttpStats; import org.elasticsearch.rest.BaseRestHandler; -import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.RestStatus; import java.io.IOException; diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilElasticsearchCliTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilElasticsearchCliTests.java index 2f74a5180e195..04b866c870c02 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilElasticsearchCliTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilElasticsearchCliTests.java @@ -19,15 +19,11 @@ package org.elasticsearch.bootstrap; -import java.util.Map; - import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.settings.Settings; import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.Matchers.hasEntry; -import static org.hamcrest.Matchers.hasKey; public class EvilElasticsearchCliTests extends ESElasticsearchCliTestCase { diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/threadpool/EvilThreadPoolTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/threadpool/EvilThreadPoolTests.java index c7848267ff17f..da43927d1dfee 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/threadpool/EvilThreadPoolTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/threadpool/EvilThreadPoolTests.java @@ -27,7 +27,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import java.util.function.Supplier; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasToString; diff --git a/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java index f46359db8f0f0..7c234bce6cdb7 100644 --- a/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java +++ b/qa/wildfly/src/main/java/org/elasticsearch/wildfly/transport/TransportClientProducer.java @@ -20,7 +20,6 @@ package org.elasticsearch.wildfly.transport; import org.elasticsearch.client.transport.TransportClient; -import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.settings.Settings; @@ -29,8 +28,6 @@ import javax.enterprise.inject.Produces; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; diff --git a/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java b/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java index 1ace6fc34bab1..6b670953ecbf0 100644 --- a/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java +++ b/server/src/main/java/org/apache/lucene/search/vectorhighlight/CustomFieldQuery.java @@ -30,7 +30,6 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.SynonymQuery; import org.apache.lucene.search.TermQuery; -import org.apache.lucene.search.join.ToParentBlockJoinQuery; import org.apache.lucene.search.spans.SpanTermQuery; import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery; diff --git a/server/src/main/java/org/elasticsearch/action/TransportActionNodeProxy.java b/server/src/main/java/org/elasticsearch/action/TransportActionNodeProxy.java index 7d57e5bd60a1c..2e7cbec93d9ae 100644 --- a/server/src/main/java/org/elasticsearch/action/TransportActionNodeProxy.java +++ b/server/src/main/java/org/elasticsearch/action/TransportActionNodeProxy.java @@ -21,7 +21,6 @@ import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.component.AbstractComponent; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportService; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java index fbc81d2995511..cfab4f596cda3 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/cancel/CancelTasksResponse.java @@ -22,7 +22,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; -import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -32,7 +31,6 @@ import java.io.IOException; import java.util.List; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; /** * Returns the list of tasks that were cancelled diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java index cb1fcb0b091ee..0ab1391faa211 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/node/tasks/list/ListTasksResponse.java @@ -44,7 +44,6 @@ import java.util.Map; import java.util.stream.Collectors; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; /** diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java index c1d1420eef8c8..24228aa565871 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/repositories/get/GetRepositoriesResponse.java @@ -19,7 +19,6 @@ package org.elasticsearch.action.admin.cluster.repositories.get; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.cluster.metadata.RepositoriesMetaData; import org.elasticsearch.cluster.metadata.RepositoryMetaData; @@ -31,7 +30,6 @@ import java.io.IOException; import java.util.Collections; -import java.util.Iterator; import java.util.List; import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java index 50af29bbdc369..6f702cbbe7c0a 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/storedscripts/PutStoredScriptRequest.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.script.StoredScriptSource; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java index a2e102e0689c5..e51046f85be90 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/delete/TransportDeleteIndexAction.java @@ -27,7 +27,6 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ack.ClusterStateUpdateResponse; import org.elasticsearch.cluster.block.ClusterBlockException; -import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.metadata.MetaDataDeleteIndexService; import org.elasticsearch.cluster.service.ClusterService; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/FlushRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/FlushRequest.java index 284b98b328055..8c278d5616774 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/FlushRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/FlushRequest.java @@ -19,7 +19,6 @@ package org.elasticsearch.action.admin.indices.flush; -import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.support.broadcast.BroadcastRequest; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/SyncedFlushRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/SyncedFlushRequest.java index 2a14d66a765fe..2862ae10e0502 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/SyncedFlushRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/SyncedFlushRequest.java @@ -19,7 +19,6 @@ package org.elasticsearch.action.admin.indices.flush; -import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.support.broadcast.BroadcastRequest; import java.util.Arrays; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportShardFlushAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportShardFlushAction.java index dd39f6c8ca33c..c0dc528588fc6 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportShardFlushAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/flush/TransportShardFlushAction.java @@ -23,7 +23,6 @@ import org.elasticsearch.action.support.replication.ReplicationResponse; import org.elasticsearch.action.support.replication.TransportReplicationAction; import org.elasticsearch.cluster.action.shard.ShardStateAction; -import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java index d21261abad89e..2bf52151d4b14 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java @@ -20,7 +20,6 @@ package org.elasticsearch.action.admin.indices.mapping.get; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionResponse; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.ParseField; @@ -29,11 +28,8 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ObjectParser; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; -import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java index 19cc1b134d7fb..0b5975cf025af 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java @@ -24,7 +24,6 @@ import org.elasticsearch.action.support.replication.ReplicationResponse; import org.elasticsearch.action.support.replication.TransportReplicationAction; import org.elasticsearch.cluster.action.shard.ShardStateAction; -import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponse.java index 6d410a7eed39d..d06eeabfaf3d9 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponse.java @@ -27,7 +27,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -37,10 +36,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.CharBuffer; -import java.util.Arrays; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.Objects; diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java index bbee9e7b1130e..027c14cda8ea7 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/settings/get/TransportGetSettingsAction.java @@ -39,7 +39,6 @@ import org.elasticsearch.transport.TransportService; import org.elasticsearch.common.settings.IndexScopedSettings; -import java.util.Arrays; public class TransportGetSettingsAction extends TransportMasterNodeReadAction { diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java index 3ccba85502569..f23dafdb5e873 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/shrink/TransportResizeAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.action.admin.indices.shrink; import org.apache.lucene.index.IndexWriter; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.create.CreateIndexClusterStateUpdateRequest; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportMultiGetAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportMultiGetAction.java index bea65283cc034..7b38f6c1f54f4 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportMultiGetAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportMultiGetAction.java @@ -29,7 +29,6 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.AtomicArray; -import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; diff --git a/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java b/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java index 55b0a0511c2e7..632592897b959 100644 --- a/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java +++ b/server/src/main/java/org/elasticsearch/action/index/IndexRequest.java @@ -38,7 +38,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.uid.Versions; import org.elasticsearch.common.unit.ByteSizeValue; -import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; diff --git a/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineRequest.java b/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineRequest.java index 6e5b9d80c6724..65afad730a0ba 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineRequest.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/DeletePipelineRequest.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.util.Objects; -import static org.elasticsearch.action.ValidateActions.addValidationError; public class DeletePipelineRequest extends AcknowledgedRequest { 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 ba546660f9395..fb450b2ce8359 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchPhaseController.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchPhaseController.java @@ -35,8 +35,6 @@ import org.elasticsearch.common.collect.HppcMaps; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; diff --git a/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java b/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java index 51e3148d41adc..696b1e9a9aa8c 100644 --- a/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java +++ b/server/src/main/java/org/elasticsearch/action/search/SearchTransportService.java @@ -60,7 +60,6 @@ import java.io.IOException; import java.io.UncheckedIOException; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.function.BiFunction; diff --git a/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java b/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java index 54ab886ef4b1d..9828dade31a94 100644 --- a/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java +++ b/server/src/main/java/org/elasticsearch/action/support/master/AcknowledgedRequest.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.unit.TimeValue; import java.io.IOException; -import java.util.Objects; import static org.elasticsearch.common.unit.TimeValue.timeValueSeconds; diff --git a/server/src/main/java/org/elasticsearch/action/support/master/MasterNodeRequest.java b/server/src/main/java/org/elasticsearch/action/support/master/MasterNodeRequest.java index 9a4a21e208164..644994c9e2878 100644 --- a/server/src/main/java/org/elasticsearch/action/support/master/MasterNodeRequest.java +++ b/server/src/main/java/org/elasticsearch/action/support/master/MasterNodeRequest.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.unit.TimeValue; import java.io.IOException; -import java.util.Objects; /** * A based request for master based operation. diff --git a/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java b/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java index 340496ca35363..78bf9f2fc722e 100644 --- a/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java +++ b/server/src/main/java/org/elasticsearch/action/support/replication/ReplicationOperation.java @@ -31,7 +31,6 @@ import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.index.shard.ReplicationGroup; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; @@ -41,7 +40,6 @@ import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; 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 8d6bf9780f7a2..e06c771481f42 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 @@ -58,7 +58,6 @@ import org.elasticsearch.index.seqno.SequenceNumbers; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShardClosedException; -import org.elasticsearch.index.shard.IndexShardState; import org.elasticsearch.index.shard.ReplicationGroup; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardNotFoundException; diff --git a/server/src/main/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationAction.java b/server/src/main/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationAction.java index 421e2458b0db7..b75828327035b 100644 --- a/server/src/main/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationAction.java +++ b/server/src/main/java/org/elasticsearch/action/support/single/instance/TransportInstanceSingleOperationAction.java @@ -30,7 +30,6 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.service.ClusterService; diff --git a/server/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java b/server/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java index 95de5ccca59ba..0785e4d491f76 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/ESPolicy.java @@ -33,7 +33,6 @@ import java.security.ProtectionDomain; import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.function.Predicate; /** custom policy for union of static and dynamic permissions */ diff --git a/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java b/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java index 468863266fcf3..5b21036b8cd4f 100644 --- a/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java +++ b/server/src/main/java/org/elasticsearch/client/ClusterAdminClient.java @@ -113,7 +113,6 @@ import org.elasticsearch.action.ingest.SimulatePipelineRequestBuilder; import org.elasticsearch.action.ingest.SimulatePipelineResponse; import org.elasticsearch.action.ingest.WritePipelineResponse; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.tasks.TaskId; diff --git a/server/src/main/java/org/elasticsearch/client/transport/TransportClient.java b/server/src/main/java/org/elasticsearch/client/transport/TransportClient.java index 1c810a3e040a8..50dad09dfa7b2 100644 --- a/server/src/main/java/org/elasticsearch/client/transport/TransportClient.java +++ b/server/src/main/java/org/elasticsearch/client/transport/TransportClient.java @@ -19,7 +19,6 @@ package org.elasticsearch.client.transport; -import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.action.Action; import org.elasticsearch.action.ActionListener; diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java b/server/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java index e30f02ad4060d..024389dd22c7f 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterStateTaskExecutor.java @@ -23,7 +23,6 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import java.util.stream.Stream; public interface ClusterStateTaskExecutor { /** diff --git a/server/src/main/java/org/elasticsearch/cluster/Diffable.java b/server/src/main/java/org/elasticsearch/cluster/Diffable.java index 57d5ea9ed1f75..e9a658cbe54f9 100644 --- a/server/src/main/java/org/elasticsearch/cluster/Diffable.java +++ b/server/src/main/java/org/elasticsearch/cluster/Diffable.java @@ -19,10 +19,8 @@ package org.elasticsearch.cluster; -import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.Writeable; -import java.io.IOException; /** * Cluster state part, changes in which can be serialized diff --git a/server/src/main/java/org/elasticsearch/cluster/InternalClusterInfoService.java b/server/src/main/java/org/elasticsearch/cluster/InternalClusterInfoService.java index ae16dbe88a621..dbfc4b3445e07 100644 --- a/server/src/main/java/org/elasticsearch/cluster/InternalClusterInfoService.java +++ b/server/src/main/java/org/elasticsearch/cluster/InternalClusterInfoService.java @@ -30,8 +30,6 @@ import org.elasticsearch.action.admin.indices.stats.ShardStats; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.cluster.block.ClusterBlockException; -import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings; @@ -49,7 +47,6 @@ import org.elasticsearch.transport.ReceiveTimeoutTransportException; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; diff --git a/server/src/main/java/org/elasticsearch/cluster/action/index/NodeMappingRefreshAction.java b/server/src/main/java/org/elasticsearch/cluster/action/index/NodeMappingRefreshAction.java index 3fc4f4361540c..fc7a4206486a3 100644 --- a/server/src/main/java/org/elasticsearch/cluster/action/index/NodeMappingRefreshAction.java +++ b/server/src/main/java/org/elasticsearch/cluster/action/index/NodeMappingRefreshAction.java @@ -21,11 +21,9 @@ import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.support.IndicesOptions; -import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaDataMappingService; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java b/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java index b73a9d6032801..92a3db82d6cc7 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java @@ -35,7 +35,6 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; import java.util.Collections; diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java index 4ce6141929425..ed5ab46069df0 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetaData.java @@ -50,7 +50,6 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.gateway.MetaDataStateFormat; import org.elasticsearch.index.Index; import org.elasticsearch.index.mapper.MapperService; diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/IndexShardRoutingTable.java b/server/src/main/java/org/elasticsearch/cluster/routing/IndexShardRoutingTable.java index 3b3de800194e5..87410bd0ffaab 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/IndexShardRoutingTable.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/IndexShardRoutingTable.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; diff --git a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/ResizeAllocationDecider.java b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/ResizeAllocationDecider.java index a0ebf7ddba923..8babcd5484f6c 100644 --- a/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/ResizeAllocationDecider.java +++ b/server/src/main/java/org/elasticsearch/cluster/routing/allocation/decider/ResizeAllocationDecider.java @@ -19,7 +19,6 @@ package org.elasticsearch.cluster.routing.allocation.decider; -import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.shrink.ResizeAction; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.RecoverySource; @@ -27,13 +26,10 @@ import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.UnassignedInfo; import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; -import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.Index; -import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.shard.ShardId; -import java.util.Set; /** * An allocation decider that ensures we allocate the shards of a target index for resize operations next to the source primaries diff --git a/server/src/main/java/org/elasticsearch/common/RandomBasedUUIDGenerator.java b/server/src/main/java/org/elasticsearch/common/RandomBasedUUIDGenerator.java index 9f5e5f34a1b37..59e5960b99d09 100644 --- a/server/src/main/java/org/elasticsearch/common/RandomBasedUUIDGenerator.java +++ b/server/src/main/java/org/elasticsearch/common/RandomBasedUUIDGenerator.java @@ -20,7 +20,6 @@ package org.elasticsearch.common; -import java.io.IOException; import java.util.Base64; import java.util.Random; diff --git a/server/src/main/java/org/elasticsearch/common/compress/Compressor.java b/server/src/main/java/org/elasticsearch/common/compress/Compressor.java index b39e7f6e142f4..0723147109f85 100644 --- a/server/src/main/java/org/elasticsearch/common/compress/Compressor.java +++ b/server/src/main/java/org/elasticsearch/common/compress/Compressor.java @@ -20,7 +20,6 @@ package org.elasticsearch.common.compress; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.ReleasableBytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; diff --git a/server/src/main/java/org/elasticsearch/common/document/DocumentField.java b/server/src/main/java/org/elasticsearch/common/document/DocumentField.java index f7747c9da254d..969c3347d67ae 100644 --- a/server/src/main/java/org/elasticsearch/common/document/DocumentField.java +++ b/server/src/main/java/org/elasticsearch/common/document/DocumentField.java @@ -19,8 +19,6 @@ package org.elasticsearch.common.document; -import org.apache.lucene.util.BytesRef; -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.Streamable; diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java index be356d4ac2f11..c85f6bd439673 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java @@ -27,12 +27,10 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.xcontent.XContentBuilder; import org.locationtech.spatial4j.shape.Point; -import org.locationtech.spatial4j.shape.Shape; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Objects; public class MultiPointBuilder extends ShapeBuilder, MultiPointBuilder> { diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java index fbf6a3681a4dd..ef08236d12e74 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java @@ -28,7 +28,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; -import java.util.ArrayList; public class PointBuilder extends ShapeBuilder { public static final GeoShapeType TYPE = GeoShapeType.POINT; diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java index 3b98f5b98e439..dbcb46d9936f3 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java @@ -33,7 +33,6 @@ import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.common.xcontent.XContentBuilder; import org.locationtech.spatial4j.exception.InvalidShapeException; -import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.jts.JtsGeometry; import java.io.IOException; diff --git a/server/src/main/java/org/elasticsearch/common/geo/parsers/ShapeParser.java b/server/src/main/java/org/elasticsearch/common/geo/parsers/ShapeParser.java index e7ec489191762..79582c3365bdb 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/parsers/ShapeParser.java +++ b/server/src/main/java/org/elasticsearch/common/geo/parsers/ShapeParser.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.mapper.GeoPointFieldMapper; import org.elasticsearch.index.mapper.GeoShapeFieldMapper; import java.io.IOException; diff --git a/server/src/main/java/org/elasticsearch/common/inject/AbstractModule.java b/server/src/main/java/org/elasticsearch/common/inject/AbstractModule.java index a10f94c1ab856..7c0a5ce2680c4 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/AbstractModule.java +++ b/server/src/main/java/org/elasticsearch/common/inject/AbstractModule.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.inject.spi.Message; import org.elasticsearch.common.inject.spi.TypeConverter; import org.elasticsearch.common.inject.spi.TypeListener; -import org.elasticsearch.common.settings.SettingsModule; import java.lang.annotation.Annotation; import java.util.Objects; diff --git a/server/src/main/java/org/elasticsearch/common/inject/Injector.java b/server/src/main/java/org/elasticsearch/common/inject/Injector.java index 2417e3b1411ee..33a7911570fc3 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/Injector.java +++ b/server/src/main/java/org/elasticsearch/common/inject/Injector.java @@ -17,7 +17,6 @@ package org.elasticsearch.common.inject; import java.util.List; -import java.util.Map; /** * Builds the graphs of objects that make up your application. The injector tracks the dependencies diff --git a/server/src/main/java/org/elasticsearch/common/inject/InjectorBuilder.java b/server/src/main/java/org/elasticsearch/common/inject/InjectorBuilder.java index 93c92d480e29f..3a8cb51fee7f4 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/InjectorBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/inject/InjectorBuilder.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.inject.spi.Dependency; import java.util.List; -import java.util.Map; /** * Builds a tree of injectors. This is a primary injector, plus child injectors needed for each diff --git a/server/src/main/java/org/elasticsearch/common/inject/InjectorImpl.java b/server/src/main/java/org/elasticsearch/common/inject/InjectorImpl.java index 3721c0cc8b178..2c5864d2620f3 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/InjectorImpl.java +++ b/server/src/main/java/org/elasticsearch/common/inject/InjectorImpl.java @@ -17,7 +17,6 @@ package org.elasticsearch.common.inject; import org.elasticsearch.common.Classes; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.internal.Annotations; import org.elasticsearch.common.inject.internal.BindingImpl; import org.elasticsearch.common.inject.internal.Errors; @@ -45,7 +44,6 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; diff --git a/server/src/main/java/org/elasticsearch/common/inject/PrivateModule.java b/server/src/main/java/org/elasticsearch/common/inject/PrivateModule.java index b56c0e11ab184..1a74579385f25 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/PrivateModule.java +++ b/server/src/main/java/org/elasticsearch/common/inject/PrivateModule.java @@ -17,15 +17,10 @@ package org.elasticsearch.common.inject; import org.elasticsearch.common.inject.binder.AnnotatedBindingBuilder; -import org.elasticsearch.common.inject.binder.AnnotatedConstantBindingBuilder; import org.elasticsearch.common.inject.binder.AnnotatedElementBuilder; import org.elasticsearch.common.inject.binder.LinkedBindingBuilder; -import org.elasticsearch.common.inject.matcher.Matcher; import org.elasticsearch.common.inject.spi.Message; -import org.elasticsearch.common.inject.spi.TypeConverter; -import org.elasticsearch.common.inject.spi.TypeListener; -import java.lang.annotation.Annotation; /** * A module whose configuration information is hidden from its environment by default. Only bindings diff --git a/server/src/main/java/org/elasticsearch/common/inject/assistedinject/FactoryProvider.java b/server/src/main/java/org/elasticsearch/common/inject/assistedinject/FactoryProvider.java index 099ff66159599..9e2d0e379867c 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/assistedinject/FactoryProvider.java +++ b/server/src/main/java/org/elasticsearch/common/inject/assistedinject/FactoryProvider.java @@ -19,7 +19,6 @@ import org.elasticsearch.common.inject.ConfigurationException; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Injector; -import org.elasticsearch.common.inject.Key; import org.elasticsearch.common.inject.Provider; import org.elasticsearch.common.inject.TypeLiteral; import org.elasticsearch.common.inject.internal.Errors; @@ -27,20 +26,13 @@ import org.elasticsearch.common.inject.spi.HasDependencies; import org.elasticsearch.common.inject.spi.Message; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; -import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static java.util.Collections.unmodifiableSet; diff --git a/server/src/main/java/org/elasticsearch/common/inject/assistedinject/Parameter.java b/server/src/main/java/org/elasticsearch/common/inject/assistedinject/Parameter.java index a21dc3aa7f54d..5a3f10ebb135f 100644 --- a/server/src/main/java/org/elasticsearch/common/inject/assistedinject/Parameter.java +++ b/server/src/main/java/org/elasticsearch/common/inject/assistedinject/Parameter.java @@ -17,7 +17,6 @@ package org.elasticsearch.common.inject.assistedinject; import org.elasticsearch.common.inject.BindingAnnotation; -import org.elasticsearch.common.inject.ConfigurationException; import org.elasticsearch.common.inject.Injector; import org.elasticsearch.common.inject.Key; import org.elasticsearch.common.inject.Provider; diff --git a/server/src/main/java/org/elasticsearch/common/io/Channels.java b/server/src/main/java/org/elasticsearch/common/io/Channels.java index 71a2d66dee360..cb8ac062fbcb2 100644 --- a/server/src/main/java/org/elasticsearch/common/io/Channels.java +++ b/server/src/main/java/org/elasticsearch/common/io/Channels.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; -import java.nio.channels.GatheringByteChannel; import java.nio.channels.WritableByteChannel; @SuppressForbidden(reason = "Channel#read") diff --git a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java index 1c559cf64fbb7..7c5a6f9b2361f 100644 --- a/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java +++ b/server/src/main/java/org/elasticsearch/common/logging/DeprecationLogger.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.SuppressLoggerChecks; import org.elasticsearch.common.util.concurrent.ThreadContext; -import java.io.CharArrayWriter; import java.nio.charset.Charset; import java.time.ZoneId; import java.time.ZonedDateTime; diff --git a/server/src/main/java/org/elasticsearch/common/lucene/search/Queries.java b/server/src/main/java/org/elasticsearch/common/lucene/search/Queries.java index d004c798996c9..673d4a9bb438f 100644 --- a/server/src/main/java/org/elasticsearch/common/lucene/search/Queries.java +++ b/server/src/main/java/org/elasticsearch/common/lucene/search/Queries.java @@ -24,7 +24,6 @@ import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchNoDocsQuery; diff --git a/server/src/main/java/org/elasticsearch/common/path/PathTrie.java b/server/src/main/java/org/elasticsearch/common/path/PathTrie.java index 15553c9f24e72..5243809c64a1b 100644 --- a/server/src/main/java/org/elasticsearch/common/path/PathTrie.java +++ b/server/src/main/java/org/elasticsearch/common/path/PathTrie.java @@ -23,12 +23,10 @@ import java.util.Arrays; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Set; import java.util.function.BiFunction; import java.util.function.Supplier; diff --git a/server/src/main/java/org/elasticsearch/common/settings/AddFileKeyStoreCommand.java b/server/src/main/java/org/elasticsearch/common/settings/AddFileKeyStoreCommand.java index ba65f1453ad11..f5b3cb9cf7104 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/AddFileKeyStoreCommand.java +++ b/server/src/main/java/org/elasticsearch/common/settings/AddFileKeyStoreCommand.java @@ -19,14 +19,8 @@ package org.elasticsearch.common.settings; -import java.io.BufferedReader; -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Arrays; import java.util.List; diff --git a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index 9b1cf1411daef..f58ce3eead937 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -44,7 +44,6 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; diff --git a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreCli.java b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreCli.java index b3d448dae509e..3deb5f19c95fe 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreCli.java +++ b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreCli.java @@ -20,7 +20,6 @@ package org.elasticsearch.common.settings; import org.elasticsearch.cli.LoggingAwareMultiCommand; -import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; /** diff --git a/server/src/main/java/org/elasticsearch/common/text/Text.java b/server/src/main/java/org/elasticsearch/common/text/Text.java index bc0674d0b33c2..6dbbe42a0237f 100644 --- a/server/src/main/java/org/elasticsearch/common/text/Text.java +++ b/server/src/main/java/org/elasticsearch/common/text/Text.java @@ -21,7 +21,6 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/main/java/org/elasticsearch/common/transport/TransportAddress.java b/server/src/main/java/org/elasticsearch/common/transport/TransportAddress.java index f486bdd926bdf..b87b6a2e78fb9 100644 --- a/server/src/main/java/org/elasticsearch/common/transport/TransportAddress.java +++ b/server/src/main/java/org/elasticsearch/common/transport/TransportAddress.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.network.NetworkAddress; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/main/java/org/elasticsearch/common/unit/ByteSizeValue.java b/server/src/main/java/org/elasticsearch/common/unit/ByteSizeValue.java index 6e05c576d4d89..69b7116e29114 100644 --- a/server/src/main/java/org/elasticsearch/common/unit/ByteSizeValue.java +++ b/server/src/main/java/org/elasticsearch/common/unit/ByteSizeValue.java @@ -27,7 +27,6 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/main/java/org/elasticsearch/common/util/BigArrays.java b/server/src/main/java/org/elasticsearch/common/util/BigArrays.java index ec46a2b937f40..ce00019630b3a 100644 --- a/server/src/main/java/org/elasticsearch/common/util/BigArrays.java +++ b/server/src/main/java/org/elasticsearch/common/util/BigArrays.java @@ -28,7 +28,6 @@ import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.recycler.Recycler; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.indices.breaker.CircuitBreakerService; import java.util.Arrays; diff --git a/server/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java b/server/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java index 7cf019bd6c58b..8ebd79dd4b3f9 100644 --- a/server/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java +++ b/server/src/main/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutor.java @@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; -import java.util.function.Supplier; /** * An extension to thread pool executor, which automatically adjusts the queue size of the diff --git a/server/src/main/java/org/elasticsearch/common/util/concurrent/ThreadContext.java b/server/src/main/java/org/elasticsearch/common/util/concurrent/ThreadContext.java index 901c6425d7131..ba72561f0c145 100644 --- a/server/src/main/java/org/elasticsearch/common/util/concurrent/ThreadContext.java +++ b/server/src/main/java/org/elasticsearch/common/util/concurrent/ThreadContext.java @@ -23,12 +23,10 @@ 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.logging.DeprecationLogger; import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.http.HttpTransportSettings; import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_MAX_WARNING_HEADER_COUNT; diff --git a/server/src/main/java/org/elasticsearch/discovery/DiscoveryStats.java b/server/src/main/java/org/elasticsearch/discovery/DiscoveryStats.java index a4c788d5c22ad..1032b18f3a8dc 100644 --- a/server/src/main/java/org/elasticsearch/discovery/DiscoveryStats.java +++ b/server/src/main/java/org/elasticsearch/discovery/DiscoveryStats.java @@ -20,7 +20,6 @@ package org.elasticsearch.discovery; import org.elasticsearch.Version; -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; diff --git a/server/src/main/java/org/elasticsearch/env/ESFileStore.java b/server/src/main/java/org/elasticsearch/env/ESFileStore.java index bba1e1e609675..52b0cea6748d8 100644 --- a/server/src/main/java/org/elasticsearch/env/ESFileStore.java +++ b/server/src/main/java/org/elasticsearch/env/ESFileStore.java @@ -25,12 +25,10 @@ import java.io.IOException; import java.nio.file.FileStore; -import java.nio.file.FileSystemException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileAttributeView; import java.nio.file.attribute.FileStoreAttributeView; -import java.util.Arrays; import java.util.List; /** diff --git a/server/src/main/java/org/elasticsearch/env/ShardLock.java b/server/src/main/java/org/elasticsearch/env/ShardLock.java index 99dd973b4ceb0..63b3f8e046ea8 100644 --- a/server/src/main/java/org/elasticsearch/env/ShardLock.java +++ b/server/src/main/java/org/elasticsearch/env/ShardLock.java @@ -22,7 +22,6 @@ import org.elasticsearch.index.shard.ShardId; import java.io.Closeable; -import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; /** diff --git a/server/src/main/java/org/elasticsearch/gateway/GatewayModule.java b/server/src/main/java/org/elasticsearch/gateway/GatewayModule.java index 944baa58cb5c7..b958e42a8b563 100644 --- a/server/src/main/java/org/elasticsearch/gateway/GatewayModule.java +++ b/server/src/main/java/org/elasticsearch/gateway/GatewayModule.java @@ -20,7 +20,6 @@ package org.elasticsearch.gateway; import org.elasticsearch.common.inject.AbstractModule; -import org.elasticsearch.common.settings.Settings; public class GatewayModule extends AbstractModule { diff --git a/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java b/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java index 116d181ccd3a2..c8986b0493459 100644 --- a/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java +++ b/server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java @@ -28,7 +28,6 @@ import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.metadata.MetaDataIndexUpgradeService; import org.elasticsearch.cluster.node.DiscoveryNode; -import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.cluster.routing.allocation.AllocationService; import org.elasticsearch.cluster.service.ClusterService; diff --git a/server/src/main/java/org/elasticsearch/gateway/PrimaryShardAllocator.java b/server/src/main/java/org/elasticsearch/gateway/PrimaryShardAllocator.java index f9344186c5753..e63f349b55c3e 100644 --- a/server/src/main/java/org/elasticsearch/gateway/PrimaryShardAllocator.java +++ b/server/src/main/java/org/elasticsearch/gateway/PrimaryShardAllocator.java @@ -38,7 +38,6 @@ import org.elasticsearch.env.ShardLockObtainFailedException; import org.elasticsearch.gateway.AsyncShardFetch.FetchResult; import org.elasticsearch.gateway.TransportNodesListGatewayStartedShards.NodeGatewayStartedShards; -import org.elasticsearch.index.shard.ShardStateMetaData; import java.util.ArrayList; import java.util.Collection; diff --git a/server/src/main/java/org/elasticsearch/index/analysis/StandardAnalyzerProvider.java b/server/src/main/java/org/elasticsearch/index/analysis/StandardAnalyzerProvider.java index 01a0dc00d5884..5470f005e64df 100644 --- a/server/src/main/java/org/elasticsearch/index/analysis/StandardAnalyzerProvider.java +++ b/server/src/main/java/org/elasticsearch/index/analysis/StandardAnalyzerProvider.java @@ -20,9 +20,7 @@ package org.elasticsearch.index.analysis; import org.apache.lucene.analysis.CharArraySet; -import org.apache.lucene.analysis.core.StopAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; -import org.elasticsearch.Version; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.index.IndexSettings; diff --git a/server/src/main/java/org/elasticsearch/index/engine/EngineSearcher.java b/server/src/main/java/org/elasticsearch/index/engine/EngineSearcher.java index ac461c1f58da2..7fd0fe6cc3904 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/EngineSearcher.java +++ b/server/src/main/java/org/elasticsearch/index/engine/EngineSearcher.java @@ -22,11 +22,9 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.ReferenceManager; -import org.apache.lucene.search.SearcherManager; import org.apache.lucene.store.AlreadyClosedException; import org.elasticsearch.index.store.Store; -import java.io.Closeable; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldData.java b/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldData.java index 6519e8297bf4b..da3dc75f4ef52 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldData.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/IndexFieldData.java @@ -36,7 +36,6 @@ import org.apache.lucene.util.BitSet; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.index.IndexComponent; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/plain/LatLonPointDVAtomicFieldData.java b/server/src/main/java/org/elasticsearch/index/fielddata/plain/LatLonPointDVAtomicFieldData.java index 93fa5bf22acc7..dba7dfb0c9e99 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/plain/LatLonPointDVAtomicFieldData.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/plain/LatLonPointDVAtomicFieldData.java @@ -23,8 +23,6 @@ import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.SortedNumericDocValues; import org.apache.lucene.util.Accountable; -import org.apache.lucene.util.ArrayUtil; -import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.index.fielddata.MultiGeoPointValues; diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/plain/PagedBytesIndexFieldData.java b/server/src/main/java/org/elasticsearch/index/fielddata/plain/PagedBytesIndexFieldData.java index fa126d6813251..e6100d2f89311 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/plain/PagedBytesIndexFieldData.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/plain/PagedBytesIndexFieldData.java @@ -20,7 +20,6 @@ import org.apache.lucene.codecs.blocktree.FieldReader; import org.apache.lucene.codecs.blocktree.Stats; -import org.apache.lucene.index.Fields; import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java index 30b4d422d5c9c..112411d06cc97 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java @@ -46,7 +46,6 @@ import java.util.Map; import java.util.Objects; -import static java.util.Collections.emptyMap; public class DocumentMapper implements ToXContentFragment { diff --git a/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java index be5fca365d892..ee1779d3d5190 100644 --- a/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java @@ -41,7 +41,6 @@ import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Stream; -import java.util.stream.StreamSupport; import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded; diff --git a/server/src/main/java/org/elasticsearch/index/query/MatchPhraseQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/MatchPhraseQueryBuilder.java index 1b7b6f92df80b..445720cb934ca 100644 --- a/server/src/main/java/org/elasticsearch/index/query/MatchPhraseQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/MatchPhraseQueryBuilder.java @@ -28,7 +28,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.search.MatchQuery; import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery; 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 d8ad16f3af052..01580201911d5 100644 --- a/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/MultiMatchQueryBuilder.java @@ -39,7 +39,6 @@ import org.elasticsearch.index.search.MatchQuery; import org.elasticsearch.index.search.MultiMatchQuery; import org.elasticsearch.index.search.QueryParserHelper; -import org.elasticsearch.index.search.QueryStringQueryParser; import java.io.IOException; import java.util.HashMap; diff --git a/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java index 9aca0c0fc0d38..1f410a2564cdf 100644 --- a/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java @@ -37,7 +37,6 @@ import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.search.spans.SpanTermQuery; import org.elasticsearch.Version; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.StreamInput; @@ -45,7 +44,6 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.mapper.TextFieldMapper; import org.elasticsearch.index.query.support.QueryParsers; import java.io.IOException; diff --git a/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java index 96782dac0b8da..2136e030dbdb1 100644 --- a/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java @@ -34,7 +34,6 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.mapper.IndexFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.query.support.QueryParsers; 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 9592ffe0b1fe5..a860bd19d7c5f 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 @@ -30,7 +30,6 @@ import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.script.ScoreScript; import org.elasticsearch.script.Script; -import org.elasticsearch.script.SearchScript; import java.io.IOException; import java.util.Objects; diff --git a/server/src/main/java/org/elasticsearch/index/reindex/LeaderBulkByScrollTaskState.java b/server/src/main/java/org/elasticsearch/index/reindex/LeaderBulkByScrollTaskState.java index e7790fc07c319..dd640de2ac264 100644 --- a/server/src/main/java/org/elasticsearch/index/reindex/LeaderBulkByScrollTaskState.java +++ b/server/src/main/java/org/elasticsearch/index/reindex/LeaderBulkByScrollTaskState.java @@ -21,7 +21,6 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.util.concurrent.AtomicArray; -import org.elasticsearch.tasks.TaskInfo; import java.util.ArrayList; import java.util.Arrays; diff --git a/server/src/main/java/org/elasticsearch/index/shard/ShardId.java b/server/src/main/java/org/elasticsearch/index/shard/ShardId.java index e243c916aa232..c1649c7069f02 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/ShardId.java +++ b/server/src/main/java/org/elasticsearch/index/shard/ShardId.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.Index; diff --git a/server/src/main/java/org/elasticsearch/index/shard/ShardSplittingQuery.java b/server/src/main/java/org/elasticsearch/index/shard/ShardSplittingQuery.java index 633894f3c62e2..e27c68c7570a7 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/ShardSplittingQuery.java +++ b/server/src/main/java/org/elasticsearch/index/shard/ShardSplittingQuery.java @@ -51,7 +51,6 @@ import java.io.IOException; import java.util.function.Function; import java.util.function.IntConsumer; -import java.util.function.IntPredicate; import java.util.function.Predicate; /** diff --git a/server/src/main/java/org/elasticsearch/index/store/StoreFileMetaData.java b/server/src/main/java/org/elasticsearch/index/store/StoreFileMetaData.java index 97310c0f65d98..59ad749f638ee 100644 --- a/server/src/main/java/org/elasticsearch/index/store/StoreFileMetaData.java +++ b/server/src/main/java/org/elasticsearch/index/store/StoreFileMetaData.java @@ -24,7 +24,6 @@ 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.lucene.Lucene; import java.io.IOException; import java.text.ParseException; diff --git a/server/src/main/java/org/elasticsearch/index/translog/BufferedChecksumStreamInput.java b/server/src/main/java/org/elasticsearch/index/translog/BufferedChecksumStreamInput.java index ba6da4ba522bb..37740b460b766 100644 --- a/server/src/main/java/org/elasticsearch/index/translog/BufferedChecksumStreamInput.java +++ b/server/src/main/java/org/elasticsearch/index/translog/BufferedChecksumStreamInput.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.io.stream.FilterStreamInput; import org.elasticsearch.common.io.stream.StreamInput; -import java.io.EOFException; import java.io.IOException; import java.util.zip.CRC32; import java.util.zip.Checksum; diff --git a/server/src/main/java/org/elasticsearch/index/translog/TranslogToolCli.java b/server/src/main/java/org/elasticsearch/index/translog/TranslogToolCli.java index b9cbf03295135..f7d830a32ec1b 100644 --- a/server/src/main/java/org/elasticsearch/index/translog/TranslogToolCli.java +++ b/server/src/main/java/org/elasticsearch/index/translog/TranslogToolCli.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.translog; import org.elasticsearch.cli.LoggingAwareMultiCommand; -import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; /** diff --git a/server/src/main/java/org/elasticsearch/index/translog/TruncateTranslogCommand.java b/server/src/main/java/org/elasticsearch/index/translog/TruncateTranslogCommand.java index b8bd93e05a6f8..86995ae7c5a99 100644 --- a/server/src/main/java/org/elasticsearch/index/translog/TruncateTranslogCommand.java +++ b/server/src/main/java/org/elasticsearch/index/translog/TruncateTranslogCommand.java @@ -32,7 +32,6 @@ import org.apache.lucene.store.Lock; import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.NativeFSLockFactory; -import org.apache.lucene.store.OutputStreamDataOutput; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.cli.EnvironmentAwareCommand; @@ -46,7 +45,6 @@ import org.elasticsearch.index.seqno.SequenceNumbers; import java.io.IOException; -import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.file.DirectoryStream; import java.nio.file.Files; diff --git a/server/src/main/java/org/elasticsearch/indices/IndexCreationException.java b/server/src/main/java/org/elasticsearch/indices/IndexCreationException.java index da823abf5375d..6482abdd3c99e 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndexCreationException.java +++ b/server/src/main/java/org/elasticsearch/indices/IndexCreationException.java @@ -22,7 +22,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchWrapperException; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.index.Index; import java.io.IOException; diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesService.java b/server/src/main/java/org/elasticsearch/indices/IndicesService.java index 0333b673a9312..d6c0878108f37 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndicesService.java +++ b/server/src/main/java/org/elasticsearch/indices/IndicesService.java @@ -44,7 +44,6 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.component.AbstractLifecycleComponent; import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.io.stream.BytesStreamOutput; diff --git a/server/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java b/server/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java index 3b296bf52d306..a2aa8e385e3f9 100644 --- a/server/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java +++ b/server/src/main/java/org/elasticsearch/ingest/PipelineConfiguration.java @@ -29,13 +29,11 @@ import org.elasticsearch.common.xcontent.ContextParser; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ToXContentObject; -import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; -import java.util.Collections; import java.util.Map; import java.util.Objects; diff --git a/server/src/main/java/org/elasticsearch/node/AdaptiveSelectionStats.java b/server/src/main/java/org/elasticsearch/node/AdaptiveSelectionStats.java index 819b3365ce066..ca156375725a9 100644 --- a/server/src/main/java/org/elasticsearch/node/AdaptiveSelectionStats.java +++ b/server/src/main/java/org/elasticsearch/node/AdaptiveSelectionStats.java @@ -29,7 +29,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; -import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; diff --git a/server/src/main/java/org/elasticsearch/node/NodeValidationException.java b/server/src/main/java/org/elasticsearch/node/NodeValidationException.java index 58e2c4ef951f6..913a43223e2e1 100644 --- a/server/src/main/java/org/elasticsearch/node/NodeValidationException.java +++ b/server/src/main/java/org/elasticsearch/node/NodeValidationException.java @@ -19,7 +19,6 @@ package org.elasticsearch.node; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import java.util.List; diff --git a/server/src/main/java/org/elasticsearch/node/ResponseCollectorService.java b/server/src/main/java/org/elasticsearch/node/ResponseCollectorService.java index 9881d4404af87..4ab1bc4cee80b 100644 --- a/server/src/main/java/org/elasticsearch/node/ResponseCollectorService.java +++ b/server/src/main/java/org/elasticsearch/node/ResponseCollectorService.java @@ -25,7 +25,6 @@ import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.ExponentiallyWeightedMovingAverage; import org.elasticsearch.common.component.AbstractComponent; -import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; @@ -33,13 +32,11 @@ import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import java.io.IOException; -import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentMap; -import java.util.stream.Collectors; /** * Collects statistics about queue size, response time, and service time of diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginInfo.java b/server/src/main/java/org/elasticsearch/plugins/PluginInfo.java index 37f4fcc953564..74a911b0ae4fc 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginInfo.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginInfo.java @@ -21,7 +21,6 @@ import org.elasticsearch.Version; import org.elasticsearch.bootstrap.JarHell; -import org.elasticsearch.common.Booleans; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java index 5b64b5be6390d..b60288900ab20 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginsService.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginsService.java @@ -41,7 +41,6 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.IndexModule; import org.elasticsearch.threadpool.ExecutorBuilder; import org.elasticsearch.transport.TcpTransport; @@ -67,7 +66,6 @@ import java.util.Optional; import java.util.Set; import java.util.TreeMap; -import java.util.TreeSet; import java.util.function.Function; import java.util.stream.Collectors; diff --git a/server/src/main/java/org/elasticsearch/plugins/SearchPlugin.java b/server/src/main/java/org/elasticsearch/plugins/SearchPlugin.java index c61d571f39a60..952aa76fd17a9 100644 --- a/server/src/main/java/org/elasticsearch/plugins/SearchPlugin.java +++ b/server/src/main/java/org/elasticsearch/plugins/SearchPlugin.java @@ -20,8 +20,6 @@ package org.elasticsearch.plugins; import org.apache.lucene.search.Query; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.common.CheckedFunction; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.NamedWriteable; diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/ChecksumBlobStoreFormat.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/ChecksumBlobStoreFormat.java index df9b41ba87299..b974be2b869ab 100644 --- a/server/src/main/java/org/elasticsearch/repositories/blobstore/ChecksumBlobStoreFormat.java +++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/ChecksumBlobStoreFormat.java @@ -46,7 +46,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.Locale; /** * Snapshot metadata file format used in v2.0 and above diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetRepositoriesAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetRepositoriesAction.java index 4e06497c7fe51..ccb10b603d3b2 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetRepositoriesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestGetRepositoriesAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.rest.action.admin.cluster; import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesRequest; -import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesResponse; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAllAliasesAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAllAliasesAction.java index 0d10ac5800e76..87cadbafd8321 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAllAliasesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAllAliasesAction.java @@ -26,9 +26,7 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.settings.SettingsFilter; import org.elasticsearch.common.xcontent.ToXContent.Params; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.BaseRestHandler; @@ -40,10 +38,8 @@ import java.io.IOException; import java.util.List; -import java.util.Set; import static org.elasticsearch.rest.RestRequest.Method.GET; -import static org.elasticsearch.rest.RestRequest.Method.HEAD; import static org.elasticsearch.rest.RestStatus.OK; /** diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpdateSettingsAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpdateSettingsAction.java index 68f696b180267..b06314aeb1a4b 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpdateSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpdateSettingsAction.java @@ -24,15 +24,12 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.action.RestToXContentListener; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import java.util.Set; import static org.elasticsearch.client.Requests.updateSettingsRequest; diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpgradeAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpgradeAction.java index 9201c4504823d..6ed39c70efdc9 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpgradeAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestUpgradeAction.java @@ -19,7 +19,6 @@ package org.elasticsearch.rest.action.admin.indices; -import org.elasticsearch.action.admin.indices.upgrade.get.UpgradeStatusRequest; import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.node.NodeClient; diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java index 513643f99ab03..2a60262b32f57 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestMultiSearchAction.java @@ -19,7 +19,6 @@ package org.elasticsearch.rest.action.search; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.action.search.MultiSearchRequest; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; @@ -41,13 +40,8 @@ import java.io.IOException; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.function.BiConsumer; -import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeBooleanValue; -import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringArrayValue; -import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeStringValue; import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestRequest.Method.POST; diff --git a/server/src/main/java/org/elasticsearch/script/FilterScript.java b/server/src/main/java/org/elasticsearch/script/FilterScript.java index f1d3c13786009..500ab99c6a56b 100644 --- a/server/src/main/java/org/elasticsearch/script/FilterScript.java +++ b/server/src/main/java/org/elasticsearch/script/FilterScript.java @@ -23,7 +23,6 @@ import org.apache.lucene.index.LeafReaderContext; import org.elasticsearch.index.fielddata.ScriptDocValues; -import org.elasticsearch.search.lookup.LeafDocLookup; import org.elasticsearch.search.lookup.LeafSearchLookup; import org.elasticsearch.search.lookup.SearchLookup; diff --git a/server/src/main/java/org/elasticsearch/search/SearchHit.java b/server/src/main/java/org/elasticsearch/search/SearchHit.java index a6e0a0b5afa40..8ede464b7be54 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchHit.java +++ b/server/src/main/java/org/elasticsearch/search/SearchHit.java @@ -49,7 +49,6 @@ import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; import org.elasticsearch.search.lookup.SourceLookup; -import org.elasticsearch.search.suggest.completion.CompletionSuggestion; import org.elasticsearch.transport.RemoteClusterAware; import java.io.IOException; diff --git a/server/src/main/java/org/elasticsearch/search/SearchModule.java b/server/src/main/java/org/elasticsearch/search/SearchModule.java index 0faaa14c9205f..1f448e6217fbb 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchModule.java +++ b/server/src/main/java/org/elasticsearch/search/SearchModule.java @@ -100,7 +100,6 @@ import org.elasticsearch.search.aggregations.PipelineAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.adjacency.InternalAdjacencyMatrix; -import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregation; import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.composite.InternalComposite; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java index f844073fd0c1c..7a3d3fee8ac4a 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/AggregationBuilder.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.search.internal.SearchContext; -import org.elasticsearch.search.slice.SliceBuilder; import java.io.IOException; import java.util.List; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java index 68822f3f10c63..16f8aaf8f52de 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/Aggregator.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.xcontent.DeprecationHandler; -import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; import org.elasticsearch.search.internal.SearchContext; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java b/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java index 98cadd4aefa15..da328edd7aa4a 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java @@ -22,7 +22,6 @@ import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.util.BigArray; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.action.search.RestSearchAction; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java index d94f3ab02c30a..4484aa494b4e1 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java @@ -30,7 +30,6 @@ import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.apache.lucene.util.RoaringDocIdSet; -import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.search.DocValueFormat; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FiltersAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FiltersAggregationBuilder.java index 262164fb4004e..e35bf376aae4d 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FiltersAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FiltersAggregationBuilder.java @@ -30,7 +30,6 @@ import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.MultiBucketAggregationBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java index 2f66531834d38..569845fcdf0e4 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java @@ -30,13 +30,11 @@ import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.fielddata.AbstractSortingNumericDocValues; import org.elasticsearch.index.fielddata.MultiGeoPointValues; import org.elasticsearch.index.fielddata.SortedBinaryDocValues; import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.BucketUtils; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/global/GlobalAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/global/GlobalAggregationBuilder.java index 8aafd6f70973c..cae359d9ecb1a 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/global/GlobalAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/global/GlobalAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.internal.SearchContext; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramInterval.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramInterval.java index 9b34739b96d6e..c01a1190ff381 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramInterval.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramInterval.java @@ -22,7 +22,6 @@ 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.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java index 39ce2b8d02a83..3be7bcd596d7b 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregationBuilder.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.BucketOrder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingAggregationBuilder.java index fa9dc91384f65..b72c004464427 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregationBuilder.java index 20b7eb0aed3dd..8429cf79ed903 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregationBuilder.java @@ -28,7 +28,6 @@ import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationExecutionException; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.internal.SearchContext; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregationBuilder.java index 7485d99a89228..06c73ef797e28 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregationBuilder.java @@ -30,7 +30,6 @@ import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationExecutionException; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.internal.SearchContext; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/AbstractRangeBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/AbstractRangeBuilder.java index a53db9ee32a71..e5b62d85ca17c 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/AbstractRangeBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/AbstractRangeBuilder.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.bucket.MultiBucketAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator.Range; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregationBuilder.java index f7567f855bfc5..b5bdba85b78ef 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/DateRangeAggregationBuilder.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/GeoDistanceAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/GeoDistanceAggregationBuilder.java index ee943793e349f..c612b303fb134 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/GeoDistanceAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/GeoDistanceAggregationBuilder.java @@ -32,10 +32,8 @@ import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.common.xcontent.XContentParserUtils; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.bucket.geogrid.GeoGridAggregationBuilder; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/IpRangeAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/IpRangeAggregationBuilder.java index 69582d49060a6..1a3eb4957e2c5 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/IpRangeAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/IpRangeAggregationBuilder.java @@ -33,7 +33,6 @@ import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java index df072309508ed..9a7edf7ef95dd 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregationBuilder.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator.Range; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilder.java index dc5fb8cbcfd62..d6ff4c3d18f5d 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValuesSource; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregationBuilder.java index ea987dcc64c93..1aed0a0374aa9 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregationBuilder.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.internal.SearchContext; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java index b5c5a17d11d7b..02216c5a5cb35 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java @@ -21,7 +21,6 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ParseFieldRegistry; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -29,7 +28,6 @@ import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.Aggregator; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.MultiBucketAggregationBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregationBuilder.java index 5a2c65f7a4725..5e8bc2f4c1888 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregationBuilder.java @@ -22,7 +22,6 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ParseFieldRegistry; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -33,7 +32,6 @@ import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationInitializationException; import org.elasticsearch.search.aggregations.Aggregator; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.significant.heuristics.SignificanceHeuristic; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregator.java index 4dae78aa14078..7f62813278b64 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregator.java @@ -26,15 +26,11 @@ import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Terms; -import org.apache.lucene.search.highlight.TokenStreamFromTermVector; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.util.BytesRefHash; import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactories; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java index 8b7f1b678b166..547c9d0a80ec6 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/InternalMappedTerms.java @@ -27,7 +27,6 @@ import org.elasticsearch.search.aggregations.BucketOrder; import java.io.IOException; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java index 3def87d7dfec0..5887f6b525891 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregationBuilder.java @@ -27,7 +27,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.Aggregator.SubAggCollectionMode; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.BucketOrder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgAggregationBuilder.java index 24cda11754ec5..f0d917715ace4 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityAggregationBuilder.java index 23e24e1003d12..17b3849c5eb16 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityAggregationBuilder.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/GeoBoundsAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/GeoBoundsAggregationBuilder.java index aa8464c42cebd..2d616ebe07168 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/GeoBoundsAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geobounds/GeoBoundsAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/GeoCentroidAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/GeoCentroidAggregationBuilder.java index e7d8d1af83347..32fcaf32775c9 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/GeoCentroidAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/geocentroid/GeoCentroidAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxAggregationBuilder.java index f7cae995d6a93..7135aceba95c8 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinAggregationBuilder.java index d4aeeb2ed0d31..380569f18969a 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java index df8a6668cb2af..6bb956452ef01 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java @@ -23,12 +23,10 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.ConstructingObjectParser; -import org.elasticsearch.common.xcontent.ContextParser; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.HDRPercentileRanksAggregatorFactory; @@ -48,7 +46,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.function.BiConsumer; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java index 0bc759a0d47a7..225398e51b7c0 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/scripted/ScriptedMetricAggregationBuilder.java @@ -31,7 +31,6 @@ import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.internal.SearchContext; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsAggregationBuilder.java index cc48ca915d406..3d9d9e6c030a1 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsAggregationBuilder.java index 35642be750652..09a12fb188fe3 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumAggregationBuilder.java index 42d58b8e835d6..ed47f245111ee 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/tophits/TopHitsAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/tophits/TopHitsAggregationBuilder.java index 836bd7fc9ff03..6b8ae8d79cac4 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/metrics/tophits/TopHitsAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/metrics/tophits/TopHitsAggregationBuilder.java @@ -33,7 +33,6 @@ import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationInitializationException; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.builder.SearchSourceBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/MovAvgPipelineAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/MovAvgPipelineAggregationBuilder.java index 8fdc6d3eb62b3..074ea1c25892b 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/MovAvgPipelineAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/MovAvgPipelineAggregationBuilder.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.ParseFieldRegistry; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/HoltWintersModel.java b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/HoltWintersModel.java index e7c2007955fd2..d61cb5c41873b 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/HoltWintersModel.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/HoltWintersModel.java @@ -27,14 +27,12 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.search.aggregations.AggregationExecutionException; import org.elasticsearch.search.aggregations.pipeline.movavg.MovAvgPipelineAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.movfn.MovingFunctions; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctionScript.java b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctionScript.java index 131f6eb0fab58..2f6751b73cc91 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctionScript.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctionScript.java @@ -21,7 +21,6 @@ import org.elasticsearch.script.ScriptContext; -import java.util.Collection; import java.util.Map; /** diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctions.java b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctions.java index 4261271d185c3..c38ce27cd8ad2 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctions.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovingFunctions.java @@ -20,7 +20,6 @@ package org.elasticsearch.search.aggregations.pipeline.movfn; import java.util.Arrays; -import java.util.Collection; /** * Provides a collection of static utility methods that can be referenced from MovingFunction script contexts diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java index 81b6d23f3873d..040cc1b542f07 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregationBuilder.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.script.Script; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; -import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationInitializationException; import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java index d8414c7b31f94..b33ba8796604a 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java @@ -21,7 +21,6 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.geo.GeoPoint; -import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexGeoPointFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData; @@ -29,7 +28,6 @@ import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationExecutionException; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParserHelper.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParserHelper.java index f3f8fa3056898..365233122c43e 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParserHelper.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParserHelper.java @@ -22,7 +22,6 @@ import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.xcontent.AbstractObjectParser; -import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.script.Script; diff --git a/server/src/main/java/org/elasticsearch/search/collapse/CollapseContext.java b/server/src/main/java/org/elasticsearch/search/collapse/CollapseContext.java index 9e329da9b0075..95fee901a30e1 100644 --- a/server/src/main/java/org/elasticsearch/search/collapse/CollapseContext.java +++ b/server/src/main/java/org/elasticsearch/search/collapse/CollapseContext.java @@ -25,7 +25,6 @@ import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.query.InnerHitBuilder; -import java.io.IOException; import java.util.Collections; import java.util.List; diff --git a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java index 683f7bbde168f..08251c6a73b94 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java @@ -63,7 +63,6 @@ import java.util.Map; import java.util.Set; -import static org.elasticsearch.common.xcontent.XContentFactory.contentBuilder; /** * Fetch phase of a search request, used to fetch the actual top matching documents to be returned to the client, identified diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/SimpleFragmentsBuilder.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/SimpleFragmentsBuilder.java index fd3b069c1c0ae..851d916cde7e2 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/SimpleFragmentsBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/SimpleFragmentsBuilder.java @@ -22,7 +22,6 @@ import org.apache.lucene.search.highlight.Encoder; import org.apache.lucene.search.vectorhighlight.BoundaryScanner; import org.apache.lucene.search.vectorhighlight.FieldFragList.WeightedFragInfo; -import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; /** diff --git a/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java b/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java index 6f00740487e2c..862c1a6960bed 100644 --- a/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java +++ b/server/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java @@ -21,7 +21,6 @@ import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.CheckedFunction; diff --git a/server/src/main/java/org/elasticsearch/search/query/TopDocsCollectorContext.java b/server/src/main/java/org/elasticsearch/search/query/TopDocsCollectorContext.java index cf4ff6c77b823..dc110b2797710 100644 --- a/server/src/main/java/org/elasticsearch/search/query/TopDocsCollectorContext.java +++ b/server/src/main/java/org/elasticsearch/search/query/TopDocsCollectorContext.java @@ -54,7 +54,6 @@ import static org.elasticsearch.search.profile.query.CollectorResult.REASON_SEARCH_COUNT; import static org.elasticsearch.search.profile.query.CollectorResult.REASON_SEARCH_TOP_HITS; -import static org.elasticsearch.search.query.QueryPhase.canEarlyTerminate; /** * A {@link QueryCollectorContext} that creates top docs collector diff --git a/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java b/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java index b8ce8f8118b50..7baaa61bbb8c5 100644 --- a/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java +++ b/server/src/main/java/org/elasticsearch/search/rescore/RescorePhase.java @@ -20,8 +20,6 @@ package org.elasticsearch.search.rescore; import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.Sort; -import org.apache.lucene.search.SortField; import org.apache.lucene.search.TopDocs; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.component.AbstractComponent; diff --git a/server/src/main/java/org/elasticsearch/search/suggest/completion/context/CategoryContextMapping.java b/server/src/main/java/org/elasticsearch/search/suggest/completion/context/CategoryContextMapping.java index d0b82b10588f3..073e7da3accb2 100644 --- a/server/src/main/java/org/elasticsearch/search/suggest/completion/context/CategoryContextMapping.java +++ b/server/src/main/java/org/elasticsearch/search/suggest/completion/context/CategoryContextMapping.java @@ -23,7 +23,6 @@ import org.apache.lucene.document.SortedSetDocValuesField; import org.apache.lucene.document.StoredField; import org.apache.lucene.index.IndexableField; -import org.apache.lucene.search.SortedSetSortField; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/suggest/phrase/MultiCandidateGeneratorWrapper.java b/server/src/main/java/org/elasticsearch/search/suggest/phrase/MultiCandidateGeneratorWrapper.java index 18174d351e7ac..904822e389483 100644 --- a/server/src/main/java/org/elasticsearch/search/suggest/phrase/MultiCandidateGeneratorWrapper.java +++ b/server/src/main/java/org/elasticsearch/search/suggest/phrase/MultiCandidateGeneratorWrapper.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.util.Arrays; -import java.util.Comparator; //TODO public for tests public final class MultiCandidateGeneratorWrapper extends CandidateGenerator { diff --git a/server/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionContext.java b/server/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionContext.java index 22ac3c26a18a2..4fd37d01ca5ee 100644 --- a/server/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionContext.java +++ b/server/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionContext.java @@ -23,7 +23,6 @@ import org.apache.lucene.index.Terms; import org.apache.lucene.util.BytesRef; import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.script.ExecutableScript; import org.elasticsearch.script.TemplateScript; import org.elasticsearch.search.suggest.DirectSpellcheckerSettings; import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext; @@ -32,7 +31,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; class PhraseSuggestionContext extends SuggestionContext { static final boolean DEFAULT_COLLATE_PRUNE = false; diff --git a/server/src/main/java/org/elasticsearch/transport/CompressibleBytesOutputStream.java b/server/src/main/java/org/elasticsearch/transport/CompressibleBytesOutputStream.java index a90256806f11b..54f4d1d0c8d84 100644 --- a/server/src/main/java/org/elasticsearch/transport/CompressibleBytesOutputStream.java +++ b/server/src/main/java/org/elasticsearch/transport/CompressibleBytesOutputStream.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.io.Streams; import org.elasticsearch.common.io.stream.BytesStream; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.lease.Releasable; import java.io.IOException; import java.util.zip.DeflaterOutputStream; diff --git a/server/src/main/java/org/elasticsearch/transport/TcpTransportChannel.java b/server/src/main/java/org/elasticsearch/transport/TcpTransportChannel.java index 1bf1d027329b5..dbaacb7bd8719 100644 --- a/server/src/main/java/org/elasticsearch/transport/TcpTransportChannel.java +++ b/server/src/main/java/org/elasticsearch/transport/TcpTransportChannel.java @@ -22,7 +22,6 @@ import org.elasticsearch.Version; import java.io.IOException; -import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/server/src/test/java/org/elasticsearch/VersionTests.java b/server/src/test/java/org/elasticsearch/VersionTests.java index 68d455134bfc0..15affc83ce786 100644 --- a/server/src/test/java/org/elasticsearch/VersionTests.java +++ b/server/src/test/java/org/elasticsearch/VersionTests.java @@ -20,7 +20,6 @@ package org.elasticsearch; import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.common.Booleans; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.test.ESTestCase; @@ -36,7 +35,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import static org.elasticsearch.Version.V_5_3_0; import static org.elasticsearch.Version.V_6_0_0_beta1; diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java index 62313d01b95c3..4baf184e22b96 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TaskManagerTestCase.java @@ -56,7 +56,6 @@ import org.junit.BeforeClass; import java.io.IOException; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java index 4ab54cdd206be..072a9a095b358 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/node/tasks/TasksIT.java @@ -24,7 +24,6 @@ import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.FailedNodeException; import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse; diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryResponseTests.java index 30fbe61bb172a..a4dbc6a9d9b2d 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/repositories/put/PutRepositoryResponseTests.java @@ -20,14 +20,11 @@ import org.elasticsearch.action.admin.cluster.repositories.put.PutRepositoryResponse; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.AbstractStreamableXContentTestCase; -import org.elasticsearch.test.ESTestCase; import java.io.IOException; import static org.elasticsearch.test.ESTestCase.randomBoolean; -import static org.hamcrest.Matchers.equalTo; public class PutRepositoryResponseTests extends AbstractStreamableXContentTestCase { diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java index d582141898684..5fcd369a8a433 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/settings/SettingsUpdaterTests.java @@ -32,8 +32,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiFunction; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/alias/AliasActionsTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/alias/AliasActionsTests.java index fcd73a6f1dda3..202530ccf9289 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/alias/AliasActionsTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/alias/AliasActionsTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.action.admin.indices.alias; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java b/server/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java index 1ace701572c45..c0e1e6f03a012 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/flush/FlushBlocksIT.java @@ -29,7 +29,6 @@ import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_BLOCKS_WRITE; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_READ_ONLY_ALLOW_DELETE; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertBlocked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.hamcrest.Matchers.equalTo; diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponseTests.java index cf125257c36a8..8bd76e2f4f2a8 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/settings/get/GetSettingsResponseTests.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.util.Base64; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Set; diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java b/server/src/test/java/org/elasticsearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java index b682cd6e27d59..708b6111057e5 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/shards/IndicesShardStoreRequestIT.java @@ -55,7 +55,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; diff --git a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java index 61556fd9b28ed..b38240632421a 100644 --- a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesResponseTests.java @@ -19,18 +19,13 @@ package org.elasticsearch.action.fieldcaps; -import org.elasticsearch.action.admin.indices.close.CloseIndexResponse; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.io.stream.BytesStreamOutput; -import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.test.AbstractStreamableXContentTestCase; -import org.elasticsearch.test.ESTestCase; import java.io.IOException; import java.util.Collections; @@ -38,7 +33,6 @@ import java.util.Map; import java.util.function.Predicate; -import static org.elasticsearch.test.XContentTestUtils.insertRandomFields; public class FieldCapabilitiesResponseTests extends AbstractStreamableXContentTestCase { diff --git a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesTests.java b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesTests.java index 0237ace962a80..5dc27f8e6ec1f 100644 --- a/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesTests.java +++ b/server/src/test/java/org/elasticsearch/action/fieldcaps/FieldCapabilitiesTests.java @@ -22,7 +22,6 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.AbstractSerializingTestCase; -import org.elasticsearch.test.AbstractWireSerializingTestCase; import java.io.IOException; import java.util.Arrays; diff --git a/server/src/test/java/org/elasticsearch/action/get/MultiGetResponseTests.java b/server/src/test/java/org/elasticsearch/action/get/MultiGetResponseTests.java index 82638870eef58..1eae583316e15 100644 --- a/server/src/test/java/org/elasticsearch/action/get/MultiGetResponseTests.java +++ b/server/src/test/java/org/elasticsearch/action/get/MultiGetResponseTests.java @@ -28,7 +28,6 @@ import java.io.IOException; -import static org.elasticsearch.test.XContentTestUtils.insertRandomFields; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; diff --git a/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java b/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java index 8701817cec445..4e536cc14d61b 100644 --- a/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/search/MultiSearchRequestTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.action.search; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.CheckedRunnable; @@ -40,11 +39,9 @@ import org.elasticsearch.test.rest.FakeRestRequest; import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.function.BiConsumer; import static java.util.Collections.singletonList; import static org.elasticsearch.search.RandomSearchRequestGenerator.randomSearchRequest; diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java index 4e7844950d6b2..a34c755e05272 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/TransportReplicationActionTests.java @@ -62,7 +62,6 @@ import org.elasticsearch.index.IndexService; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.IndexShardClosedException; -import org.elasticsearch.index.shard.IndexShardState; import org.elasticsearch.index.shard.ReplicationGroup; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardNotFoundException; diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java b/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java index d32fbf1578714..b894188dabef5 100644 --- a/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java @@ -42,7 +42,6 @@ import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.shard.IndexShard; -import org.elasticsearch.index.shard.IndexShardState; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardNotFoundException; import org.elasticsearch.index.translog.Translog; diff --git a/server/src/test/java/org/elasticsearch/bootstrap/ElasticsearchCliTests.java b/server/src/test/java/org/elasticsearch/bootstrap/ElasticsearchCliTests.java index b3e9eda4e1303..fa65a7a12a230 100644 --- a/server/src/test/java/org/elasticsearch/bootstrap/ElasticsearchCliTests.java +++ b/server/src/test/java/org/elasticsearch/bootstrap/ElasticsearchCliTests.java @@ -27,13 +27,11 @@ import java.nio.file.Path; import java.util.Locale; -import java.util.Map; import java.util.function.Consumer; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.hasEntry; public class ElasticsearchCliTests extends ESElasticsearchCliTestCase { diff --git a/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java b/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java index 89be94b45066b..9eeef54dfd796 100644 --- a/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/action/shard/ShardFailedClusterStateTaskExecutorTests.java @@ -60,7 +60,6 @@ import java.util.stream.IntStream; import static org.elasticsearch.cluster.routing.ShardRoutingState.INITIALIZING; -import static org.elasticsearch.cluster.routing.ShardRoutingState.STARTED; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.Matchers.contains; diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetaDataTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetaDataTests.java index 5a206407648b6..7734a9d7b4e6a 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetaDataTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetaDataTests.java @@ -35,7 +35,6 @@ import java.util.Set; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; public class IndexMetaDataTests extends ESTestCase { diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java index 9ad4aeb69fb49..069dafa97546c 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/TemplateUpgradeServiceTests.java @@ -39,7 +39,6 @@ import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.test.ClusterServiceUtils; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -57,7 +56,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java index 659813f62d46f..da0920e69373b 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/AllocationCommandsTests.java @@ -22,7 +22,6 @@ import org.apache.logging.log4j.Logger; import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterInfo; -import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ESAllocationTestCase; import org.elasticsearch.cluster.metadata.IndexMetaData; diff --git a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/DiskThresholdMonitorTests.java b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/DiskThresholdMonitorTests.java index bc243b2db0bb4..b245b0d35d6c6 100644 --- a/server/src/test/java/org/elasticsearch/cluster/routing/allocation/DiskThresholdMonitorTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/routing/allocation/DiskThresholdMonitorTests.java @@ -33,7 +33,6 @@ import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.test.ESTestCase; import java.util.Arrays; import java.util.HashSet; diff --git a/server/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java index 3e7c415db7b96..f4c2090895b83 100644 --- a/server/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/service/ClusterApplierServiceTests.java @@ -24,7 +24,6 @@ import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateObserver; -import org.elasticsearch.cluster.ClusterStateTaskListener; import org.elasticsearch.cluster.LocalNodeMasterListener; import org.elasticsearch.cluster.NodeConnectionsService; import org.elasticsearch.cluster.block.ClusterBlocks; diff --git a/server/src/test/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java b/server/src/test/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java index 3795d845094d5..08a16b89eca05 100644 --- a/server/src/test/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java +++ b/server/src/test/java/org/elasticsearch/cluster/settings/SettingsFilteringIT.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.settings.SettingsModule; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; diff --git a/server/src/test/java/org/elasticsearch/common/BooleansTests.java b/server/src/test/java/org/elasticsearch/common/BooleansTests.java index 2e14159c07cde..33dc4857f5a38 100644 --- a/server/src/test/java/org/elasticsearch/common/BooleansTests.java +++ b/server/src/test/java/org/elasticsearch/common/BooleansTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.common; import org.elasticsearch.test.ESTestCase; -import org.hamcrest.Matchers; import java.util.Locale; diff --git a/server/src/test/java/org/elasticsearch/common/bytes/PagedBytesReferenceTests.java b/server/src/test/java/org/elasticsearch/common/bytes/PagedBytesReferenceTests.java index ea592b12e3b1f..0938b70906f52 100644 --- a/server/src/test/java/org/elasticsearch/common/bytes/PagedBytesReferenceTests.java +++ b/server/src/test/java/org/elasticsearch/common/bytes/PagedBytesReferenceTests.java @@ -20,20 +20,11 @@ package org.elasticsearch.common.bytes; import org.apache.lucene.util.BytesRef; -import org.apache.lucene.util.BytesRefBuilder; -import org.apache.lucene.util.BytesRefIterator; -import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.ReleasableBytesStreamOutput; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.ByteArray; -import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matchers; -import java.io.EOFException; import java.io.IOException; -import java.util.Arrays; public class PagedBytesReferenceTests extends AbstractBytesReferenceTestCase { diff --git a/server/src/test/java/org/elasticsearch/common/geo/GeoHashTests.java b/server/src/test/java/org/elasticsearch/common/geo/GeoHashTests.java index 1ab67b058f115..87f98389231e4 100644 --- a/server/src/test/java/org/elasticsearch/common/geo/GeoHashTests.java +++ b/server/src/test/java/org/elasticsearch/common/geo/GeoHashTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.common.geo; import org.apache.lucene.geo.Rectangle; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.test.ESTestCase; /** diff --git a/server/src/test/java/org/elasticsearch/common/unit/DistanceUnitTests.java b/server/src/test/java/org/elasticsearch/common/unit/DistanceUnitTests.java index 3d8ecdba424ed..eafb7c69b8d9d 100644 --- a/server/src/test/java/org/elasticsearch/common/unit/DistanceUnitTests.java +++ b/server/src/test/java/org/elasticsearch/common/unit/DistanceUnitTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.common.unit; -import com.carrotsearch.randomizedtesting.generators.RandomStrings; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; diff --git a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java index 87a9441cb25dd..79b6aa5f60436 100644 --- a/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java +++ b/server/src/test/java/org/elasticsearch/common/unit/FuzzinessTests.java @@ -18,7 +18,6 @@ */ package org.elasticsearch.common.unit; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/test/java/org/elasticsearch/common/util/CollectionUtilsTests.java b/server/src/test/java/org/elasticsearch/common/util/CollectionUtilsTests.java index 2ca8189a972fd..2fb56df12d144 100644 --- a/server/src/test/java/org/elasticsearch/common/util/CollectionUtilsTests.java +++ b/server/src/test/java/org/elasticsearch/common/util/CollectionUtilsTests.java @@ -25,7 +25,6 @@ import org.apache.lucene.util.Counter; import org.elasticsearch.test.ESTestCase; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/server/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java b/server/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java index b46485952d702..e06e378c8fbdf 100644 --- a/server/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java +++ b/server/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java @@ -25,7 +25,6 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.Constants; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.Strings; diff --git a/server/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java b/server/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java index a2121d3ca4e37..9273ab1514372 100644 --- a/server/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java +++ b/server/src/test/java/org/elasticsearch/discovery/zen/ZenDiscoveryUnitTests.java @@ -83,7 +83,6 @@ import static org.elasticsearch.cluster.service.MasterServiceTests.discoveryState; import static org.elasticsearch.discovery.zen.ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING; import static org.elasticsearch.discovery.zen.ZenDiscovery.shouldIgnoreOrRejectNewClusterState; -import static org.elasticsearch.test.ClusterServiceUtils.setState; import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyArray; diff --git a/server/src/test/java/org/elasticsearch/env/ESFileStoreTests.java b/server/src/test/java/org/elasticsearch/env/ESFileStoreTests.java index 7ce278708a0e8..d7d3a71a763ab 100644 --- a/server/src/test/java/org/elasticsearch/env/ESFileStoreTests.java +++ b/server/src/test/java/org/elasticsearch/env/ESFileStoreTests.java @@ -20,10 +20,7 @@ import org.elasticsearch.test.ESTestCase; -import java.io.IOException; import java.nio.file.FileStore; -import java.nio.file.attribute.FileAttributeView; -import java.nio.file.attribute.FileStoreAttributeView; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/server/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java b/server/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java index 209fbd37c5953..4a0d6a8e8884b 100644 --- a/server/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java +++ b/server/src/test/java/org/elasticsearch/gateway/GatewayIndexStateIT.java @@ -55,7 +55,6 @@ import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; diff --git a/server/src/test/java/org/elasticsearch/gateway/MetaDataStateFormatTests.java b/server/src/test/java/org/elasticsearch/gateway/MetaDataStateFormatTests.java index 543b5e39026fb..6a8cf5bf6ab43 100644 --- a/server/src/test/java/org/elasticsearch/gateway/MetaDataStateFormatTests.java +++ b/server/src/test/java/org/elasticsearch/gateway/MetaDataStateFormatTests.java @@ -41,7 +41,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.Index; import org.elasticsearch.test.ESTestCase; diff --git a/server/src/test/java/org/elasticsearch/gateway/MetaStateServiceTests.java b/server/src/test/java/org/elasticsearch/gateway/MetaStateServiceTests.java index ec7607085c090..d0bf02e3c4e1f 100644 --- a/server/src/test/java/org/elasticsearch/gateway/MetaStateServiceTests.java +++ b/server/src/test/java/org/elasticsearch/gateway/MetaStateServiceTests.java @@ -22,7 +22,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.Index; import org.elasticsearch.test.ESTestCase; diff --git a/server/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java b/server/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java index 4ef9c36d9306c..23d2f7bcafa96 100644 --- a/server/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java +++ b/server/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java @@ -26,10 +26,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.QueryRewriteContext; -import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.Scroll; diff --git a/server/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java b/server/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java index 0a1f625f20323..1dcb2d4e39fd6 100644 --- a/server/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java +++ b/server/src/test/java/org/elasticsearch/index/analysis/CustomNormalizerTests.java @@ -21,7 +21,6 @@ import org.apache.lucene.analysis.MockLowerCaseFilter; import org.apache.lucene.analysis.MockTokenizer; -import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; diff --git a/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/MultiOrdinalsTests.java b/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/MultiOrdinalsTests.java index 3656d59e788e4..f835d83ff79d9 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/MultiOrdinalsTests.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/MultiOrdinalsTests.java @@ -22,7 +22,6 @@ import org.apache.lucene.index.SortedDocValues; import org.apache.lucene.index.SortedSetDocValues; import org.apache.lucene.util.packed.PackedInts; -import org.elasticsearch.index.fielddata.FieldData; import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.test.ESTestCase; diff --git a/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/SingleOrdinalsTests.java b/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/SingleOrdinalsTests.java index b0fbda6a9407e..6fd6a17670635 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/SingleOrdinalsTests.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/ordinals/SingleOrdinalsTests.java @@ -27,7 +27,6 @@ import java.util.HashMap; import java.util.Map; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.not; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java index 467bc18a33a68..0824e1e0afcc7 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java @@ -25,7 +25,6 @@ import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.MetadataFieldMapper; -import org.elasticsearch.indices.IndicesModule; import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.plugins.Plugin; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java index 9f815a7fdea9e..2ae6345d44e6e 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.mapper; -import org.apache.lucene.document.Field; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.StringField; import org.apache.lucene.index.IndexableField; diff --git a/server/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java index 4ca37638a2226..6ff134931f4ae 100644 --- a/server/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/GeoPolygonQueryBuilderTests.java @@ -25,8 +25,6 @@ import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.builders.ShapeBuilder; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.AbstractQueryTestCase; import org.elasticsearch.test.geo.RandomShapeGenerator; diff --git a/server/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java index b51e2c22a90eb..1cedb0f058c22 100644 --- a/server/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java @@ -58,7 +58,6 @@ import java.util.Set; import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; diff --git a/server/src/test/java/org/elasticsearch/index/query/SpanGapQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/SpanGapQueryBuilderTests.java index 024d43b1a6bab..90784129a0479 100644 --- a/server/src/test/java/org/elasticsearch/index/query/SpanGapQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/SpanGapQueryBuilderTests.java @@ -24,7 +24,6 @@ import org.apache.lucene.search.spans.SpanNearQuery; import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.search.spans.SpanTermQuery; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.test.AbstractQueryTestCase; @@ -32,7 +31,6 @@ import java.util.Iterator; import static org.elasticsearch.index.query.SpanNearQueryBuilder.SpanGapQueryBuilder; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.either; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; diff --git a/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java b/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java index e2bf25ce1b531..9e5383a259adc 100644 --- a/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java +++ b/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskStatusTests.java @@ -37,7 +37,6 @@ import static java.util.stream.Collectors.toList; import static org.apache.lucene.util.TestUtil.randomSimpleString; import static org.elasticsearch.common.unit.TimeValue.parseTimeValue; -import static org.hamcrest.Matchers.hasSize; public class BulkByScrollTaskStatusTests extends ESTestCase { public void testBulkByTaskStatus() throws IOException { diff --git a/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java b/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java index 109f9cbd686c5..87a80cfaa98f5 100644 --- a/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java +++ b/server/src/test/java/org/elasticsearch/index/reindex/BulkByScrollTaskTests.java @@ -34,8 +34,6 @@ import static org.elasticsearch.common.unit.TimeValue.timeValueMillis; import static org.elasticsearch.common.unit.TimeValue.timeValueNanos; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.not; public class BulkByScrollTaskTests extends ESTestCase { public void testStatusHatesNegatives() { diff --git a/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryTests.java b/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryTests.java index 2f1e6e64ff39d..753d9eab3b490 100644 --- a/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryTests.java +++ b/server/src/test/java/org/elasticsearch/index/search/MultiMatchQueryTests.java @@ -22,7 +22,6 @@ import org.apache.lucene.analysis.MockSynonymAnalyzer; import org.apache.lucene.index.Term; import org.apache.lucene.queries.BlendedTermQuery; -import org.apache.lucene.queryparser.xml.builders.DisjunctionMaxQueryBuilder; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; @@ -38,7 +37,6 @@ import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.engine.Engine; diff --git a/server/src/test/java/org/elasticsearch/index/search/nested/NestedSortingTests.java b/server/src/test/java/org/elasticsearch/index/search/nested/NestedSortingTests.java index 823ccf91d30ea..57f2310e87757 100644 --- a/server/src/test/java/org/elasticsearch/index/search/nested/NestedSortingTests.java +++ b/server/src/test/java/org/elasticsearch/index/search/nested/NestedSortingTests.java @@ -44,7 +44,6 @@ import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.index.fielddata.AbstractFieldDataTestCase; import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource; import org.elasticsearch.index.fielddata.NoOrdinalsStringFieldDataTests; import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource; import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData; diff --git a/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java b/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java index 7ac02874494d4..9a889801c6c92 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java @@ -57,9 +57,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.security.AccessControlException; import java.util.Arrays; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.function.Predicate; import static org.hamcrest.CoreMatchers.equalTo; diff --git a/server/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java b/server/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java index 4c1c27905ec2c..8aaf6de1d6a00 100644 --- a/server/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java +++ b/server/src/test/java/org/elasticsearch/index/store/CorruptedFileIT.java @@ -68,7 +68,6 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.MockIndexEventListener; -import org.elasticsearch.test.junit.annotations.TestLogging; import org.elasticsearch.test.store.MockFSIndexStore; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.transport.TransportRequest; diff --git a/server/src/test/java/org/elasticsearch/indexing/IndexActionIT.java b/server/src/test/java/org/elasticsearch/indexing/IndexActionIT.java index 7abb603b8eb2d..ea2bf3f7199ef 100644 --- a/server/src/test/java/org/elasticsearch/indexing/IndexActionIT.java +++ b/server/src/test/java/org/elasticsearch/indexing/IndexActionIT.java @@ -40,7 +40,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicIntegerArray; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.lessThanOrEqualTo; diff --git a/server/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java b/server/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java index 1bd255349ca44..5278a6c01d779 100644 --- a/server/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java +++ b/server/src/test/java/org/elasticsearch/indexlifecycle/IndexLifecycleActionIT.java @@ -27,7 +27,6 @@ import org.elasticsearch.cluster.routing.RoutingNode; import org.elasticsearch.cluster.routing.RoutingNodes; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.discovery.Discovery; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; diff --git a/server/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java b/server/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java index 9d0c512f785d2..f814f4c227ae4 100644 --- a/server/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java +++ b/server/src/test/java/org/elasticsearch/indices/analyze/AnalyzeActionIT.java @@ -22,9 +22,6 @@ import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder; import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.analysis.CharFilterFactory; -import org.elasticsearch.indices.analysis.AnalysisModule; -import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.MockKeywordPlugin; diff --git a/server/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java b/server/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java index 3d9b2aab7ad16..4d3b15c5ec003 100644 --- a/server/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java +++ b/server/src/test/java/org/elasticsearch/indices/settings/GetSettingsBlocksIT.java @@ -22,7 +22,6 @@ import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.test.ESIntegTestCase; import java.util.Arrays; diff --git a/server/src/test/java/org/elasticsearch/indices/state/RareClusterStateIT.java b/server/src/test/java/org/elasticsearch/indices/state/RareClusterStateIT.java index 75120a36b3cfd..60e16da5e29b5 100644 --- a/server/src/test/java/org/elasticsearch/indices/state/RareClusterStateIT.java +++ b/server/src/test/java/org/elasticsearch/indices/state/RareClusterStateIT.java @@ -24,7 +24,6 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse; import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.cluster.ClusterInfo; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.block.ClusterBlocks; @@ -33,18 +32,14 @@ import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.cluster.routing.RoutingNodes; import org.elasticsearch.cluster.routing.RoutingTable; import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.allocation.AllocationService; -import org.elasticsearch.cluster.routing.allocation.RoutingAllocation; -import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.discovery.DiscoverySettings; -import org.elasticsearch.gateway.GatewayAllocator; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.mapper.DocumentMapper; @@ -55,13 +50,9 @@ import org.elasticsearch.test.disruption.BlockClusterStateProcessing; import org.elasticsearch.test.junit.annotations.TestLogging; -import java.io.IOException; import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import static java.util.Collections.emptyMap; diff --git a/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java b/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java index 6b57c9757366a..5c1c0d91e608b 100644 --- a/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java +++ b/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreIntegrationIT.java @@ -24,7 +24,6 @@ import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.ClusterStateTaskListener; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.IndexRoutingTable; diff --git a/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreTests.java b/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreTests.java index e7575e49e6b34..ed360b7c1c20d 100644 --- a/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreTests.java +++ b/server/src/test/java/org/elasticsearch/indices/store/IndicesStoreTests.java @@ -35,7 +35,6 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.emptySet; -import static org.elasticsearch.test.ClusterServiceUtils.createClusterService; public class IndicesStoreTests extends ESTestCase { private static final ShardRoutingState[] NOT_STARTED_STATES; diff --git a/server/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java b/server/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java index 04285b3432e12..b1cf390bd8d30 100644 --- a/server/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java +++ b/server/src/test/java/org/elasticsearch/ingest/IngestDocumentTests.java @@ -29,7 +29,6 @@ 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.Map; diff --git a/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java b/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java index a1d7d67854756..c0353acb7f9d0 100644 --- a/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java +++ b/server/src/test/java/org/elasticsearch/ingest/IngestServiceTests.java @@ -23,7 +23,6 @@ import java.util.Collections; import java.util.Map; -import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.IngestPlugin; import org.elasticsearch.test.ESTestCase; diff --git a/server/src/test/java/org/elasticsearch/persistent/PersistentTasksExecutorResponseTests.java b/server/src/test/java/org/elasticsearch/persistent/PersistentTasksExecutorResponseTests.java index ada0e24baa7bd..342098f6867f7 100644 --- a/server/src/test/java/org/elasticsearch/persistent/PersistentTasksExecutorResponseTests.java +++ b/server/src/test/java/org/elasticsearch/persistent/PersistentTasksExecutorResponseTests.java @@ -26,7 +26,6 @@ import java.util.Collections; -import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiOfLength; public class PersistentTasksExecutorResponseTests extends AbstractStreamableTestCase { diff --git a/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java b/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java index a4a68f2bfd0b0..5000f73445b0c 100644 --- a/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java +++ b/server/src/test/java/org/elasticsearch/persistent/PersistentTasksNodeServiceTests.java @@ -60,7 +60,6 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class PersistentTasksNodeServiceTests extends ESTestCase { diff --git a/server/src/test/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java b/server/src/test/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java index 564eca54d8e6c..b33cfb508b930 100644 --- a/server/src/test/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java +++ b/server/src/test/java/org/elasticsearch/recovery/TruncatedRecoveryIT.java @@ -25,7 +25,6 @@ import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.ByteSizeValue; @@ -36,9 +35,6 @@ import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.transport.MockTransportService; -import org.elasticsearch.transport.ConnectionProfile; -import org.elasticsearch.transport.Transport; -import org.elasticsearch.transport.TransportException; import org.elasticsearch.transport.TransportRequest; import org.elasticsearch.transport.TransportRequestOptions; import org.elasticsearch.transport.TransportService; diff --git a/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java b/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java index db8aa615c1440..d0cf5d374897d 100644 --- a/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java +++ b/server/src/test/java/org/elasticsearch/repositories/RepositoryDataTests.java @@ -42,10 +42,7 @@ import java.util.Map; import java.util.Set; -import static java.util.Collections.emptySet; -import static java.util.Collections.singleton; import static org.elasticsearch.repositories.RepositoryData.EMPTY_REPO_GEN; -import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; diff --git a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestResizeHandlerTests.java b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestResizeHandlerTests.java index 2c30184ee4e35..8522c22b02b23 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestResizeHandlerTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/admin/indices/RestResizeHandlerTests.java @@ -20,11 +20,9 @@ package org.elasticsearch.rest.action.admin.indices; import org.elasticsearch.client.node.NodeClient; -import org.elasticsearch.common.Booleans; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.rest.RestController; -import org.elasticsearch.rest.RestHandler; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; diff --git a/server/src/test/java/org/elasticsearch/script/ScriptMetaDataTests.java b/server/src/test/java/org/elasticsearch/script/ScriptMetaDataTests.java index bef20190acf87..6a17556a1035b 100644 --- a/server/src/test/java/org/elasticsearch/script/ScriptMetaDataTests.java +++ b/server/src/test/java/org/elasticsearch/script/ScriptMetaDataTests.java @@ -22,8 +22,6 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.Writeable; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/test/java/org/elasticsearch/script/ScriptServiceTests.java b/server/src/test/java/org/elasticsearch/script/ScriptServiceTests.java index 21b5fa89a30d6..b35fcbcc03c17 100644 --- a/server/src/test/java/org/elasticsearch/script/ScriptServiceTests.java +++ b/server/src/test/java/org/elasticsearch/script/ScriptServiceTests.java @@ -18,7 +18,6 @@ */ package org.elasticsearch.script; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.action.admin.cluster.storedscripts.GetStoredScriptRequest; import org.elasticsearch.cluster.ClusterName; diff --git a/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java b/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java index 78040f5bfb254..c01fd7acd7bd5 100644 --- a/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java +++ b/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java @@ -33,7 +33,6 @@ import org.elasticsearch.index.query.functionscore.GaussDecayFunctionBuilder; import org.elasticsearch.plugins.SearchPlugin; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.BaseAggregationBuilder; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java index bd6298f5d0338..6ab53d6a62f19 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/AggregationsIntegrationIT.java @@ -30,7 +30,6 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.terms; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java index dc0e3c5a8e45f..fc7a24cf7985b 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/GlobalAggregatorTests.java @@ -26,7 +26,6 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.store.Directory; -import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.common.CheckedConsumer; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.NumberFieldMapper; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java index 2c823acf6565e..81034a0355061 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/SamplerIT.java @@ -32,7 +32,6 @@ import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.test.ESIntegTestCase; -import java.util.Collection; import java.util.List; import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java index 204de33440d7b..8fa965ab46d18 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/TermsDocCountErrorIT.java @@ -32,7 +32,6 @@ import org.elasticsearch.test.ESIntegTestCase; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java index 633eccbf19b11..ca2750d5e4105 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java @@ -24,7 +24,6 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.BucketOrder; import org.elasticsearch.search.aggregations.InternalAggregation; -import org.elasticsearch.search.aggregations.InternalAggregation.ReduceContext; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.test.InternalMultiBucketAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregatorTests.java index 900db1399955d..2b217f4ff6e6b 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/SamplerAggregatorTests.java @@ -27,7 +27,6 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; -import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.TermQuery; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificanceHeuristicTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificanceHeuristicTests.java index 02909d673beb0..414954a2d905b 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificanceHeuristicTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificanceHeuristicTests.java @@ -20,7 +20,6 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.Version; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregatorTests.java index 19b83b11a0f81..ec15e2fc3e853 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTextAggregatorTests.java @@ -22,32 +22,23 @@ import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; -import org.apache.lucene.document.SortedNumericDocValuesField; import org.apache.lucene.document.StoredField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; -import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.elasticsearch.index.analysis.AnalyzerScope; import org.elasticsearch.index.analysis.NamedAnalyzer; -import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.NumberFieldMapper; -import org.elasticsearch.index.mapper.SourceFieldMapper; import org.elasticsearch.index.mapper.TextFieldMapper.TextFieldType; -import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.search.aggregations.AggregatorTestCase; import org.elasticsearch.search.aggregations.bucket.sampler.Sampler; import org.elasticsearch.search.aggregations.bucket.sampler.SamplerAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.significant.SignificantTerms; -import org.elasticsearch.search.aggregations.bucket.significant.SignificantTerms.Bucket; import org.elasticsearch.search.aggregations.bucket.significant.SignificantTextAggregationBuilder; import java.io.IOException; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactoryTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactoryTests.java index fe32bff86b877..83a05738163c9 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactoryTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactoryTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.search.aggregations.bucket.terms; import org.elasticsearch.search.aggregations.Aggregator; -import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.ESTestCase; import static org.hamcrest.Matchers.equalTo; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/PercentilesTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/PercentilesTests.java index 7410ce0c3e372..ea0c9f3969669 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/PercentilesTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/PercentilesTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.search.aggregations.metrics; import org.elasticsearch.ExceptionsHelper; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.xcontent.XContentParseException; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java index 9db5b237a858c..816c0464d95d9 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/ScriptedMetricIT.java @@ -28,7 +28,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.script.MockScriptEngine; import org.elasticsearch.script.MockScriptPlugin; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentileRanksAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentileRanksAggregatorTests.java index 505480b2161d0..3513beee6687c 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentileRanksAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/HDRPercentileRanksAggregatorTests.java @@ -40,7 +40,6 @@ import java.io.IOException; import java.util.Iterator; -import static org.hamcrest.core.IsEqual.equalTo; public class HDRPercentileRanksAggregatorTests extends AggregatorTestCase { diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentileRanksAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentileRanksAggregatorTests.java index 9bf7fdf0486c2..6545fe9d3ffe1 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentileRanksAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/tdigest/TDigestPercentileRanksAggregatorTests.java @@ -40,7 +40,6 @@ import java.io.IOException; import java.util.Iterator; -import static org.hamcrest.core.IsEqual.equalTo; public class TDigestPercentileRanksAggregatorTests extends AggregatorTestCase { diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovFnUnitTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovFnUnitTests.java index 4f9e653a20df6..88bbe3671b232 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovFnUnitTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/pipeline/movfn/MovFnUnitTests.java @@ -45,7 +45,6 @@ import java.io.IOException; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; diff --git a/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java b/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java index 3ecb34861eb06..6f248b0142c97 100644 --- a/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java @@ -19,9 +19,6 @@ package org.elasticsearch.search.query; -import org.apache.lucene.analysis.CharacterUtils; -import org.apache.lucene.analysis.MockLowerCaseFilter; -import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; @@ -33,17 +30,12 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.index.analysis.CharFilterFactory; -import org.elasticsearch.index.analysis.MultiTermAwareComponent; -import org.elasticsearch.index.analysis.PreConfiguredCharFilter; import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; -import org.elasticsearch.index.analysis.TokenizerFactory; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.SimpleQueryStringFlag; -import org.elasticsearch.indices.analysis.AnalysisModule; import org.elasticsearch.plugins.AnalysisPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.SearchHit; @@ -53,19 +45,15 @@ import org.elasticsearch.test.InternalSettingsPlugin; import java.io.IOException; -import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.function.Function; import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.simpleQueryStringQuery; diff --git a/server/src/test/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderTests.java b/server/src/test/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderTests.java index 9a3f520f96f22..b70a87ea9860f 100644 --- a/server/src/test/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/search/sort/GeoDistanceSortBuilderTests.java @@ -47,7 +47,6 @@ import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.geo.RandomGeoGenerator; import java.io.IOException; diff --git a/server/src/test/java/org/elasticsearch/search/suggest/SuggestBuilderTests.java b/server/src/test/java/org/elasticsearch/search/suggest/SuggestBuilderTests.java index 7c63b59ffc61a..16de2a3506740 100644 --- a/server/src/test/java/org/elasticsearch/search/suggest/SuggestBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/search/suggest/SuggestBuilderTests.java @@ -40,7 +40,6 @@ import java.util.Map.Entry; import static java.util.Collections.emptyList; -import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode; public class SuggestBuilderTests extends ESTestCase { diff --git a/server/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java b/server/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java index b9da305e132f5..6ebced51e1ea1 100644 --- a/server/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java +++ b/server/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java @@ -27,7 +27,6 @@ import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.suggest.document.ContextSuggestField; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; diff --git a/server/src/test/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionTests.java b/server/src/test/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionTests.java index 2a5a89bdde332..bfa9bcb809339 100644 --- a/server/src/test/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionTests.java +++ b/server/src/test/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionTests.java @@ -24,7 +24,6 @@ import org.elasticsearch.test.ESTestCase; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/server/src/test/java/org/elasticsearch/search/suggest/term/SortByTests.java b/server/src/test/java/org/elasticsearch/search/suggest/term/SortByTests.java index 63fd31b42dac6..1de513b44c50f 100644 --- a/server/src/test/java/org/elasticsearch/search/suggest/term/SortByTests.java +++ b/server/src/test/java/org/elasticsearch/search/suggest/term/SortByTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.search.suggest.term; import org.elasticsearch.common.io.stream.AbstractWriteableEnumTestCase; -import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.search.suggest.SortBy; import java.io.IOException; diff --git a/server/src/test/java/org/elasticsearch/transport/ConnectionProfileTests.java b/server/src/test/java/org/elasticsearch/transport/ConnectionProfileTests.java index c4a7ca5bee190..318ee78ece594 100644 --- a/server/src/test/java/org/elasticsearch/transport/ConnectionProfileTests.java +++ b/server/src/test/java/org/elasticsearch/transport/ConnectionProfileTests.java @@ -23,7 +23,6 @@ import org.hamcrest.Matchers; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; 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 0ddf7af653391..ce7401fe25cae 100644 --- a/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java +++ b/test/fixtures/hdfs-fixture/src/main/java/hdfs/MiniHDFS.java @@ -42,7 +42,6 @@ import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.MiniDFSNNTopology; -import org.apache.hadoop.hdfs.client.HdfsClientConfigKeys; import org.apache.hadoop.hdfs.server.namenode.ha.HATestUtil; import org.apache.hadoop.security.UserGroupInformation; diff --git a/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java b/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java index c98ca47febd22..44c968cf507ed 100644 --- a/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java +++ b/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java @@ -24,9 +24,7 @@ import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Deque; import java.util.List; /** diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java index 6b9ce05c1e4a9..67eba5281d9b4 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -61,7 +61,6 @@ import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCache; import org.elasticsearch.mock.orig.Mockito; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.search.fetch.FetchPhase; import org.elasticsearch.search.fetch.subphase.DocValueFieldsFetchSubPhase; diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java index de5e238199693..4a6e17005499f 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/BaseAggregationTestCase.java @@ -22,31 +22,16 @@ import org.elasticsearch.common.Strings; 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.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; 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.indices.IndicesModule; -import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.plugins.PluginsService; -import org.elasticsearch.plugins.SearchPlugin; -import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder; import org.elasticsearch.test.AbstractBuilderTestCase; -import org.elasticsearch.test.AbstractQueryTestCase; -import org.elasticsearch.test.ESTestCase; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode; import static org.hamcrest.Matchers.hasSize; diff --git a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java index 8f5fe5d5622e7..838b0e315ea0e 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/InternalAggregationTestCase.java @@ -41,7 +41,6 @@ import org.elasticsearch.search.aggregations.InternalAggregation; import org.elasticsearch.search.aggregations.MultiBucketConsumerService.MultiBucketConsumer; import org.elasticsearch.search.aggregations.ParsedAggregation; -import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; import org.elasticsearch.search.aggregations.bucket.adjacency.AdjacencyMatrixAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.adjacency.ParsedAdjacencyMatrix; import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; diff --git a/test/framework/src/main/java/org/elasticsearch/test/MockLogAppender.java b/test/framework/src/main/java/org/elasticsearch/test/MockLogAppender.java index 6e5f919f33fdf..a53ba046d32c3 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/MockLogAppender.java +++ b/test/framework/src/main/java/org/elasticsearch/test/MockLogAppender.java @@ -30,7 +30,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertTrue; /** * Test appender that can be used to verify that certain events were logged correctly diff --git a/test/framework/src/main/java/org/elasticsearch/test/TestCustomMetaData.java b/test/framework/src/main/java/org/elasticsearch/test/TestCustomMetaData.java index fb709a7fd06d9..1a758075c7e7e 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/TestCustomMetaData.java +++ b/test/framework/src/main/java/org/elasticsearch/test/TestCustomMetaData.java @@ -20,12 +20,9 @@ package org.elasticsearch.test; import org.elasticsearch.ElasticsearchParseException; -import org.elasticsearch.cluster.AbstractDiffable; import org.elasticsearch.cluster.AbstractNamedDiffable; -import org.elasticsearch.cluster.Diffable; import org.elasticsearch.cluster.NamedDiff; import org.elasticsearch.cluster.metadata.MetaData; -import org.elasticsearch.common.io.stream.NamedWriteable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -33,7 +30,6 @@ import java.io.IOException; import java.util.function.Function; -import java.util.function.Supplier; public abstract class TestCustomMetaData extends AbstractNamedDiffable implements MetaData.Custom { private final String data; diff --git a/test/framework/src/main/java/org/elasticsearch/test/engine/MockInternalEngine.java b/test/framework/src/main/java/org/elasticsearch/test/engine/MockInternalEngine.java index 6fdfce83b6504..35aedb67accdc 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/engine/MockInternalEngine.java +++ b/test/framework/src/main/java/org/elasticsearch/test/engine/MockInternalEngine.java @@ -19,7 +19,6 @@ package org.elasticsearch.test.engine; import org.apache.lucene.index.FilterDirectoryReader; -import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.EngineConfig; import org.elasticsearch.index.engine.EngineException; import org.elasticsearch.index.engine.InternalEngine; diff --git a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java index 723184410f247..3ef6d38e67e14 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java +++ b/test/framework/src/main/java/org/elasticsearch/test/hamcrest/ElasticsearchAssertions.java @@ -24,7 +24,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionFuture; -import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequestBuilder; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -48,11 +47,8 @@ import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.RestStatus; diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java index 90a1d2c7f1df2..df92b101bf1fd 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java @@ -32,7 +32,6 @@ import org.apache.http.ssl.SSLContexts; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksAction; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; import org.elasticsearch.client.RestClient; diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSuite.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSuite.java index b9988128b02a4..a8cc96461292a 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSuite.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/section/ClientYamlTestSuite.java @@ -19,7 +19,6 @@ package org.elasticsearch.test.rest.yaml.section; import org.elasticsearch.common.ParsingException; -import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.XContentParser; diff --git a/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransportPlugin.java b/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransportPlugin.java index c85fee985feee..886dea5c72c56 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransportPlugin.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/MockTcpTransportPlugin.java @@ -19,7 +19,6 @@ package org.elasticsearch.transport; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; -import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.BigArrays; @@ -30,7 +29,6 @@ import org.elasticsearch.threadpool.ThreadPool; import java.util.Collections; -import java.util.List; import java.util.Map; import java.util.function.Supplier; diff --git a/test/framework/src/test/java/org/elasticsearch/node/MockNodeTests.java b/test/framework/src/test/java/org/elasticsearch/node/MockNodeTests.java index 5eb437d14d608..bd437e7cd3972 100644 --- a/test/framework/src/test/java/org/elasticsearch/node/MockNodeTests.java +++ b/test/framework/src/test/java/org/elasticsearch/node/MockNodeTests.java @@ -25,7 +25,6 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.env.Environment; -import org.elasticsearch.http.HttpTransportSettings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.MockSearchService; import org.elasticsearch.search.SearchService; diff --git a/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java b/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java index e1807f44174ea..240120a7a8530 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/VersionUtilsTests.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import static java.util.stream.Collectors.toList; diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java index 5a8ea91d3627b..c9c2e5079338f 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java @@ -7,7 +7,6 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; -import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.LoggingAwareCommand; import org.elasticsearch.cli.Terminal; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ExpirationCallback.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ExpirationCallback.java index ba74ddf300306..01437bd265ba3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ExpirationCallback.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/ExpirationCallback.java @@ -170,4 +170,4 @@ public final String toString() { orientation.name(), TimeValue.timeValueMillis(min), TimeValue.timeValueMillis(max), TimeValue.timeValueMillis(frequency)); } -} \ No newline at end of file +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClient.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClient.java index 21381b376925d..07979d15ea5ef 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClient.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/LicensingClient.java @@ -7,7 +7,6 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.ElasticsearchClient; -import org.elasticsearch.rest.action.RestBuilderListener; public class LicensingClient { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/OperationModeFileWatcher.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/OperationModeFileWatcher.java index 6ad3a28c8cea9..b8e6446b9f49f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/OperationModeFileWatcher.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/OperationModeFileWatcher.java @@ -10,14 +10,12 @@ import org.apache.logging.log4j.message.ParameterizedMessage; import org.apache.logging.log4j.util.Supplier; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.license.License.OperationMode; import org.elasticsearch.watcher.FileChangesListener; import org.elasticsearch.watcher.FileWatcher; import org.elasticsearch.watcher.ResourceWatcherService; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java index 2bf0555fde111..98fb6115710e3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/StartTrialClusterTask.java @@ -17,11 +17,8 @@ import java.time.Clock; import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; public class StartTrialClusterTask extends ClusterStateUpdateTask { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/OpenJobAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/OpenJobAction.java index a8f3d08061dd1..32087097b2ed0 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/OpenJobAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/OpenJobAction.java @@ -21,7 +21,6 @@ import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.persistent.PersistentTaskParams; import org.elasticsearch.tasks.Task; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.ml.MachineLearningField; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSizeStats.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSizeStats.java index 2da8226093052..58c0a567ada0d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSizeStats.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSizeStats.java @@ -10,7 +10,6 @@ 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.ObjectParser; import org.elasticsearch.common.xcontent.ObjectParser.ValueType; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/RollupJobCaps.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/RollupJobCaps.java index f37a351c0136d..c8874ae459d2d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/RollupJobCaps.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/RollupJobCaps.java @@ -9,13 +9,8 @@ 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.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; -import org.elasticsearch.xpack.core.rollup.RollupField; import org.elasticsearch.xpack.core.rollup.job.RollupJobConfig; import java.io.IOException; @@ -25,7 +20,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.function.BiConsumer; /** * Represents the Rollup capabilities for a specific job on a single rollup index diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StartRollupJobAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StartRollupJobAction.java index 042dbd18d42d4..f18ca67586f9f 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StartRollupJobAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StartRollupJobAction.java @@ -18,7 +18,6 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.tasks.Task; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.core.rollup.RollupField; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StopRollupJobAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StopRollupJobAction.java index cfb7da0bd9323..d407e9f77d0ad 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StopRollupJobAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/action/StopRollupJobAction.java @@ -17,7 +17,6 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.tasks.Task; import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper; import org.elasticsearch.xpack.core.rollup.RollupField; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java index 52f565ee6ae39..f26c67935edff 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java @@ -27,7 +27,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/scheduler/SchedulerEngine.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/scheduler/SchedulerEngine.java index 11107ef86f486..e405c5e4e007e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/scheduler/SchedulerEngine.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/scheduler/SchedulerEngine.java @@ -17,7 +17,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; public class SchedulerEngine { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/saml/SamlAuthenticateResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/saml/SamlAuthenticateResponse.java index 84e029adac7f5..b6d7797f851b4 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/saml/SamlAuthenticateResponse.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/saml/SamlAuthenticateResponse.java @@ -8,7 +8,6 @@ import java.io.IOException; import org.elasticsearch.action.ActionResponse; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.unit.TimeValue; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/RoleMapperExpression.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/RoleMapperExpression.java index 89b80d9296eb3..67654e205881b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/RoleMapperExpression.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/support/mapper/expressiondsl/RoleMapperExpression.java @@ -18,7 +18,7 @@ public interface RoleMapperExpression extends ToXContentObject, NamedWriteable { /** * Determines whether this expression matches against the provided object. - * @param model + * @param model the {@link ExpressionModel} */ boolean match(ExpressionModel model); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java index 2e03cbb24a320..65bde9bd1dfe5 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java @@ -19,7 +19,6 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ToXContentObject; -import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java index ab09b41af880e..3cc147c0a0bdf 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ssl/PEMKeyConfig.java @@ -15,10 +15,7 @@ import javax.net.ssl.X509ExtendedTrustManager; import java.io.IOException; -import java.io.Reader; import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java index 1661c5c27ee40..059718973201c 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java @@ -13,7 +13,6 @@ import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/integration/MlRestTestStateCleaner.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/integration/MlRestTestStateCleaner.java index 426b9d7edd866..23f357eb1885e 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/integration/MlRestTestStateCleaner.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/integration/MlRestTestStateCleaner.java @@ -13,7 +13,6 @@ import org.elasticsearch.test.rest.ESRestTestCase; import java.io.IOException; -import java.util.Collections; import java.util.List; import java.util.Map; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimitsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimitsTests.java index 1c2cc40c776e9..bc7c48fabfa9c 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimitsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/AnalysisLimitsTests.java @@ -7,7 +7,6 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.xcontent.DeprecationHandler; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/DataDescriptionTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/DataDescriptionTests.java index 48f36823e3119..4670420a9dd04 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/DataDescriptionTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/DataDescriptionTests.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.core.ml.job.config; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.xcontent.DeprecationHandler; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/MlFilterTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/MlFilterTests.java index 08efc1c883fca..1b61e3ec9a43d 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/MlFilterTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/config/MlFilterTests.java @@ -9,7 +9,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.AbstractSerializingTestCase; -import org.elasticsearch.xpack.core.ml.job.results.CategoryDefinition; import java.io.IOException; import java.util.ArrayList; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSnapshotTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSnapshotTests.java index e119bdc05acb3..9908458e4a418 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSnapshotTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/process/autodetect/state/ModelSnapshotTests.java @@ -7,12 +7,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.DeprecationHandler; -import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.common.xcontent.XContent; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.test.AbstractSerializingTestCase; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/results/AnomalyRecordTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/results/AnomalyRecordTests.java index b66fa4b2889d5..ef285b87cf17a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/results/AnomalyRecordTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/results/AnomalyRecordTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.core.ml.job.results; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.XContentHelper; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/RollupRestTestStateCleaner.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/RollupRestTestStateCleaner.java index 5c8bdd30ea523..a9a8223863d72 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/RollupRestTestStateCleaner.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/RollupRestTestStateCleaner.java @@ -7,7 +7,6 @@ import org.apache.http.HttpStatus; import org.apache.logging.log4j.Logger; -import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksAction; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; import org.elasticsearch.common.xcontent.support.XContentMapValues; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/TermsGroupConfigSerializingTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/TermsGroupConfigSerializingTests.java index 140b0d9c04b95..f48f678fac85a 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/TermsGroupConfigSerializingTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/TermsGroupConfigSerializingTests.java @@ -10,10 +10,8 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; -import org.elasticsearch.search.aggregations.bucket.composite.TermsValuesSourceBuilder; import org.elasticsearch.test.AbstractSerializingTestCase; import org.elasticsearch.xpack.core.rollup.ConfigTestHelpers; -import org.elasticsearch.xpack.core.rollup.RollupField; import java.io.IOException; import java.util.Arrays; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/CertParsingUtilsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/CertParsingUtilsTests.java index 91891198c1757..49ce0fdb80e70 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/CertParsingUtilsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/CertParsingUtilsTests.java @@ -9,8 +9,6 @@ import org.elasticsearch.test.ESTestCase; import java.io.InputStream; -import java.io.Reader; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.security.Key; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java index 727a2b52999f2..b583dbb33f9ea 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ssl/SSLServiceTests.java @@ -32,23 +32,17 @@ import org.mockito.ArgumentCaptor; import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509ExtendedTrustManager; -import java.net.Socket; import java.nio.file.Path; import java.security.AccessController; -import java.security.KeyStore; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; -import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; diff --git a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/LogstashFeatureSet.java b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/LogstashFeatureSet.java index 6c5c88377757f..001ab713350b0 100644 --- a/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/LogstashFeatureSet.java +++ b/x-pack/plugin/logstash/src/main/java/org/elasticsearch/xpack/logstash/LogstashFeatureSet.java @@ -8,10 +8,7 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.xpack.core.XPackFeatureSet; import org.elasticsearch.xpack.core.XPackField; @@ -19,7 +16,6 @@ import org.elasticsearch.xpack.core.logstash.LogstashFeatureSetUsage; -import java.io.IOException; import java.util.Map; public class LogstashFeatureSet implements XPackFeatureSet { diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java index 0514424213a01..c3c1a7c44b46d 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportUpdateJobAction.java @@ -12,7 +12,6 @@ import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.block.ClusterBlockLevel; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/persistence/JobProvider.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/persistence/JobProvider.java index 8014bacf1e0f9..9db1877df1850 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/persistence/JobProvider.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/persistence/JobProvider.java @@ -70,7 +70,6 @@ import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.xpack.core.ml.MlMetaIndex; -import org.elasticsearch.xpack.core.ml.MlMetadata; import org.elasticsearch.xpack.core.ml.action.GetBucketsAction; import org.elasticsearch.xpack.core.ml.action.GetCategoriesAction; import org.elasticsearch.xpack.core.ml.action.GetInfluencersAction; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlMetadataTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlMetadataTests.java index e1ad110223da0..8049b5655d63b 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlMetadataTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/MlMetadataTests.java @@ -30,7 +30,6 @@ import org.elasticsearch.xpack.core.ml.job.config.JobTaskStatus; import org.elasticsearch.xpack.core.ml.job.config.JobTests; import org.elasticsearch.persistent.PersistentTasksCustomMetaData; -import org.elasticsearch.xpack.ml.datafeed.DatafeedManagerTests; import java.util.Collections; import java.util.Date; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java index 9c1850c167b59..40b59e4dbec65 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java @@ -33,7 +33,6 @@ import org.elasticsearch.xpack.ml.LocalStateMachineLearning; import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.core.ml.MachineLearningField; -import org.elasticsearch.xpack.core.ml.MlMetadata; import org.elasticsearch.xpack.core.ml.action.CloseJobAction; import org.elasticsearch.xpack.core.ml.action.DeleteDatafeedAction; import org.elasticsearch.xpack.core.ml.action.DeleteJobAction; @@ -56,7 +55,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; diff --git a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpHostBuilder.java b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpHostBuilder.java index 49825458a7780..69c5a80e9e0b9 100644 --- a/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpHostBuilder.java +++ b/x-pack/plugin/monitoring/src/main/java/org/elasticsearch/xpack/monitoring/exporter/http/HttpHostBuilder.java @@ -224,4 +224,4 @@ public HttpHost build() { return new HttpHost(host, port == -1 ? 9200 : port, scheme.toString()); } -} \ No newline at end of file +} diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java index f0ee3788f2e6d..057c6d8f3a225 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/MultiNodesStatsTests.java @@ -18,7 +18,6 @@ import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; import org.junit.After; -import java.util.concurrent.TimeUnit; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout; import static org.hamcrest.Matchers.equalTo; diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java index d4869f7c52660..c53bdb4db866d 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/cleaner/AbstractIndicesCleanerTestCase.java @@ -10,7 +10,6 @@ import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.xpack.core.monitoring.MonitoringField; import org.elasticsearch.xpack.core.monitoring.exporter.MonitoringTemplateUtils; -import org.elasticsearch.xpack.monitoring.MonitoringService; import org.elasticsearch.xpack.monitoring.exporter.Exporter; import org.elasticsearch.xpack.monitoring.exporter.Exporters; import org.elasticsearch.xpack.monitoring.test.MonitoringIntegTestCase; diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java index 44db39d857361..ef96726aaee01 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/indices/IndexStatsCollectorTests.java @@ -23,7 +23,6 @@ import org.elasticsearch.xpack.monitoring.BaseCollectorTestCase; import java.util.Collection; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/ExportersTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/ExportersTests.java index 8f12f017149f3..5df8df74f7b06 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/ExportersTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/ExportersTests.java @@ -24,13 +24,11 @@ import org.elasticsearch.xpack.core.monitoring.exporter.MonitoringDoc; import org.elasticsearch.xpack.monitoring.MonitoringService; import org.elasticsearch.xpack.monitoring.cleaner.CleanerService; -import org.elasticsearch.xpack.monitoring.exporter.http.HttpExporter; import org.elasticsearch.xpack.monitoring.exporter.local.LocalExporter; import org.junit.Before; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -43,7 +41,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static java.util.Collections.singleton; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/AbstractPublishableHttpResourceTestCase.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/AbstractPublishableHttpResourceTestCase.java index 37b59ddcdc162..208f878543f56 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/AbstractPublishableHttpResourceTestCase.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/exporter/http/AbstractPublishableHttpResourceTestCase.java @@ -11,7 +11,6 @@ import org.apache.http.StatusLine; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.elasticsearch.Version; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; import org.elasticsearch.client.RestClient; diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/RollupResponseTranslator.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/RollupResponseTranslator.java index fecb53c9cf3a9..ba1002896c041 100644 --- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/RollupResponseTranslator.java +++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/RollupResponseTranslator.java @@ -10,7 +10,6 @@ import org.elasticsearch.action.search.MultiSearchResponse; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.TriFunction; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.index.IndexNotFoundException; @@ -40,14 +39,11 @@ import java.nio.charset.StandardCharsets; 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.stream.Collectors; diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportStopRollupAction.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportStopRollupAction.java index 2ae764c3c3a01..5659282e93f0a 100644 --- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportStopRollupAction.java +++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/action/TransportStopRollupAction.java @@ -16,12 +16,9 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.license.LicenseUtils; -import org.elasticsearch.license.XPackLicenseState; import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.rollup.action.StopRollupJobAction; import org.elasticsearch.xpack.rollup.job.RollupJobTask; diff --git a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/job/IndexerUtils.java b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/job/IndexerUtils.java index 092c76c97317b..e180e34c4cc26 100644 --- a/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/job/IndexerUtils.java +++ b/x-pack/plugin/rollup/src/main/java/org/elasticsearch/xpack/rollup/job/IndexerUtils.java @@ -24,10 +24,8 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; import java.util.zip.CRC32; diff --git a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/SearchActionTests.java b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/SearchActionTests.java index 218a1360ae88f..7ede27a1c2b62 100644 --- a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/SearchActionTests.java +++ b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/action/SearchActionTests.java @@ -25,7 +25,6 @@ import org.elasticsearch.index.query.DisMaxQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.MatchPhraseQueryBuilder; -import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.RangeQueryBuilder; import org.elasticsearch.index.query.TermQueryBuilder; diff --git a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/IndexerUtilsTests.java b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/IndexerUtilsTests.java index c51aac3c98ff7..53421faa9bc38 100644 --- a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/IndexerUtilsTests.java +++ b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/IndexerUtilsTests.java @@ -39,7 +39,6 @@ import org.elasticsearch.xpack.core.rollup.job.MetricConfig; import org.elasticsearch.xpack.core.rollup.job.RollupJobStats; import org.elasticsearch.xpack.core.rollup.ConfigTestHelpers; -import org.elasticsearch.xpack.rollup.Rollup; import org.joda.time.DateTime; import org.mockito.stubbing.Answer; diff --git a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/RollupJobTaskTests.java b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/RollupJobTaskTests.java index d39bcb5f6b5ae..d12be5e6fc196 100644 --- a/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/RollupJobTaskTests.java +++ b/x-pack/plugin/rollup/src/test/java/org/elasticsearch/xpack/rollup/job/RollupJobTaskTests.java @@ -10,7 +10,6 @@ import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.client.Client; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.persistent.PersistentTasksCustomMetaData; import org.elasticsearch.search.aggregations.Aggregations; @@ -34,7 +33,6 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -42,7 +40,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java index dcc2308f9f0c1..073b2f8268c56 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PkiRealmBootstrapCheck.java @@ -17,7 +17,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; import static org.elasticsearch.xpack.core.XPackSettings.HTTP_SSL_ENABLED; import static org.elasticsearch.xpack.core.security.SecurityField.setting; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRequestHandler.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRequestHandler.java index 2cb33b2c770e3..0f48c996a773f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRequestHandler.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRequestHandler.java @@ -56,7 +56,6 @@ import java.util.Base64; import java.util.Collection; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java index bc2a0d415b3ee..facce36e461c9 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java @@ -16,7 +16,6 @@ import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.env.Environment; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.XPackPlugin; import javax.crypto.KeyGenerator; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlAuthenticateAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlAuthenticateAction.java index 8f241b0b14f83..7a0e9a25c5af6 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlAuthenticateAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlAuthenticateAction.java @@ -23,7 +23,6 @@ import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateRequestBuilder; import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateResponse; import org.elasticsearch.xpack.core.security.client.SecurityClient; -import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler; import java.io.IOException; import java.util.Base64; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlInvalidateSessionAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlInvalidateSessionAction.java index ac287a13d1625..6d3b41775b140 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlInvalidateSessionAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlInvalidateSessionAction.java @@ -23,7 +23,6 @@ import org.elasticsearch.xpack.core.security.action.saml.SamlInvalidateSessionAction; import org.elasticsearch.xpack.core.security.action.saml.SamlInvalidateSessionRequest; import org.elasticsearch.xpack.core.security.action.saml.SamlInvalidateSessionResponse; -import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler; import static org.elasticsearch.rest.RestRequest.Method.POST; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlPrepareAuthenticationAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlPrepareAuthenticationAction.java index e5b6cdc494283..0860f3648721c 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlPrepareAuthenticationAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/saml/RestSamlPrepareAuthenticationAction.java @@ -23,7 +23,6 @@ import org.elasticsearch.xpack.core.security.action.saml.SamlPrepareAuthenticationAction; import org.elasticsearch.xpack.core.security.action.saml.SamlPrepareAuthenticationRequest; import org.elasticsearch.xpack.core.security.action.saml.SamlPrepareAuthenticationResponse; -import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler; import static org.elasticsearch.rest.RestRequest.Method.POST; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/SecurityIpFilterRule.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/SecurityIpFilterRule.java index 226dad8c343ed..74e2fa72d86e8 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/SecurityIpFilterRule.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/transport/filter/SecurityIpFilterRule.java @@ -13,7 +13,6 @@ import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.common.transport.TransportAddress; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -22,9 +21,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.StringJoiner; import java.util.StringTokenizer; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java index 2f1123a9461de..1690ab652c067 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySettingsSource.java @@ -21,7 +21,6 @@ import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.XPackSettings; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.security.LocalStateSecurity; import org.elasticsearch.xpack.core.security.SecurityField; import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/TemplateUpgraderTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/TemplateUpgraderTests.java index bfa7e8ce1e821..7c254723868c1 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/TemplateUpgraderTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/TemplateUpgraderTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.security; -import org.apache.lucene.util.LuceneTestCase.AwaitsFix; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse; import org.elasticsearch.client.Client; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportGetRoleMappingsActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportGetRoleMappingsActionTests.java index 219ce1d1f79dd..be3c86d6a6a52 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportGetRoleMappingsActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/rolemapping/TransportGetRoleMappingsActionTests.java @@ -29,7 +29,6 @@ import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.notNullValue; import static org.mockito.Matchers.any; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java index 153621075ac7b..4f1d14d267c9c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java @@ -6,14 +6,12 @@ package org.elasticsearch.xpack.security.audit.index; import org.apache.lucene.util.SetOnce; -import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; -import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.Client; @@ -77,7 +75,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutionException; import java.util.function.Function; import static java.util.Collections.emptyMap; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java index 633360318c217..56131c87001d1 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmTests.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.security.authc.esnative; import org.elasticsearch.cluster.health.ClusterHealthStatus; -import org.elasticsearch.cluster.health.ClusterIndexHealth; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.env.TestEnvironment; @@ -17,7 +16,6 @@ import java.util.concurrent.atomic.AtomicInteger; -import static org.elasticsearch.xpack.security.test.SecurityTestUtils.getClusterIndexHealth; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStoreTests.java index b2560da88e805..f987f4df5724f 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStoreTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.audit.logfile.CapturingLogger; import org.elasticsearch.xpack.core.security.authc.RealmConfig; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java index 6ab4dbf3e0cfc..bcd7996e32a8c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRealmTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.MockSecureSettings; import org.elasticsearch.common.settings.SecureString; -import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.env.TestEnvironment; 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 ea1b9117922ce..2e79a45363b2d 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 @@ -9,7 +9,6 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.settings.MockSecureSettings; -import org.elasticsearch.common.settings.SecureSettings; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactoryTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactoryTests.java index b96da799e48b5..f719546cade87 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactoryTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/LdapSessionFactoryTests.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.security.authc.ldap; import com.unboundid.ldap.listener.InMemoryDirectoryServer; -import com.unboundid.ldap.sdk.BindRequest; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.LDAPURL; import com.unboundid.ldap.sdk.SimpleBindRequest; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java index 7bec1671c6996..da017c460661b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java @@ -7,7 +7,6 @@ import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.sdk.Attribute; -import com.unboundid.ldap.sdk.BindRequest; import com.unboundid.ldap.sdk.LDAPConnection; import com.unboundid.ldap.sdk.LDAPConnectionPool; import com.unboundid.ldap.sdk.LDAPException; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java index cacdbc5fa1016..825ce4ee44c60 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/IndicesPermissionTests.java @@ -7,7 +7,6 @@ import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.Version; -import org.elasticsearch.action.get.GetAction; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index ff9d93b3ba818..b1728fd5f04be 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -9,7 +9,6 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.cluster.health.ClusterHealthStatus; -import org.elasticsearch.cluster.health.ClusterIndexHealth; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.bytes.BytesReference; @@ -46,7 +45,6 @@ import static org.elasticsearch.mock.orig.Mockito.times; import static org.elasticsearch.mock.orig.Mockito.verifyNoMoreInteractions; -import static org.elasticsearch.xpack.security.test.SecurityTestUtils.getClusterIndexHealth; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Matchers.any; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java index 9ad9927dd9154..14be1e260db36 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/FileRolesStoreTests.java @@ -17,7 +17,6 @@ import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.audit.logfile.CapturingLogger; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java index 0964bc5a45df7..9aa2fdd3d1b06 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ServerTransportFilterIntegrationTests.java @@ -28,7 +28,6 @@ import org.elasticsearch.transport.TransportResponse; import org.elasticsearch.transport.TransportResponseHandler; import org.elasticsearch.transport.TransportService; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.SecurityField; import org.elasticsearch.xpack.core.security.authc.file.FileRealmSettings; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java index e12b2186b4d36..42c5cd7c7ab45 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/EllipticCurveSSLTests.java @@ -23,8 +23,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509ExtendedKeyManager; -import java.io.Reader; -import java.nio.file.Files; import java.nio.file.Path; import java.security.AccessController; import java.security.PrivateKey; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java index 261f9939ed8b6..bb0ba8e9e4513 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java @@ -29,7 +29,6 @@ import org.elasticsearch.xpack.core.TestXPackTransportClient; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.common.socket.SocketAccess; -import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings; import org.elasticsearch.xpack.core.ssl.SSLService; import org.elasticsearch.xpack.security.LocalStateSecurity; diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java index b0af977c4ecfb..5ace03ff8a33e 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbc/JdbcPreparedStatement.java @@ -28,7 +28,6 @@ import java.sql.Timestamp; import java.sql.Types; import java.util.Calendar; -import java.util.Collections; class JdbcPreparedStatement extends JdbcStatement implements PreparedStatement { final PreparedQuery query; diff --git a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbcx/JdbcDataSource.java b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbcx/JdbcDataSource.java index ded8e8893feef..595cd893ebc76 100644 --- a/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbcx/JdbcDataSource.java +++ b/x-pack/plugin/sql/jdbc/src/main/java/org/elasticsearch/xpack/sql/jdbc/jdbcx/JdbcDataSource.java @@ -17,12 +17,10 @@ import java.sql.SQLFeatureNotSupportedException; import java.sql.Wrapper; import java.util.Properties; -import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import javax.sql.DataSource; -import static org.elasticsearch.xpack.sql.client.shared.ConnectionConfiguration.CONNECT_TIMEOUT; public class JdbcDataSource implements DataSource, Wrapper { diff --git a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliSessionTests.java b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliSessionTests.java index 8d6984b1daeac..8b0138865ce20 100644 --- a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliSessionTests.java +++ b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/CliSessionTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.sql.cli; -import org.elasticsearch.Build; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.common.UUIDs; import org.elasticsearch.test.ESTestCase; diff --git a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/JLineTerminalTests.java b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/JLineTerminalTests.java index d301fc69db980..e33104b6d8e7f 100644 --- a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/JLineTerminalTests.java +++ b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/JLineTerminalTests.java @@ -15,8 +15,6 @@ import java.io.IOException; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; diff --git a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerInfoCliCommandTests.java b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerInfoCliCommandTests.java index 9a70cbab39120..f45574574345e 100644 --- a/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerInfoCliCommandTests.java +++ b/x-pack/plugin/sql/sql-cli/src/test/java/org/elasticsearch/xpack/sql/cli/command/ServerInfoCliCommandTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.sql.cli.command; -import org.elasticsearch.Build; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.common.UUIDs; import org.elasticsearch.test.ESTestCase; diff --git a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java index 8d34d59c1e0af..dcb210ca68d9b 100644 --- a/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java +++ b/x-pack/plugin/sql/sql-proto/src/main/java/org/elasticsearch/xpack/sql/plugin/AbstractSqlQueryRequest.java @@ -10,11 +10,9 @@ import org.elasticsearch.common.ParseField; 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.ObjectParser; import org.elasticsearch.common.xcontent.ToXContentFragment; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.query.AbstractQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.xpack.sql.proto.Mode; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java index f5a0b72a2bd38..f45368afc06f0 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java @@ -50,7 +50,6 @@ import org.elasticsearch.xpack.sql.type.DataTypeConversion; import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.type.UnsupportedEsField; -import org.joda.time.DateTimeZone; import java.util.ArrayList; import java.util.Arrays; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java index c8f38113bcf9a..5143a7eceb415 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Attribute.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.sql.expression; import org.elasticsearch.xpack.sql.tree.Location; -import org.elasticsearch.xpack.sql.type.DataType; import java.util.Objects; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java index 32c2ee253f1ab..6cffd46fbc86d 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Exists.java @@ -9,7 +9,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; public class Exists extends SubQueryExpression { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Foldables.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Foldables.java index 1f8d658c380fd..6e06a1d1c8581 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Foldables.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/Foldables.java @@ -8,7 +8,6 @@ import org.elasticsearch.xpack.sql.SqlIllegalArgumentException; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.type.DataTypeConversion; -import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.ArrayList; import java.util.List; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistry.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistry.java index 25915c1a0a858..22141497a5c25 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistry.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/FunctionRegistry.java @@ -61,7 +61,6 @@ import org.elasticsearch.xpack.sql.parser.ParsingException; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.util.StringUtils; -import org.joda.time.DateTimeZone; import java.util.Arrays; import java.util.Collection; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java index f913cf0ea5610..c4a4097102f4b 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/Score.java @@ -11,7 +11,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import static java.util.Collections.emptyList; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java index 654369d0b961b..c3f6ed8b26ae4 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/ScoreAttribute.java @@ -11,7 +11,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; /** * {@link Attribute} that represents Elasticsearch's {@code _score}. diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java index 397ff1e7c9e68..3653f84be29b6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/Count.java @@ -12,7 +12,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; /** * Count the number of documents matched ({@code COUNT}) diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java index 57fdc01e935d8..a71dcfbbb9e67 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/aggregate/NumericAggregate.java @@ -9,7 +9,6 @@ import org.elasticsearch.xpack.sql.expression.Expressions; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.List; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java index 84ad136e95bcb..0e2870acd0944 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/ScalarFunctionAttribute.java @@ -8,7 +8,6 @@ import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.expression.ExpressionId; -import org.elasticsearch.xpack.sql.expression.NamedExpression; import org.elasticsearch.xpack.sql.expression.function.FunctionAttribute; import org.elasticsearch.xpack.sql.expression.function.scalar.processor.definition.ProcessorDefinition; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java index 3b3717b1318df..77a6db3009d0c 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java @@ -20,7 +20,6 @@ import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import java.time.Instant; import java.time.ZoneId; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java index eaa5a862ab0c0..5a2bc681ab882 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/HourOfDay.java @@ -9,7 +9,6 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeProcessor.DateTimeExtractor; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo.NodeCtor2; -import org.joda.time.DateTimeZone; import java.time.temporal.ChronoField; import java.util.TimeZone; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java index b5f9e7f3f9cfd..323ccc4e072c6 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java @@ -8,7 +8,6 @@ import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.StringUtils; import java.util.List; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java index 4d4d7082eea68..90bffebecd067 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/Equals.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.sql.expression.predicate; -import org.elasticsearch.xpack.sql.expression.BinaryOperator; import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java index ca3a08f95136e..5793f46c465b8 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/In.java @@ -12,7 +12,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.CollectionUtils; public class In extends Expression { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java index c0f5edb8095d7..0ec11351320d9 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/fulltext/FullTextPredicate.java @@ -8,7 +8,6 @@ import org.elasticsearch.xpack.sql.expression.Expression; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.List; import java.util.Map; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java index ef2635cec3761..3716b92591139 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/Like.java @@ -10,7 +10,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.regex.Pattern; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java index b9c10435660a9..45f2fc0bd54ad 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/LikePattern.java @@ -9,7 +9,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import org.elasticsearch.xpack.sql.util.StringUtils; import java.util.Objects; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java index 7d3879ef84fad..8d48c4d532e9b 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/regex/RLike.java @@ -11,7 +11,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.regex.Pattern; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java index e294b831a88e6..efcce00ab6647 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/ParsingException.java @@ -5,8 +5,6 @@ */ package org.elasticsearch.xpack.sql.parser; -import org.antlr.v4.runtime.RecognitionException; -import org.elasticsearch.common.logging.LoggerMessageFormat; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.xpack.sql.ClientSqlException; import org.elasticsearch.xpack.sql.tree.Location; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java index 1367b98a89982..c54c5e3810c8f 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseLexer.java @@ -7,12 +7,9 @@ package org.elasticsearch.xpack.sql.parser; import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.TokenStream; import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA; -import org.antlr.v4.runtime.misc.*; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) class SqlBaseLexer extends Lexer { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java index e9d7ff2639954..3535977943ba7 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/parser/SqlBaseParser.java @@ -8,11 +8,8 @@ import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.misc.*; import org.antlr.v4.runtime.tree.*; import java.util.List; -import java.util.Iterator; -import java.util.ArrayList; @SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"}) class SqlBaseParser extends Parser { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java index 6a513eed10790..74083a408b86c 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/logical/Join.java @@ -13,7 +13,6 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import static java.util.stream.Collectors.toList; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java index 8b2f224260a09..db790e0c44964 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/OrderExec.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Objects; -import org.elasticsearch.xpack.sql.expression.Attribute; import org.elasticsearch.xpack.sql.expression.Order; import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.NodeInfo; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/Unexecutable.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/Unexecutable.java index ad3c566919178..e1b08b3e492fc 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/Unexecutable.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plan/physical/Unexecutable.java @@ -11,7 +11,6 @@ import org.elasticsearch.xpack.sql.session.SchemaRowSet; import org.elasticsearch.xpack.sql.session.SqlSession; -import java.util.Locale; // this is mainly a marker interface to validate a plan before being executed public interface Unexecutable extends Executable { diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/PlanningException.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/PlanningException.java index 66ad7013cf2e7..bbfda467d93f0 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/PlanningException.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/PlanningException.java @@ -9,7 +9,6 @@ import org.elasticsearch.xpack.sql.ClientSqlException; import org.elasticsearch.xpack.sql.planner.Verifier.Failure; import org.elasticsearch.xpack.sql.tree.Location; -import org.elasticsearch.xpack.sql.util.StringUtils; import java.util.Collection; import java.util.stream.Collectors; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java index 9ef79642abc57..2a31d69743190 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/planner/QueryFolder.java @@ -60,7 +60,6 @@ import org.elasticsearch.xpack.sql.session.EmptyExecutable; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.Check; -import org.joda.time.DateTimeZone; import java.util.Arrays; import java.util.LinkedHashMap; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AndAggFilter.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AndAggFilter.java index 920be9e8198f8..424b957db7acc 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AndAggFilter.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/AndAggFilter.java @@ -9,7 +9,6 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Locale; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/OrAggFilter.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/OrAggFilter.java index e06253cc75bec..42500b9c60680 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/OrAggFilter.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/querydsl/agg/OrAggFilter.java @@ -9,7 +9,6 @@ import org.elasticsearch.xpack.sql.expression.function.scalar.script.ParamsBuilder; import org.elasticsearch.xpack.sql.expression.function.scalar.script.ScriptTemplate; import org.elasticsearch.xpack.sql.type.DataType; -import org.elasticsearch.xpack.sql.type.DataTypes; import java.util.Locale; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Configuration.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Configuration.java index ae43d4a988922..eafb71d12a8f2 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Configuration.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/session/Configuration.java @@ -8,7 +8,6 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.xpack.sql.plugin.AbstractSqlQueryRequest; import org.elasticsearch.xpack.sql.proto.Protocol; import java.util.TimeZone; diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java index e0d3d2b35e9b2..63fc16c6eeaa3 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/Types.java @@ -10,7 +10,6 @@ import java.util.Collections; import java.util.LinkedHashMap; -import java.util.Locale; import java.util.Map; import java.util.Map.Entry; diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/BoolQueryTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/BoolQueryTests.java index 901709479d3d8..76c582f236ba9 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/BoolQueryTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/querydsl/query/BoolQueryTests.java @@ -10,16 +10,11 @@ import org.elasticsearch.xpack.sql.tree.Location; import org.elasticsearch.xpack.sql.tree.LocationTests; -import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.function.Function; -import java.util.function.Supplier; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode; -import static org.hamcrest.Matchers.hasEntry; import static java.util.Collections.singletonMap; public class BoolQueryTests extends ESTestCase { diff --git a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/tree/LocationTests.java b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/tree/LocationTests.java index 45c429d04028b..b54cb503c8d10 100644 --- a/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/tree/LocationTests.java +++ b/x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/tree/LocationTests.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.sql.tree; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.EqualsHashCodeTestUtils.MutateFunction; import java.util.Arrays; import java.util.List; diff --git a/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/IndexUpgradeIntegTestCase.java b/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/IndexUpgradeIntegTestCase.java index 4f99960f23f8e..d7b709c4c3f6b 100644 --- a/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/IndexUpgradeIntegTestCase.java +++ b/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/IndexUpgradeIntegTestCase.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.upgrade; import org.elasticsearch.analysis.common.CommonAnalysisPlugin; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.reindex.ReindexPlugin; import org.elasticsearch.license.AbstractLicensesIntegrationTestCase; @@ -19,7 +18,6 @@ import java.util.Arrays; import java.util.Collection; -import java.util.Collections; public abstract class IndexUpgradeIntegTestCase extends AbstractLicensesIntegrationTestCase { @Override diff --git a/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/InternalIndexReindexerIT.java b/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/InternalIndexReindexerIT.java index be5251ad577d2..cd83803d1884c 100644 --- a/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/InternalIndexReindexerIT.java +++ b/x-pack/plugin/upgrade/src/test/java/org/elasticsearch/xpack/upgrade/InternalIndexReindexerIT.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.upgrade; import com.carrotsearch.hppc.cursors.ObjectCursor; -import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.ResourceAlreadyExistsException; import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java index 4c3316c7d7273..5f65ce3b629e7 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/EncryptSensitiveDataBootstrapCheck.java @@ -8,7 +8,6 @@ import org.elasticsearch.bootstrap.BootstrapCheck; import org.elasticsearch.bootstrap.BootstrapContext; import org.elasticsearch.env.Environment; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.watcher.WatcherField; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java index 1549a94ac350a..5d5a30344ab12 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/WatcherFeatureSet.java @@ -9,7 +9,6 @@ import org.elasticsearch.client.Client; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.license.XPackLicenseState; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpProxy.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpProxy.java index 135e74cd86924..cca302261518d 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpProxy.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/common/http/HttpProxy.java @@ -8,18 +8,11 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.ParseField; 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.Streamable; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.UnknownHostException; import java.util.Objects; public class HttpProxy implements ToXContentFragment { diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/Email.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/Email.java index 88800f8709aa6..afa8795e1cb5a 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/Email.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/notification/email/Email.java @@ -8,7 +8,6 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestExecuteWatchAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestExecuteWatchAction.java index f3b8b8fd6c412..8c5748beef921 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestExecuteWatchAction.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestExecuteWatchAction.java @@ -7,11 +7,9 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.ParseField; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.BytesRestResponse; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestGetWatchAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestGetWatchAction.java index 10ffa3fec4af4..0199d184d098e 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestGetWatchAction.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestGetWatchAction.java @@ -19,7 +19,6 @@ import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchResponse; import org.elasticsearch.xpack.watcher.rest.WatcherRestHandler; -import java.io.IOException; import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestStatus.NOT_FOUND; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java index 474057031e7da..8c92aa8077d6a 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/stats/TransportWatcherStatsAction.java @@ -20,7 +20,6 @@ import org.elasticsearch.xpack.core.watcher.transport.actions.stats.WatcherStatsRequest; import org.elasticsearch.xpack.core.watcher.transport.actions.stats.WatcherStatsResponse; import org.elasticsearch.xpack.watcher.WatcherLifeCycleService; -import org.elasticsearch.xpack.watcher.WatcherService; import org.elasticsearch.xpack.watcher.execution.ExecutionService; import org.elasticsearch.xpack.watcher.trigger.TriggerService; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatActionFactoryTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatActionFactoryTests.java index 363a0c498c176..7fc577e87c4c9 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatActionFactoryTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/hipchat/HipChatActionFactoryTests.java @@ -15,7 +15,6 @@ import org.elasticsearch.xpack.watcher.notification.hipchat.HipChatService; import org.junit.Before; -import java.util.Collections; import java.util.HashSet; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/pagerduty/PagerDutyActionFactoryTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/pagerduty/PagerDutyActionFactoryTests.java index ca2b8b365ad47..e48dab8b36d01 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/pagerduty/PagerDutyActionFactoryTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/pagerduty/PagerDutyActionFactoryTests.java @@ -15,7 +15,6 @@ import org.elasticsearch.xpack.watcher.notification.pagerduty.PagerDutyService; import org.junit.Before; -import java.util.Collections; import java.util.HashSet; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionFactoryTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionFactoryTests.java index b8edb2e41a9e8..3b3e7fa981b64 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionFactoryTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/actions/slack/SlackActionFactoryTests.java @@ -15,7 +15,6 @@ import org.elasticsearch.xpack.watcher.notification.slack.SlackService; import org.junit.Before; -import java.util.Collections; import java.util.HashSet; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpRequestTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpRequestTests.java index 2350fb4e416e8..223735cad54e4 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpRequestTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/common/http/HttpRequestTests.java @@ -8,7 +8,6 @@ import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.ESTestCase; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountTests.java index 4eae885c4af1a..8e83d30ffa5b6 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.watcher.notification.email; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountsTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountsTests.java index cfe359925e6bf..7060dcab0ebe8 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountsTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/AccountsTests.java @@ -10,7 +10,6 @@ import org.elasticsearch.common.settings.SettingsException; import org.elasticsearch.test.ESTestCase; -import java.util.Collections; import java.util.HashSet; import static org.hamcrest.Matchers.equalTo; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailSecretsIntegrationTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailSecretsIntegrationTests.java index 1b48529ad057e..81cb04a95f5a9 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailSecretsIntegrationTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailSecretsIntegrationTests.java @@ -32,7 +32,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.xpack.watcher.client.WatchSourceBuilders.watchBuilder; import static org.elasticsearch.xpack.watcher.input.InputBuilders.simpleInput; import static org.elasticsearch.xpack.watcher.trigger.TriggerBuilders.schedule; @@ -40,7 +39,6 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; public class EmailSecretsIntegrationTests extends AbstractWatcherIntegrationTestCase { diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailServiceTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailServiceTests.java index 42ae7f8d9ce77..d2d4e585afbb7 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailServiceTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/EmailServiceTests.java @@ -11,12 +11,9 @@ import org.elasticsearch.xpack.core.watcher.common.secret.Secret; import org.junit.Before; -import java.util.Collections; import java.util.HashSet; import java.util.Properties; -import static org.apache.logging.log4j.ThreadContext.containsKey; -import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.is; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/HtmlSanitizerTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/HtmlSanitizerTests.java index 60e3475437b9e..3ffd8dbf548e6 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/HtmlSanitizerTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/HtmlSanitizerTests.java @@ -10,8 +10,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.watcher.Watcher; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/ProfileTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/ProfileTests.java index 19e7e9de40ca4..8ab3e38550d3f 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/ProfileTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/ProfileTests.java @@ -15,7 +15,6 @@ import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; -import java.util.Collections; import java.util.HashSet; import static org.hamcrest.Matchers.instanceOf; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParserTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParserTests.java index 5f08217a33522..e8678f5c40e91 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParserTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/email/attachment/ReportingAttachmentParserTests.java @@ -7,7 +7,6 @@ import com.fasterxml.jackson.core.io.JsonEOFException; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.settings.Settings; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatAccountsTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatAccountsTests.java index ace63da8192b1..b793bf9202382 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatAccountsTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatAccountsTests.java @@ -17,7 +17,6 @@ import org.junit.Before; import org.mockito.ArgumentCaptor; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatServiceTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatServiceTests.java index bfcf2f408b087..7d3960a93449f 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatServiceTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/hipchat/HipChatServiceTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.watcher.notification.hipchat; -import com.carrotsearch.randomizedtesting.annotations.Repeat; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsException; @@ -13,7 +12,6 @@ import org.elasticsearch.xpack.watcher.common.http.HttpClient; import org.junit.Before; -import java.util.Collections; import java.util.HashSet; import static org.hamcrest.Matchers.arrayContaining; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/slack/message/SlackMessageTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/slack/message/SlackMessageTests.java index 4075bd3dadde4..0432fa41d864c 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/slack/message/SlackMessageTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/notification/slack/message/SlackMessageTests.java @@ -10,10 +10,8 @@ import org.elasticsearch.common.xcontent.DeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.watcher.support.xcontent.WatcherParams; import org.elasticsearch.xpack.watcher.common.http.HttpRequest; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/ScheduleTriggerEngineMock.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/ScheduleTriggerEngineMock.java index f6c06117970dc..331416bd69086 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/ScheduleTriggerEngineMock.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/trigger/ScheduleTriggerEngineMock.java @@ -10,7 +10,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.xpack.core.watcher.common.stats.Counters; import org.elasticsearch.xpack.core.watcher.watch.ClockMock; import org.elasticsearch.xpack.core.watcher.watch.Watch; import org.elasticsearch.xpack.watcher.trigger.schedule.ScheduleRegistry; diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java index 23bd5c7f7ddf1..e5aaf5f4fdb10 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DeleteExpiredDataIT.java @@ -37,7 +37,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.equalTo; diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java index db854a6cc647b..b9074f86bf356 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ModelPlotsIT.java @@ -29,7 +29,6 @@ import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/OverallBucketsIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/OverallBucketsIT.java index 785c3c6f67742..fe344bf835991 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/OverallBucketsIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/OverallBucketsIT.java @@ -15,7 +15,6 @@ import org.elasticsearch.xpack.core.ml.job.config.Detector; import org.elasticsearch.xpack.core.ml.job.config.Job; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.ModelSizeStats; -import org.elasticsearch.xpack.core.ml.job.results.OverallBucket; import org.junit.After; import java.util.ArrayList; diff --git a/x-pack/qa/reindex-tests-with-security/src/test/java/org/elasticsearch/xpack/security/ReindexWithSecurityIT.java b/x-pack/qa/reindex-tests-with-security/src/test/java/org/elasticsearch/xpack/security/ReindexWithSecurityIT.java index d21900ba47a5b..bba447b19dce2 100644 --- a/x-pack/qa/reindex-tests-with-security/src/test/java/org/elasticsearch/xpack/security/ReindexWithSecurityIT.java +++ b/x-pack/qa/reindex-tests-with-security/src/test/java/org/elasticsearch/xpack/security/ReindexWithSecurityIT.java @@ -5,8 +5,6 @@ */ package org.elasticsearch.xpack.security; -import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; -import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.index.reindex.BulkByScrollResponse; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; @@ -18,10 +16,7 @@ import org.elasticsearch.test.SecurityIntegTestCase; import org.elasticsearch.xpack.core.security.SecurityField; -import java.util.Collection; -import java.util.stream.Collectors; -import static org.hamcrest.core.IsCollectionContaining.hasItem; public class ReindexWithSecurityIT extends SecurityIntegTestCase { diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateToolTests.java b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateToolTests.java index 834a54bb5f4c4..e11b62642eb57 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateToolTests.java +++ b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateGenerateToolTests.java @@ -57,11 +57,9 @@ import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAKey; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateToolTests.java b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateToolTests.java index 3cc6c73b3af8c..795dd074a80d7 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateToolTests.java +++ b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/core/ssl/CertificateToolTests.java @@ -70,11 +70,9 @@ import java.security.Principal; import java.security.PrivateKey; import java.security.cert.Certificate; -import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAKey; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java index d31a0bbcefbc6..72e69a873c26d 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java +++ b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java @@ -20,7 +20,6 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; import org.elasticsearch.test.SecuritySettingsSourceField; -import org.elasticsearch.xpack.core.XPackField; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; diff --git a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java index c7e63919c23ad..f72655d0c4df8 100644 --- a/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java +++ b/x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; -import org.elasticsearch.xpack.core.XPackField; import org.junit.After; import java.nio.file.FileSystem; diff --git a/x-pack/qa/sql/multinode/src/test/java/org/elasticsearch/xpack/qa/sql/multinode/RestSqlMultinodeIT.java b/x-pack/qa/sql/multinode/src/test/java/org/elasticsearch/xpack/qa/sql/multinode/RestSqlMultinodeIT.java index 32dd60cfa2dce..75eaefed77925 100644 --- a/x-pack/qa/sql/multinode/src/test/java/org/elasticsearch/xpack/qa/sql/multinode/RestSqlMultinodeIT.java +++ b/x-pack/qa/sql/multinode/src/test/java/org/elasticsearch/xpack/qa/sql/multinode/RestSqlMultinodeIT.java @@ -6,8 +6,6 @@ package org.elasticsearch.xpack.qa.sql.multinode; import org.apache.http.HttpHost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestClient; @@ -25,11 +23,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; -import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; import static org.elasticsearch.xpack.qa.sql.rest.RestSqlTestCase.columnInfo; import static org.elasticsearch.xpack.qa.sql.rest.RestSqlTestCase.randomMode; diff --git a/x-pack/qa/sql/security/src/test/java/org/elasticsearch/xpack/qa/sql/security/SqlSecurityTestCase.java b/x-pack/qa/sql/security/src/test/java/org/elasticsearch/xpack/qa/sql/security/SqlSecurityTestCase.java index 481e7a4f60f19..83047c93da327 100644 --- a/x-pack/qa/sql/security/src/test/java/org/elasticsearch/xpack/qa/sql/security/SqlSecurityTestCase.java +++ b/x-pack/qa/sql/security/src/test/java/org/elasticsearch/xpack/qa/sql/security/SqlSecurityTestCase.java @@ -5,8 +5,6 @@ */ package org.elasticsearch.xpack.qa.sql.security; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.apache.lucene.util.SuppressForbidden; import org.elasticsearch.SpecialPermission; import org.elasticsearch.action.admin.indices.get.GetIndexAction; diff --git a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/CliIntegrationTestCase.java b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/CliIntegrationTestCase.java index 6adf37ff325e6..5f992fa06e9f1 100644 --- a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/CliIntegrationTestCase.java +++ b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/CliIntegrationTestCase.java @@ -5,8 +5,6 @@ */ package org.elasticsearch.xpack.qa.sql.cli; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import org.elasticsearch.common.CheckedConsumer; import org.elasticsearch.common.Strings; diff --git a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/ErrorsTestCase.java b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/ErrorsTestCase.java index f93ae339a820d..18895fbabbc7f 100644 --- a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/ErrorsTestCase.java +++ b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/ErrorsTestCase.java @@ -6,8 +6,6 @@ package org.elasticsearch.xpack.qa.sql.cli; import java.io.IOException; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import static org.hamcrest.Matchers.startsWith; diff --git a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/FetchSizeTestCase.java b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/FetchSizeTestCase.java index 542e71ea1841e..19162c34e3ccd 100644 --- a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/FetchSizeTestCase.java +++ b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/cli/FetchSizeTestCase.java @@ -5,8 +5,6 @@ */ package org.elasticsearch.xpack.qa.sql.cli; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import java.io.IOException; diff --git a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/DataLoader.java b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/DataLoader.java index f3fdd8e267ac3..655f02d97b8ad 100644 --- a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/DataLoader.java +++ b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/DataLoader.java @@ -6,8 +6,6 @@ package org.elasticsearch.xpack.qa.sql.jdbc; import org.apache.http.HttpHost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import org.elasticsearch.client.RestClient; import org.elasticsearch.common.CheckedBiConsumer; @@ -29,8 +27,6 @@ import java.util.List; import java.util.Map; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; public class DataLoader { diff --git a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/ErrorsTestCase.java b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/ErrorsTestCase.java index ea6c5f165ee6f..8574dd3ece16e 100644 --- a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/ErrorsTestCase.java +++ b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/ErrorsTestCase.java @@ -7,8 +7,6 @@ import java.sql.Connection; import java.sql.SQLException; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import static org.hamcrest.Matchers.startsWith; diff --git a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/FetchSizeTestCase.java b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/FetchSizeTestCase.java index 4d2487a0c03ff..784fd358a4548 100644 --- a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/FetchSizeTestCase.java +++ b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/FetchSizeTestCase.java @@ -5,8 +5,6 @@ */ package org.elasticsearch.xpack.qa.sql.jdbc; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.elasticsearch.client.Request; import org.junit.Before; diff --git a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/JdbcIntegrationTestCase.java b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/JdbcIntegrationTestCase.java index fc0cd67efac14..a2b524c20b070 100644 --- a/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/JdbcIntegrationTestCase.java +++ b/x-pack/qa/sql/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc/JdbcIntegrationTestCase.java @@ -5,9 +5,6 @@ */ package org.elasticsearch.xpack.qa.sql.jdbc; -import org.apache.http.HttpEntity; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; import org.apache.http.util.EntityUtils; import org.elasticsearch.client.Request; import org.elasticsearch.common.CheckedConsumer; @@ -34,7 +31,6 @@ import java.util.Set; import java.util.TimeZone; -import static java.util.Collections.singletonMap; import static org.elasticsearch.xpack.qa.sql.rest.RestSqlTestCase.assertNoSearchContexts; public abstract class JdbcIntegrationTestCase extends ESRestTestCase { diff --git a/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java b/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java index 2f1aa0f5eb573..a11647844e79b 100644 --- a/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java +++ b/x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectorySessionFactoryTests.java @@ -31,7 +31,6 @@ import java.util.List; import java.util.concurrent.ExecutionException; -import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasItem; diff --git a/x-pack/test/feature-aware/src/main/java/org/elasticsearch/xpack/test/feature_aware/FeatureAwareCheck.java b/x-pack/test/feature-aware/src/main/java/org/elasticsearch/xpack/test/feature_aware/FeatureAwareCheck.java index 7746692b408e9..e7dae5bdd46be 100644 --- a/x-pack/test/feature-aware/src/main/java/org/elasticsearch/xpack/test/feature_aware/FeatureAwareCheck.java +++ b/x-pack/test/feature-aware/src/main/java/org/elasticsearch/xpack/test/feature_aware/FeatureAwareCheck.java @@ -9,7 +9,6 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.SuppressForbidden; -import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.persistent.PersistentTaskParams; import org.elasticsearch.xpack.core.XPackPlugin; import org.objectweb.asm.ClassReader; From 8c76a1fb1d1439b0d6aa3469909b75b37d5acf76 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 11 Jun 2018 07:51:24 -0700 Subject: [PATCH 10/71] [DOCS] Adds machine learning 6.3.0 release notes (#31217) --- docs/Versions.asciidoc | 2 ++ docs/reference/release-notes/6.3.asciidoc | 24 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/Versions.asciidoc b/docs/Versions.asciidoc index a88bc2867852a..e62bd0df9ff63 100644 --- a/docs/Versions.asciidoc +++ b/docs/Versions.asciidoc @@ -13,7 +13,9 @@ release-state can be: released | prerelease | unreleased :release-state: unreleased :issue: https://github.com/elastic/elasticsearch/issues/ +:ml-issue: https://github.com/elastic/ml-cpp/issues/ :pull: https://github.com/elastic/elasticsearch/pull/ +:ml-pull: https://github.com/elastic/ml-cpp/pull/ :docker-repo: docker.elastic.co/elasticsearch/elasticsearch :docker-image: {docker-repo}:{version} diff --git a/docs/reference/release-notes/6.3.asciidoc b/docs/reference/release-notes/6.3.asciidoc index 6e1152bcfea3f..a2fc2ad52145e 100644 --- a/docs/reference/release-notes/6.3.asciidoc +++ b/docs/reference/release-notes/6.3.asciidoc @@ -24,9 +24,6 @@ //[float] //=== Enhancements -//[float] -//=== Bug Fixes - //[float] //=== Regressions @@ -66,12 +63,29 @@ elasticsearch plugin. //[float] //=== New Features -//[float] -//=== Enhancements +[float] +=== Enhancements + +Machine Learning:: +* Synchronize long and short tests for periodicity {ml-pull}62[#62] +* Improvements to trend modelling and periodicity testing for forecasting {ml-pull}7[#7] (issue: {ml-issue}5[#5]) [float] === Bug Fixes +Machine Learning:: +* By-fields should respect model_plot_config.terms {ml-pull}86[#86] (issue: {issue}30004[#30004]) +* Function description for population lat_long results should be lat_long instead of mean {ml-pull}81[#81] (issue: {ml-issue}80[#80]) +* Fix error causing us to overestimate effective history length {ml-pull}66[#66] (issue: {ml-issue}57[#57]) +* Clearing JSON memory allocators {ml-pull}30[#30] (issue: {ml-issue}26[#26]) +* Fix sparse data edge cases for periodicity testing {ml-pull}28[#28] (issue: {ml-issue}20[#20]) +* Impose an absolute cutoff on the minimum variance {ml-pull}8[#8] (issue: {ml-issue}488[#488]) +* Check accesses in bounds when clearing recycled models {ml-pull}79[#79] (issue: {ml-issue}76[#76]) +* Set forecast progress to 100% and status finished in the case of insufficient history (data) {ml-pull}44[#44] +* Add control message to start background persistence {ml-pull}19[#19] +* Fail start up if state is missing {ml-pull}4[#4] +* Do not log incorrect model memory limit {ml-pull}3[#3] + Security:: * Reduces the number of object allocations made by {security} when resolving the indices and aliases for a request ({pull}30180[#30180]) * Respects accept header on requests with no handler ({pull}30383[#30383]) From cccc9c6de680b0cac78ba5b38d33a814f83fbe87 Mon Sep 17 00:00:00 2001 From: Lee Hinman Date: Mon, 11 Jun 2018 09:44:50 -0600 Subject: [PATCH 11/71] Encapsulate Translog in Engine (#31220) This removes the abstract `getTranslog` method in `Engine`, instead leaving it to the abstract implementations of the other methods that use the translog. This allows future Engines not to have a Translog, as instead they must implement the methods that use the translog pieces to return necessary values. --- .../elasticsearch/index/engine/Engine.java | 38 ++++------------ .../index/engine/InternalEngine.java | 44 ++++++++++++++++++- .../index/engine/InternalEngineTests.java | 16 +++---- .../index/engine/EngineTestCase.java | 4 +- 4 files changed, 62 insertions(+), 40 deletions(-) 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 2dc39f093a5ab..c8a80665b4df6 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -565,18 +565,10 @@ public enum SearcherScope { EXTERNAL, INTERNAL } - /** - * Returns the translog associated with this engine. - * Prefer to keep the translog package-private, so that an engine can control all accesses to the translog. - */ - abstract Translog getTranslog(); - /** * Checks if the underlying storage sync is required. */ - public boolean isTranslogSyncNeeded() { - return getTranslog().syncNeeded(); - } + public abstract boolean isTranslogSyncNeeded(); /** * Ensures that all locations in the given stream have been written to the underlying storage. @@ -585,35 +577,25 @@ public boolean isTranslogSyncNeeded() { public abstract void syncTranslog() throws IOException; - public Closeable acquireTranslogRetentionLock() { - return getTranslog().acquireRetentionLock(); - } + public abstract Closeable acquireTranslogRetentionLock(); /** * Creates a new translog snapshot from this engine for reading translog operations whose seq# at least the provided seq#. * The caller has to close the returned snapshot after finishing the reading. */ - public Translog.Snapshot newTranslogSnapshotFromMinSeqNo(long minSeqNo) throws IOException { - return getTranslog().newSnapshotFromMinSeqNo(minSeqNo); - } + public abstract Translog.Snapshot newTranslogSnapshotFromMinSeqNo(long minSeqNo) throws IOException; /** * Returns the estimated number of translog operations in this engine whose seq# at least the provided seq#. */ - public int estimateTranslogOperationsFromMinSeq(long minSeqNo) { - return getTranslog().estimateTotalOperationsFromMinSeq(minSeqNo); - } + public abstract int estimateTranslogOperationsFromMinSeq(long minSeqNo); - public TranslogStats getTranslogStats() { - return getTranslog().stats(); - } + public abstract TranslogStats getTranslogStats(); /** * Returns the last location that the translog of this engine has written into. */ - public Translog.Location getTranslogLastWriteLocation() { - return getTranslog().getLastWriteLocation(); - } + public abstract Translog.Location getTranslogLastWriteLocation(); protected final void ensureOpen(Exception suppressed) { if (isClosed.get()) { @@ -661,9 +643,7 @@ public CommitStats commitStats() { /** * Returns the latest global checkpoint value that has been persisted in the underlying storage (i.e. translog's checkpoint) */ - public long getLastSyncedGlobalCheckpoint() { - return getTranslog().getLastSyncedGlobalCheckpoint(); - } + public abstract long getLastSyncedGlobalCheckpoint(); /** * Global stats on segments. @@ -935,9 +915,7 @@ public final boolean refreshNeeded() { * * @return {@code true} if the current generation should be rolled to a new generation */ - public boolean shouldRollTranslogGeneration() { - return getTranslog().shouldRollGeneration(); - } + public abstract boolean shouldRollTranslogGeneration(); /** * Rolls the translog generation and cleans unneeded. diff --git a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index 9b2a109a0512b..bc2393415e644 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -75,8 +75,10 @@ import org.elasticsearch.index.translog.TranslogConfig; import org.elasticsearch.index.translog.TranslogCorruptedException; import org.elasticsearch.index.translog.TranslogDeletionPolicy; +import org.elasticsearch.index.translog.TranslogStats; import org.elasticsearch.threadpool.ThreadPool; +import java.io.Closeable; import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -435,12 +437,17 @@ private Translog openTranslog(EngineConfig engineConfig, TranslogDeletionPolicy return new Translog(translogConfig, translogUUID, translogDeletionPolicy, globalCheckpointSupplier, engineConfig.getPrimaryTermSupplier()); } - @Override + // Package private for testing purposes only Translog getTranslog() { ensureOpen(); return translog; } + @Override + public boolean isTranslogSyncNeeded() { + return getTranslog().syncNeeded(); + } + @Override public boolean ensureTranslogSynced(Stream locations) throws IOException { final boolean synced = translog.ensureSynced(locations); @@ -456,6 +463,31 @@ public void syncTranslog() throws IOException { revisitIndexDeletionPolicyOnTranslogSynced(); } + @Override + public Closeable acquireTranslogRetentionLock() { + return getTranslog().acquireRetentionLock(); + } + + @Override + public Translog.Snapshot newTranslogSnapshotFromMinSeqNo(long minSeqNo) throws IOException { + return getTranslog().newSnapshotFromMinSeqNo(minSeqNo); + } + + @Override + public int estimateTranslogOperationsFromMinSeq(long minSeqNo) { + return getTranslog().estimateTotalOperationsFromMinSeq(minSeqNo); + } + + @Override + public TranslogStats getTranslogStats() { + return getTranslog().stats(); + } + + @Override + public Translog.Location getTranslogLastWriteLocation() { + return getTranslog().getLastWriteLocation(); + } + private void revisitIndexDeletionPolicyOnTranslogSynced() throws IOException { if (combinedDeletionPolicy.hasUnreferencedCommits()) { indexWriter.deleteUnusedFiles(); @@ -1619,6 +1651,11 @@ public void trimUnreferencedTranslogFiles() throws EngineException { } } + @Override + public boolean shouldRollTranslogGeneration() { + return getTranslog().shouldRollGeneration(); + } + @Override public void trimOperationsFromTranslog(long belowTerm, long aboveSeqNo) throws EngineException { try (ReleasableLock lock = readLock.acquire()) { @@ -2240,6 +2277,11 @@ LocalCheckpointTracker getLocalCheckpointTracker() { return localCheckpointTracker; } + @Override + public long getLastSyncedGlobalCheckpoint() { + return getTranslog().getLastSyncedGlobalCheckpoint(); + } + @Override public long getLocalCheckpoint() { return localCheckpointTracker.getCheckpoint(); diff --git a/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index 8f1d80d22dde1..b5f0b5de60c7c 100644 --- a/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -731,7 +731,7 @@ protected void commitIndexWriter(IndexWriter writer, Translog translog, String s super.commitIndexWriter(writer, translog, syncId); } }; - assertThat(recoveringEngine.getTranslog().stats().getUncommittedOperations(), equalTo(docs)); + assertThat(getTranslog(recoveringEngine).stats().getUncommittedOperations(), equalTo(docs)); recoveringEngine.recoverFromTranslog(); assertTrue(committed.get()); } finally { @@ -758,7 +758,7 @@ public void testTranslogRecoveryWithMultipleGenerations() throws IOException { final ParsedDocument doc = testParsedDocument(id, null, testDocumentWithTextField(), SOURCE, null); initialEngine.index(indexForDoc(doc)); if (rarely()) { - initialEngine.getTranslog().rollGeneration(); + getTranslog(initialEngine).rollGeneration(); } else if (rarely()) { initialEngine.flush(); } @@ -4007,14 +4007,14 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { assertEquals(checkpointOnReplica, replicaEngine.getLocalCheckpoint()); trimUnsafeCommits(copy(replicaEngine.config(), globalCheckpoint::get)); recoveringEngine = new InternalEngine(copy(replicaEngine.config(), globalCheckpoint::get)); - assertEquals(numDocsOnReplica, recoveringEngine.getTranslog().stats().getUncommittedOperations()); + assertEquals(numDocsOnReplica, getTranslog(recoveringEngine).stats().getUncommittedOperations()); recoveringEngine.recoverFromTranslog(); assertEquals(maxSeqIDOnReplica, recoveringEngine.getSeqNoStats(-1).getMaxSeqNo()); assertEquals(checkpointOnReplica, recoveringEngine.getLocalCheckpoint()); assertEquals((maxSeqIDOnReplica + 1) - numDocsOnReplica, recoveringEngine.fillSeqNoGaps(2)); // now snapshot the tlog and ensure the primary term is updated - try (Translog.Snapshot snapshot = recoveringEngine.getTranslog().newSnapshot()) { + try (Translog.Snapshot snapshot = getTranslog(recoveringEngine).newSnapshot()) { assertTrue((maxSeqIDOnReplica + 1) - numDocsOnReplica <= snapshot.totalOperations()); Translog.Operation operation; while ((operation = snapshot.next()) != null) { @@ -4029,7 +4029,7 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { assertEquals(maxSeqIDOnReplica, recoveringEngine.getLocalCheckpoint()); if ((flushed = randomBoolean())) { globalCheckpoint.set(recoveringEngine.getSeqNoStats(-1).getMaxSeqNo()); - recoveringEngine.getTranslog().sync(); + getTranslog(recoveringEngine).sync(); recoveringEngine.flush(true, true); } } @@ -4042,7 +4042,7 @@ public void testFillUpSequenceIdGapsOnRecovery() throws IOException { trimUnsafeCommits(replicaEngine.config()); recoveringEngine = new InternalEngine(copy(replicaEngine.config(), globalCheckpoint::get)); if (flushed) { - assertThat(recoveringEngine.getTranslog().stats().getUncommittedOperations(), equalTo(0)); + assertThat(recoveringEngine.getTranslogStats().getUncommittedOperations(), equalTo(0)); } recoveringEngine.recoverFromTranslog(); assertEquals(maxSeqIDOnReplica, recoveringEngine.getSeqNoStats(-1).getMaxSeqNo()); @@ -4245,7 +4245,7 @@ protected void commitIndexWriter(IndexWriter writer, Translog translog, String s engine.index(indexForDoc(testParsedDocument(Integer.toString(docId), null, document, B_1, null))); if (frequently()) { globalCheckpoint.set(randomLongBetween(globalCheckpoint.get(), engine.getLocalCheckpoint())); - engine.getTranslog().sync(); + engine.syncTranslog(); } if (frequently()) { final long lastSyncedGlobalCheckpoint = Translog.readGlobalCheckpoint(translogPath, translogUUID); @@ -4267,7 +4267,7 @@ protected void commitIndexWriter(IndexWriter writer, Translog translog, String s } // Make sure we keep all translog operations after the local checkpoint of the safe commit. long localCheckpointFromSafeCommit = Long.parseLong(safeCommit.getUserData().get(SequenceNumbers.LOCAL_CHECKPOINT_KEY)); - try (Translog.Snapshot snapshot = engine.getTranslog().newSnapshot()) { + try (Translog.Snapshot snapshot = getTranslog(engine).newSnapshot()) { assertThat(snapshot, SnapshotMatchers.containsSeqNoRange(localCheckpointFromSafeCommit + 1, docId)); } } 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 0d5e693d62da6..a23e29b0bcd6b 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 @@ -507,6 +507,8 @@ protected Engine.Delete replicaDeleteForDoc(String id, long version, long seqNo, * Exposes a translog associated with the given engine for testing purpose. */ public static Translog getTranslog(Engine engine) { - return engine.getTranslog(); + assert engine instanceof InternalEngine : "only InternalEngines have translogs, got: " + engine.getClass(); + InternalEngine internalEngine = (InternalEngine) engine; + return internalEngine.getTranslog(); } } From a27b52653904adf7e30bb763b0e4819fd87ef2cd Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Mon, 11 Jun 2018 12:47:38 -0400 Subject: [PATCH 12/71] Move ESIndexLevelReplicationTestCase to test framework (#31243) Other components might benefit from the testing infra provided by ESIndexLevelReplicationTestCase. This commit moves it to the test framework. --- .../resources/checkstyle_suppressions.xml | 1 - .../TransportWriteActionTestHelper.java | 0 .../ESIndexLevelReplicationTestCase.java | 22 +++++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) rename {server/src/test => test/framework/src/main}/java/org/elasticsearch/action/support/replication/TransportWriteActionTestHelper.java (100%) rename {server/src/test => test/framework/src/main}/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java (97%) diff --git a/buildSrc/src/main/resources/checkstyle_suppressions.xml b/buildSrc/src/main/resources/checkstyle_suppressions.xml index 09142139b311d..956562316b90a 100644 --- a/buildSrc/src/main/resources/checkstyle_suppressions.xml +++ b/buildSrc/src/main/resources/checkstyle_suppressions.xml @@ -600,7 +600,6 @@ - diff --git a/server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTestHelper.java b/test/framework/src/main/java/org/elasticsearch/action/support/replication/TransportWriteActionTestHelper.java similarity index 100% rename from server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTestHelper.java rename to test/framework/src/main/java/org/elasticsearch/action/support/replication/TransportWriteActionTestHelper.java diff --git a/server/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java similarity index 97% rename from server/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java rename to test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index e32cea1fb182e..284c71b238e2b 100644 --- a/server/src/test/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -29,8 +29,8 @@ import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkShardRequest; import org.elasticsearch.action.bulk.BulkShardResponse; +import org.elasticsearch.action.bulk.MappingUpdatePerformer; import org.elasticsearch.action.bulk.TransportShardBulkAction; -import org.elasticsearch.action.bulk.TransportShardBulkActionTests; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.resync.ResyncReplicationRequest; @@ -595,7 +595,8 @@ class IndexingAction extends ReplicationAction result = executeShardBulkOnPrimary(primary, request); + final TransportWriteAction.WritePrimaryResult + result = executeShardBulkOnPrimary(primary, request); return new PrimaryResult(result.replicaRequest(), result.finalResponseIfSuccessful); } @@ -605,7 +606,8 @@ protected void performOnReplica(BulkShardRequest request, IndexShard replica) th } } - private TransportWriteAction.WritePrimaryResult executeShardBulkOnPrimary(IndexShard primary, BulkShardRequest request) throws Exception { + private TransportWriteAction.WritePrimaryResult executeShardBulkOnPrimary( + IndexShard primary, BulkShardRequest request) throws Exception { for (BulkItemRequest itemRequest : request.items()) { if (itemRequest.request() instanceof IndexRequest) { ((IndexRequest) itemRequest.request()).process(Version.CURRENT, null, index.getName()); @@ -615,8 +617,8 @@ private TransportWriteAction.WritePrimaryResult result; try (Releasable ignored = permitAcquiredFuture.actionGet()) { - result = TransportShardBulkAction.performOnPrimary(request, primary, null, System::currentTimeMillis, - new TransportShardBulkActionTests.NoopMappingUpdatePerformer()); + MappingUpdatePerformer noopMappingUpdater = (update, shardId, type) -> { }; + result = TransportShardBulkAction.performOnPrimary(request, primary, null, System::currentTimeMillis, noopMappingUpdater); } TransportWriteActionTestHelper.performPostWriteActions(primary, request, result.location, logger); return result; @@ -629,9 +631,11 @@ BulkShardRequest executeReplicationRequestOnPrimary(IndexShard primary, Request return executeShardBulkOnPrimary(primary, bulkShardRequest).replicaRequest(); } - private void executeShardBulkOnReplica(BulkShardRequest request, IndexShard replica, long operationPrimaryTerm, long globalCheckpointOnPrimary) throws Exception { + private void executeShardBulkOnReplica(BulkShardRequest request, IndexShard replica, long operationPrimaryTerm, + long globalCheckpointOnPrimary) throws Exception { final PlainActionFuture permitAcquiredFuture = new PlainActionFuture<>(); - replica.acquireReplicaOperationPermit(operationPrimaryTerm, globalCheckpointOnPrimary, permitAcquiredFuture, ThreadPool.Names.SAME, request); + replica.acquireReplicaOperationPermit( + operationPrimaryTerm, globalCheckpointOnPrimary, permitAcquiredFuture, ThreadPool.Names.SAME, request); final Translog.Location location; try (Releasable ignored = permitAcquiredFuture.actionGet()) { location = TransportShardBulkAction.performOnReplica(request, replica); @@ -695,8 +699,8 @@ protected void performOnReplica(final GlobalCheckpointSyncAction.Request request class ResyncAction extends ReplicationAction { - ResyncAction(ResyncReplicationRequest request, ActionListener listener, ReplicationGroup replicationGroup) { - super(request, listener, replicationGroup, "resync"); + ResyncAction(ResyncReplicationRequest request, ActionListener listener, ReplicationGroup group) { + super(request, listener, group, "resync"); } @Override From 518bbf9cfdb88d2122141efdc0c726d3fa5419a8 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 11 Jun 2018 16:53:40 -0400 Subject: [PATCH 13/71] Enable custom credentials for core REST tests (#31235) The core REST tests with security currently use a hardcoded username and password. This is not amenable to running these tests in scenarios where the user controls the creation of the cluster and owns the credentials for this cluster. This commit enables running the core REST tests with security with a custom username and password. --- x-pack/qa/core-rest-tests-with-security/build.gradle | 8 +++++++- .../security/CoreWithSecurityClientYamlTestSuiteIT.java | 8 +++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/x-pack/qa/core-rest-tests-with-security/build.gradle b/x-pack/qa/core-rest-tests-with-security/build.gradle index c23432e5127f7..1ef0a3a98ec6e 100644 --- a/x-pack/qa/core-rest-tests-with-security/build.gradle +++ b/x-pack/qa/core-rest-tests-with-security/build.gradle @@ -19,6 +19,9 @@ integTestRunner { 'cat.templates/10_basic/Sort templates', 'cat.templates/10_basic/Multiple template', ].join(',') + + systemProperty 'tests.rest.cluster.username', System.getProperty('tests.rest.cluster.username', 'test_user') + systemProperty 'tests.rest.cluster.password', System.getProperty('tests.rest.cluster.password', 'x-pack-test-password') } integTestCluster { @@ -28,7 +31,10 @@ integTestCluster { setting 'xpack.ml.enabled', 'false' setting 'xpack.license.self_generated.type', 'trial' setupCommand 'setupDummyUser', - 'bin/elasticsearch-users', 'useradd', 'test_user', '-p', 'x-pack-test-password', '-r', 'superuser' + 'bin/elasticsearch-users', + 'useradd', System.getProperty('tests.rest.cluster.username', 'test_user'), + '-p', System.getProperty('tests.rest.cluster.password', 'x-pack-test-password'), + '-r', 'superuser' waitCondition = { node, ant -> File tmpFile = new File(node.cwd, 'wait.success') ant.get(src: "http://${node.httpUri()}/_cluster/health?wait_for_nodes=>=${numNodes}&wait_for_status=yellow", diff --git a/x-pack/qa/core-rest-tests-with-security/src/test/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java b/x-pack/qa/core-rest-tests-with-security/src/test/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java index 89ebc2db0eebb..e5c82a23a59a5 100644 --- a/x-pack/qa/core-rest-tests-with-security/src/test/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java +++ b/x-pack/qa/core-rest-tests-with-security/src/test/java/org/elasticsearch/xpack/security/CoreWithSecurityClientYamlTestSuiteIT.java @@ -3,11 +3,11 @@ * 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.security; import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; - import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite; import org.apache.lucene.util.TimeUnits; import org.elasticsearch.common.settings.SecureString; @@ -16,13 +16,15 @@ import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; +import java.util.Objects; + import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; @TimeoutSuite(millis = 30 * TimeUnits.MINUTE) // as default timeout seems not enough on the jenkins VMs public class CoreWithSecurityClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { - private static final String USER = "test_user"; - private static final String PASS = "x-pack-test-password"; + private static final String USER = Objects.requireNonNull(System.getProperty("tests.rest.cluster.username")); + private static final String PASS = Objects.requireNonNull(System.getProperty("tests.rest.cluster.password")); public CoreWithSecurityClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) { super(testCandidate); From b56cef85701ee4ded15c69ae61f05f8f7b38025b Mon Sep 17 00:00:00 2001 From: debadair Date: Mon, 11 Jun 2018 15:53:25 -0700 Subject: [PATCH 14/71] [DOCS] Added 6.3 info & updated the upgrade table. (#30940) --- docs/reference/upgrade.asciidoc | 62 +++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/docs/reference/upgrade.asciidoc b/docs/reference/upgrade.asciidoc index 2ca2a01249629..16ad61fe9a0f4 100644 --- a/docs/reference/upgrade.asciidoc +++ b/docs/reference/upgrade.asciidoc @@ -40,25 +40,24 @@ required. [cols="1> (where `y > x`) -|5.6 |6.x |<> footnoteref:[reindexfn, You must delete or reindex any indices created in 2.x before upgrading.] -|5.0-5.5 |6.x |<> footnoteref:[reindexfn] -|<5.x |6.x |<> -|6.x |6.y |<> (where `y > x`) +|6.0-6.2 |{version} |<> +|5.6 |{version} |<> footnoteref:[reindexfn, You must delete or reindex any indices created in 2.x before upgrading.] +|5.0-5.5 |{version} |<> footnoteref:[reindexfn] +|Pre-5.0 |{version} |<> |======================================================================= [IMPORTANT] =============================================== Elasticsearch can read indices created in the *previous major version*. -Older indices must be reindexed or deleted. Elasticsearch 6.x -can use indices created in Elasticsearch 5.x, but not those created in -Elasticsearch 2.x or before. Elasticsearch 5.x can use indices created in -Elasticsearch 2.x, but not those created in 1.x or before. +Older indices must be reindexed or deleted. Elasticsearch 6.n +can use indices created in Elasticsearch 5.n, but not those created in +Elasticsearch 2.n or before. Elasticsearch 5.n can use indices created in +Elasticsearch 2.n, but not those created in 1.n or before. This also applies to indices backed up with <>. If an index was originally created in 2.x, it cannot be -restored to a 6.x cluster even if the snapshot was created by a 5.x cluster. +and restore>>. If an index was originally created in 2.n, it cannot be +restored to a 6.n cluster even if the snapshot was created by a 5.n cluster. Elasticsearch nodes will fail to start if incompatible indices are present. @@ -66,6 +65,47 @@ For information about how to upgrade old indices, see <>. =============================================== + +[float] +[[elasticsearch-upgrade-6.3]] +=== Upgrading to 6.3 +Starting in 6.3, the default distribution includes {xpack} with a free +Basic license. If you already have a Basic license, once you upgrade to 6.3 +it will never expire or need to be renewed. If you have a license subscription, +your license and settings are preserved when you upgrade. + +You can perform rolling upgrades to 6.3 from OSS-only clusters running 5.6 +or 6.0-6.2. {xpack} Basic features will be operational once the cluster is fully +upgraded. + +If you are using {xpack} for the first time, you must explicitly enable data +collection after the upgrade to use monitoring. Set +`xpack.monitoring.collection.enabled` to `true` with the `_cluster/settings` +API: + +[source,json] +---------------------------------------------------------- +PUT /_cluster/settings +{ + "persistent" : { + "xpack.monitoring.collection.enabled" : "true" + } +} +---------------------------------------------------------- +// CONSOLE + +To take all of the {xpack} features for a spin, you can start a 30-day trial +from Kibana, or with the Start Trial API: + +[source,json] +---------------------------------------------------------- +POST _xpack/license/start_trial +---------------------------------------------------------- +// CONSOLE + +The 30-day trial enables you to try out the full set of Platinum features, +including security, machine learning, alerting, graph capabilities, and more. + -- include::upgrade/rolling_upgrade.asciidoc[] From 15b027efffa68122b1433719b87e1ce1e6c3e35b Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 11 Jun 2018 21:32:39 -0400 Subject: [PATCH 15/71] Fix snippets in upgrade docs The upgrade docs were using the json language tag but console wants these to be js for proper formatting. This commit addresses that. --- docs/reference/upgrade.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/upgrade.asciidoc b/docs/reference/upgrade.asciidoc index 16ad61fe9a0f4..e1cab8832c9e8 100644 --- a/docs/reference/upgrade.asciidoc +++ b/docs/reference/upgrade.asciidoc @@ -83,7 +83,7 @@ collection after the upgrade to use monitoring. Set `xpack.monitoring.collection.enabled` to `true` with the `_cluster/settings` API: -[source,json] +[source,js] ---------------------------------------------------------- PUT /_cluster/settings { @@ -97,7 +97,7 @@ PUT /_cluster/settings To take all of the {xpack} features for a spin, you can start a 30-day trial from Kibana, or with the Start Trial API: -[source,json] +[source,js] ---------------------------------------------------------- POST _xpack/license/start_trial ---------------------------------------------------------- From 71c41cdff41632037567bef550ef1f7de51a20d0 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 11 Jun 2018 22:05:53 -0400 Subject: [PATCH 16/71] Revert "Fix snippets in upgrade docs" This reverts commit 15b027efffa68122b1433719b87e1ce1e6c3e35b. --- docs/reference/upgrade.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/upgrade.asciidoc b/docs/reference/upgrade.asciidoc index e1cab8832c9e8..16ad61fe9a0f4 100644 --- a/docs/reference/upgrade.asciidoc +++ b/docs/reference/upgrade.asciidoc @@ -83,7 +83,7 @@ collection after the upgrade to use monitoring. Set `xpack.monitoring.collection.enabled` to `true` with the `_cluster/settings` API: -[source,js] +[source,json] ---------------------------------------------------------- PUT /_cluster/settings { @@ -97,7 +97,7 @@ PUT /_cluster/settings To take all of the {xpack} features for a spin, you can start a 30-day trial from Kibana, or with the Start Trial API: -[source,js] +[source,json] ---------------------------------------------------------- POST _xpack/license/start_trial ---------------------------------------------------------- From e370b69f2a3cbfc2fbb5f4990aac758a3d1490a5 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 11 Jun 2018 22:05:58 -0400 Subject: [PATCH 17/71] Revert "[DOCS] Added 6.3 info & updated the upgrade table. (#30940)" This reverts commit b56cef85701ee4ded15c69ae61f05f8f7b38025b. --- docs/reference/upgrade.asciidoc | 62 ++++++--------------------------- 1 file changed, 11 insertions(+), 51 deletions(-) diff --git a/docs/reference/upgrade.asciidoc b/docs/reference/upgrade.asciidoc index 16ad61fe9a0f4..2ca2a01249629 100644 --- a/docs/reference/upgrade.asciidoc +++ b/docs/reference/upgrade.asciidoc @@ -40,24 +40,25 @@ required. [cols="1> -|5.6 |{version} |<> footnoteref:[reindexfn, You must delete or reindex any indices created in 2.x before upgrading.] -|5.0-5.5 |{version} |<> footnoteref:[reindexfn] -|Pre-5.0 |{version} |<> +|5.x |5.y |<> (where `y > x`) +|5.6 |6.x |<> footnoteref:[reindexfn, You must delete or reindex any indices created in 2.x before upgrading.] +|5.0-5.5 |6.x |<> footnoteref:[reindexfn] +|<5.x |6.x |<> +|6.x |6.y |<> (where `y > x`) |======================================================================= [IMPORTANT] =============================================== Elasticsearch can read indices created in the *previous major version*. -Older indices must be reindexed or deleted. Elasticsearch 6.n -can use indices created in Elasticsearch 5.n, but not those created in -Elasticsearch 2.n or before. Elasticsearch 5.n can use indices created in -Elasticsearch 2.n, but not those created in 1.n or before. +Older indices must be reindexed or deleted. Elasticsearch 6.x +can use indices created in Elasticsearch 5.x, but not those created in +Elasticsearch 2.x or before. Elasticsearch 5.x can use indices created in +Elasticsearch 2.x, but not those created in 1.x or before. This also applies to indices backed up with <>. If an index was originally created in 2.n, it cannot be -restored to a 6.n cluster even if the snapshot was created by a 5.n cluster. +and restore>>. If an index was originally created in 2.x, it cannot be +restored to a 6.x cluster even if the snapshot was created by a 5.x cluster. Elasticsearch nodes will fail to start if incompatible indices are present. @@ -65,47 +66,6 @@ For information about how to upgrade old indices, see <>. =============================================== - -[float] -[[elasticsearch-upgrade-6.3]] -=== Upgrading to 6.3 -Starting in 6.3, the default distribution includes {xpack} with a free -Basic license. If you already have a Basic license, once you upgrade to 6.3 -it will never expire or need to be renewed. If you have a license subscription, -your license and settings are preserved when you upgrade. - -You can perform rolling upgrades to 6.3 from OSS-only clusters running 5.6 -or 6.0-6.2. {xpack} Basic features will be operational once the cluster is fully -upgraded. - -If you are using {xpack} for the first time, you must explicitly enable data -collection after the upgrade to use monitoring. Set -`xpack.monitoring.collection.enabled` to `true` with the `_cluster/settings` -API: - -[source,json] ----------------------------------------------------------- -PUT /_cluster/settings -{ - "persistent" : { - "xpack.monitoring.collection.enabled" : "true" - } -} ----------------------------------------------------------- -// CONSOLE - -To take all of the {xpack} features for a spin, you can start a 30-day trial -from Kibana, or with the Start Trial API: - -[source,json] ----------------------------------------------------------- -POST _xpack/license/start_trial ----------------------------------------------------------- -// CONSOLE - -The 30-day trial enables you to try out the full set of Platinum features, -including security, machine learning, alerting, graph capabilities, and more. - -- include::upgrade/rolling_upgrade.asciidoc[] From dbef9d94c787e9caedc98f9bf27d855b4d1ce259 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 11 Jun 2018 22:19:19 -0400 Subject: [PATCH 18/71] Suppress extras FS on caching directory tests This filesystem needs to be suppressed during these tests because it adds random files to the directory upon directory creation. That means that the size of these directories is off from what we expect them to be. Rather than loosening the assertion which could hide bugs on real directories, this commit suppresses this file system in this test suite. --- .../index/store/ByteSizeCachingDirectoryTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/test/java/org/elasticsearch/index/store/ByteSizeCachingDirectoryTests.java b/server/src/test/java/org/elasticsearch/index/store/ByteSizeCachingDirectoryTests.java index 25d783d25315f..49de00dd8bef6 100644 --- a/server/src/test/java/org/elasticsearch/index/store/ByteSizeCachingDirectoryTests.java +++ b/server/src/test/java/org/elasticsearch/index/store/ByteSizeCachingDirectoryTests.java @@ -23,11 +23,13 @@ import org.apache.lucene.store.FilterDirectory; import org.apache.lucene.store.IOContext; import org.apache.lucene.store.IndexOutput; +import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; import java.io.IOException; +@LuceneTestCase.SuppressFileSystems("ExtrasFS") public class ByteSizeCachingDirectoryTests extends ESTestCase { private static class LengthCountingDirectory extends FilterDirectory { From 7d7463fecee5d7ed799c92e5e11f462b5b112be6 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Mon, 11 Jun 2018 16:55:07 -0400 Subject: [PATCH 19/71] Upgrade to Netty 4.1.25.Final (#31232) This commit upgrades us to Netty 4.1.25. This upgrade is more challenging than past upgrades, all because of a new object cleaner thread that they have added. This thread requires an additional security permission (set context class loader, needed to avoid leaks in certain scenarios). Additionally, there is not a clean way to shutdown this thread which means that the thread can fail thread leak control during tests. As such, we have to filter this thread from thread leak control. --- .../ReindexFromRemoteWithAuthTests.java | 3 ++ .../index/reindex/RetryTests.java | 3 ++ .../test/ObjectCleanerThreadThreadFilter.java | 32 ++++++++++++++++ modules/transport-netty4/build.gradle | 16 ++++---- .../netty-buffer-4.1.16.Final.jar.sha1 | 1 - .../netty-buffer-4.1.25.Final.jar.sha1 | 1 + .../netty-codec-4.1.16.Final.jar.sha1 | 1 - .../netty-codec-4.1.25.Final.jar.sha1 | 1 + .../netty-codec-http-4.1.16.Final.jar.sha1 | 1 - .../netty-codec-http-4.1.25.Final.jar.sha1 | 1 + .../netty-common-4.1.16.Final.jar.sha1 | 1 - .../netty-common-4.1.25.Final.jar.sha1 | 1 + .../netty-handler-4.1.16.Final.jar.sha1 | 1 - .../netty-handler-4.1.25.Final.jar.sha1 | 1 + .../netty-resolver-4.1.16.Final.jar.sha1 | 1 - .../netty-resolver-4.1.25.Final.jar.sha1 | 1 + .../netty-transport-4.1.16.Final.jar.sha1 | 1 - .../netty-transport-4.1.25.Final.jar.sha1 | 1 + .../plugin-metadata/plugin-security.policy | 2 + .../http/netty4/Netty4BadRequestTests.java | 4 +- .../http/netty4/Netty4HttpChannelTests.java | 4 +- .../netty4/Netty4HttpRequestSizeLimitIT.java | 4 +- .../Netty4HttpServerPipeliningTests.java | 5 +-- .../Netty4HttpServerTransportTests.java | 4 +- .../netty4/Netty4PipeliningDisabledIT.java | 4 +- .../netty4/Netty4PipeliningEnabledIT.java | 4 +- .../Netty4HttpPipeliningHandlerTests.java | 4 +- .../Netty4IntegTestCase.java} | 8 ++-- .../elasticsearch/test/Netty4TestCase.java | 26 +++++++++++++ .../test/ObjectCleanerThreadThreadFilter.java | 38 +++++++++++++++++++ .../netty4/ByteBufBytesReferenceTests.java | 3 ++ .../transport/netty4/ESLoggingHandlerIT.java | 4 +- .../netty4/Netty4ScheduledPingTests.java | 4 +- .../Netty4SizeHeaderFrameDecoderTests.java | 4 +- .../transport/netty4/Netty4TransportIT.java | 4 +- ...Netty4TransportMultiPortIntegrationIT.java | 4 +- .../Netty4TransportPublishAddressIT.java | 4 +- .../netty4/NettyTransportMultiPortTests.java | 4 +- .../netty4/SimpleNetty4TransportTests.java | 3 ++ .../smoketest/ESSmokeClientTestCase.java | 18 +++++++++ .../elasticsearch/http/HttpSmokeTestCase.java | 14 +++++++ .../plugin-metadata/plugin-security.policy | 2 + .../AbstractLicensesIntegrationTestCase.java | 5 ++- .../license/StartBasicLicenseTests.java | 1 + .../test/ObjectCleanerThreadThreadFilter.java | 24 ++++++++++++ .../xpack/core/test/XPackIntegTestCase.java | 14 +++++++ .../core/test/XPackSingleNodeTestCase.java | 14 +++++++ .../xpack/ml/support/BaseMlIntegTestCase.java | 15 ++++---- .../plugin-metadata/plugin-security.policy | 2 + .../test/SecurityIntegTestCase.java | 5 ++- .../test/SecuritySingleNodeTestCase.java | 4 +- .../watcher/WatcherPluginDisableTests.java | 3 +- .../AbstractWatcherIntegrationTestCase.java | 4 +- .../xpack/security/audit/IndexAuditIT.java | 5 ++- .../MlNativeAutodetectIntegTestCase.java | 4 ++ x-pack/qa/security-client-tests/build.gradle | 1 + .../qa/SecurityTransportClientIT.java | 10 +++-- .../build.gradle | 1 + .../example/realm/CustomRealmIT.java | 5 ++- .../example/role/CustomRolesProviderIT.java | 3 +- x-pack/qa/security-migrate-tests/build.gradle | 1 + .../xpack/security/MigrateToolTestCase.java | 4 ++ x-pack/qa/smoke-test-plugins-ssl/build.gradle | 1 + .../SmokeTestMonitoringWithSecurityIT.java | 3 +- x-pack/qa/transport-client-tests/build.gradle | 1 + .../ml/client/ESXPackSmokeClientTestCase.java | 3 ++ 66 files changed, 304 insertions(+), 72 deletions(-) create mode 100644 modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.java delete mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 rename modules/transport-netty4/src/test/java/org/elasticsearch/{ESNetty4IntegTestCase.java => test/Netty4IntegTestCase.java} (90%) create mode 100644 modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java create mode 100644 modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java index 31077c405d8e1..75da686c1af73 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.lucene.util.SetOnce; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.ElasticsearchStatusException; @@ -41,6 +42,7 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; +import org.elasticsearch.index.reindex.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; @@ -64,6 +66,7 @@ import static org.elasticsearch.index.reindex.ReindexTestCase.matcher; import static org.hamcrest.Matchers.containsString; +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class ReindexFromRemoteWithAuthTests extends ESSingleNodeTestCase { private TransportAddress address; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java index bd9642c2ed2e6..5562f8fd4a6b6 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.index.reindex; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; @@ -33,6 +34,7 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.reindex.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; @@ -56,6 +58,7 @@ * Integration test for retry behavior. Useful because retrying relies on the way that the * rest of Elasticsearch throws exceptions and unit tests won't verify that. */ +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class RetryTests extends ESIntegTestCase { private static final int DOC_COUNT = 20; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.java new file mode 100644 index 0000000000000..948f1ec7fd6f6 --- /dev/null +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.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.index.reindex.test; + +import com.carrotsearch.randomizedtesting.ThreadFilter; + +public class ObjectCleanerThreadThreadFilter implements ThreadFilter { + + @Override + public boolean reject(final Thread t) { + // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated + return "ObjectCleanerThread".equals(t.getName()); + } + +} diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index 5d4bcd7c10a84..ed905a530c48e 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -34,13 +34,13 @@ compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-tr dependencies { // network stack - compile "io.netty:netty-buffer:4.1.16.Final" - compile "io.netty:netty-codec:4.1.16.Final" - compile "io.netty:netty-codec-http:4.1.16.Final" - compile "io.netty:netty-common:4.1.16.Final" - compile "io.netty:netty-handler:4.1.16.Final" - compile "io.netty:netty-resolver:4.1.16.Final" - compile "io.netty:netty-transport:4.1.16.Final" + compile "io.netty:netty-buffer:4.1.25.Final" + compile "io.netty:netty-codec:4.1.25.Final" + compile "io.netty:netty-codec-http:4.1.25.Final" + compile "io.netty:netty-common:4.1.25.Final" + compile "io.netty:netty-handler:4.1.25.Final" + compile "io.netty:netty-resolver:4.1.25.Final" + compile "io.netty:netty-transport:4.1.25.Final" } dependencyLicenses { @@ -161,6 +161,6 @@ thirdPartyAudit.excludes = [ 'org.conscrypt.AllocatedBuffer', 'org.conscrypt.BufferAllocator', - 'org.conscrypt.Conscrypt$Engines', + 'org.conscrypt.Conscrypt', 'org.conscrypt.HandshakeListener' ] diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 deleted file mode 100644 index c546222971985..0000000000000 --- a/modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -63b5fa95c74785e16f2c30ce268bc222e35c8cb5 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 new file mode 100644 index 0000000000000..3ca0cbb45ec31 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 @@ -0,0 +1 @@ +f366d0cc87b158ca064d27507127e3cc4eb2f089 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 deleted file mode 100644 index 1e6c241ea0b17..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d84a1f21768b7309c2954521cf5a1f46c2309eb1 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 new file mode 100644 index 0000000000000..5e2bc85c548dd --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 @@ -0,0 +1 @@ +3e465c75bead40d06b5b9c0612b37cf77c548887 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 deleted file mode 100644 index 71c33af1c5fc2..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -d64312378b438dfdad84267c599a053327c6f02a \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 new file mode 100644 index 0000000000000..58cb7fd987949 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 @@ -0,0 +1 @@ +70888d3f2a829541378f68503ddd52c3193df35a \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 deleted file mode 100644 index 3edf5fcea59b3..0000000000000 --- a/modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -177a6b30cca92f6f5f9873c9befd681377a4c328 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 new file mode 100644 index 0000000000000..62f85f8965513 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 @@ -0,0 +1 @@ +e17d5c05c101fe14536ce3fb34b36c54e04791f6 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 deleted file mode 100644 index cba27387268d1..0000000000000 --- a/modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -fec0e63e7dd7f4eeef7ea8dc47a1ff32dfc7ebc2 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 new file mode 100644 index 0000000000000..5391f625a4df0 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 @@ -0,0 +1 @@ +ecdfb8fe93a8b75db3ea8746d3437eed845c24bd \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 deleted file mode 100644 index 3571d2ecfdc48..0000000000000 --- a/modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f6eb553b53fb3a90a8ac1170697093fed82eae28 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 new file mode 100644 index 0000000000000..8225fb799e3ff --- /dev/null +++ b/modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 @@ -0,0 +1 @@ +dc0965d00746b782b33f419b005cbc130973030d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 deleted file mode 100644 index e502d4c77084c..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3c8ee2c4d4a1cbb947a5c184c7aeb2204260958b \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 new file mode 100644 index 0000000000000..1049ea4b98bc6 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 @@ -0,0 +1 @@ +19a6f1f649894b6705aa9d8cbcced188dff133b0 \ No newline at end of file diff --git a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy index 32b2dc9bd1540..3775931efb150 100644 --- a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy +++ b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy @@ -21,6 +21,8 @@ grant codeBase "${codebase.netty-common}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; + permission java.lang.RuntimePermission "setContextClassLoader"; + // netty makes and accepts socket connections permission java.net.SocketPermission "*", "accept,connect"; }; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java index 094f339059876..8baf818975ed8 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; +import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; @@ -33,7 +34,6 @@ import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.junit.After; @@ -48,7 +48,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; -public class Netty4BadRequestTests extends ESTestCase { +public class Netty4BadRequestTests extends Netty4TestCase { private NetworkService networkService; private MockBigArrays bigArrays; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java index 918e98fd2e7c0..b903b25c78ea3 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java @@ -41,6 +41,7 @@ import io.netty.handler.codec.http.HttpVersion; import io.netty.util.Attribute; import io.netty.util.AttributeKey; +import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.ReleasablePagedBytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; @@ -64,7 +65,6 @@ import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.netty4.Netty4Utils; @@ -90,7 +90,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class Netty4HttpChannelTests extends ESTestCase { +public class Netty4HttpChannelTests extends Netty4TestCase { private NetworkService networkService; private ThreadPool threadPool; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java index d99820bb86465..550b14018236e 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java @@ -20,7 +20,7 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; -import org.elasticsearch.ESNetty4IntegTestCase; +import org.elasticsearch.test.Netty4IntegTestCase; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; @@ -46,7 +46,7 @@ * a single node "cluster". We also force test infrastructure to use the node client instead of the transport client for the same reason. */ @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numClientNodes = 0, numDataNodes = 1, transportClientRatio = 0) -public class Netty4HttpRequestSizeLimitIT extends ESNetty4IntegTestCase { +public class Netty4HttpRequestSizeLimitIT extends Netty4IntegTestCase { private static final ByteSizeValue LIMIT = new ByteSizeValue(2, ByteSizeUnit.KB); diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java index 1c7475379bb87..d7b72c38f7d06 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java @@ -32,7 +32,6 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; -import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -45,7 +44,7 @@ import org.elasticsearch.http.NullDispatcher; import org.elasticsearch.http.netty4.pipelining.HttpPipelinedRequest; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.junit.After; @@ -69,7 +68,7 @@ /** * This test just tests, if he pipelining works in general with out any connection the Elasticsearch handler */ -public class Netty4HttpServerPipeliningTests extends ESTestCase { +public class Netty4HttpServerPipeliningTests extends Netty4TestCase { private NetworkService networkService; private ThreadPool threadPool; private MockBigArrays bigArrays; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java index 96b436ce7de43..25bc94352a4ee 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java @@ -38,6 +38,7 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; +import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; @@ -59,7 +60,6 @@ import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.junit.After; @@ -94,7 +94,7 @@ /** * Tests for the {@link Netty4HttpServerTransport} class. */ -public class Netty4HttpServerTransportTests extends ESTestCase { +public class Netty4HttpServerTransportTests extends Netty4TestCase { private NetworkService networkService; private ThreadPool threadPool; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java index 9f117d4ee21fc..17fafbde3cbbd 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java @@ -19,13 +19,13 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; -import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; +import org.elasticsearch.test.Netty4IntegTestCase; import java.util.ArrayList; import java.util.Collection; @@ -36,7 +36,7 @@ import static org.hamcrest.Matchers.hasSize; @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numDataNodes = 1) -public class Netty4PipeliningDisabledIT extends ESNetty4IntegTestCase { +public class Netty4PipeliningDisabledIT extends Netty4IntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java index cc3f22be453fb..678bcbc1d5089 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java @@ -20,13 +20,13 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; -import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; +import org.elasticsearch.test.Netty4IntegTestCase; import java.util.Collection; import java.util.Locale; @@ -35,7 +35,7 @@ import static org.hamcrest.Matchers.is; @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numDataNodes = 1) -public class Netty4PipeliningEnabledIT extends ESNetty4IntegTestCase { +public class Netty4PipeliningEnabledIT extends Netty4IntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java index ffb6c8fb3569d..35f12bef6b6ef 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java @@ -37,7 +37,7 @@ import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.QueryStringDecoder; import org.elasticsearch.common.Randomness; -import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.Netty4TestCase; import org.junit.After; import java.nio.channels.ClosedChannelException; @@ -60,7 +60,7 @@ import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import static org.hamcrest.core.Is.is; -public class Netty4HttpPipeliningHandlerTests extends ESTestCase { +public class Netty4HttpPipeliningHandlerTests extends Netty4TestCase { private final ExecutorService executorService = Executors.newFixedThreadPool(randomIntBetween(4, 8)); private final Map waitingRequests = new ConcurrentHashMap<>(); diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/ESNetty4IntegTestCase.java b/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4IntegTestCase.java similarity index 90% rename from modules/transport-netty4/src/test/java/org/elasticsearch/ESNetty4IntegTestCase.java rename to modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4IntegTestCase.java index b38cda76c6980..c21b863d196b7 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/ESNetty4IntegTestCase.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4IntegTestCase.java @@ -16,19 +16,21 @@ * specific language governing permissions and limitations * under the License. */ -package org.elasticsearch; +package org.elasticsearch.test; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.transport.netty4.Netty4Transport; import java.util.Arrays; import java.util.Collection; -public abstract class ESNetty4IntegTestCase extends ESIntegTestCase { +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) +public abstract class Netty4IntegTestCase extends ESIntegTestCase { @Override protected boolean ignoreExternalCluster() { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java b/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java new file mode 100644 index 0000000000000..df931d61992ef --- /dev/null +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java @@ -0,0 +1,26 @@ +/* + * 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.test; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; + +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) +public abstract class Netty4TestCase extends ESTestCase { +} diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java b/modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java new file mode 100644 index 0000000000000..e47c536665d13 --- /dev/null +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java @@ -0,0 +1,38 @@ +/* + * 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.test; + +import com.carrotsearch.randomizedtesting.ThreadFilter; + +/** + * The Netty object cleaner thread is not closeable and it does not terminate in a timely manner. This means that thread leak control in + * tests will fail test suites when the object cleaner thread has not terminated. Since there is not a reliable way to terminate this thread + * we instead filter it out of thread leak control. + */ +public class ObjectCleanerThreadThreadFilter implements ThreadFilter { + + @Override + public boolean reject(final Thread t) { + // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated + return "ObjectCleanerThread".equals(t.getName()); + } + +} + diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java index 4a41aaec952a0..618d3ffe8f96a 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java @@ -18,15 +18,18 @@ */ package org.elasticsearch.transport.netty4; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.common.bytes.AbstractBytesReferenceTestCase; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.ReleasableBytesStreamOutput; import java.io.IOException; +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class ByteBufBytesReferenceTests extends AbstractBytesReferenceTestCase { @Override diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java index acd71749e2333..8ef8b28dc1497 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java @@ -20,7 +20,7 @@ package org.elasticsearch.transport.netty4; import org.apache.logging.log4j.Level; -import org.elasticsearch.ESNetty4IntegTestCase; +import org.elasticsearch.test.Netty4IntegTestCase; import org.elasticsearch.action.admin.cluster.node.hotthreads.NodesHotThreadsRequest; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.test.ESIntegTestCase; @@ -29,7 +29,7 @@ @ESIntegTestCase.ClusterScope(numDataNodes = 2) @TestLogging(value = "org.elasticsearch.transport.netty4.ESLoggingHandler:trace") -public class ESLoggingHandlerIT extends ESNetty4IntegTestCase { +public class ESLoggingHandlerIT extends Netty4IntegTestCase { private MockLogAppender appender; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java index b967a7ea41069..b4b33ba96211c 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.transport.netty4; +import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.lease.Releasables; @@ -26,7 +27,6 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -47,7 +47,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -public class Netty4ScheduledPingTests extends ESTestCase { +public class Netty4ScheduledPingTests extends Netty4TestCase { public void testScheduledPing() throws Exception { ThreadPool threadPool = new TestThreadPool(getClass().getName()); diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java index 7343da6c3b11a..0819548907296 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.transport.netty4; +import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; @@ -28,7 +29,6 @@ import org.elasticsearch.common.util.MockPageCacheRecycler; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.mocksocket.MockSocket; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TcpTransport; import org.junit.After; @@ -47,7 +47,7 @@ * This test checks, if a HTTP look-alike request (starting with a HTTP method and a space) * actually returns text response instead of just dropping the connection */ -public class Netty4SizeHeaderFrameDecoderTests extends ESTestCase { +public class Netty4SizeHeaderFrameDecoderTests extends Netty4TestCase { private final Settings settings = Settings.builder() .put("node.name", "NettySizeHeaderFrameDecoderTests") diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java index b81c8efcb47ee..a426c11db8245 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java @@ -18,7 +18,7 @@ */ package org.elasticsearch.transport.netty4; -import org.elasticsearch.ESNetty4IntegTestCase; +import org.elasticsearch.test.Netty4IntegTestCase; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -54,7 +54,7 @@ import static org.hamcrest.Matchers.is; @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numDataNodes = 1) -public class Netty4TransportIT extends ESNetty4IntegTestCase { +public class Netty4TransportIT extends Netty4IntegTestCase { // static so we can use it in anonymous classes private static String channelProfileName = null; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java index 52ad32efb5645..ddef8abcf0181 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java @@ -18,7 +18,7 @@ */ package org.elasticsearch.transport.netty4; -import org.elasticsearch.ESNetty4IntegTestCase; +import org.elasticsearch.test.Netty4IntegTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; @@ -48,7 +48,7 @@ import static org.hamcrest.Matchers.lessThanOrEqualTo; @ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 1, numClientNodes = 0) -public class Netty4TransportMultiPortIntegrationIT extends ESNetty4IntegTestCase { +public class Netty4TransportMultiPortIntegrationIT extends Netty4IntegTestCase { private static int randomPort = -1; private static String randomPortRange; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java index 922031d3c3dea..dee104a73f7d7 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java @@ -19,7 +19,7 @@ package org.elasticsearch.transport.netty4; -import org.elasticsearch.ESNetty4IntegTestCase; +import org.elasticsearch.test.Netty4IntegTestCase; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.common.network.NetworkModule; @@ -41,7 +41,7 @@ * different ports on ipv4 and ipv6. */ @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0) -public class Netty4TransportPublishAddressIT extends ESNetty4IntegTestCase { +public class Netty4TransportPublishAddressIT extends Netty4IntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { return Settings.builder() diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java index a49df3caaba4e..05d6d55ac42da 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.transport.netty4; +import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkService; @@ -27,7 +28,6 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.MockPageCacheRecycler; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TcpTransport; @@ -37,7 +37,7 @@ import static org.hamcrest.Matchers.is; -public class NettyTransportMultiPortTests extends ESTestCase { +public class NettyTransportMultiPortTests extends Netty4TestCase { private String host; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java index efa296b6278af..278b00965c9a5 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java @@ -19,6 +19,8 @@ package org.elasticsearch.transport.netty4; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; +import org.elasticsearch.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.Version; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -49,6 +51,7 @@ import static java.util.Collections.emptySet; import static org.hamcrest.Matchers.containsString; +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class SimpleNetty4TransportTests extends AbstractSimpleTransportTestCase { public static MockTransportService nettyFromThreadPool(Settings settings, ThreadPool threadPool, final Version version, diff --git a/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java index 908e8e1c71114..c34a2f44b73af 100644 --- a/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java +++ b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java @@ -19,6 +19,8 @@ package org.elasticsearch.smoketest; +import com.carrotsearch.randomizedtesting.ThreadFilter; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -62,8 +64,24 @@ * then run JUnit. If you changed the default port, set "-Dtests.cluster=localhost:PORT" when running your test. */ @LuceneTestCase.SuppressSysoutChecks(bugUrl = "we log a lot on purpose") +@ThreadLeakFilters(filters = {ESSmokeClientTestCase.ObjectCleanerThreadThreadFilter.class}) public abstract class ESSmokeClientTestCase extends LuceneTestCase { + /** + * The Netty object cleaner thread is not closeable and it does not terminate in a timely manner. This means that thread leak control in + * tests will fail test suites when the object cleaner thread has not terminated. Since there is not a reliable way to terminate this + * thread we instead filter it out of thread leak control. + */ + public static class ObjectCleanerThreadThreadFilter implements ThreadFilter { + + @Override + public boolean reject(final Thread t) { + // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated + return "ObjectCleanerThread".equals(t.getName()); + } + + } + /** * Key used to eventually switch to using an external cluster and provide its transport addresses */ diff --git a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java index bb13d486a9adc..8899284573bba 100644 --- a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java +++ b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java @@ -18,10 +18,13 @@ */ package org.elasticsearch.http; +import com.carrotsearch.randomizedtesting.ThreadFilter; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.MockTcpTransportPlugin; import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.transport.nio.NioTransportPlugin; @@ -30,8 +33,19 @@ import java.util.Arrays; import java.util.Collection; +@ThreadLeakFilters(filters = {HttpSmokeTestCase.ObjectCleanerThreadThreadFilter.class}) public abstract class HttpSmokeTestCase extends ESIntegTestCase { + public static class ObjectCleanerThreadThreadFilter implements ThreadFilter { + + @Override + public boolean reject(final Thread t) { + // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated + return "ObjectCleanerThread".equals(t.getName()); + } + + } + private static String nodeTransportTypeKey; private static String nodeHttpTypeKey; private static String clientTypeKey; diff --git a/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy b/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy index 0cd7a32bcc47b..1f1bd66005693 100644 --- a/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy +++ b/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy @@ -15,6 +15,8 @@ grant { grant codeBase "${codebase.netty-common}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; + + permission java.lang.RuntimePermission "setContextClassLoader"; }; grant codeBase "${codebase.netty-transport}" { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java index e9c9ba95bfd38..57ba7a0c4674d 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java @@ -3,6 +3,7 @@ * 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.license; import org.elasticsearch.analysis.common.CommonAnalysisPlugin; @@ -13,16 +14,16 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.CountDownLatch; -public abstract class AbstractLicensesIntegrationTestCase extends ESIntegTestCase { +public abstract class AbstractLicensesIntegrationTestCase extends XPackIntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java index 55b14a4d79280..3ca0ae7087875 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java @@ -3,6 +3,7 @@ * 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.license; import org.elasticsearch.client.Response; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java new file mode 100644 index 0000000000000..e911920953ac2 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java @@ -0,0 +1,24 @@ +/* + * 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.test; + +import com.carrotsearch.randomizedtesting.ThreadFilter; + +/** + * The Netty object cleaner thread is not closeable and it does not terminate in a timely manner. This means that thread leak control in + * tests will fail test suites when the object cleaner thread has not terminated. Since there is not a reliable way to terminate this + * thread we instead filter it out of thread leak control. + */ +public class ObjectCleanerThreadThreadFilter implements ThreadFilter { + + @Override + public boolean reject(final Thread t) { + // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated + return "ObjectCleanerThread".equals(t.getName()); + } + +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java new file mode 100644 index 0000000000000..87cf2d87f02a4 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java @@ -0,0 +1,14 @@ +/* + * 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.test; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; +import org.elasticsearch.test.ESIntegTestCase; + +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) +public abstract class XPackIntegTestCase extends ESIntegTestCase { +} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java new file mode 100644 index 0000000000000..a3cbd875503f5 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java @@ -0,0 +1,14 @@ +/* + * 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.test; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; +import org.elasticsearch.test.ESSingleNodeTestCase; + +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) +public abstract class XPackSingleNodeTestCase extends ESSingleNodeTestCase { +} diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java index 40b59e4dbec65..e8a5193b04003 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java @@ -26,19 +26,17 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.discovery.TestZenDiscovery; import org.elasticsearch.xpack.core.XPackSettings; -import org.elasticsearch.xpack.core.ml.action.GetDatafeedsAction; -import org.elasticsearch.xpack.core.ml.action.GetJobsAction; -import org.elasticsearch.xpack.core.ml.action.util.QueryPage; -import org.elasticsearch.xpack.core.ml.client.MachineLearningClient; -import org.elasticsearch.xpack.ml.LocalStateMachineLearning; -import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.core.ml.MachineLearningField; import org.elasticsearch.xpack.core.ml.action.CloseJobAction; import org.elasticsearch.xpack.core.ml.action.DeleteDatafeedAction; import org.elasticsearch.xpack.core.ml.action.DeleteJobAction; +import org.elasticsearch.xpack.core.ml.action.GetDatafeedsAction; import org.elasticsearch.xpack.core.ml.action.GetDatafeedsStatsAction; +import org.elasticsearch.xpack.core.ml.action.GetJobsAction; import org.elasticsearch.xpack.core.ml.action.GetJobsStatsAction; import org.elasticsearch.xpack.core.ml.action.StopDatafeedAction; +import org.elasticsearch.xpack.core.ml.action.util.QueryPage; +import org.elasticsearch.xpack.core.ml.client.MachineLearningClient; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedState; import org.elasticsearch.xpack.core.ml.job.config.AnalysisConfig; @@ -48,6 +46,9 @@ import org.elasticsearch.xpack.core.ml.job.config.Job; import org.elasticsearch.xpack.core.ml.job.config.JobState; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.DataCounts; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; +import org.elasticsearch.xpack.ml.LocalStateMachineLearning; +import org.elasticsearch.xpack.ml.MachineLearning; import org.junit.After; import org.junit.Before; @@ -68,7 +69,7 @@ */ @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, numClientNodes = 0, transportClientRatio = 0, supportsDedicatedMasters = false) -public abstract class BaseMlIntegTestCase extends ESIntegTestCase { +public abstract class BaseMlIntegTestCase extends XPackIntegTestCase { @Override protected boolean ignoreExternalCluster() { diff --git a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy index 857c2f6e472d5..f56affec02be0 100644 --- a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy +++ b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy @@ -23,6 +23,8 @@ grant codeBase "${codebase.xmlsec-2.0.8.jar}" { grant codeBase "${codebase.netty-common}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; + + permission java.lang.RuntimePermission "setContextClassLoader"; }; grant codeBase "${codebase.netty-transport}" { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java index aac16e7c7ab78..bb5733c2e7423 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java @@ -3,6 +3,7 @@ * 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.test; import io.netty.util.ThreadDeathWatcher; @@ -40,9 +41,9 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.client.SecurityClient; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.elasticsearch.xpack.security.LocalStateSecurity; import org.elasticsearch.xpack.security.Security; - import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.junit.AfterClass; import org.junit.Before; @@ -75,7 +76,7 @@ * * @see SecuritySettingsSource */ -public abstract class SecurityIntegTestCase extends ESIntegTestCase { +public abstract class SecurityIntegTestCase extends XPackIntegTestCase { private static SecuritySettingsSource SECURITY_DEFAULT_SETTINGS; protected static SecureString BOOTSTRAP_PASSWORD = null; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java index cda627806e7b5..1c6f8e847d39c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java @@ -3,6 +3,7 @@ * 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.test; import io.netty.util.ThreadDeathWatcher; @@ -22,6 +23,7 @@ import org.elasticsearch.license.LicenseService; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.PluginInfo; +import org.elasticsearch.xpack.core.test.XPackSingleNodeTestCase; import org.elasticsearch.xpack.security.LocalStateSecurity; import org.junit.AfterClass; import org.junit.Before; @@ -49,7 +51,7 @@ * {@link SecurityIntegTestCase} due to simplicity and improved speed from not needing to start * multiple nodes and wait for the cluster to form. */ -public abstract class SecuritySingleNodeTestCase extends ESSingleNodeTestCase { +public abstract class SecuritySingleNodeTestCase extends XPackSingleNodeTestCase { private static SecuritySettingsSource SECURITY_DEFAULT_SETTINGS = null; private static CustomSecuritySettingsSource customSecuritySettingsSource = null; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java index 3227ba4c4a1e0..d71b0575715fa 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java @@ -19,6 +19,7 @@ import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor; import org.elasticsearch.xpack.watcher.test.LocalStateWatcher; @@ -31,7 +32,7 @@ import static org.hamcrest.Matchers.not; @ClusterScope(scope = SUITE, numClientNodes = 0, transportClientRatio = 0, maxNumDataNodes = 3) -public class WatcherPluginDisableTests extends ESIntegTestCase { +public class WatcherPluginDisableTests extends XPackIntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { return Settings.builder() diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java index 4eb4bd1aa2c6e..4cf5e3faa232d 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java @@ -31,7 +31,6 @@ import org.elasticsearch.script.Script; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.disruption.ServiceDisruptionScheme; @@ -40,6 +39,7 @@ import org.elasticsearch.xpack.core.XPackClient; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.SecurityField; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.elasticsearch.xpack.core.watcher.WatcherState; import org.elasticsearch.xpack.core.watcher.client.WatcherClient; import org.elasticsearch.xpack.core.watcher.execution.ExecutionState; @@ -94,7 +94,7 @@ import static org.hamcrest.core.IsNot.not; @ClusterScope(scope = SUITE, numClientNodes = 0, transportClientRatio = 0, maxNumDataNodes = 3) -public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase { +public abstract class AbstractWatcherIntegrationTestCase extends XPackIntegTestCase { public static final String WATCHER_LANG = Script.DEFAULT_SCRIPT_LANG; diff --git a/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java b/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java index 3467316c24f6c..26a98691f1323 100644 --- a/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java +++ b/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.security.audit; import com.carrotsearch.hppc.cursors.ObjectCursor; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.http.message.BasicHeader; import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateResponse; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; @@ -24,8 +25,9 @@ import org.elasticsearch.test.TestCluster; import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.security.SecurityField; -import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; +import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; +import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail; import java.io.IOException; import java.net.InetSocketAddress; @@ -38,6 +40,7 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class IndexAuditIT extends ESIntegTestCase { private static final String USER = "test_user"; private static final String PASS = "x-pack-test-password"; diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java index f70efc72506d3..a744f3ebb6380 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.ml.integration; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; @@ -34,6 +35,7 @@ import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.SecuritySettingsSourceField; import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; @@ -84,6 +86,7 @@ import org.elasticsearch.xpack.core.ml.job.results.Result; import org.elasticsearch.xpack.core.security.SecurityField; import org.elasticsearch.xpack.core.security.authc.TokenMetaData; +import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; import java.io.IOException; import java.net.URISyntaxException; @@ -107,6 +110,7 @@ /** * Base class of ML integration tests that use a native autodetect process */ +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) abstract class MlNativeAutodetectIntegTestCase extends ESIntegTestCase { private List jobs = new ArrayList<>(); diff --git a/x-pack/qa/security-client-tests/build.gradle b/x-pack/qa/security-client-tests/build.gradle index 4e517f4d3633e..9706d554e95c9 100644 --- a/x-pack/qa/security-client-tests/build.gradle +++ b/x-pack/qa/security-client-tests/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') + testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java b/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java index 519f365d515a0..5342c2bd78095 100644 --- a/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java +++ b/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.security.qa; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; @@ -15,10 +16,11 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; +import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.security.SecurityField; +import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Collection; import java.util.Collections; @@ -32,7 +34,9 @@ /** * Integration tests that test a transport client with security being loaded that connect to an external cluster */ -public class SecurityTransportClientIT extends ESIntegTestCase { +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) +public class SecurityTransportClientIT extends XPackIntegTestCase { + static final String ADMIN_USER_PW = "test_user:x-pack-test-password"; static final String TRANSPORT_USER_PW = "transport:x-pack-test-password"; diff --git a/x-pack/qa/security-example-spi-extension/build.gradle b/x-pack/qa/security-example-spi-extension/build.gradle index b2fac075cb315..94a7bccca8034 100644 --- a/x-pack/qa/security-example-spi-extension/build.gradle +++ b/x-pack/qa/security-example-spi-extension/build.gradle @@ -9,6 +9,7 @@ esplugin { dependencies { compileOnly project(path: xpackModule('core'), configuration: 'runtime') + testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java index 65ec595a0d409..8f8f45f0448b6 100644 --- a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java +++ b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java @@ -3,6 +3,7 @@ * 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.example.realm; import org.apache.http.message.BasicHeader; @@ -19,9 +20,9 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.env.Environment; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; import org.elasticsearch.xpack.core.XPackClientPlugin; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Collection; import java.util.Collections; @@ -32,7 +33,7 @@ /** * Integration test to test authentication with the custom realm */ -public class CustomRealmIT extends ESIntegTestCase { +public class CustomRealmIT extends XPackIntegTestCase { @Override protected Settings externalClusterClientSettings() { diff --git a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java index 4e1fb72256086..85b34a9612f46 100644 --- a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java +++ b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java @@ -18,6 +18,7 @@ import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.client.SecurityClient; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Collection; import java.util.Collections; @@ -31,7 +32,7 @@ /** * Integration test for custom roles providers. */ -public class CustomRolesProviderIT extends ESIntegTestCase { +public class CustomRolesProviderIT extends XPackIntegTestCase { private static final String TEST_USER = "test_user"; private static final String TEST_PWD = "change_me"; diff --git a/x-pack/qa/security-migrate-tests/build.gradle b/x-pack/qa/security-migrate-tests/build.gradle index 7ccf6d2349b8b..de6f1c86b993f 100644 --- a/x-pack/qa/security-migrate-tests/build.gradle +++ b/x-pack/qa/security-migrate-tests/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') + testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackModule('security'), configuration: 'runtime') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java b/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java index 2987c1afc8daf..e7f3a5ef48061 100644 --- a/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java +++ b/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.security; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -13,8 +14,10 @@ import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; import org.elasticsearch.xpack.core.security.SecurityField; +import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -40,6 +43,7 @@ * then run JUnit. If you changed the default port, set "tests.cluster=localhost:PORT" when running * your test. */ +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) @LuceneTestCase.SuppressSysoutChecks(bugUrl = "we log a lot on purpose") public abstract class MigrateToolTestCase extends LuceneTestCase { diff --git a/x-pack/qa/smoke-test-plugins-ssl/build.gradle b/x-pack/qa/smoke-test-plugins-ssl/build.gradle index 595c562af3707..7c1f7a8d0e558 100644 --- a/x-pack/qa/smoke-test-plugins-ssl/build.gradle +++ b/x-pack/qa/smoke-test-plugins-ssl/build.gradle @@ -16,6 +16,7 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') + testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') } String outputDir = "${buildDir}/generated-resources/${project.name}" diff --git a/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java b/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java index f8d1dd5e2b717..02c2faad2b085 100644 --- a/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java +++ b/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java @@ -17,6 +17,7 @@ import org.elasticsearch.xpack.core.action.XPackUsageResponse; import org.elasticsearch.xpack.core.monitoring.MonitoringFeatureSetUsage; import org.elasticsearch.xpack.core.security.SecurityField; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.junit.After; import org.junit.Before; @@ -41,7 +42,7 @@ * then uses a transport client to check that the data have been correctly received and * indexed in the cluster. */ -public class SmokeTestMonitoringWithSecurityIT extends ESIntegTestCase { +public class SmokeTestMonitoringWithSecurityIT extends XPackIntegTestCase { private static final String USER = "test_user"; private static final String PASS = "x-pack-test-password"; private static final String MONITORING_PATTERN = ".monitoring-*"; diff --git a/x-pack/qa/transport-client-tests/build.gradle b/x-pack/qa/transport-client-tests/build.gradle index c864a9084cba8..d179fee378c63 100644 --- a/x-pack/qa/transport-client-tests/build.gradle +++ b/x-pack/qa/transport-client-tests/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') + testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java b/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java index c77715431ec5e..4eb845ec9e90e 100644 --- a/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java +++ b/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java @@ -5,6 +5,7 @@ */ package org.elasticsearch.xpack.ml.client; +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -15,6 +16,7 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; +import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -46,6 +48,7 @@ * test. */ @LuceneTestCase.SuppressSysoutChecks(bugUrl = "we log a lot on purpose") +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public abstract class ESXPackSmokeClientTestCase extends LuceneTestCase { /** From 0c38c944afa3f474c5cf2e992044ee1d9021d811 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Tue, 12 Jun 2018 07:49:03 +0200 Subject: [PATCH 20/71] [INGEST] Interrupt the current thread if evaluation grok expressions take too long (#31024) This adds a thread interrupter that allows us to encapsulate calls to org.joni.Matcher#search() This method can hang forever if the regex expression is too complex. The thread interrupter in the background checks every 3 seconds whether there are threads execution the org.joni.Matcher#search() method for longer than 5 seconds and if so interrupts these threads. Joni has checks that that for every 30k iterations it checks if the current thread is interrupted and if so returns org.joni.Matcher#INTERRUPTED Closes #28731 --- docs/reference/ingest/ingest-node.asciidoc | 18 +++ .../java/org/elasticsearch/grok/Grok.java | 53 +++++-- .../elasticsearch/grok/ThreadWatchdog.java | 148 ++++++++++++++++++ .../org/elasticsearch/grok/GrokTests.java | 38 ++++- .../grok/ThreadWatchdogTests.java | 70 +++++++++ .../ingest/common/GrokProcessor.java | 12 +- .../ingest/common/IngestCommonPlugin.java | 20 ++- .../common/GrokProcessorFactoryTests.java | 17 +- .../ingest/common/GrokProcessorTests.java | 37 +++-- .../elasticsearch/ingest/IngestService.java | 9 +- .../org/elasticsearch/ingest/Processor.java | 16 +- 11 files changed, 385 insertions(+), 53 deletions(-) create mode 100644 libs/grok/src/main/java/org/elasticsearch/grok/ThreadWatchdog.java create mode 100644 libs/grok/src/test/java/org/elasticsearch/grok/ThreadWatchdogTests.java diff --git a/docs/reference/ingest/ingest-node.asciidoc b/docs/reference/ingest/ingest-node.asciidoc index 25065ee2eb19c..a7aaf0177f5b9 100644 --- a/docs/reference/ingest/ingest-node.asciidoc +++ b/docs/reference/ingest/ingest-node.asciidoc @@ -1512,6 +1512,24 @@ The above request will return a response body containing a key-value representat This can be useful to reference as the built-in patterns change across versions. +[[grok-watchdog]] +==== Grok watchdog + +Grok expressions that take too long to execute are interrupted and +the grok processor then fails with an exception. The grok +processor has a watchdog thread that determines when evaluation of +a grok expression takes too long and is controlled by the following +settings: + +[[grok-watchdog-options]] +.Grok watchdog settings +[options="header"] +|====== +| Name | Default | Description +| `ingest.grok.watchdog.interval` | 1s | How often to check whether there are grok evaluations that take longer than the maximum allowed execution time. +| `ingest.grok.watchdog.max_execution_time` | 1s | The maximum allowed execution of a grok expression evaluation. +|====== + [[gsub-processor]] === Gsub Processor Converts a string field by applying a regular expression and a replacement. diff --git a/libs/grok/src/main/java/org/elasticsearch/grok/Grok.java b/libs/grok/src/main/java/org/elasticsearch/grok/Grok.java index 3800c7711a2fd..02388d838bc2a 100644 --- a/libs/grok/src/main/java/org/elasticsearch/grok/Grok.java +++ b/libs/grok/src/main/java/org/elasticsearch/grok/Grok.java @@ -76,15 +76,24 @@ public final class Grok { private final Map patternBank; private final boolean namedCaptures; private final Regex compiledExpression; + private final ThreadWatchdog threadWatchdog; public Grok(Map patternBank, String grokPattern) { - this(patternBank, grokPattern, true); + this(patternBank, grokPattern, true, ThreadWatchdog.noop()); } - - @SuppressWarnings("unchecked") + + public Grok(Map patternBank, String grokPattern, ThreadWatchdog threadWatchdog) { + this(patternBank, grokPattern, true, threadWatchdog); + } + Grok(Map patternBank, String grokPattern, boolean namedCaptures) { + this(patternBank, grokPattern, namedCaptures, ThreadWatchdog.noop()); + } + + private Grok(Map patternBank, String grokPattern, boolean namedCaptures, ThreadWatchdog threadWatchdog) { this.patternBank = patternBank; this.namedCaptures = namedCaptures; + this.threadWatchdog = threadWatchdog; for (Map.Entry entry : patternBank.entrySet()) { String name = entry.getKey(); @@ -163,7 +172,13 @@ public String toRegex(String grokPattern) { byte[] grokPatternBytes = grokPattern.getBytes(StandardCharsets.UTF_8); Matcher matcher = GROK_PATTERN_REGEX.matcher(grokPatternBytes); - int result = matcher.search(0, grokPatternBytes.length, Option.NONE); + int result; + try { + threadWatchdog.register(); + result = matcher.search(0, grokPatternBytes.length, Option.NONE); + } finally { + threadWatchdog.unregister(); + } if (result != -1) { Region region = matcher.getEagerRegion(); String namedPatternRef = groupMatch(NAME_GROUP, region, grokPattern); @@ -205,7 +220,13 @@ public String toRegex(String grokPattern) { */ public boolean match(String text) { Matcher matcher = compiledExpression.matcher(text.getBytes(StandardCharsets.UTF_8)); - int result = matcher.search(0, text.length(), Option.DEFAULT); + int result; + try { + threadWatchdog.register(); + result = matcher.search(0, text.length(), Option.DEFAULT); + } finally { + threadWatchdog.unregister(); + } return (result != -1); } @@ -220,8 +241,20 @@ public Map captures(String text) { byte[] textAsBytes = text.getBytes(StandardCharsets.UTF_8); Map fields = new HashMap<>(); Matcher matcher = compiledExpression.matcher(textAsBytes); - int result = matcher.search(0, textAsBytes.length, Option.DEFAULT); - if (result != -1 && compiledExpression.numberOfNames() > 0) { + int result; + try { + threadWatchdog.register(); + result = matcher.search(0, textAsBytes.length, Option.DEFAULT); + } finally { + threadWatchdog.unregister(); + } + if (result == Matcher.INTERRUPTED) { + throw new RuntimeException("grok pattern matching was interrupted after [" + + threadWatchdog.maxExecutionTimeInMillis() + "] ms"); + } else if (result == Matcher.FAILED) { + // TODO: I think we should throw an error here? + return null; + } else if (compiledExpression.numberOfNames() > 0) { Region region = matcher.getEagerRegion(); for (Iterator entry = compiledExpression.namedBackrefIterator(); entry.hasNext();) { NameEntry e = entry.next(); @@ -235,13 +268,9 @@ public Map captures(String text) { break; } } - } - return fields; - } else if (result != -1) { - return fields; } - return null; + return fields; } public static Map getBuiltinPatterns() { diff --git a/libs/grok/src/main/java/org/elasticsearch/grok/ThreadWatchdog.java b/libs/grok/src/main/java/org/elasticsearch/grok/ThreadWatchdog.java new file mode 100644 index 0000000000000..d0de7637d2c08 --- /dev/null +++ b/libs/grok/src/main/java/org/elasticsearch/grok/ThreadWatchdog.java @@ -0,0 +1,148 @@ +/* + * 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.grok; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; +import java.util.function.BiFunction; +import java.util.function.LongSupplier; + +/** + * Protects against long running operations that happen between the register and unregister invocations. + * Threads that invoke {@link #register()}, but take too long to invoke the {@link #unregister()} method + * will be interrupted. + * + * This is needed for Joni's {@link org.joni.Matcher#search(int, int, int)} method, because + * it can end up spinning endlessly if the regular expression is too complex. Joni has checks + * that for every 30k iterations it checks if the current thread is interrupted and if so + * returns {@link org.joni.Matcher#INTERRUPTED}. + */ +public interface ThreadWatchdog { + + /** + * Registers the current thread and interrupts the current thread + * if the takes too long for this thread to invoke {@link #unregister()}. + */ + void register(); + + /** + * @return The maximum allowed time in milliseconds for a thread to invoke {@link #unregister()} + * after {@link #register()} has been invoked before this ThreadWatchDog starts to interrupting that thread. + */ + long maxExecutionTimeInMillis(); + + /** + * Unregisters the current thread and prevents it from being interrupted. + */ + void unregister(); + + /** + * Returns an implementation that checks for each fixed interval if there are threads that have invoked {@link #register()} + * and not {@link #unregister()} and have been in this state for longer than the specified max execution interval and + * then interrupts these threads. + * + * @param interval The fixed interval to check if there are threads to interrupt + * @param maxExecutionTime The time a thread has the execute an operation. + * @param relativeTimeSupplier A supplier that returns relative time + * @param scheduler A scheduler that is able to execute a command for each fixed interval + */ + static ThreadWatchdog newInstance(long interval, + long maxExecutionTime, + LongSupplier relativeTimeSupplier, + BiFunction> scheduler) { + return new Default(interval, maxExecutionTime, relativeTimeSupplier, scheduler); + } + + /** + * @return A noop implementation that does not interrupt threads and is useful for testing and pre-defined grok expressions. + */ + static ThreadWatchdog noop() { + return Noop.INSTANCE; + } + + class Noop implements ThreadWatchdog { + + private static final Noop INSTANCE = new Noop(); + + private Noop() { + } + + @Override + public void register() { + } + + @Override + public long maxExecutionTimeInMillis() { + return Long.MAX_VALUE; + } + + @Override + public void unregister() { + } + } + + class Default implements ThreadWatchdog { + + private final long interval; + private final long maxExecutionTime; + private final LongSupplier relativeTimeSupplier; + private final BiFunction> scheduler; + final ConcurrentHashMap registry = new ConcurrentHashMap<>(); + + private Default(long interval, + long maxExecutionTime, + LongSupplier relativeTimeSupplier, + BiFunction> scheduler) { + this.interval = interval; + this.maxExecutionTime = maxExecutionTime; + this.relativeTimeSupplier = relativeTimeSupplier; + this.scheduler = scheduler; + scheduler.apply(interval, this::interruptLongRunningExecutions); + } + + public void register() { + Long previousValue = registry.put(Thread.currentThread(), relativeTimeSupplier.getAsLong()); + assert previousValue == null; + } + + @Override + public long maxExecutionTimeInMillis() { + return maxExecutionTime; + } + + public void unregister() { + Long previousValue = registry.remove(Thread.currentThread()); + assert previousValue != null; + } + + private void interruptLongRunningExecutions() { + final long currentRelativeTime = relativeTimeSupplier.getAsLong(); + for (Map.Entry entry : registry.entrySet()) { + if ((currentRelativeTime - entry.getValue()) > maxExecutionTime) { + entry.getKey().interrupt(); + // not removing the entry here, this happens in the unregister() method. + } + } + scheduler.apply(interval, this::interruptLongRunningExecutions); + } + + } + +} diff --git a/libs/grok/src/test/java/org/elasticsearch/grok/GrokTests.java b/libs/grok/src/test/java/org/elasticsearch/grok/GrokTests.java index 92ab8daba1f12..983c84cf76b4c 100644 --- a/libs/grok/src/test/java/org/elasticsearch/grok/GrokTests.java +++ b/libs/grok/src/test/java/org/elasticsearch/grok/GrokTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.grok; import org.elasticsearch.test.ESTestCase; -import org.junit.Before; import java.util.Arrays; import java.util.Collections; @@ -28,6 +27,9 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiFunction; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -35,12 +37,7 @@ public class GrokTests extends ESTestCase { - private Map basePatterns; - - @Before - public void setup() { - basePatterns = Grok.getBuiltinPatterns(); - } + private static final Map basePatterns = Grok.getBuiltinPatterns(); public void testMatchWithoutCaptures() { String line = "value"; @@ -382,4 +379,31 @@ public void testMultipleNamedCapturesWithSameName() { expected.put("num", "1"); assertThat(grok.captures("12"), equalTo(expected)); } + + public void testExponentialExpressions() { + AtomicBoolean run = new AtomicBoolean(true); // to avoid a lingering thread when test has completed + + String grokPattern = "Bonsuche mit folgender Anfrage: Belegart->\\[%{WORD:param2},(?(\\s*%{NOTSPACE})*)\\] " + + "Zustand->ABGESCHLOSSEN Kassennummer->%{WORD:param9} Bonnummer->%{WORD:param10} Datum->%{DATESTAMP_OTHER:param11}"; + String logLine = "Bonsuche mit folgender Anfrage: Belegart->[EINGESCHRAENKTER_VERKAUF, VERKAUF, NACHERFASSUNG] " + + "Zustand->ABGESCHLOSSEN Kassennummer->2 Bonnummer->6362 Datum->Mon Jan 08 00:00:00 UTC 2018"; + BiFunction> scheduler = (delay, command) -> { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + Thread t = new Thread(() -> { + if (run.get()) { + command.run(); + } + }); + t.start(); + return null; + }; + Grok grok = new Grok(basePatterns, grokPattern, ThreadWatchdog.newInstance(10, 200, System::currentTimeMillis, scheduler)); + Exception e = expectThrows(RuntimeException.class, () -> grok.captures(logLine)); + run.set(false); + assertThat(e.getMessage(), equalTo("grok pattern matching was interrupted after [200] ms")); + } } diff --git a/libs/grok/src/test/java/org/elasticsearch/grok/ThreadWatchdogTests.java b/libs/grok/src/test/java/org/elasticsearch/grok/ThreadWatchdogTests.java new file mode 100644 index 0000000000000..46faa4ae05d38 --- /dev/null +++ b/libs/grok/src/test/java/org/elasticsearch/grok/ThreadWatchdogTests.java @@ -0,0 +1,70 @@ +/* + * 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.grok; + +import org.elasticsearch.test.ESTestCase; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.hamcrest.Matchers.is; + +public class ThreadWatchdogTests extends ESTestCase { + + public void testInterrupt() throws Exception { + AtomicBoolean run = new AtomicBoolean(true); // to avoid a lingering thread when test has completed + ThreadWatchdog watchdog = ThreadWatchdog.newInstance(10, 100, System::currentTimeMillis, (delay, command) -> { + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + Thread thread = new Thread(() -> { + if (run.get()) { + command.run(); + } + }); + thread.start(); + return null; + }); + + Map registry = ((ThreadWatchdog.Default) watchdog).registry; + assertThat(registry.size(), is(0)); + // need to call #register() method on a different thread, assertBusy() fails if current thread gets interrupted + AtomicBoolean interrupted = new AtomicBoolean(false); + Thread thread = new Thread(() -> { + Thread currentThread = Thread.currentThread(); + watchdog.register(); + while (currentThread.isInterrupted() == false) {} + interrupted.set(true); + while (run.get()) {} // wait here so that the size of the registry can be asserted + watchdog.unregister(); + }); + thread.start(); + assertBusy(() -> { + assertThat(interrupted.get(), is(true)); + assertThat(registry.size(), is(1)); + }); + run.set(false); + assertBusy(() -> { + assertThat(registry.size(), is(0)); + }); + } + +} diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GrokProcessor.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GrokProcessor.java index 8d1d2127e7213..7bb3ebfba6e36 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GrokProcessor.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/GrokProcessor.java @@ -20,6 +20,7 @@ package org.elasticsearch.ingest.common; import org.elasticsearch.grok.Grok; +import org.elasticsearch.grok.ThreadWatchdog; import org.elasticsearch.ingest.AbstractProcessor; import org.elasticsearch.ingest.ConfigurationUtils; import org.elasticsearch.ingest.IngestDocument; @@ -43,11 +44,11 @@ public final class GrokProcessor extends AbstractProcessor { private final boolean ignoreMissing; GrokProcessor(String tag, Map patternBank, List matchPatterns, String matchField, - boolean traceMatch, boolean ignoreMissing) { + boolean traceMatch, boolean ignoreMissing, ThreadWatchdog threadWatchdog) { super(tag); this.matchField = matchField; this.matchPatterns = matchPatterns; - this.grok = new Grok(patternBank, combinePatterns(matchPatterns, traceMatch)); + this.grok = new Grok(patternBank, combinePatterns(matchPatterns, traceMatch), threadWatchdog); this.traceMatch = traceMatch; this.ignoreMissing = ignoreMissing; } @@ -132,9 +133,11 @@ static String combinePatterns(List patterns, boolean traceMatch) { public static final class Factory implements Processor.Factory { private final Map builtinPatterns; + private final ThreadWatchdog threadWatchdog; - public Factory(Map builtinPatterns) { + public Factory(Map builtinPatterns, ThreadWatchdog threadWatchdog) { this.builtinPatterns = builtinPatterns; + this.threadWatchdog = threadWatchdog; } @Override @@ -155,7 +158,8 @@ public GrokProcessor create(Map registry, String proc } try { - return new GrokProcessor(processorTag, patternBank, matchPatterns, matchField, traceMatch, ignoreMissing); + return new GrokProcessor(processorTag, patternBank, matchPatterns, matchField, traceMatch, ignoreMissing, + threadWatchdog); } catch (Exception e) { throw newConfigurationException(TYPE, processorTag, "patterns", "Invalid regex pattern found in: " + matchPatterns + ". " + e.getMessage()); diff --git a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/IngestCommonPlugin.java b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/IngestCommonPlugin.java index a29c994f10d37..591060098b728 100644 --- a/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/IngestCommonPlugin.java +++ b/modules/ingest-common/src/main/java/org/elasticsearch/ingest/common/IngestCommonPlugin.java @@ -25,9 +25,12 @@ import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.grok.Grok; +import org.elasticsearch.grok.ThreadWatchdog; import org.elasticsearch.ingest.Processor; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.IngestPlugin; @@ -45,6 +48,10 @@ public class IngestCommonPlugin extends Plugin implements ActionPlugin, IngestPlugin { static final Map GROK_PATTERNS = Grok.getBuiltinPatterns(); + static final Setting WATCHDOG_INTERVAL = + Setting.timeSetting("ingest.grok.watchdog.interval", TimeValue.timeValueSeconds(1), Setting.Property.NodeScope); + static final Setting WATCHDOG_MAX_EXECUTION_TIME = + Setting.timeSetting("ingest.grok.watchdog.max_execution_time", TimeValue.timeValueSeconds(1), Setting.Property.NodeScope); public IngestCommonPlugin() { } @@ -68,7 +75,7 @@ public Map getProcessors(Processor.Parameters paramet processors.put(ForEachProcessor.TYPE, new ForEachProcessor.Factory()); processors.put(DateIndexNameProcessor.TYPE, new DateIndexNameProcessor.Factory()); processors.put(SortProcessor.TYPE, new SortProcessor.Factory()); - processors.put(GrokProcessor.TYPE, new GrokProcessor.Factory(GROK_PATTERNS)); + processors.put(GrokProcessor.TYPE, new GrokProcessor.Factory(GROK_PATTERNS, createGrokThreadWatchdog(parameters))); processors.put(ScriptProcessor.TYPE, new ScriptProcessor.Factory(parameters.scriptService)); processors.put(DotExpanderProcessor.TYPE, new DotExpanderProcessor.Factory()); processors.put(JsonProcessor.TYPE, new JsonProcessor.Factory()); @@ -89,5 +96,16 @@ public List getRestHandlers(Settings settings, RestController restC Supplier nodesInCluster) { return Arrays.asList(new GrokProcessorGetAction.RestAction(settings, restController)); } + + @Override + public List> getSettings() { + return Arrays.asList(WATCHDOG_INTERVAL, WATCHDOG_MAX_EXECUTION_TIME); + } + + private static ThreadWatchdog createGrokThreadWatchdog(Processor.Parameters parameters) { + long intervalMillis = WATCHDOG_INTERVAL.get(parameters.env.settings()).getMillis(); + long maxExecutionTimeMillis = WATCHDOG_MAX_EXECUTION_TIME.get(parameters.env.settings()).getMillis(); + return ThreadWatchdog.newInstance(intervalMillis, maxExecutionTimeMillis, parameters.relativeTimeSupplier, parameters.scheduler); + } } diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorFactoryTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorFactoryTests.java index 4cac94cd5b571..f35fa34eec46d 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorFactoryTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorFactoryTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.ingest.common; import org.elasticsearch.ElasticsearchParseException; +import org.elasticsearch.grok.ThreadWatchdog; import org.elasticsearch.test.ESTestCase; import java.util.Collections; @@ -33,7 +34,7 @@ public class GrokProcessorFactoryTests extends ESTestCase { public void testBuild() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("field", "_field"); @@ -47,7 +48,7 @@ public void testBuild() throws Exception { } public void testBuildWithIgnoreMissing() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("field", "_field"); @@ -62,7 +63,7 @@ public void testBuildWithIgnoreMissing() throws Exception { } public void testBuildMissingField() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("patterns", Collections.singletonList("(?\\w+)")); ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, null, config)); @@ -70,7 +71,7 @@ public void testBuildMissingField() throws Exception { } public void testBuildMissingPatterns() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("field", "foo"); ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, null, config)); @@ -78,7 +79,7 @@ public void testBuildMissingPatterns() throws Exception { } public void testBuildEmptyPatternsList() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("field", "foo"); config.put("patterns", Collections.emptyList()); @@ -87,7 +88,7 @@ public void testBuildEmptyPatternsList() throws Exception { } public void testCreateWithCustomPatterns() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("field", "_field"); @@ -100,7 +101,7 @@ public void testCreateWithCustomPatterns() throws Exception { } public void testCreateWithInvalidPattern() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("field", "_field"); config.put("patterns", Collections.singletonList("[")); @@ -109,7 +110,7 @@ public void testCreateWithInvalidPattern() throws Exception { } public void testCreateWithInvalidPatternDefinition() throws Exception { - GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap()); + GrokProcessor.Factory factory = new GrokProcessor.Factory(Collections.emptyMap(), ThreadWatchdog.noop()); Map config = new HashMap<>(); config.put("field", "_field"); config.put("patterns", Collections.singletonList("%{MY_PATTERN:name}!")); diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorTests.java index 37c26db4b74f3..0eba79523aca2 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/GrokProcessorTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.ingest.common; +import org.elasticsearch.grok.ThreadWatchdog; import org.elasticsearch.ingest.IngestDocument; import org.elasticsearch.ingest.RandomDocumentPicks; import org.elasticsearch.test.ESTestCase; @@ -39,7 +40,7 @@ public void testMatch() throws Exception { IngestDocument doc = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); doc.setFieldValue(fieldName, "1"); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, false); + Collections.singletonList("%{ONE:one}"), fieldName, false, false, ThreadWatchdog.noop()); processor.execute(doc); assertThat(doc.getFieldValue("one", String.class), equalTo("1")); } @@ -49,7 +50,7 @@ public void testNoMatch() { IngestDocument doc = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); doc.setFieldValue(fieldName, "23"); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, false); + Collections.singletonList("%{ONE:one}"), fieldName, false, false, ThreadWatchdog.noop()); Exception e = expectThrows(Exception.class, () -> processor.execute(doc)); assertThat(e.getMessage(), equalTo("Provided Grok expressions do not match field value: [23]")); } @@ -60,7 +61,7 @@ public void testNoMatchingPatternName() { doc.setFieldValue(fieldName, "23"); Exception e = expectThrows(IllegalArgumentException.class, () -> new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), Collections.singletonList("%{NOTONE:not_one}"), fieldName, - false, false)); + false, false, ThreadWatchdog.noop())); assertThat(e.getMessage(), equalTo("Unable to find pattern [NOTONE] in Grok's pattern dictionary")); } @@ -70,7 +71,7 @@ public void testMatchWithoutCaptures() throws Exception { originalDoc.setFieldValue(fieldName, fieldName); IngestDocument doc = new IngestDocument(originalDoc); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.emptyMap(), - Collections.singletonList(fieldName), fieldName, false, false); + Collections.singletonList(fieldName), fieldName, false, false, ThreadWatchdog.noop()); processor.execute(doc); assertThat(doc, equalTo(originalDoc)); } @@ -80,7 +81,7 @@ public void testNullField() { IngestDocument doc = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); doc.setFieldValue(fieldName, null); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, false); + Collections.singletonList("%{ONE:one}"), fieldName, false, false, ThreadWatchdog.noop()); Exception e = expectThrows(Exception.class, () -> processor.execute(doc)); assertThat(e.getMessage(), equalTo("field [" + fieldName + "] is null, cannot process it.")); } @@ -91,7 +92,7 @@ public void testNullFieldWithIgnoreMissing() throws Exception { originalIngestDocument.setFieldValue(fieldName, null); IngestDocument ingestDocument = new IngestDocument(originalIngestDocument); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, true); + Collections.singletonList("%{ONE:one}"), fieldName, false, true, ThreadWatchdog.noop()); processor.execute(ingestDocument); assertIngestDocument(originalIngestDocument, ingestDocument); } @@ -101,7 +102,7 @@ public void testNotStringField() { IngestDocument doc = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); doc.setFieldValue(fieldName, 1); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, false); + Collections.singletonList("%{ONE:one}"), fieldName, false, false, ThreadWatchdog.noop()); Exception e = expectThrows(Exception.class, () -> processor.execute(doc)); assertThat(e.getMessage(), equalTo("field [" + fieldName + "] of type [java.lang.Integer] cannot be cast to [java.lang.String]")); } @@ -111,7 +112,7 @@ public void testNotStringFieldWithIgnoreMissing() { IngestDocument doc = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); doc.setFieldValue(fieldName, 1); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, true); + Collections.singletonList("%{ONE:one}"), fieldName, false, true, ThreadWatchdog.noop()); Exception e = expectThrows(Exception.class, () -> processor.execute(doc)); assertThat(e.getMessage(), equalTo("field [" + fieldName + "] of type [java.lang.Integer] cannot be cast to [java.lang.String]")); } @@ -120,7 +121,7 @@ public void testMissingField() { String fieldName = "foo.bar"; IngestDocument doc = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, false); + Collections.singletonList("%{ONE:one}"), fieldName, false, false, ThreadWatchdog.noop()); Exception e = expectThrows(Exception.class, () -> processor.execute(doc)); assertThat(e.getMessage(), equalTo("field [foo] not present as part of path [foo.bar]")); } @@ -130,7 +131,7 @@ public void testMissingFieldWithIgnoreMissing() throws Exception { IngestDocument originalIngestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); IngestDocument ingestDocument = RandomDocumentPicks.randomIngestDocument(random(), new HashMap<>()); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), Collections.singletonMap("ONE", "1"), - Collections.singletonList("%{ONE:one}"), fieldName, false, true); + Collections.singletonList("%{ONE:one}"), fieldName, false, true, ThreadWatchdog.noop()); processor.execute(ingestDocument); assertIngestDocument(originalIngestDocument, ingestDocument); } @@ -144,7 +145,7 @@ public void testMultiplePatternsWithMatchReturn() throws Exception { patternBank.put("TWO", "2"); patternBank.put("THREE", "3"); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), patternBank, - Arrays.asList("%{ONE:one}", "%{TWO:two}", "%{THREE:three}"), fieldName, false, false); + Arrays.asList("%{ONE:one}", "%{TWO:two}", "%{THREE:three}"), fieldName, false, false, ThreadWatchdog.noop()); processor.execute(doc); assertThat(doc.hasField("one"), equalTo(false)); assertThat(doc.getFieldValue("two", String.class), equalTo("2")); @@ -160,7 +161,7 @@ public void testSetMetadata() throws Exception { patternBank.put("TWO", "2"); patternBank.put("THREE", "3"); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), patternBank, - Arrays.asList("%{ONE:one}", "%{TWO:two}", "%{THREE:three}"), fieldName, true, false); + Arrays.asList("%{ONE:one}", "%{TWO:two}", "%{THREE:three}"), fieldName, true, false, ThreadWatchdog.noop()); processor.execute(doc); assertThat(doc.hasField("one"), equalTo(false)); assertThat(doc.getFieldValue("two", String.class), equalTo("2")); @@ -175,7 +176,7 @@ public void testTraceWithOnePattern() throws Exception { Map patternBank = new HashMap<>(); patternBank.put("ONE", "1"); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), patternBank, - Arrays.asList("%{ONE:one}"), fieldName, true, false); + Arrays.asList("%{ONE:one}"), fieldName, true, false, ThreadWatchdog.noop()); processor.execute(doc); assertThat(doc.hasField("one"), equalTo(true)); assertThat(doc.getFieldValue("_ingest._grok_match_index", String.class), equalTo("0")); @@ -205,8 +206,8 @@ public void testCombineSamePatternNameAcrossPatterns() throws Exception { patternBank.put("ONE", "1"); patternBank.put("TWO", "2"); patternBank.put("THREE", "3"); - GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), patternBank, - Arrays.asList("%{ONE:first}-%{TWO:second}", "%{ONE:first}-%{THREE:second}"), fieldName, randomBoolean(), randomBoolean()); + GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), patternBank, Arrays.asList("%{ONE:first}-%{TWO:second}", + "%{ONE:first}-%{THREE:second}"), fieldName, randomBoolean(), randomBoolean(), ThreadWatchdog.noop()); processor.execute(doc); assertThat(doc.getFieldValue("first", String.class), equalTo("1")); assertThat(doc.getFieldValue("second", String.class), equalTo("3")); @@ -219,7 +220,8 @@ public void testFirstWinNamedCapture() throws Exception { Map patternBank = new HashMap<>(); patternBank.put("ONETWO", "1|2"); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), patternBank, - Collections.singletonList("%{ONETWO:first}%{ONETWO:first}"), fieldName, randomBoolean(), randomBoolean()); + Collections.singletonList("%{ONETWO:first}%{ONETWO:first}"), fieldName, randomBoolean(), randomBoolean(), + ThreadWatchdog.noop()); processor.execute(doc); assertThat(doc.getFieldValue("first", String.class), equalTo("1")); } @@ -232,7 +234,8 @@ public void testUnmatchedNamesNotIncludedInDocument() throws Exception { patternBank.put("ONETWO", "1|2"); patternBank.put("THREE", "3"); GrokProcessor processor = new GrokProcessor(randomAlphaOfLength(10), patternBank, - Collections.singletonList("%{ONETWO:first}|%{THREE:second}"), fieldName, randomBoolean(), randomBoolean()); + Collections.singletonList("%{ONETWO:first}|%{THREE:second}"), fieldName, randomBoolean(), randomBoolean(), + ThreadWatchdog.noop()); processor.execute(doc); assertFalse(doc.hasField("first")); assertThat(doc.getFieldValue("second", String.class), equalTo("3")); diff --git a/server/src/main/java/org/elasticsearch/ingest/IngestService.java b/server/src/main/java/org/elasticsearch/ingest/IngestService.java index ad2b8643f7ae3..46b11f7ac141d 100644 --- a/server/src/main/java/org/elasticsearch/ingest/IngestService.java +++ b/server/src/main/java/org/elasticsearch/ingest/IngestService.java @@ -24,8 +24,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.function.BiFunction; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.env.Environment; import org.elasticsearch.index.analysis.AnalysisRegistry; import org.elasticsearch.plugins.IngestPlugin; @@ -42,8 +45,10 @@ public class IngestService { public IngestService(Settings settings, ThreadPool threadPool, Environment env, ScriptService scriptService, AnalysisRegistry analysisRegistry, List ingestPlugins) { - Processor.Parameters parameters = new Processor.Parameters(env, scriptService, - analysisRegistry, threadPool.getThreadContext()); + BiFunction> scheduler = + (delay, command) -> threadPool.schedule(TimeValue.timeValueMillis(delay), ThreadPool.Names.GENERIC, command); + Processor.Parameters parameters = new Processor.Parameters(env, scriptService, analysisRegistry, + threadPool.getThreadContext(), threadPool::relativeTimeInMillis, scheduler); Map processorFactories = new HashMap<>(); for (IngestPlugin ingestPlugin : ingestPlugins) { Map newProcessors = ingestPlugin.getProcessors(parameters); diff --git a/server/src/main/java/org/elasticsearch/ingest/Processor.java b/server/src/main/java/org/elasticsearch/ingest/Processor.java index 39d74fb09a945..c318d478814de 100644 --- a/server/src/main/java/org/elasticsearch/ingest/Processor.java +++ b/server/src/main/java/org/elasticsearch/ingest/Processor.java @@ -25,6 +25,9 @@ import org.elasticsearch.script.ScriptService; import java.util.Map; +import java.util.concurrent.ScheduledFuture; +import java.util.function.BiFunction; +import java.util.function.LongSupplier; /** * A processor implementation may modify the data belonging to a document. @@ -94,13 +97,22 @@ class Parameters { * instances that have run prior to in ingest. */ public final ThreadContext threadContext; + + public final LongSupplier relativeTimeSupplier; + + /** + * Provides scheduler support + */ + public final BiFunction> scheduler; - public Parameters(Environment env, ScriptService scriptService, - AnalysisRegistry analysisRegistry, ThreadContext threadContext) { + public Parameters(Environment env, ScriptService scriptService, AnalysisRegistry analysisRegistry, ThreadContext threadContext, + LongSupplier relativeTimeSupplier, BiFunction> scheduler) { this.env = env; this.scriptService = scriptService; this.threadContext = threadContext; this.analysisRegistry = analysisRegistry; + this.relativeTimeSupplier = relativeTimeSupplier; + this.scheduler = scheduler; } } From b9944aa63763f12bf7ef45f805d1c43f98b7ec0d Mon Sep 17 00:00:00 2001 From: Aditya Dhulipala Date: Tue, 12 Jun 2018 00:53:36 -0700 Subject: [PATCH 21/71] Validate xContentType in PutWatchRequest. (#31088) Trying to post a new watch without any body currently results in a NullPointerException. This change fixes that by validating that Post and Put requests always have a body. Closes #30057 --- .../transport/actions/put/PutWatchRequest.java | 3 +++ .../rest-api-spec/api/xpack.watcher.put_watch.json | 2 +- .../test/watcher/put_watch/10_basic.yml | 13 +++++++++++++ .../action/WatchRequestValidationTests.java | 6 ++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/put/PutWatchRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/put/PutWatchRequest.java index 21b3fc39bba10..a29b3b0a86b2e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/put/PutWatchRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/put/PutWatchRequest.java @@ -126,6 +126,9 @@ public ActionRequestValidationException validate() { if (source == null) { validationException = ValidateActions.addValidationError("watch source is missing", validationException); } + if (xContentType == null) { + validationException = ValidateActions.addValidationError("request body is missing", validationException); + } return validationException; } diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/api/xpack.watcher.put_watch.json b/x-pack/plugin/src/test/resources/rest-api-spec/api/xpack.watcher.put_watch.json index d41f8ea221eac..7e29aeaaf43f7 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/api/xpack.watcher.put_watch.json +++ b/x-pack/plugin/src/test/resources/rest-api-spec/api/xpack.watcher.put_watch.json @@ -29,7 +29,7 @@ }, "body": { "description" : "The watch", - "required" : true + "required" : false } } } diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/watcher/put_watch/10_basic.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/watcher/put_watch/10_basic.yml index 50bb7e60ff41c..74d78820560a6 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/watcher/put_watch/10_basic.yml +++ b/x-pack/plugin/src/test/resources/rest-api-spec/test/watcher/put_watch/10_basic.yml @@ -37,3 +37,16 @@ } } - match: { _id: "my_watch" } + +--- +"Test empty body is rejected by put watch": + - do: + cluster.health: + wait_for_status: yellow + + - do: + catch: bad_request + xpack.watcher.put_watch: + id: "my_watch" + - match: { error.root_cause.0.type: "action_request_validation_exception" } + - match: { error.root_cause.0.reason: "Validation Failed: 1: request body is missing;" } diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transport/action/WatchRequestValidationTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transport/action/WatchRequestValidationTests.java index 05505398ff5c9..e2e13b3022b9b 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transport/action/WatchRequestValidationTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/transport/action/WatchRequestValidationTests.java @@ -86,6 +86,12 @@ public void testPutWatchSourceNull() { assertThat(e.validationErrors(), hasItem("watch source is missing")); } + public void testPutWatchContentNull() { + ActionRequestValidationException e = new PutWatchRequest("foo", BytesArray.EMPTY, null).validate(); + assertThat(e, is(notNullValue())); + assertThat(e.validationErrors(), hasItem("request body is missing")); + } + public void testGetWatchInvalidWatchId() { ActionRequestValidationException e = new GetWatchRequest("id with whitespaces").validate(); assertThat(e, is(notNullValue())); From 6af8eb3213ebc95296b58869e43f70d33f470650 Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Tue, 12 Jun 2018 12:23:40 +0300 Subject: [PATCH 22/71] Support RequestedAuthnContext (#31238) This implements limited support for RequestedAuthnContext by : - Allowing SP administrators to define a list of authnContextClassRef to be included in the RequestedAuthnContext of a SAML Authn Request - Veirifying that the authnContext in the incoming SAML Asertion's AuthnStatement contains one of the requested authnContextClassRef - Only EXACT comparison is supported as the semantics of validating the incoming authnContextClassRef are deployment dependant and require pre-established rules for MINIMUM, MAXIMUM and BETTER Also adds necessary AuthnStatement validation as indicated by [1] and [2] [1] https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf 3.4.1.4, line 2250-2253 [2] https://kantarainitiative.github.io/SAMLprofiles/saml2int.html [SDP-IDP10] --- .../authc/saml/SamlRealmSettings.java | 5 +- .../authc/saml/SamlAuthenticator.java | 31 +++- .../authc/saml/SamlAuthnRequestBuilder.java | 22 ++- .../xpack/security/authc/saml/SamlRealm.java | 4 +- .../security/authc/saml/SpConfiguration.java | 9 +- .../authc/saml/SamlAuthenticatorTests.java | 157 +++++++++++++++++- .../saml/SamlAuthnRequestBuilderTests.java | 82 ++++++++- .../saml/SamlLogoutRequestHandlerTests.java | 2 +- .../SamlLogoutRequestMessageBuilderTests.java | 5 +- .../authc/saml/SamlRealmTestHelper.java | 2 +- .../security/authc/saml/SamlRealmTests.java | 10 +- 11 files changed, 298 insertions(+), 31 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java index 996b886c0dbcf..cf28b995127c3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/saml/SamlRealmSettings.java @@ -62,7 +62,8 @@ public class SamlRealmSettings { Setting.simpleString("signing.keystore.alias", Setting.Property.NodeScope); public static final Setting> SIGNING_MESSAGE_TYPES = Setting.listSetting("signing.saml_messages", Collections.singletonList("*"), Function.identity(), Setting.Property.NodeScope); - + public static final Setting> REQUESTED_AUTHN_CONTEXT_CLASS_REF = Setting.listSetting("req_authn_context_class_ref", + Collections.emptyList(), Function.identity(),Setting.Property.NodeScope); public static final Setting CLOCK_SKEW = Setting.positiveTimeSetting("allowed_clock_skew", TimeValue.timeValueMinutes(3), Setting.Property.NodeScope); @@ -79,7 +80,7 @@ public static Set> getSettings() { SP_ENTITY_ID, SP_ACS, SP_LOGOUT, NAMEID_FORMAT, NAMEID_ALLOW_CREATE, NAMEID_SP_QUALIFIER, FORCE_AUTHN, POPULATE_USER_METADATA, CLOCK_SKEW, - ENCRYPTION_KEY_ALIAS, SIGNING_KEY_ALIAS, SIGNING_MESSAGE_TYPES); + ENCRYPTION_KEY_ALIAS, SIGNING_KEY_ALIAS, SIGNING_MESSAGE_TYPES, REQUESTED_AUTHN_CONTEXT_CLASS_REF); set.addAll(ENCRYPTION_SETTINGS.getAllSettings()); set.addAll(SIGNING_SETTINGS.getAllSettings()); set.addAll(SSLConfigurationSettings.withPrefix(SSL_PREFIX).getAllSettings()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java index 61e451150cd08..c780055edd5ba 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticator.java @@ -26,6 +26,7 @@ import org.opensaml.saml.saml2.core.AttributeStatement; import org.opensaml.saml.saml2.core.Audience; import org.opensaml.saml.saml2.core.AudienceRestriction; +import org.opensaml.saml.saml2.core.AuthnStatement; import org.opensaml.saml.saml2.core.Conditions; import org.opensaml.saml.saml2.core.EncryptedAssertion; import org.opensaml.saml.saml2.core.EncryptedAttribute; @@ -219,6 +220,7 @@ private List processAssertion(Assertion assertion, boolean requireSig checkConditions(assertion.getConditions()); checkIssuer(assertion.getIssuer(), assertion); checkSubject(assertion.getSubject(), assertion, allowedSamlRequestIds); + checkAuthnStatement(assertion.getAuthnStatements()); List attributes = new ArrayList<>(); for (AttributeStatement statement : assertion.getAttributeStatements()) { @@ -236,6 +238,33 @@ private List processAssertion(Assertion assertion, boolean requireSig return attributes; } + private void checkAuthnStatement(List authnStatements) { + if (authnStatements.size() != 1) { + throw samlException("SAML Assertion subject contains {} Authn Statements while exactly one was expected.", + authnStatements.size()); + } + final AuthnStatement authnStatement = authnStatements.get(0); + // "past now" that is now - the maximum skew we will tolerate. Essentially "if our clock is 2min fast, what time is it now?" + final Instant now = now(); + final Instant pastNow = now.minusMillis(maxSkewInMillis()); + if (authnStatement.getSessionNotOnOrAfter() != null && + pastNow.isBefore(toInstant(authnStatement.getSessionNotOnOrAfter())) == false) { + throw samlException("Rejecting SAML assertion's Authentication Statement because [{}] is on/after [{}]", pastNow, + authnStatement.getSessionNotOnOrAfter()); + } + List reqAuthnCtxClassRef = this.getSpConfiguration().getReqAuthnCtxClassRef(); + if (reqAuthnCtxClassRef.isEmpty() == false) { + String authnCtxClassRefValue = null; + if (authnStatement.getAuthnContext() != null && authnStatement.getAuthnContext().getAuthnContextClassRef() != null) { + authnCtxClassRefValue = authnStatement.getAuthnContext().getAuthnContextClassRef().getAuthnContextClassRef(); + } + if (Strings.isNullOrEmpty(authnCtxClassRefValue) || reqAuthnCtxClassRef.contains(authnCtxClassRefValue) == false) { + throw samlException("Rejecting SAML assertion as the AuthnContextClassRef [{}] is not one of the ({}) that were " + + "requested in the corresponding AuthnRequest", authnCtxClassRefValue, reqAuthnCtxClassRef); + } + } + } + private Attribute decrypt(EncryptedAttribute encrypted) { if (decrypter == null) { logger.info("SAML message has encrypted attribute [" + text(encrypted, 32) + "], but no encryption key has been configured"); @@ -254,7 +283,7 @@ private void checkConditions(Conditions conditions) { if (logger.isTraceEnabled()) { logger.trace("SAML Assertion was intended for the following Service providers: {}", conditions.getAudienceRestrictions().stream().map(r -> text(r, 32)) - .collect(Collectors.joining(" | "))); + .collect(Collectors.joining(" | "))); logger.trace("SAML Assertion is only valid between: " + conditions.getNotBefore() + " and " + conditions.getNotOnOrAfter()); } checkAudienceRestrictions(conditions.getAudienceRestrictions()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilder.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilder.java index 531213a942217..9524a12c0cb86 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilder.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilder.java @@ -7,14 +7,16 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Strings; +import org.opensaml.saml.saml2.core.AuthnContextClassRef; +import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration; import org.opensaml.saml.saml2.core.AuthnRequest; import org.opensaml.saml.saml2.core.NameID; import org.opensaml.saml.saml2.core.NameIDPolicy; +import org.opensaml.saml.saml2.core.RequestedAuthnContext; import org.opensaml.saml.saml2.metadata.EntityDescriptor; import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; import java.time.Clock; - /** * Generates a SAML {@link AuthnRequest} from a simplified set of parameters. */ @@ -55,10 +57,27 @@ AuthnRequest build() { if (nameIdSettings != null) { request.setNameIDPolicy(buildNameIDPolicy()); } + if (super.serviceProvider.getReqAuthnCtxClassRef().isEmpty() == false) { + request.setRequestedAuthnContext(buildRequestedAuthnContext()); + } request.setForceAuthn(forceAuthn); return request; } + private RequestedAuthnContext buildRequestedAuthnContext() { + RequestedAuthnContext requestedAuthnContext = SamlUtils.buildObject(RequestedAuthnContext.class, RequestedAuthnContext + .DEFAULT_ELEMENT_NAME); + for (String authnCtxClass : super.serviceProvider.getReqAuthnCtxClassRef()) { + AuthnContextClassRef authnContextClassRef = SamlUtils.buildObject(AuthnContextClassRef.class, AuthnContextClassRef + .DEFAULT_ELEMENT_NAME); + authnContextClassRef.setAuthnContextClassRef(authnCtxClass); + requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef); + } + // We handle only EXACT comparison + requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.EXACT); + return requestedAuthnContext; + } + private NameIDPolicy buildNameIDPolicy() { NameIDPolicy nameIDPolicy = SamlUtils.buildObject(NameIDPolicy.class, NameIDPolicy.DEFAULT_ELEMENT_NAME); nameIDPolicy.setFormat(nameIdSettings.format); @@ -87,5 +106,4 @@ static class NameIDPolicySettings { this.spNameQualifier = spNameQualifier; } } - } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java index 765d1dc8ad8f7..dfa86ffe5eda0 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java @@ -123,6 +123,7 @@ import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SP_ENTITY_ID; import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.SP_LOGOUT; import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.TYPE; +import static org.elasticsearch.xpack.core.security.authc.saml.SamlRealmSettings.REQUESTED_AUTHN_CONTEXT_CLASS_REF; /** * This class is {@link Releasable} because it uses a library that thinks timers and timer tasks @@ -273,8 +274,9 @@ static SpConfiguration getSpConfiguration(RealmConfig config) throws IOException final String serviceProviderId = require(config, SP_ENTITY_ID); final String assertionConsumerServiceURL = require(config, SP_ACS); final String logoutUrl = SP_LOGOUT.get(config.settings()); + final List reqAuthnCtxClassRef = REQUESTED_AUTHN_CONTEXT_CLASS_REF.get(config.settings()); return new SpConfiguration(serviceProviderId, assertionConsumerServiceURL, - logoutUrl, buildSigningConfiguration(config), buildEncryptionCredential(config)); + logoutUrl, buildSigningConfiguration(config), buildEncryptionCredential(config), reqAuthnCtxClassRef); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java index 984deb0a6938f..bc1ac3999211b 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SpConfiguration.java @@ -20,10 +20,12 @@ public class SpConfiguration { private final String ascUrl; private final String logoutUrl; private final SigningConfiguration signingConfiguration; + private final List reqAuthnCtxClassRef; private final List encryptionCredentials; public SpConfiguration(final String entityId, final String ascUrl, final String logoutUrl, - final SigningConfiguration signingConfiguration, @Nullable final List encryptionCredential) { + final SigningConfiguration signingConfiguration, @Nullable final List encryptionCredential, + final List authnCtxClassRef) { this.entityId = entityId; this.ascUrl = ascUrl; this.logoutUrl = logoutUrl; @@ -33,6 +35,7 @@ public SpConfiguration(final String entityId, final String ascUrl, final String } else { this.encryptionCredentials = Collections.emptyList(); } + this.reqAuthnCtxClassRef = authnCtxClassRef; } /** @@ -57,4 +60,8 @@ List getEncryptionCredentials() { SigningConfiguration getSigningConfiguration() { return signingConfiguration; } + + List getReqAuthnCtxClassRef() { + return reqAuthnCtxClassRef; + } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java index 913258cf45c5d..aed6d2456014c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java @@ -14,6 +14,7 @@ import org.apache.xml.security.keys.content.X509Data; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.common.CheckedConsumer; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.logging.Loggers; @@ -101,7 +102,9 @@ import static org.hamcrest.Matchers.nullValue; import static org.opensaml.saml.common.xml.SAMLConstants.SAML20P_NS; import static org.opensaml.saml.common.xml.SAMLConstants.SAML20_NS; +import static org.opensaml.saml.saml2.core.AuthnContext.KERBEROS_AUTHN_CTX; import static org.opensaml.saml.saml2.core.AuthnContext.PASSWORD_AUTHN_CTX; +import static org.opensaml.saml.saml2.core.AuthnContext.X509_AUTHN_CTX; import static org.opensaml.saml.saml2.core.NameIDType.TRANSIENT; import static org.opensaml.saml.saml2.core.SubjectConfirmation.METHOD_ATTRIB_NAME; import static org.opensaml.saml.saml2.core.SubjectConfirmation.METHOD_BEARER; @@ -175,11 +178,12 @@ public static void cleanup() { public void setupAuthenticator() throws Exception { this.clock = new ClockMock(); this.maxSkew = TimeValue.timeValueMinutes(1); - this.authenticator = buildAuthenticator(() -> buildOpenSamlCredential(idpSigningCertificatePair)); + this.authenticator = buildAuthenticator(() -> buildOpenSamlCredential(idpSigningCertificatePair), emptyList()); this.requestId = randomId(); } - private SamlAuthenticator buildAuthenticator(Supplier> credentials) throws Exception { + private SamlAuthenticator buildAuthenticator(Supplier> credentials, List reqAuthnCtxClassRef) throws + Exception { final Settings globalSettings = Settings.builder().put("path.home", createTempDir()).build(); final Settings realmSettings = Settings.EMPTY; final IdpConfiguration idp = new IdpConfiguration(IDP_ENTITY_ID, credentials); @@ -188,7 +192,8 @@ private SamlAuthenticator buildAuthenticator(Supplier> credenti (X509Credential) buildOpenSamlCredential(spSigningCertificatePair).get(0)); final List spEncryptionCredentials = buildOpenSamlCredential(spEncryptionCertificatePairs).stream() .map((cred) -> (X509Credential) cred).collect(Collectors.toList()); - final SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, SP_ACS_URL, null, signingConfiguration, spEncryptionCredentials); + final SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, SP_ACS_URL, null, signingConfiguration, spEncryptionCredentials, + reqAuthnCtxClassRef); final Environment env = TestEnvironment.newEnvironment(globalSettings); return new SamlAuthenticator( new RealmConfig("saml_test", realmSettings, globalSettings, env, new ThreadContext(globalSettings)), @@ -689,7 +694,143 @@ public void testAssertionWithoutSubjectIsRejected() throws Exception { assertThat(exception.getMessage(), containsString("has no Subject")); assertThat(exception.getCause(), nullValue()); assertThat(SamlUtils.isSamlException(exception), is(true)); + } + public void testAssertionWithoutAuthnStatementIsRejected() throws Exception { + Instant now = clock.instant(); + Instant validUntil = now.plusSeconds(30); + final String xml = "\n" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "randomopaquestring" + + "" + + "" + + "" + + "" + + "" + + "daredevil" + + "" + + "" + + ""; + SamlToken token = token(signDoc(xml)); + final ElasticsearchSecurityException exception = expectSamlException(() -> authenticator.authenticate(token)); + assertThat(exception.getMessage(), containsString("Authn Statements while exactly one was expected.")); + assertThat(exception.getCause(), nullValue()); + assertThat(SamlUtils.isSamlException(exception), is(true)); + } + + public void testExpiredAuthnStatementSessionIsRejected() throws Exception { + Instant now = clock.instant(); + Instant validUntil = now.plusSeconds(120); + Instant sessionValidUntil = now.plusSeconds(60); + final String nameId = randomAlphaOfLengthBetween(12, 24); + final String sessionindex = randomId(); + final String xml = "\n" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "" + nameId + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + PASSWORD_AUTHN_CTX + "" + + "" + + "" + + "" + + "daredevil" + + "" + + "" + + ""; + // check that the content is valid "now" + final SamlToken token = token(signDoc(xml)); + assertThat(authenticator.authenticate(token), notNullValue()); + + // and still valid if we advance partway through the session expiry time + clock.fastForwardSeconds(30); + assertThat(authenticator.authenticate(token), notNullValue()); + + // and still valid if we advance past the expiry time, but allow for clock skew + clock.fastForwardSeconds((int) (30 + maxSkew.seconds() / 2)); + assertThat(authenticator.authenticate(token), notNullValue()); + + // but fails once we get past the clock skew allowance + clock.fastForwardSeconds((int) (1 + maxSkew.seconds() / 2)); + final ElasticsearchSecurityException exception = expectSamlException(() -> authenticator.authenticate(token)); + assertThat(exception.getMessage(), containsString("on/after")); + assertThat(exception.getMessage(), containsString("Authentication Statement")); + assertThat(exception.getCause(), nullValue()); + assertThat(SamlUtils.isSamlException(exception), is(true)); + } + + public void testIncorrectAuthnContextClassRefIsRejected() throws Exception { + Instant now = clock.instant(); + Instant validUntil = now.plusSeconds(30); + final String nameId = randomAlphaOfLengthBetween(12, 24); + final String sessionindex = randomId(); + final String xml = "\n" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "" + + "" + IDP_ENTITY_ID + "" + + "" + + "" + nameId + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + PASSWORD_AUTHN_CTX + "" + + "" + + "" + + "" + + "daredevil" + + "" + + "" + + ""; + SamlAuthenticator authenticatorWithReqAuthnCtx = buildAuthenticator(() -> buildOpenSamlCredential(idpSigningCertificatePair), + Arrays.asList(X509_AUTHN_CTX, KERBEROS_AUTHN_CTX)); + SamlToken token = token(signDoc(xml)); + final ElasticsearchSecurityException exception = expectSamlException(() -> authenticatorWithReqAuthnCtx.authenticate(token)); + assertThat(exception.getMessage(), containsString("Rejecting SAML assertion as the AuthnContextClassRef")); + assertThat(SamlUtils.isSamlException(exception), is(true)); } public void testAssertionWithoutSubjectConfirmationIsRejected() throws Exception { @@ -1066,7 +1207,7 @@ public void testSigningWhenIdpHasMultipleKeys() throws Exception { keys.add(key); credentials.addAll(buildOpenSamlCredential(key)); } - this.authenticator = buildAuthenticator(() -> credentials); + this.authenticator = buildAuthenticator(() -> credentials, emptyList()); final CryptoTransform signer = randomBoolean() ? this::signDoc : this::signAssertions; Instant now = clock.instant(); Instant validUntil = now.plusSeconds(30); @@ -1634,7 +1775,7 @@ public void testIgnoredCommentsInResponseUsingCanonicalizationWithComments() thr } public void testFailureWhenIdPCredentialsAreEmpty() throws Exception { - authenticator = buildAuthenticator(() -> emptyList()); + authenticator = buildAuthenticator(() -> emptyList(), emptyList()); final String xml = getSimpleResponse(clock.instant()); final SamlToken token = token(signDoc(xml)); final ElasticsearchSecurityException exception = expectSamlException(() -> authenticator.authenticate(token)); @@ -1642,11 +1783,11 @@ public void testFailureWhenIdPCredentialsAreEmpty() throws Exception { assertThat(exception.getMessage(), containsString("SAML Signature")); assertThat(exception.getMessage(), containsString("could not be validated")); //Restore the authenticator with credentials for the rest of the test cases - authenticator = buildAuthenticator(() -> buildOpenSamlCredential(idpSigningCertificatePair)); + authenticator = buildAuthenticator(() -> buildOpenSamlCredential(idpSigningCertificatePair), emptyList()); } public void testFailureWhenIdPCredentialsAreNull() throws Exception { - authenticator = buildAuthenticator(() -> singletonList(null)); + authenticator = buildAuthenticator(() -> singletonList(null), emptyList()); final String xml = getSimpleResponse(clock.instant()); final SamlToken token = token(signDoc(xml)); final ElasticsearchSecurityException exception = expectSamlException(() -> authenticator.authenticate(token)); @@ -1654,7 +1795,7 @@ public void testFailureWhenIdPCredentialsAreNull() throws Exception { assertThat(exception.getMessage(), containsString("SAML Signature")); assertThat(exception.getMessage(), containsString("could not be validated")); //Restore the authenticator with credentials for the rest of the test cases - authenticator = buildAuthenticator(() -> buildOpenSamlCredential(idpSigningCertificatePair)); + authenticator = buildAuthenticator(() -> buildOpenSamlCredential(idpSigningCertificatePair), emptyList()); } private interface CryptoTransform { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java index b1b1b3098f063..94f6637d6d598 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthnRequestBuilderTests.java @@ -6,6 +6,9 @@ package org.elasticsearch.xpack.security.authc.saml; import java.time.Clock; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import org.joda.time.Instant; import org.junit.Before; @@ -20,6 +23,8 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.opensaml.saml.saml2.core.AuthnContext.KERBEROS_AUTHN_CTX; +import static org.opensaml.saml.saml2.core.AuthnContext.SMARTCARD_AUTHN_CTX; public class SamlAuthnRequestBuilderTests extends SamlTestCase { @@ -47,7 +52,7 @@ public void init() throws Exception { } public void testBuildRequestWithPersistentNameAndNoForceAuth() throws Exception { - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null); + SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( sp, SAMLConstants.SAML2_POST_BINDING_URI, idpDescriptor, SAMLConstants.SAML2_REDIRECT_BINDING_URI, @@ -68,15 +73,16 @@ public void testBuildRequestWithPersistentNameAndNoForceAuth() throws Exception assertThat(request.getNameIDPolicy().getAllowCreate(), equalTo(Boolean.FALSE)); assertThat(request.isForceAuthn(), equalTo(Boolean.FALSE)); + assertThat(request.getRequestedAuthnContext(), equalTo(null)); } public void testBuildRequestWithTransientNameAndForceAuthTrue() throws Exception { - SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null); + SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, Collections.emptyList()); final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( - sp, SAMLConstants.SAML2_POST_BINDING_URI, - idpDescriptor, SAMLConstants.SAML2_REDIRECT_BINDING_URI, - Clock.systemUTC()); - + sp, SAMLConstants.SAML2_POST_BINDING_URI, + idpDescriptor, SAMLConstants.SAML2_REDIRECT_BINDING_URI, + Clock.systemUTC()); + final String noSpNameQualifier = randomBoolean() ? "" : null; builder.nameIDPolicy(new SamlAuthnRequestBuilder.NameIDPolicySettings(NameID.TRANSIENT, true, noSpNameQualifier)); builder.forceAuthn(Boolean.TRUE); @@ -94,6 +100,68 @@ public void testBuildRequestWithTransientNameAndForceAuthTrue() throws Exception assertThat(request.getNameIDPolicy().getAllowCreate(), equalTo(Boolean.TRUE)); assertThat(request.isForceAuthn(), equalTo(Boolean.TRUE)); + assertThat(request.getRequestedAuthnContext(), equalTo(null)); + } + + public void testBuildRequestWithRequestedAuthnContext() throws Exception { + SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, + Collections.singletonList(KERBEROS_AUTHN_CTX)); + final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( + sp, SAMLConstants.SAML2_POST_BINDING_URI, + idpDescriptor, SAMLConstants.SAML2_REDIRECT_BINDING_URI, + Clock.systemUTC()); + builder.nameIDPolicy(new SamlAuthnRequestBuilder.NameIDPolicySettings(NameID.PERSISTENT, false, SP_ENTITY_ID)); + builder.forceAuthn(null); + + final AuthnRequest request = buildAndValidateAuthnRequest(builder); + + assertThat(request.getIssuer().getValue(), equalTo(SP_ENTITY_ID)); + assertThat(request.getProtocolBinding(), equalTo(SAMLConstants.SAML2_POST_BINDING_URI)); + + assertThat(request.getAssertionConsumerServiceURL(), equalTo(ACS_URL)); + + assertThat(request.getNameIDPolicy(), notNullValue()); + assertThat(request.getNameIDPolicy().getFormat(), equalTo(NameID.PERSISTENT)); + assertThat(request.getNameIDPolicy().getSPNameQualifier(), equalTo(SP_ENTITY_ID)); + assertThat(request.getNameIDPolicy().getAllowCreate(), equalTo(Boolean.FALSE)); + + assertThat(request.isForceAuthn(), equalTo(Boolean.FALSE)); + assertThat(request.getRequestedAuthnContext().getAuthnContextClassRefs().size(), equalTo(1)); + assertThat(request.getRequestedAuthnContext().getAuthnContextClassRefs().get(0).getAuthnContextClassRef(), + equalTo(KERBEROS_AUTHN_CTX)); + } + + public void testBuildRequestWithRequestedAuthnContexts() throws Exception { + List reqAuthnCtxClassRef = Arrays.asList(KERBEROS_AUTHN_CTX, + SMARTCARD_AUTHN_CTX, + "http://an.arbitrary/mfa-profile"); + SpConfiguration sp = new SpConfiguration(SP_ENTITY_ID, ACS_URL, null, null, null, reqAuthnCtxClassRef); + final SamlAuthnRequestBuilder builder = new SamlAuthnRequestBuilder( + sp, SAMLConstants.SAML2_POST_BINDING_URI, + idpDescriptor, SAMLConstants.SAML2_REDIRECT_BINDING_URI, + Clock.systemUTC()); + builder.nameIDPolicy(new SamlAuthnRequestBuilder.NameIDPolicySettings(NameID.PERSISTENT, false, SP_ENTITY_ID)); + builder.forceAuthn(null); + final AuthnRequest request = buildAndValidateAuthnRequest(builder); + + assertThat(request.getIssuer().getValue(), equalTo(SP_ENTITY_ID)); + assertThat(request.getProtocolBinding(), equalTo(SAMLConstants.SAML2_POST_BINDING_URI)); + + assertThat(request.getAssertionConsumerServiceURL(), equalTo(ACS_URL)); + + assertThat(request.getNameIDPolicy(), notNullValue()); + assertThat(request.getNameIDPolicy().getFormat(), equalTo(NameID.PERSISTENT)); + assertThat(request.getNameIDPolicy().getSPNameQualifier(), equalTo(SP_ENTITY_ID)); + assertThat(request.getNameIDPolicy().getAllowCreate(), equalTo(Boolean.FALSE)); + + assertThat(request.isForceAuthn(), equalTo(Boolean.FALSE)); + assertThat(request.getRequestedAuthnContext().getAuthnContextClassRefs().size(), equalTo(3)); + assertThat(request.getRequestedAuthnContext().getAuthnContextClassRefs().get(0).getAuthnContextClassRef(), + equalTo(KERBEROS_AUTHN_CTX)); + assertThat(request.getRequestedAuthnContext().getAuthnContextClassRefs().get(1).getAuthnContextClassRef(), + equalTo(SMARTCARD_AUTHN_CTX)); + assertThat(request.getRequestedAuthnContext().getAuthnContextClassRefs().get(2).getAuthnContextClassRef(), + equalTo("http://an.arbitrary/mfa-profile")); } private AuthnRequest buildAndValidateAuthnRequest(SamlAuthnRequestBuilder builder) { @@ -114,4 +182,4 @@ private AuthnRequest buildAndValidateAuthnRequest(SamlAuthnRequestBuilder builde return request; } -} \ No newline at end of file +} diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java index d88ad14def627..5d39d90a76cf7 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestHandlerTests.java @@ -213,7 +213,7 @@ private SamlLogoutRequestHandler buildHandler() throws Exception { final X509Credential spCredential = (X509Credential) buildOpenSamlCredential(readRandomKeyPair()).get(0); final SigningConfiguration signingConfiguration = new SigningConfiguration(Collections.singleton("*"), spCredential); final SpConfiguration sp = new SpConfiguration("https://sp.test/", "https://sp.test/saml/asc", LOGOUT_URL, - signingConfiguration, Arrays.asList(spCredential)); + signingConfiguration, Arrays.asList(spCredential), Collections.emptyList()); final Environment env = TestEnvironment.newEnvironment(globalSettings); return new SamlLogoutRequestHandler( new RealmConfig("saml_test", realmSettings, globalSettings, env, new ThreadContext(globalSettings)), diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java index 88d574de48970..5bb58105ce87c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlLogoutRequestMessageBuilderTests.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.security.authc.saml; import java.time.Clock; +import java.util.Collections; import org.elasticsearch.xpack.common.time.HaltedClock; import org.hamcrest.Matchers; @@ -39,7 +40,7 @@ public class SamlLogoutRequestMessageBuilderTests extends SamlTestCase { @Before public void init() throws Exception { SamlUtils.initialize(logger); - sp = new SpConfiguration(SP_ENTITY_ID, "http://sp.example.com/saml/acs", null, null, null); + sp = new SpConfiguration(SP_ENTITY_ID, "http://sp.example.com/saml/acs", null, null, null, Collections.emptyList()); idpRole = SamlUtils.buildObject(IDPSSODescriptor.class, IDPSSODescriptor.DEFAULT_ELEMENT_NAME); idp = SamlUtils.buildObject(EntityDescriptor.class, EntityDescriptor.DEFAULT_ELEMENT_NAME); idp.setEntityID(IDP_ENTITY_ID); @@ -99,4 +100,4 @@ private SingleLogoutService logoutService(String binding, String location) { return sls; } -} \ No newline at end of file +} diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java index beacb491cf0ed..132a3b7bac989 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTestHelper.java @@ -38,7 +38,7 @@ public static SamlRealm buildRealm(RealmConfig realmConfig, @Nullable X509Creden slo.setLocation(IDP_LOGOUT_URL); final SpConfiguration spConfiguration = new SpConfiguration(SP_ENTITY_ID, SP_ACS_URL, SP_LOGOUT_URL, - new SigningConfiguration(Collections.singleton("*"), credential), Arrays.asList(credential)); + new SigningConfiguration(Collections.singleton("*"), credential), Arrays.asList(credential), Collections.emptyList()); return new SamlRealm(realmConfig, mock(UserRoleMapper.class), mock(SamlAuthenticator.class), mock(SamlLogoutRequestHandler.class), () -> idpDescriptor, spConfiguration); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java index f831af9ba5ef9..6dc9c021fc813 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlRealmTests.java @@ -141,7 +141,7 @@ public void testAuthenticateWithRoleMapping() throws Exception { final Boolean populateUserMetadata = randomFrom(Boolean.TRUE, Boolean.FALSE, null); final UserRoleMapper roleMapper = mock(UserRoleMapper.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null); + final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); @@ -240,7 +240,7 @@ public void testSettingPatternWithoutAttributeThrowsSettingsException() throws E final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null); + final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SettingsException settingsException = expectThrows(SettingsException.class, () -> new SamlRealm(config, roleMapper, authenticator, logoutHandler, () -> idp, sp)); @@ -256,7 +256,7 @@ public void testMissingPrincipalSettingThrowsSettingsException() throws Exceptio final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null); + final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SettingsException settingsException = expectThrows(SettingsException.class, () -> new SamlRealm(config, roleMapper, authenticator, logoutHandler, () -> idp, sp)); @@ -266,7 +266,7 @@ public void testMissingPrincipalSettingThrowsSettingsException() throws Exceptio public void testNonMatchingPrincipalPatternThrowsSamlException() throws Exception { final UserRoleMapper roleMapper = mock(UserRoleMapper.class); final EntityDescriptor idp = mockIdp(); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null); + final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); @@ -516,7 +516,7 @@ public void testBuildLogoutRequest() throws Exception { slo.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); slo.setLocation("https://logout.saml/"); - final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null); + final SpConfiguration sp = new SpConfiguration("", "https://saml/", null, null, null, Collections.emptyList()); final SamlAuthenticator authenticator = mock(SamlAuthenticator.class); final SamlLogoutRequestHandler logoutHandler = mock(SamlLogoutRequestHandler.class); From 8fa278f6235a2e8377ac0c259d0e28b16618cd39 Mon Sep 17 00:00:00 2001 From: Dimitris Athanasiou Date: Tue, 12 Jun 2018 11:36:26 +0100 Subject: [PATCH 23/71] [ML][TEST] Mute tests using rules (#31204) This is in preparation of pushing the new rules design in the `ml-cpp` side. These tests will be switched on again after merging in the new rules implementation. --- .../elasticsearch/xpack/ml/integration/DetectionRulesIT.java | 3 +++ .../elasticsearch/xpack/ml/integration/ScheduledEventsIT.java | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java index 28565c5923c38..47ced4a96dde8 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java @@ -24,6 +24,7 @@ import org.elasticsearch.xpack.core.ml.job.config.RuleConditionType; import org.elasticsearch.xpack.core.ml.job.results.AnomalyRecord; import org.junit.After; +import org.junit.Ignore; import java.io.IOException; import java.util.ArrayList; @@ -51,6 +52,7 @@ public void cleanUpTest() throws Exception { cleanUp(); } + @AwaitsFix(bugUrl = "this test is muted temporarily until the new rules implementation is merged in") public void testNumericalRule() throws Exception { RuleCondition condition1 = RuleCondition.createNumerical( RuleConditionType.NUMERICAL_ACTUAL, @@ -152,6 +154,7 @@ public void testNumericalRule() throws Exception { assertThat(secondHaldRecordByFieldValues, contains("by_field_value_1", "by_field_value_2")); } + @AwaitsFix(bugUrl = "this test is muted temporarily until the new rules implementation is merged in") public void testCategoricalRule() throws Exception { MlFilter safeIps = new MlFilter("safe_ips", Arrays.asList("111.111.111.111", "222.222.222.222")); assertThat(putMlFilter(safeIps), is(true)); diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java index 6703e4ef2365b..8410075ff5e27 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java @@ -20,6 +20,7 @@ import org.elasticsearch.xpack.core.ml.job.results.AnomalyRecord; import org.elasticsearch.xpack.core.ml.job.results.Bucket; import org.junit.After; +import org.junit.Ignore; import java.io.IOException; import java.time.Instant; @@ -41,6 +42,7 @@ public void cleanUpTest() { cleanUp(); } + @AwaitsFix(bugUrl = "this test is muted temporarily until the new rules implementation is merged in") public void testScheduledEvents() throws IOException { TimeValue bucketSpan = TimeValue.timeValueMinutes(30); @@ -152,6 +154,7 @@ public void testScheduledEvents() throws IOException { assertThat(records, is(empty())); } + @AwaitsFix(bugUrl = "this test is muted temporarily until the new rules implementation is merged in") public void testScheduledEventWithInterimResults() throws IOException { TimeValue bucketSpan = TimeValue.timeValueMinutes(30); Job.Builder job = createJob("scheduled-events-interim-results", bucketSpan); @@ -193,6 +196,7 @@ public void testScheduledEventWithInterimResults() throws IOException { /** * Test an open job picks up changes to scheduled events/calendars */ + @AwaitsFix(bugUrl = "this test is muted temporarily until the new rules implementation is merged in") public void testOnlineUpdate() throws Exception { TimeValue bucketSpan = TimeValue.timeValueMinutes(30); Job.Builder job = createJob("scheduled-events-online-update", bucketSpan); From 5156223f38944f2dc004502e2e08664fbc7de0ab Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Tue, 12 Jun 2018 16:19:43 +0300 Subject: [PATCH 24/71] [DOCS] Clarify audit index settings when remote indexing (#30923) --- docs/reference/settings/audit-settings.asciidoc | 9 +++++++++ x-pack/docs/en/security/auditing/output-index.asciidoc | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/docs/reference/settings/audit-settings.asciidoc b/docs/reference/settings/audit-settings.asciidoc index 5995c65a01c9f..524198df58c47 100644 --- a/docs/reference/settings/audit-settings.asciidoc +++ b/docs/reference/settings/audit-settings.asciidoc @@ -112,6 +112,15 @@ xpack.security.audit.index.settings: number_of_replicas: 1 ---------------------------- -- ++ +-- +NOTE: These settings apply to the local audit indices, as well as to the +<>, but only if the remote cluster +does *not* have {security} installed, or the {es} versions are different. +If the remote cluster has {security} installed, and the versions coincide, the +settings for the audit indices there will take precedence, +even if they are unspecified (i.e. left to defaults). +-- [[remote-audit-settings]] ==== Remote Audit Log Indexing Configuration Settings diff --git a/x-pack/docs/en/security/auditing/output-index.asciidoc b/x-pack/docs/en/security/auditing/output-index.asciidoc index a07bd7a8d06eb..1c59762ea2a98 100644 --- a/x-pack/docs/en/security/auditing/output-index.asciidoc +++ b/x-pack/docs/en/security/auditing/output-index.asciidoc @@ -36,6 +36,13 @@ xpack.security.audit.index.settings: number_of_replicas: 1 ---------------------------- +These settings apply to the local audit indices, as well as to the +<>, but only if the remote cluster +does *not* have {security} installed, or the {es} versions are different. +If the remote cluster has {security} installed, and the versions coincide, the +settings for the audit indices there will take precedence, +even if they are unspecified (i.e. left to defaults). + NOTE: Audit events are batched for indexing so there is a lag before events appear in the index. You can control how frequently batches of events are pushed to the index by setting From d5f663f6f65b84329515e0ef76efdd13f95a1330 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 12 Jun 2018 10:17:41 -0400 Subject: [PATCH 25/71] Fix race in clear scroll (#31259) Here is the problem: if two threads are racing and one hits a failure freeing a context and the other succeeded, we can expose the value of the has failure marker to the succeeding thread before the failing thread has had a chance to set the failure marker. This is a problem if the failing thread counted down the expected number of operations, then be put to sleep by a gentle lullaby from the OS, and then the other thread could count down to zero. Since the failing thread did not get to set the failure marker, the succeeding thread would respond that the clear scroll succeeded and that makes that thread a liar. This commit addresses by first setting the failure marker before we potentially expose its value to another thread. --- .../elasticsearch/action/search/ClearScrollController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/search/ClearScrollController.java b/server/src/main/java/org/elasticsearch/action/search/ClearScrollController.java index 9b98691dc9005..c33eecee8bc75 100644 --- a/server/src/main/java/org/elasticsearch/action/search/ClearScrollController.java +++ b/server/src/main/java/org/elasticsearch/action/search/ClearScrollController.java @@ -133,10 +133,13 @@ private void onFreedContext(boolean freed) { private void onFailedFreedContext(Throwable e, DiscoveryNode node) { logger.warn(() -> new ParameterizedMessage("Clear SC failed on node[{}]", node), e); + /* + * We have to set the failure marker before we count down otherwise we can expose the failure marker before we have set it to a + * racing thread successfully freeing a context. This would lead to that thread responding that the clear scroll succeeded. + */ + hasFailed.set(true); if (expectedOps.countDown()) { listener.onResponse(new ClearScrollResponse(false, freedSearchContexts.get())); - } else { - hasFailed.set(true); } } } From 61d2c38aca112714c2e228d97667054a4a3605d6 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 12 Jun 2018 11:31:43 -0400 Subject: [PATCH 26/71] Fix security Netty 4 transport tests This test suite needs to filter out the object cleaner thread too so this commit does that. --- .../xpack/core/test/XPackTestCase.java | 14 ++++++++++++++ .../SecurityNetty4HttpServerTransportTests.java | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java new file mode 100644 index 0000000000000..1f69b7c9bafb3 --- /dev/null +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java @@ -0,0 +1,14 @@ +/* + * 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.test; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; +import org.elasticsearch.test.ESTestCase; + +@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) +public class XPackTestCase extends ESTestCase { +} diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java index 3ef298f3f232d..d964f4f997bf1 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java @@ -22,6 +22,8 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.ssl.SSLClientAuth; import org.elasticsearch.xpack.core.ssl.SSLService; +import org.elasticsearch.xpack.core.test.XPackIntegTestCase; +import org.elasticsearch.xpack.core.test.XPackTestCase; import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.junit.Before; @@ -38,7 +40,7 @@ import static org.hamcrest.Matchers.not; import static org.mockito.Mockito.mock; -public class SecurityNetty4HttpServerTransportTests extends ESTestCase { +public class SecurityNetty4HttpServerTransportTests extends XPackTestCase { private SSLService sslService; private Environment env; From edc108a82d0c9f756e86a070e5aff1c6e1af37aa Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 12 Jun 2018 11:34:40 -0400 Subject: [PATCH 27/71] Fix naming conventions check for XPackTestCase This class needs to be abstract or it fails the naming convention check. --- .../java/org/elasticsearch/xpack/core/test/XPackTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java index 1f69b7c9bafb3..ab7948eeae726 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java @@ -10,5 +10,5 @@ import org.elasticsearch.test.ESTestCase; @ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) -public class XPackTestCase extends ESTestCase { +public abstract class XPackTestCase extends ESTestCase { } From 8e70dd9d65e85c163a8b12aeff80a129bee71a8c Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Tue, 12 Jun 2018 08:57:11 -0700 Subject: [PATCH 28/71] [DOCS] Updates machine learning custom URL screenshots (#31222) --- x-pack/docs/build.gradle | 21 +++++- x-pack/docs/en/ml/customurl.asciidoc | 68 +++++++++++++----- .../docs/en/ml/images/ml-customurl-detail.jpg | Bin 0 -> 133528 bytes .../en/ml/images/ml-customurl-discover.jpg | Bin 0 -> 393627 bytes .../docs/en/ml/images/ml-customurl-edit.jpg | Bin 0 -> 123042 bytes x-pack/docs/en/ml/images/ml-customurl.jpg | Bin 99011 -> 166732 bytes 6 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 x-pack/docs/en/ml/images/ml-customurl-detail.jpg create mode 100644 x-pack/docs/en/ml/images/ml-customurl-discover.jpg create mode 100644 x-pack/docs/en/ml/images/ml-customurl-edit.jpg diff --git a/x-pack/docs/build.gradle b/x-pack/docs/build.gradle index ad5c827cdd427..7662cdd2e4bbd 100644 --- a/x-pack/docs/build.gradle +++ b/x-pack/docs/build.gradle @@ -16,7 +16,6 @@ buildRestTests.expectedUnconvertedCandidates = [ 'en/ml/functions/rare.asciidoc', 'en/ml/functions/sum.asciidoc', 'en/ml/functions/time.asciidoc', - 'en/ml/customurl.asciidoc', 'en/rest-api/security/ssl.asciidoc', 'en/rest-api/security/users.asciidoc', 'en/rest-api/security/tokens.asciidoc', @@ -280,6 +279,26 @@ setups['library'] = ''' {"name": "The Moon is a Harsh Mistress", "author": "Robert A. Heinlein", "release_date": "1966-04-01", "page_count": 288} ''' +setups['sample_job'] = ''' + - do: + xpack.ml.put_job: + job_id: "sample_job" + body: > + { + "description" : "Very basic job", + "analysis_config" : { + "bucket_span":"10m", + "detectors" :[ + { + "function": "count" + } + ]}, + "data_description" : { + "time_field":"timestamp", + "time_format": "epoch_ms" + } + } +''' setups['farequote_index'] = ''' - do: indices.create: diff --git a/x-pack/docs/en/ml/customurl.asciidoc b/x-pack/docs/en/ml/customurl.asciidoc index d0b7a55763180..7c773c4b9bf49 100644 --- a/x-pack/docs/en/ml/customurl.asciidoc +++ b/x-pack/docs/en/ml/customurl.asciidoc @@ -1,22 +1,53 @@ +[role="xpack"] [[ml-configuring-url]] -=== Adding Custom URLs To Machine Learning Results +=== Adding custom URLs to machine learning results When you create an advanced job or edit any job in {kib}, you can optionally -attach one or more custom URLs. You can also specify these custom settings when -you create or update jobs by using the {ml} APIs. +attach one or more custom URLs. -The custom URLs provide links from the anomalies table in the Anomaly Explorer -or Single Metric Viewer window in {kib} to custom dashboards or external -websites. For example, you can define a custom URL that provides a way for users -to drill down to the source data from the results set. +The custom URLs provide links from the anomalies table in the *Anomaly Explorer* +or *Single Metric Viewer* window in {kib} to {kib} dashboards, the *Discovery* +page, or external websites. For example, you can define a custom URL that +provides a way for users to drill down to the source data from the results set. + +When you edit a job in {kib}, it simplifies the creation of the custom URLs for +{kib} dashboards and the *Discover* page and it enables you to test your URLs. +For example: + +[role="screenshot"] +image::images/ml-customurl-edit.jpg["Edit a job to add a custom URL"] For each custom URL, you must supply the URL and a label, which is the link text -that appears in the anomalies table. +that appears in the anomalies table. You can also optionally supply a time +range. For example, these are the values that are added for `My link 1`: + +[role="screenshot"] +image::images/ml-customurl-detail.jpg["An example of a label and URL"] + +As in this case, the custom URL can contain +<>, which +are populated when you click the link in the anomalies table. In this example, +the custom URL contains `$earliest$`, `$latest$`, and `$service$` tokens, which +pass the beginning and end of the time span of the selected anomaly and the +pertinent `service` field value to the target page. If you were interested in the following anomaly, for example: [role="screenshot"] -image::images/ml-customurl.jpg["Links in the Anomaly Explorer anoamilies table"] +image::images/ml-customurl.jpg["An example of the custom URL links in the Anomaly Explorer anomalies table"] + +...clicking `My Link 1` opens the *Discover* page and shows results for the +service and date that were identified in the anomaly: + +[role="screenshot"] +image::images/ml-customurl-discover.jpg["An example of the results on the Discover page"] + +Since we specified a time range of 2 hours, the time filter restricts the +results to the time period two hours before and after the anomaly. + +You can also specify these custom URL settings when you create or update jobs by +using the {ml} APIs. [float] +[[ml-configuring-url-strings]] ==== String Substitution in Custom URLs You can use dollar sign ($) delimited tokens in a custom URL. These tokens are @@ -40,7 +71,8 @@ span of the selected anomaly to the target page. The tokens are substituted with date-time strings in ISO-8601 format. If you selected an interval of 1 hour for the anomalies table, these tokens use one hour on either side of the anomaly time as the earliest and latest times. The same is also true if the interval is -set to `Auto` and a one hour interval was chosen. +set to `Auto` and a one hour interval was chosen. You can override this behavior +by using the `time_range` setting. The `$mlcategoryregex$` and `$mlcategoryterms$` tokens pertain to jobs where you are categorizing field values. For more information about this type of analysis, @@ -55,28 +87,32 @@ the selected anomaly. Each categorization term is prefixed by a plus (+) character, so that when the token is passed to a {kib} dashboard, the resulting dashboard query seeks a match for all of the terms of the category. -For example, the following API updates a `log_categories` job to add a custom -URL that uses `$earliest$`, `$latest$`, and `$mlcategoryterms$` tokens: +For example, the following API updates a job to add a custom URL that uses +`$earliest$`, `$latest$`, and `$mlcategoryterms$` tokens: [source,js] ---------------------------------- -POST _xpack/ml/anomaly_detectors/log_categories/_update +POST _xpack/ml/anomaly_detectors/sample_job/_update { "custom_settings": { "custom_urls": [ { "url_name": "test-link1", + "time_range": "1h", "url_value": "http://localhost:5601/app/kibana#/discover?_g=(refreshInterval:(display:Off,pause:!f,value:0),time:(from:'$earliest$',mode:quick,to:'$latest$'))&_a=(columns:!(_source),index:AV3OWB68ue3Ht69t29aw,interval:auto,query:(query_string:(analyze_wildcard:!t,query:'$mlcategoryterms$')),sort:!(time,desc))" } ] } } ---------------------------------- +//CONSOLE +//TEST[setup:sample_job] When you click this custom URL in the anomalies table in {kib}, it opens up the -Discover page and displays source data for the period when the anomaly occurred. -Since this job was categorizing log messages, some `$mlcategoryterms$` token -values that were passed to the target page for an example anomaly are as follows: +*Discover* page and displays source data for the period one hour before and +after the anomaly occurred. Since this job was categorizing log messages, some +`$mlcategoryterms$` token values that were passed to the target page for an +example anomaly are as follows: [role="screenshot"] image::images/ml-categoryterms.jpg["A query for category terms on the Discover page in {kib}"] diff --git a/x-pack/docs/en/ml/images/ml-customurl-detail.jpg b/x-pack/docs/en/ml/images/ml-customurl-detail.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f7b2907c5564d1fd2327b996c01caddaed75381c GIT binary patch literal 133528 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 G|C<2d{xKN< literal 0 HcmV?d00001 diff --git a/x-pack/docs/en/ml/images/ml-customurl-discover.jpg b/x-pack/docs/en/ml/images/ml-customurl-discover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..991560919bd503fbbc5b3dc8ea7a3f691b786be1 GIT binary patch literal 393627 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

f-0HLP}&Z7Ypib{pN8gE0`eU z<9;zD8|c$Jb8l7UtPQ`Os+`t-UsZjg=M=Z_AJsYUFM3C2!Vgin7^RUk0K*oS5UUN` z@d^I%!bXz&BHW0|IG_>VAWZY$o_&KJfIloY;?^-xAap@5<#Y$9HXTs-#YfNS5PJiZ zUPixbmb(BTRBvEh*tw+v5;JPO9GA=2N0=U=4rzVNx`_rz|4$BQ6T6o}R> zPGu^TXej~B@M$s2YeVKNO`HK(U9W}NwfkpKOquVX%nq$R6M)C-u!sC;q|0roiA7t+ zr;}K+S~uDKv=ULrdEdAT9A3;$Z4yO=?M4tvVG8OF!s{nvrL;#FQxC1i$KfsGv=^!j zo22K#j=Z^3wK-FR&m%&=>pU+WE~{LsoI*c&+iNb$|F-I|N#&NEuj$n8c##Fn<+V97 z);Xi%vqC;2_{osW24wDfq(!%*CByy=7?5P^GFK+`5+B{0+`>g=v@Wv zu5n*8wrA5=S2nU4d&0avbUr}J`b#@!kswxFMAq$GS`UERvw;K?uJFsb>Zz9CIn;f~ zuTcz5fge0~Rt*x6P0OEra{F9=CFE{7eOG<&2!UxT_TN%K)~G5HH&*R(o>E!{rHlK} zulfq*5#`cNelwM)iPEvSwYCd*$zML;d$}9BM<9d2jn7+S#iJbNCQ6}VXM!Q09@N4$ z0Sr390G+Q*a`&(N)T_@R^%afhtXaN__oicu)d9~?SQUD&JK7tw6M%9C1fW+K5tA(` z0XqUkM(P4g;=>)|WB$|6&CAyeTwhLBH4fP`Sr%EADeC7b*NeBtws>~8M+=t2wF|($Z>*ly&vGcWHU&9`^b>GltqTIp9#_#C*lI z_Ck{7JY$VP94<8oy_@K(H)pvB{c1sq0RrMR=LZ4GFGUs&VoEkRzRYq@kPk&sIeAgldk=Tuho>8%eQH19JCvT{o?<5eZ6tTW{^2Ql|>)nR;Szm3V z>)&vG4>q1{Ev(fXdVaHCZBWnzBt1p-=)?h-4=MMxw>YQ{{u*{F80QTZJIIO3n%L^# z`@wSz!mVbk2c7gQIYl|MFUei!7&EZF#>pjOhbLlDTs)pz93&Mh4?xmEr{OG=m|wIj z;5UUo1?mq21e&?V>TBo4#5&{sq66%Dkmi3dQsG!q=jUYJ2W!vEVOD65>JH zBM%S70L}BZAV`Y7e}g?m`QG2Vo%)q7+4O@)t}CVECsv7|pS_V*TJKzLsfTY&*+J4v zABQ~gtkv!4!3rgt*QKzCwWUjstJ3tn>)fP=~Z<-O{K|c1qi8pp$j?Foj(LJQJ(g6t{j3LL zTZj%rFfFVR%EMxn!~7Q4#4dg*Le&!?sOK~lpPV?NnO3h=8vsr8QR0JXX;jIAL$VPO zO8h(W7OFb|=)w)c+sE3(zj8gF2Z0a1Yyx}SL;f*># zQ0mqLH|Vz=i!gMo4L`Ux07Qc}bu{IxrbY3S&}H^ut;D%%eFVwC2Brn%T$(}0tit`B zuQu*CUn1&FB!EqcQZx&lO5fdRw-L*O>CPeKgOQ>}`rOEF#N^@6fTJv~h+}Eq%87hf zLgS!I>Q zUWX;R*m{d$O8IeM`@FVe&|k1Dl ze0Pi9Rhnc4Z3bsR%;Zk_J*!S4gM*V4MKLt zeM8SEEthl;Az?*oS8v#YVV8TXd#5wBr|ZeejLM3>#(=16m;K5RIVi}jt+B?KdhRuf zVy;lxc}7NUA{9#LiZQ!Pvkoodb9+ltri};mS&SF5JcpvH{jPG!S%4@3u1ZO?$o{6mM1Sx(~t%0Xh>XX?;r{x<4WO4imf6ZxZQ)8PdI5+|C z^X`i*?;-yWpp|}1f-{{$5e5)jqHN*aYjQ#dPXSQO4<0P4&b)`twTN}%L{odk7a?4L zscY%aG49j!6x%8ZSh}uVq`1CkNzGS_#wJG;nVVCb1uf03Y~Y#WX#!?V(N5lt%LE6V zSL+{a6}-W=zQ4~qVzlRq9PE0kd=L*Ys=J{@PHxw~gfy}eN%=8)1D$F_UFtIKJn_V2 zt=g@F5!ET}dudZ0#Np+$mEq4O6!ZXi`%-IH)3NCu)a)QN+TkT&wa}#la}U}!mfnQz z78a-bE=qAfa6A}p$;P+mi;U=~X@~-F%CO38;oy5ebH@U_VGrTY8;Vzq1)b;Dkc)L; zx1U|U|Uum{>!8%h3ViP5boDnL|YPRJRS^u?^AsIbt`#%FPhaj&|XBvKA_ z;$D>q8vE2@fxbxuFuv*PLjw62rko>;k<>5oqP|~?Pfv%#&-hW!v=)UgL|%W+(J9B& z3=#TkKAJm?ZEnktHu-~^o;d2f>74YTK~wRXzMIb{R9i`A2!cUlsX=Sh0HiMGqirZr z#T;ju_e4EFPI)rR4tM+M$7zyfk&3cig6Cf4D8hwfr_%bm2RUAVGm84OXK_z(iJk@! zUVf&(TP+skn3i|l^Y-7JuLw|Y5)&tyW%BLMpcUoxij613xa+FJ zLMks@zXD}u!C|`uf8&~NsfJfmw9dC5JgDfaat+#l@^t@?2l{`!+OGkQTjaP|dSlRr z4e)=$WF8*fKR4PD#Gs!-uY5J<4szf$H(@~HBHUfV(;?!GZ#wA8=x|;tNpOx0tN1vV z`|S9W5c15MT;$*IE{i7U&vSEw>6Ol+W;tuu5~XuObmY!g8)OkSNimx4NkxuEE{47O zL*Pkyz5Yi{e8}r_XC^8p3q1^Q?uc{`XaUBMXzaH(X}hdEP+NC1gTZ_D^Sa*Z(6_G~ ztwRCXJ-b0rrjF(;o$+S2$5v8KX?&4?6w4HQEL{eaR6rA}gImv{`#g%$ux+ zX?sFPPOVMuTFz;=DZ>y63#7`1UE!2?wj)Z88_)Q~YT|v+pSlIP!r^i9(_NMJ$#!vE&5z;UQ&Y-kB3cAh8zbyK+EOQn zl&$(~23v^&)PdkbS!Beb7_7z8&TTPn7#xeuRdv>q5koq5ZM41KlXk)idW1~_d3>;P zIlVlyR9^+K9Z>ZYeiMZS5YJ@mYMnzqES6v$BcLv;)dyUdELed@r_sd zyUwl~fjPLm-h0kH#v;bvsw3&AxuT3(On`gG=+?e>B=hPOMANv=(A||m>UZy?jnp;W zmiEq}7Zk@;swP(SmZ}+|=e<)+SPo+XX;+71KlalJhDEyMELBdV_FIL;ZB=W!TR|@) zKK9%srIV;LepcXbE3OavfjGIX;L36%%GoouZwS@{IK4pufu2cuo4MD0*6%-cod$gI zw#I>754)c0KG_QcY@&vH>L_1SqYVA0JMo_JKgDWH8>s^*2h0vA^E-{*0M9(`?B5n^ zBAf(Kmn*8gN~8*ePXSVA0aAyKFVT10tVOUX_9|429Tk>mWGs==gNPuFk<=i{(=YA% zrgDOR@N5Ht8~>J5+(-@^cowpppfq@`Q_U0t)6Mrkb}i|0neVW5=J|-{+;%k5cG-b@ zifA}VLX_bp;8~YKT(*J_fZzE+;NcYD7TvU5s!j9Q&EU$%vkUr4_~R6fAABhhb>27N zwy_(!0WXS5t46O+gjlj*XIN_E&Qt1X^{r2V*BI z9J&;cxDf4120?G4$k<&O9MSePYZqN%YePKV#u(IteM*y#5R1+s3>x_5%2gYTrY{jT za}Ed+Qq|s=LKy|b<&cU(FZ|JIKo7xrD-xz)@xO^h@#?ND;f;cf~?3-yrp-8>0$5#q{>KkXZLrUiZy z?*6PmGx>^<5QTfsQ?7isSgxgMIzsA#|6p27bh@n0wK5_Cakux@`Zepw%IyT}Zhfmy z>oQico(7d;@4I7^Z9)sn@s4fxZi!f1ATz))@teeL2h7wrkEr(KIv__Dq+1kk0*q*$X30kUdiFC51QhqC-!LuaRGf zHCgOIabUeZjj|6{B+YY0K4j;q$wv;JSRBn*c$W2|=?z2f`ZbB1HhIatl629V)7#Y% zD5168{KC11bfou&pNU{H6>3`xE729@{}$p1b>w>4hK!Pd0g-wQ?naG-z|fQ;uHZ9bJtYA^ljk4Rx}dp*PXn9dj&+W|vAf%ROD_Ib4nx zcA1Jh6F2Xs)_jm}B)=Z_)4>yMY+8K%l?=r;cFX<7ptRzv9ozs;P){+K`zye4H8Ku8>GK|McIu^xM$R)5qjI_F zxCCZ5tmiW_9qi75aB{#_p_Qu5=R7{^o3*g_6`*=gN65%6hsfz!Cvu zmw2{JzHew8#xzj(419sMp44$AaH4&i0?;o^VZZy;-Y=`e*WKe-3KcMauOZJwZI9@i z-}r6q*6;7CVPV7Us5FdLzN3qT;EI%(B#iu`IyHyPSbyDmEk}L`R-##lF)oNbbMQxO z5SzN2@DRyCC3almoMjL(UB2;Yz#KD=I+BuS+@!fFTDI;cI7v}PaX<9tP7UrU1FTiI zeW4ee@pH-lsm3d#5!81g2K(Fy@oAsA;`>#z zDa7V0(e`zoMnx6#0%=a}ZGVjThH1M+bd18q57+});C31^xEe+Pr{xzp!U|b;+e-CX zU%P5gGUwWTcl}=HTayjMeC^A=`B{_r8<%f?^M*l#@8X!77mD&5W*0vUMqk;G-_!5` zo$T_$KK$-LjS8U19lkO$V2|{c%znLQq{(R_7$8o! z(XZ@g8f1E;#l*O{$t@04Hb1v+D$)xb&znmd9Zcb9?w_vR73ZM-xI>Xyi$5M1@WoZ0 zC5)w99WEP*Fs<5kYPE6WtDAVhF9vB$7%pY zw8BSk=@%EIS$nzrYVhoz?~b^I9omUgazw=%qaEh#Z$`aE{Al4!){%NOE8|JwH2r z!iE3ggRC$_&YdsmIj);qYZJJfs<~#84q+VIV|?ldkNc%P#H#L`(q=!)^(;#)FUh;3 zfU&oKs-8OpRJJ}L=rni!+1?e2H0Rjik2~YpQ69v7zlAT+;5v8dSPDE&ohTMdSiCQ-~Isk(U z`y$(kQXku#D7%_Xr8q~t0MlXrw7?tV^BPK*1Q zt7X~6b!Iqoo8ZFxMw}*0!oCCEdEbOxQ&91n=J|0*7${j}OU}v57wJvT`JiTZ<2L=O zUp*C=fI|sj+{JK)C7=G=qK^Y;znh8R%i-eyfwE{BcIy5mmsYgm)WjGt zKgJnyaZ9cOuI!S8jl=Gxb-|#X!(*IC4A7SvTAH_Qf%Bu>Rznwk)IEt7#36yzDfJ)$ z=wzK>n_cfM_jIy;i_skydC4jOM8T4N_sDh`p`ve+FZ=`=I|BE>Jk9a26fkr21Ek0L z!DFG>)D|%b1QzmcQvV~tD}OoF=CNn=#YwH1Jf(9T}+K%!u8G8l-HkhV z3;}7k+I^+!o!_v{hk~$x+2$-GcQ$7Kw8SAFX9=5BawbmbBmJkL5vpd|RGi#6kO5B@ ziM`GxGdtyP)Uh+$Na%u|+{K@;y~R3{o?IWwSJAwzy?>pX!h!cn=eFrSDfglKwsMSn>1TjM?emMWn&#tG)!HAMlpELe0x+}H zx1zkf+iY`6`2*iQ$61igRN5wdCko$pt`5i6!E8c(O)qSu*fNRW6eHihLfK`-9F?%p zYbwp9l5InCV3juAt=TUMUXK5iTKt!tivC>y0s2e4@&Cah_$Rdmuq^&nlL-Tu{AgVN z2>*cne0b02r$7H8|Bq;WTRZ3*FW{t@haX6P`HA$4h}C6hs@f+$ev`8UOl3kbqK@Px zLgly69{$BpK1JF*2zRE=`WKn)eT6yr{vM!p;zGHArV*#j6W`-n_Je1H(girSYg2yk ze6#t5_g~-juPgDdd*WYD$G_Hzf31c8+7V%{ez>x#-Gq z)n!4mv57awsjbwN6Q7Q3zPta2_D7zlf02H<1C*6y;3VF_1#~sXWH`cUf#JeuR#7L7 zME4=B;~RkK4&*qtr87%+o;(*rHa^LRI}MLvCdkz8xP-9>~prz2=r>B z)Yq4+=w+v{JFddAid2{(j)^MP)E;R5`^-!NGQ%>9A2tfToU<@yp`?6(;bY&ulIk}V zzHY+zpr_T00uGJaa+Hi*vw$NZy(T0f-VF;!;hy)p&Hg)4_9! z%qoh1-$G=3U!Qr;vb#kWK=aVg3aD=0;9j}TJ~oo-KDwhn{D`pDK0~SF6w#K(wFg%- z|3FMJ(Ue<87ZAnb5$AGlDWwv(ZHdwYj%e$Z^m~o#m4P8HpVReSFSM2AHBKq-@o_EP ziZ2%#JXsN!G$3c;oEXb8b~nsbOP(i5F|QUDBq-VB>tQsmTkIJsvc81?S5O6*8#wJF zfzKi9hr+EK;nc?3R1iPq+)%s+LpViRBRh=o)hfm^F|aoF5)&)*j_yY^okG&O8XKpZ zCwHuj?VqP|Hn(oIjc*71{yk2HnXYtdypl9ld{zu&jdOvlRK{f8wNsgZLf9)V^&K9{ z(AVW<1Whb+Sg4QP1Qn|Gxy453DZi!JJS6VDQXRk{VG&o0V5n%_LYCe_d7-D|p_{{a ziQJi=N{zC~NFeO81!+(8C-`Lq=ev=@h>JjKe8FsMCP#byo4cju*T%mmZ%s}4=FeR_ z?S59a8XO!NK7k*8R?c|SHsQ&B-OsefO{*0F0YkX<2r(VI@d^^uhhQbOCh*O<$@iTBE5a-vL=>cOknj0D$OOt9Xj}Pa|hr&);(`Ub>jviEw zmjCgJ0O=X%Hr3MyqL#uinvgTkmW4cr*1Tm7inK}KeeBn zAeq6Sm(vRs8QS@Qq8YV(W%CJ>lMI04xasFYnt1TANWa_m0m*9kbSlZJIcMp8R^eGI zmA=*1rWm|Y#UKCusOA5>$?yN|VDW$9dw+@SBKjQ)JnN_9yDj!r_bz{QGd{p|n6tNO zak?A_T5WRfp(a<0H=#CG0$9L;&@2R-hQ$D{YW?L50Ecp8#<>Vhu34k#Q@6DF*Ge5P zD0OM@;!l>!8eRIVVj1!iTk1*eV` zK2(bZK5n$6DKdTEWGOzS8EJ~$JhFG(=X-6YwqTG+WBXfx-<`Ib!`bRAM}J<@z%yQJ zysQ3z(Jpz|F8hP$k65P&Nc|CsUeqa__ilFp-%bm+CiVpw&(HO>TK=}dx<=4%vJru+ zm-4;s2j@0bT1x0KRZNv%FjnWFi(fw)jc5=1Pg0qz6DzU$Fm$S&3K1^opi8qQCJ%aB z7MR0|j~B8h-<6L$R(4F?`8Vn+FtX^jh?U=iemp#WfP<*BKhqXZa+G@Flk?;>xG#&f z+Syk7-zCz+w}sq%kns<-0a5SY&<%Y6ty$p@j!^xfe4BLDX{W_Gr`38#!kk-w&~Bk1 z6&JcXJp^W|*b`Ezm5%URWK&x_I5o6dZ<=6cjo1y|^&4vsjI_)v_`EIBT9X+77tXfs znWj#5-ztiYY{``iEbD?r%=|q2d&)W3fL|tVmSA>?A$Ls8T*Z=m*P2MkE?1H7v zl1(D8&ki4C0!jBp7gwXbY3oH2O!G}Kwtug5`rEnOCU)1R2Qxm&_^h^Kd(mWUa@<+& zit$<6oj)MsXoI^(-Z>P9?hj(QPPEpVpUL`FLe2?{H^)OHn+9cSwO=pmq=byNxpTNO z;LzCZ-1da?WfQ801f2U~NcXq+lVgbz*f+%yPu|y;^sCLQyt&B96<2?4o+p;e3dz^| z)crQ+Lrh=r&d3~!8s9+Ql#O5u_s<1U$V8ub`DoAfvRmCM!f zl`Hzgn)k`K)~6njWNIKbm_F9JT`(zPFwdse^4N)~kOaR;!pM~Yyr%g@s;3h!P_7$6 z9g)Djmx`3Jrn*V9qVG*Hq`OnV7erWqpu4R z%_s%r(2RY12l$Z;;PFJD^Us8c^w|>a!5yo*g{wU-&x@Rq=G6H6?U6vE4<)@X!G1u0 z87IpH4An;`xFUPMaH&fu3`IT`(ad#-XU-bm8AM?g<=J1z5^2@@HWg@r-ss2#^=)S= za;1x`_~>4INEl(^*GRF>F|+gNIsF=5mD)`7vU#tZXX_f9pAbjx#D2s!`G$Qgz2& z4M9{rpUIchGZFc9DhG7cLCDDhb``z}Ujp54{=p;j54~`gY=q_v{pF$-z}xjSmWWkk z$0rzxIIV%i8-tE<7lqTL4>prXoN=ZS<_k#dSJDW9s|6j`-WFbM_x{71jeTIxvTNv= zsLptmG=an|1Zj5E#{dz>-&zz4{i z7xF&Ob6@v$U%x*aC4?b!dwB4GXK;=qxI|hb&b!8y%c_x}AA!K;9yFOrwn4@`FM z@47=RR~-ABS$u2|`wzS+^D!Ag&RlBUBl@nV5<$JvucZgzr&zx+Q|W?K?c#El2;FJl zni3?nivCVHOg{K_$?G=VEU6iCeiFB|6J?cM|Dm_u42V-zP$OM!P1w1?S-jr_o4Xci zd2ut)A{pIzl~82cPw}ynd_3}n)hV7(BN3QGn;Ph)BH!gA@7~?A3M#G$ogZs75H{%7 zC1wqo(^}8@+H1g`7d=!2g0Pph-oAEzZdFdO7FL#pp`oaC)2`~-4NU4{*r;Ypj{*k5 z1Oi^4tQfFgabz$TnwYzFEIIaHkhK>ja?B9mhzb$tQL(06It616tEFQgMCZ|PvV`+) z6I13+Q|oJe_>+B7w4v%OqJ_~Bji7ymv&&2QQ-<$O)c7>3lN8ut8BK0Yf~oPmR|Xbv zi6e^CEm#dhY69Ic-9J{)MHm@E=`H1Wo;7<{|9EuA#~Cx%J%NoD<9B3N@7mt0ZXp%r=Q&SZ6g9gDq4wAMYRV{oY?3q{XNh&|0xn+?$X zD9Yes0$r2y9DSBClk7vwr1o5=OM@7Ihyx42x|FG9*84CiBC0 zW)?rcb2lt5A%s6$93_*!N+5wWz>L9Rob$N|MB3rKLIiBK41-z3@b8x4S zAVJyGVhQ!Y_vqOi8O8ames4Y5NXMb+vEWsg!Qgrx-!@FAWb&ZhNO11;B9xo9H_oFl z;c}=A4#Ni~tsc6&4C>xA&R;v+cZs3p9WyeS?_y6e9cOw=&j4eT%tTYB&&#D-)MYQ} zr{OoYHPb=1JuUPJd=0vK!D&IcmO7704S?+^{xN%aP@ZT#O&o{{s9*4gcyRuCV|~2_ z{QhUNjH|GsJ}8eEv;xiXkWha`u_#>Ze>O3d-c5838(Y)O^=X(414kMvOpp0d4HNjy zmT?L;afpff<3G1tj$sW&vz2RG;)=&$x0O+8h*KT<=6?+g{2xp!>H$FlOeagacKllR ztpS);erlJES1q#SB5iKIEa-e|-FA!G@t?-G@_&mz z)Em+tE!Lf@0%+m-C8NT!O9IvGB993K@!b+qJ=@aME__|SxmqEtO@1CK^%cr+3|w&g zDEu4uiLvRUaY@&F*Qcg);cUksK7RoqT3Q|PJ@{i!VtuaK3QD7=>b>ta3Px^7$dUBjzu--^7f z0@X~*9l42mT1z-R(+a7SyIENsIWF}Z|CmM?y*oKElcwHR1eh7nmqO7OicSB_=8a5Pk&L(Yw03Fto}G z)i38;f1PM4{i*f&l&L~n)#qD%dB zb?y>=g>LQB>EUafF}O}$esR_18;XwMs9IRcxIeSXMi7?zeXGY1B4w90+PFT3pSa5v z&zUKe_(onFW}ulf^kt`XRh1rLmJS`B_Oc=PEh@&`~(Qf(?=?LDy zM$0g)kj>u*q{)`Y4LcwM!*(B!#iYNAu&C#9-mo zPEYDp?-=OqQt`1)ymC?k^(h4r5yAb_07sJq=j{>vie1xT314LTGmMxvbS1_(4W80G zxpe(5y9{Fkm|rZJJ~XE{67^|X3Rpzu`7!G5_>H~}u6Rwh@v(h09N6d2Jc}%wJUz}X zcVlLoXZg2jMsCdo#Bk4UceF`3YtsdW?*YSw5`h|<13q;vq8hk~&9d`u4?x0uAI*&~ z;Ksl+IAM7yeUG{6w@OZluknuw2?W=i;QL;|-;HS6XY3qzsplL^4UE%@8z#FSE7o@p|i1eCU?3V^=l=NGJ(=7c3 z1Hk3*vo8PAMExt5B@e%qKW4ga5zw|4hL6nVNS7X2PD$ULZRU9RD6W;6LxGqI)$5}1 zHfb+$yky}c<%ja%l?hFJ!T`9swz_RP&Cjv0x?JolGQTq|*W5HMST{{hsnG67okpikplg4s&Cfx|^BixUqc=B_yPGE-2688cE?no3a)%BmX+iF+*u-jO$!=ew_apzYrdJ_8TObW5iru zlV0v*c+lMOgFNoH07D5|a@{9`NUPp@<8d)%y??2-kDMrhO@65L@%k`bhjq18t$L?5 zNcNyGqH;pPxc`ih!279AViN9#733~4wUQBzHJ%GM^q_@`pEAWv&guGo-n?)XUJrLy+@bigbtz-QGi?ldicUm+20q zHW-+%^WAW##THT|KXOqW(<_@E(-`Szb+%&`j1?jc2g%SJ@}Q zcLSSn7d0E--jORyDe=+)3+0OG=D)bMM^sB*{-;Ab12yC2%U{e3Ro%@R8 zqgk);$`)^f4+@B;C1+Cb9yK*~B@jEz72pn@?LZ{D)Q)`?5kO9$JlbM#^)&Fj-Tb`D ztM$>Bfn|~@rCgPXG%}^+8FlsiBwcjCL*v~{zrJ*)58w*G_0TeEs{Hc5Yd_mgmWmf$ zb~w-&XuqY9r=OEm``V>M_G_K9sq@3(OHcc&-L}D#i+B4+y99H}T<(|2g1mIi%FkKl z-eCY3CQAxFeS~(H&+KY@dxlFmIoBdg|1Qe^zDztpq1V2@OXYS)$&z4c!0{PuuL(T-3`?*Mtw#Nxz>!6-Gmx~^q3#>Q9@HzX%N{5q^RgXEodcwA z(ic+0-(Lp23R?+R=9-DHei*HkahT|BY1OUlE@2`5Vy3^|hxZap%PXEJ*I+Y7KT@|) z8`QN}VW7Mca=dY49yvI3N1Vk{f8j{q1bOb7-N5lz;(qX)(u|G0lxJMLPI|xT#DRU$ z;pAy$gC7qFbJ>}S-}48FH>hWbK!rOX0^tSn6dqB0(|j_<_b^y$Jy(6ECY<-Z>yAjP z#6D|9G<%>#pk-E-{QA|b=gg;`1(k36MjTh5;A)+Hxc^|L&R{1q&)}c}VfS>4s292o zJvZu@QxI>MuVt>N(Ep;M7$@};`eg$UufnW^%B1`|8ozrU^sf{bPV*zi7u}^3U}h{K z9!5SkK|k<$tOiD{19)45K}+jY(SFENNULZ{EI;7Ry1X`axcF$T^bOm09Wr#7@eFev zXz|urat)-HAwA9jR?gK#k1L4|Gnh;3&jZ%z724OggIz1@QD7j4uX{d?W>*3)_SIbt zMR|Ye>Zz0v_ohJM-Q~&S8yS67(>X}15-Di2!l(n~pLOFVVBSUSmbmq~w2?0b1|(0h zVXe2a9%4Vk2%>vC??&fEI*V^x8awA~+Y`lXYRlrw06m5rm??toHSE#BplJ0^V}(8g zz$@&7(qs?ish+Fonsw=fE^`XcV7gedBsy*+CVCY5$Vd)#^Pq6K#c9n!qU zbzD5jO)q%G@>3X_k(q(z;H+B3E*kClT(9wlk(tx0h;Gb~@6WW_CohMHYfY zwPluO>cP5b0~6#?dE6Vy6{2mAs$F#%mz{$|prRGY>@=?yw^oA|1SMWTVW&g+2{5ZT zznKyx$&{Rh23%Y`IJAge3XYxTvor?R0u*on&zB?YbRJs`h(HJ+H z`uOS-O{LXTkrl3%yqUfTDXs1%3!<;Z)a8QjHrPgl-8j3z$Mv`^P>z&GU5Wf7PrL4| zo1bwPB_X?50UW#)^unIN9h@GOvz#VbZref7zxXbWomMs}Hm(`!x#1XIuK$h^R#?O? zG=~=JS=p9zCqvC0;^oiIZRC{i3$=Swv-6vr)Ej=#*Mc%2&2P!HyJX4ONwQ!wISvNt zy+K}FcW1WJvc`JDn1yc4zYJofnGG+Q=TNIjC70(_!eb;Rz7B}Mx)%OML;4F$jw+uD zT~nD%KJsrZ^r76}@eDaqa=CwP?#tlWnfV-r^^%BoA4M1n*|yP)97SpZhq2K#PrGLe z1%vX?I_GUo8nbP)c}KrsE>Gs9wJc%$w4`aV&{uHtEWEkc+E=16M~|JZrpb(?KVp#CSHOI5;8pQ0r`MkOp3SGn8mz-)Ee)=^*sF zY2d!E@S&%S1rSh=S2j+|Qz|H;6=hs2xzcTJpn?M$8V{;gS8W(8O*ABr#TpnNRgdKK|D-Mhl4jB#I7~o?-b=Clgl08<<#~ciX35P5kt|{i4MHmYbh7iBX2)AngBep-e~=LdvdSjX5AZ+;bV|AV zsIUSXF1RNPBWa0SEZRRor$K--v>(oA9BSEyK;II!1KBM>g$MwRkfeaOs-IVgMnTif zjhq~d$RQrv)mnzT4o_3^H;eK6y~j?@oPQ(|h}0JxZuxxx+p)YlKz_ZN$O57xu>&H2 zvV!5q7-{ar{+3PE+I(8KOJ}CKzGH#b#wFbssH9mEQ5$<<2NqZ=g=6qUcRtTXSqXop z?GYUEq)L3_hpA1xmVsu~u1~ z+_kjOH`Be8sxT5=NG?=y+$U1o`j1fc6Ozg5Cvh?VsaYmr`{!6d4JE_Nq zrQcZX7tFjz;H>- zZQ7$J2Im53%f__)J`ji{-9r~i_K%^bQbh+=Q+xPn;Y*he2w`D(_r;aZ1>QPQ6F8qR z|BTDfJc#-mJAySo{AA)w+PBwJxu}A5`j3<`jSq4XuQs6Rum++d&a_-!Y|3pGF-n3t z+?**9J(*0f3MZ`79T2kw_;;JJ*UlGz|EfGDnw0*dn<&AZs~`NGay$wgjdX4BkjKF2 z#GW*u9|9-!xRr}f%`bH<%VP;f`pdEq{$Avb4ob!TeyZVvT4q@&x1 z?&e9J&NpZgi!{){DFEYkgu*of8(wUzi#u{|-7j;5bW1lZ40mQOr7~Or)$k_V?;1cD zVG?Pp{XO6XmR3OULPP;qa-fO{Ik`DnveGxYhVRCJ$WksYejsCd(pN}e^iJB*R2ts6 z7|VcVw1|){s;cqW>?(RTv>|oh>KAULpkz$DWNfMi^@c1>loZy@cDV)csrbDHbQH5l z4qQOWli^CLiLayP?8u5clq=p;oXk^EjdXp^@?Zms_!;OO1^;cT>}guTyT~W z)8g3f1BN@F5!T9#X1A^b(G_A$6FmAf83|vq8!8pLO1I2r=}%$A>oHdEfE=-(^Zmx% zO>zKweuj(ph+=NV-0JI5V)6iGX$JEt?Qj788%vM2Avu6OLw*22 z8xivmAM#1gJ1IRTDTMktNQLtU#*9fXzwQSCktvcL)2JX`5I+pjW6u%lUXZ2sQwhfD z+{^)Xo|;0QKPw8%;JVfSVaZwGOsOPf@%!s}Ke*Bby2|~3H=db^u^wDbkt7>ch_@ZtS6lfRSQ4e1S zhgUza4}N}USnJI+-q0uh%{}_8U|mi%c&4wfqP=E5LHem^65dKk{n{o@qCMSBIXF90 z%X)7stv>Ib`cbv1tvy}$ZJz$gHwC!AK9zycZSWKW(WDdPAG$zF8Zsi zpWCJ1ceZ|=$ok9(Ip7>}K1(hA$4lgKv6PF0I5FXq$46nmPK<;{>wf?Dg~CArs#C|n zUw`bp)h;7`>S0$%IYy(4&h?ZSdj*}^FY!G+T>AIP$PH$p3oS98q#|fWmroA=D0?`6 zaA@+51Q%dpt>ys5fYR7CAO8LU_`A&+n=~Kr-)Zf5Xt6%&e(i=;E;3iqmZ1Bwtq%uv zQ0%iZhX#jO?eyouo~7%JDjE~br|@zG4&a%}>RG&Wt^!8=kEi!>OOSB#k9*j@e*h7GYvInX6J#d{E8nas^U-(g_kS^_ z_oX}Ftk)2V_b&G9ZY6fLrj%n*zBt@@Og?@YjflwnsK#-Q(upUH9DP)^SWdd1;8*vc z)${zX6N)WnLMasY7x`D*u6>ov{b%Vc&t!6Txhe7X&h|9HMo~pF&U-o{J0igU0og9f zHVn3X=xY5X;QQhCi)t+8OI}xb0(1;RHa={L{~=)X4y~(ecO3t;X9=X zdA3uQpz<*{0TzhfsqAvqkoSY++{>qMdL;YUsxr^=@@8sm%2$b=uq zbw0u>aT{Gm?m4UUJcnv8oK!V;T$1$1R$z#v+}FVouMNdSAk^{_Ekvv!ZOVfIY%mk@ z3$8b?L-s8Q{3a1skPN)uzY$>KKuqxz5TuRWVlElU92>{J=Wwx~$6lPI-=K~kz7IEB z=rW{$K%Y66XopD#uIrGC=-nZS9GK2i)~Pi$E_93<=}?3@HVEsYI|B^vT!iLyYjI@w z{89jrMNvN!oH*0E%{MGQXONo6RO;Jdwk8aD(X|?!feDHYT0sdMotBc3 zMW2p#8iN>y{({I)@yfTDQ$D~qkmEXUDdnKx`_kps>25*PAxP#Ijz(#sZhqe^)3L=h zleQ|E_BQnGpVvQcO$u6zrVKuaOgy%da5ozmn~@_D$}HnsP(D zLx%H{cz*9J*N8*P@HpE`tg>!-6*wg@K6Bux?!#h&?cFSMCD`{zF%gX+EY7Y`(CQ_N)^kj3uE2|%%;CL%tQBQvHQ73@@L9t6u21iC6p>CLKju?kxtnZSf(ns7 zZAq2^*Ox!^Z<<7HZ!Ui@QRyCVP3;d6dOa#&AMUg_(`311uG?H!Q9{7K&9)AoMtoc8 zLjkqC(0s=BXBL}ngjp?n%QOSlF@qRTQlJ4pRhkRk2s(!*4?sXH8`Zy}P*(vJA;Um6 zJkLXcmRC3beUc%40Ub-9UUr<3UkvlPUJXrcw@?_mj<>3XYk{lFakknyV6a2=UBcK^ zYN>3L76Q6#C78N_7znMg6_$sFq#^AXvD1at1yTU&`rD6uegBel7|IJ(D=$I}s|BJi zo&mkm$;ubMPQ(Ucf2b|6=+^(a2z7V;$J#CT>x5btuul2M7>!i=a@s!pI`PF_>OU8{ zQ_B|3vD?@$ed4i-$h+o2ykoC!X?@D%tFRNlpLgI#$)ny5@QR*`PWEa|@K4MTesueC z-UA*XtDR`A#(+*8e}5nEkL`txo93Mr?YZCY7&h-mn=U{Zxpjv=^lp!qLtj#zC6H|) z1Gwx<-LG@l!1MOMU;N*R9slDt>i&Hjb--=B|NmhF+U@1YcF=hrt@=MXThWZxCXt`> zPlu?Tz8ZPkM_uaHo?eF6*W?GXqqfI3rDWMv zoO5V1prhq~EbqT^En{rGesJ<6v!81H7v|!_pii86TIpYv374Xdjx--{x8!qs?6^wu zq~>(Z27D{NAMISCqP;P#=+Q+F-^MH6dp#S1jt{tO+#|CyyAe~!Xt7P12is&%+h|vH z16Vm^x@n1$pxrr>j~))&Ha`bmt)mp`eQJGV#}qx(yRyyR-N+AiOCJ#7dDd2)%loe# z1WReGZ_~EBb*8D@(C#6B>k&LC=bVz>;w|l5A#+)L3fv0pI!nb=JDS?=6E{ra0 zA+@SUTB^d^%V5*7A9eQHu`#CatJ{KBXJ)cEW4ruK`Pw2laL;c3WlSlVc&h-?zQI1! zUd!4b ziqjd1R3+zql+yl{odxGpxR0I%g{V zB#i3ocVX!tTJ*J3%MtmHmMY8rB?e^2N{$Hc;(TmLw;ihqGfp{vo-gr91J*tgn)WZN z?IWNlMLQ>gW08DgB|8`PB}Z`@2L>1%SsSS}Nrb&Bn}h>Y^>`07IIV)hO_H$RwlC7N zP9(Qv?tP`pQ-D$7l%Hy8LE)2p09=s(dPC%~rt2gaSd^ig?l5PU7z9dmeSC>3jX0mn zZlZw1wmq&)p)6$_8yEH;5|vd03>NF-ix~M%lE;dAo(?(^W0J<`S{10R9o{2eSye{d zu|lqy1QxqvK@bmx3YOWXddwSSjaYmf91SK|>^LB%291e&b_7%0Ll4o@CQv?rj9arF zoRWJR&z(aT+Lf#737hd|F04K8(KkCbxc#%5Wt>e>UX^f)C`~_sR+Sc8W(toXH}91@ z>hHgWc7!}t@q>MWM(p9v>i<1ppH7S~;T8@eYa4p4NauG0z37IEKR>ez7M>GUO6Q95 zo|EgNo-e|tp3#89r_ZK7OTJkR=0B;K9=Q7&qW{;pcgLH z9H4fWe)yOpCY`22vlte@@HNs`2RhT``hfwr1wa{5&Ao#BIn-V5$5)4-!_h(3^fTpW zQVF}-Q7LY&t}7A(gFuTP=QlrKUjuNr>leFBkar0-)8*q2wzN!N;l@X++m?zd3PPQe z&G?Kf<$we`Zkzg~9BIhh=ZOkLb?(CgbG^ZFRm4nMc0npaO#cx}$d~b8mZR^?I`h%g33|Z$utlW7c{|U1xu2K)VUe892)Dj0Xik)7lGjVs-6m zY%`nj=kB0@n4$LWx{`@Hk@)E7Rmb}Dlsth98BWYx9Ul6wYl91xgZu(Tsvq=#ILoC? z`NM3d_4#Ks_|T(OfjY>8ua#zWsr9}R5xQ!*r{NG0&rINqF1%?a;A*A$eR8{0>b4um zPnKaVr7jKGP*O>B-xY*);qOgnb#CS6ES1CpP8y6bqf=s9qmMq2s#8*7IwAdS9MM2bM+iE0XQR1fsrN;lHCL%|ei>OT%{ar~qHby!nGKGyNQK+GZl6%D?9-{q#|H`ltB<(@alP z1P%7l{hw!xOYUy2>yF*}2Gyb8D?gQ;#Vbx`2@q_ClssVJ6mYJ5AQUJwb7jw(Oz2OS z+cqJKg2qNaEko5eix9oq-*a}Ln%lk6NAe1NMZS8*0viS~fK}{zFB}vr#=1QfD}{+* zwwGhvXl#56>oT4t%rvB~CUvaQJ|6sqM`!T?2@eJC%@IAKPb{xHz80bRFU;#K#qaIo zws5>}nz>I39 zJ4cBXT(>Hb)AYRwr5CMx6xLXN8IeEzPrai3Mw%ql`o;Tit52}v9O>A1rRP{&^mEk;1_vvK+NDELehcN?-;gs!fjI7Fq;Z7@tI?^+ksBp^djzN3W=}M& zkQX-&-+4zgFhqu>&THUdMcX4c>ij-0H5p7&)%G)w4OlA7ue5xI4?QSTKrJ0eH4o&B zuhsvy4Kr=3^BCsYj=eHhd!#0`vh64egFkH^hpqON{E=$hL04hCV0WV`cCPu+S1%bc zm#n1JL_Jz0;_;3NQay5w9chS}GP7ObD;sjzHG7b#s^J2BQ}YKe;5n!szl*aX!6Sr` zGdSx7033PVIQa^!X*M@xzYK~^k%0HvjOXq zEGZyGzDDf?I^%e)^>L3H#=5a}7IO;VN0Egt1NlyI(I)i#qYF>$p=Uqzs}Z3^2<#ZkO8#_wPIJa}@wmzz>g(q&{`>w@X+Gk8W8eQSR%myK+s;pl zdkrMD5|;8LGIaRGJVN`{=vuklVzqa?SQ1)DP3q>1{Ra264Nlj-^ke2#ze~kaBAOa{ z5f10XMpQqoBTv*?2)1q*UPHRtSlJ>>!3WlVX3e#H1geT1&h9z6C3EfNMsxR;pI1o% z+ppNxlZOeVG`q}29cpH}Q}0iwWR@$X#t`0z6~aW)zEVM%taDVg(-<}8;2QQxeyKMw zEPNQ(qdUJJ@Qp0?S&HW_>Js!Ps7Ylaj!5OZiqyYi^fFNsfQYALhi_AY?ZfnGRd|Wn zFCQ#y$}+->BH8i2V7HX1n&Y1;(;o8|7r+j}QT8&Twhj#>jrUfi-R7H`S2kRK+tDmP zN^#4z-(#0*aZfp1rO3Qy7eRtD9|PH0X>~xW|j0`?d-JdF<0fHhKBmNKo^bGm6J7-+7t@|EkMc! z>dsN=W`c2fsyQ{D^H&mwt(S&`Wg-gVVPWNVS=D#*1PGQ^MfN%C@MX3~Hv|hx`q^AA zF`DjkVA)?H*m9(3r9~jvS%rKDWn!gzBxA?nyrj}ph%m~6eWUNE zMCT81!1E8g^eAdvOtXFo71Cw{)moTYGoYVW`s%a3YGHuN;KtDh&ou3A!vW{^*e~&7 zrVjqpntr~7GD~^K?x>=1JG=QM+GRb-zQQ7ZD|)=LA@}owBcw%E zv4l|WmTl-lP1+WBIW@%;AMUi;Kl7=XWBMZcZ{E#Kv@wHgOOr7!hfB3lxe^6 zoBct(gO|Ir886{$M^@;faiGvAz$;M78Eb`ujHJ-JebX2_=3lj#J9MXk$=`u!e$Zne z(zkv9B3avIL%aK-ekew1V3qF`DbbPE$x_Pt=t(c0Aj#r#i@yKV)E3pxDcJnG6^VJB zE>UemFm5?@?5LY!IT07Sp*x)iu9*9%YsRljDY{zho762fl~bMg*aAp=%b)+~o%3>x-p8mDGcGo#sD6 zoj(QGJVEZ0JAa*Mh3BvM>Nlnp**ZGTeQZU=W~GyfwLe}1{fhrCx&hdyc*uj!|M-oC zn1BAiNpt@tcwYZ?!VRc6RWp614iA2vIQZ-TR{lo*UzNWE>l9dy#`5ypj;q&KG?Nj( zS2}NYqVFR%Eu2e^yxY+C%nBUhz5OVTxqYviruJsK3nQG6OI07`mBL!RH-uJ_wX>sM zGQR9fx+^U%9Wda6b2z!&gIVvncJC4L|8#@@tEw2p^c`BtXsGGMyDz)chE;qr4{VYt z@8@}YZdJ2-v`?er-i6zj6Sy051e`1Z5x0}oL!~&{W~D+We%92udNk(#Izc>WN3Ui| zH*2TU&QBV*&jN>n2xb|oKK+58X8m8QXIXZ1(_bg78s7!%2WqL@a?if?7}0U5&2<^w z%tR?M)sAZN@>WfnX60HsZtLMagfWnM_UAal(6QHn0>X( zRhmudV@T7(j*r6wSYFPQj*$=$J{&%IY3J7oK@8uqCdMBa9q4m~^B@6B{sm+`9nx3A zH)kMpxwYY$w2fV@%=WZ?Z`2j5Hbl$noL$OB%G0Nt$herAoE?+s*&m)zl#fJ;hCR`y&fKIl`12C6 zuCoFG`sPm3J0SC6%{U~z7 zE@y)0QWnc4YaF8}zyD`P^E&nSl9LuejkwNjII*t& zIzfljCj!8rFQxmL`g!k79- zyW`Q)`x#guew=6i$&R&lF#!}C7@y&DD0=`OI<6lw-;&H$4<;rK=IAolz46A_(i^!Eo zOoFU{5gq5djSTeyWO4VPq)c6gXGZ@RkAabnurXSEu7o?>e-5vFmCil4l@MIyF7D{$ z;I1PIA%#vR#Oh-ZG>>xHp%X1}ur#She3rxSikye@s5L{x{5t|B`#Uei{@k)#s5Q7G zOZ{b<>9`vt1S`S+BBycSm#52Lc!;U5#swDe)iWSmO1U_e4ZLShTbimM|e{9 zXy10fDuQ2^{ayyWZkw(}b?&WlZR>wm~T86Ja4c_qM5Hss(j>&{w=pyQ{&uYgjaCz~Y?UDe#=q`1o3N zTSuFTzbl^ukS%VkjngwL2nn6-g^0x}14&F46fD!F!~i(C2VzexVKhqr1$EzzJ$E(Cy z|GC^Krjh;r5iq(0Ci%I{5cKq*DlJiin749V8_k@11c&MqUdilv_0K`V;Z_AM11>e?dUVGkNVx6mdpU_1js+}HuE+c{yYpBG&Wey(4mH9S-YhuU9r5COLuNyWA6 zeV)OL94l6bm)qvlbN`J86!f_JqSC5cVVCEETrD}EC z7{pK^Akwm*#8&EhSJw;7&1yeW4$W^N>Un!1)Phj{+TT0EWGUm+=|p@y?$)`)a_irB z4X!Fq#d#&wt=oppw@EyCzJ{r;a!27VrgU72ImIn2facZcGl<4uKPN6Z7JIW zQB3{Dok8DU_m8w13v9*%4xET`EPBpJo#ncqDG8|K{t)2Z)u4UtX@^f(G6W{phW?;6 z^3{%%5Hmz}fDFQDMORKn%i<>D*CGQCDrkARn$|v)?`_4+Z2<*N5AaWVN9$YBZa3o;G9rpDRJUjM8S2qxsOHR$rf-YOZ_yiqpNIr+l=%>!oxZFk;36@6wKtnw~nAB^0W zRjaNt7OIwYEY-}iVSZ)QvR@uqFX0(+%wk|icA*VruPy&Nk@Rz}vSLMI3SaPZ;ZDm; zc!18o8eEGVHx*Qmy6;nm&09MbP|Ize?enA;ha^SM_&Q9*-biTMrynTg-~IJD6P0q! z)uG_KTZ8xg@8h{O?{7#5DMFj@rfEL1+`%b5c?LayOvsjO=6+SY>m1~7&0{LmT>(tC zQm!=z|6Ppva~2@T*{7ej?T$26#kov4&Tmbkc1jffE6IuL+i4&QU=0flN0|UJs1LoB zQwxbl{rH|N{pCtO_WeX6r~LlpEJhaS#b=8Gvz8B?IJkS(`yJ#9kh00)$))d<^T{EA zr|J~@xsfV*dF^lzl8oiUGauEX;SRGLDL;-jU4@*;xr*qKZE9miM-p#Rh|m=X-*$w6 z2j8u`UC(O2b?!PJ1fr6(gc_gOY;?_jRen%i44n<#zNchAovv(YU|*h849onGCUeuS z3mkS6@Z%hWO6d)udG&LAfaV=_Jqc&cY%H@)^Rcu`gB9VxMcYt=MsnxGbV}x)(pI#0 z!i_@ZYd7^iJ-K#P{@GudcWhl%OWglD__<3C1Ald!3UAL+F@sHOE-wB)HCTm=(6-Zz zPDyrDB2XjJIvNwX?>07WflYpE1tj147tGUCctU~Mn;$>~bJ_~mzrk+5V*-Fu+~3`T zu|Z?>Xo8X?)E;v*IO822PuBwMhNbwYjbvBWDZZ%z24QRI7StZ#V0`w?g6~3jsqmmz z6;i^1)KZKHE@DPajnQ7j0%#6^64Rt?{6)B@z8- z%_H;J3`xF-k@S?`vtB#oVJXvVFsIqCut=;FMuyqX1yFl*meRgL_Pzt>;`~w&8DS{- zMo0Se3sxju(v)shSZ_%gH!}?gojHaKmw%JkR-u zG$x)a-X2$CH#qxNVe}e4xy&OTaQZ#iwuPX;LNd_!FWYmIxp%bXETNh;>o|5%gQgdx zuC2Dtp7OYWs7Se_Kl_0|g%y#jGC{pnu+I{Fb;PQXTI76(+_Qk{ov$!HqX1Xi)|vfR zeBYQPlZiHPq(8~gLSM$D{2h&^W zx}>ucV=qjgqYORTRM9mi+MWO=T{ccbIuu39JBHwe^|KS-7n{rL-P#+q4A-g=y!YQz z4gNDn;D60215(~jevuvqy??sKwJ%BEVnhQhMs+3iK`IIjn^Tt>D+u1Id)=0THvbB^#-*#N zJMV^uV-b`x(4)cP2T>=d5nR(bVT{e`=#nSc3 zen=8l*ub9(cuV08^sS6$C`|*pYd&pV5N3Nnh z{;VB-V9t*!3pHp)Y(^SNXuSDy(=LpVH1w_p79W*es`Ai2XWP;&&xJat-}cbmOoF83 zkjO{Dn>%8TN?ZmsLo^#Mpk(Z_@&r%oSdq>u5Ikv9j4+%LLfQ+X0!T?QAJAO3OzCyL z=ylnxj2=0(JfniybnFUb(%=rC>^1uV@~t`G-wqpwbCGy1iQb$cRRTpgrJnb*hdk{l zKmOaNcDj8_2k5CX@NcZ?+&A8GzC+M5t`vPXo2xA4@M0rp&U}Sv{-8bn;+SA+jg72S zK_X%eKE<1A57Riwvhxqd9CpgkVmX`rvuSw4Q7gxEt?C5|A@ZCYr5OsfcF?IIkIu>Cq znNR)lGeu+5Ejvv;lyFXW)@{nX_f`9^6Cug3<~R-9{I=cgvWY{Kkk{Fcn2|B=GKCej zya0T78YGk4R61Ci3{-SvA9Cui6QEdCwDg-;eRhc&D@Lvn<3PhZ(N_~#A(farw31v3 zL>PwnX;nV>r94*14UffplYFC|9U|# zyk>srUWj=#>a5w);5{$P$@igS#p8DA?dK!=9Ik!a9z{6eU?pW%F$r`hfYA7g=|nrGuDqaU_pgXB z;k3&vNqRI@KB-G6U!EfMj6Sx09-zAmkW(=_v|-y0xPZ%c2Uh|{x8x7HNIvCwjN;{A z!A{v<+fY_scOU8a#_8NEHwWtmA=1lId#yzs3qlD74hEf~u6&l&4V7DtbytD`@%zod zQn#KBbhf%y*&iA((S(a9#t+3f7VVYu8oEV@q(`OZj!)xw^|NdVwh`fn@0Zur5?G$c zwg@Rv=F!?1cJY{jqstGfB^h0%$+gl#kL{TN4(t-ZhPC#kZP_N?V&?nOubaQyqy^-) zR&0A{Cf=DflCrLcZ% z`FVoF!YRiKW1ma;u-@j9m883xaPwgH?QvnBUA zM#QJg{>hj~Yq<>3_iK%{l2u#m7q&QSzq#(#=H6P__P`&?`9@#2FR6qP*d(lPy-4gF zq^UHW^E(qDa7uHl8H7BdIFm93JeUBLY4E29fnq5$2r~i_=1=W*oXMw7p4y2@Oz8Q; zFDhW`DaJC$i&}gox)ihvD}LFg{=pz<*fzhp=4la-s{8u=sBSg$p^0?BEpcw{umq{* zi)+R5N%qKbixOdBH6PoeGTYnV6lWR1163V=*SPXIfuP+IC8I484#acwI9YKm8ygg& z&mzx$!^Yv?U@#YY(C{HsZfg*-l)8rRUp;{dWOmYI-00XhL1`YpF>7ctoD0Cz>;NE# zUqJkne;S_Le94Xi8mLeB8xuws{&gZ1a8V4Kbd;LSX|96MoxLLgmB0d3pQ_aT zVdG*1m*!KHIuq80hgHhi)>sRg2~>^c4u-ZZl&AE+8@40bI#B6iQDA%AX&VcD%OQ z0e?QZdH695TSsYtZE6Y(TgR*0mK6fj~k8;-)HlcQD4sISk7WxSoy7LQL z`x%Fj6#E^!v_RSv&Pwp<)1o81M+h-}0IH5sf3CMt%^${ae%KRf;`YMxGhWqyrhgXY zX*0&AI>=EJEbS6!m&kkF0A?JX3h`RK$O>T+DAHp7rHz2IEcObQSR}Juxs}vrdfKC`~vjP2y;`(dvNB+KMi_QtpL{u3XmZ^_J*|^X^a%d6#S9j=$uvoaSzd_wyf8`2fGC^sK@#oE+wopW}Dg7z^?08`ZSLt z)JQY=-n)xU2#uX6kuP-xTxf;_*3D@HY;@(=`Xm!hhmkXTW-3hZ%jj;5xKyH$fWb5x z>`MvaQ}eM9wQ4=|PPO`y|8f^?ClRdP%!H?~N4O&H*q~b49X%t}a?l!(?WO zDc%P(iRRfAL!W0uUq=)yRx(_zcUUb9gD+c*pSo+DAxOio6Z`7NN^ql8s`S!z0LXcm zxbg=*e*lyO7xaC~2mnTujWjW~^x=#kE%&?3`axjO?;zHQSv>@#8T52c@1=c3OZa1% zCf`(a`t==JQa$~>ALfY<<$*x)HKKMqZ4$aLjY@V3`nkSUk9IShghJk58u-6hd(XHg z*0paGb=fFJ1f&xckZwQ-on#9O&>%rbDAKY3k*0K{g=8rLO5G}m(jsDvA%tEOkw`*d z2_n)3g^(c9B~igqLOiqgv-fk}=e+wpU(Wdm4D*{zX0H3XuYYszj&qs9c6)B-H|45m zG^mWlB$Z(`HT6ACki)hvuAmj7(gNzSkIUiW=9UN$*h7iSF1pU=Y3lSjj;S5yN3AJ7 z3L~juK(q_5%qx#$i{Ju8(-zWi2Cv~Hr98_?UvKu2z=q+h(br)~FV zDm4O9o8*a^FsPHd(LQqRl!>CIYd{z#Hn+i*?(ZIRLTw%eH?sbwqbnX}i#7aSxn@(f zaNAu|(fk#(`wg z3RMBk$3kNu2Fj|tDw5$tnW{K;=aHujegTVyeL5bg-@-Zk!I`Iu@fZauL_v)aslV4a zqsF2eD^V@>-mOTP`@9BsPFp$N-_*-1P^0oQv+yuvZx+)v+byh%r;EarUd<4X=VoW5 zG*jEL(dL)hq_Q58on4s${6=u#d5bxBwo*L0>=|_G&gKpQX-$GI z>ftC=0hP06+Z5sjhl(#qS^VFCU|Ix7W)f+wD1Az=WY>ky90zb*Fo^2J&z>}BZ>P#z zk+#sfwM7uzL*hosEN;3EhEW~rKY%BV|9aLT;3`GML@z*KZjPafhAmo+%fjI*rHvz%8`%ju^utiuC0B3| z>l%*LId2{}LK-NB@*TDlyVNv*U4qnEV(<1|m+dNq9NrvY%z`fI7=XcVI|p1P15Ip( zGTv`4>V}#+AmDQLgyIeg<-@pxIcm~^#BR<_*eql%&x!55=MI;nFufQz)wfA|;uoxR zj-mvdwW4PBHz*=xG>a@_J9RT8HEaqS%)N)d>&zLAYI@%_r9B!QJFm@@{XiWIaUTOW zxFCr!DD)FhGmkoV-LQzpdv<}p!RKd#>w8LJbQ8-#E=aq>I`~959h6!&;S>Pl%CD|pehftMDsJ)$c7%1F6hM2Szl9;B+K1@P-@w{iQ8BY~V%_?@ zhQHBR!u0Vs8?}+%5ejR(MpzUw;nLX{g~&Xz__dDd=K@ z_S@HOUVkC-_>rH2ownAew%?&;R~$FL$*Z<%&!?w6%_W+smA$Jj^2vF=Y31;}gK!K0 z@s!_gsA>NrMliHa4Z!dwvN{a-iJLD4U~r!*;jKuP0VGnBvUuH_BuSy&BhWA%DoKzR zo-Xe7AVB#@7BLs0!x!DD7235Gzw~Z>rujbnOc?cyWV?6hyFWmSCC{8#s^k9Bn$Nki z(-vdwVJb#0z3S$Z(m3WHapGeQ%{p$_(gd1o8Qmj6%j;dQoYLom6jZC8UWKba8uYTt zq@xth`hkik-LRmT`=DN$?)~U-abuOgQ5;#pfpQ%=TVM&=IKhCSRfHB0jJ$|cBaB}` zwj#4TmF)TJSmImVn|q9cWb1QgKrjkh z8Ezh)FCJY|j8>QSs8yHCCW8@w?-pm(ZjWadiW^Bu<8xk`ij?@I^cqhG=ab7{W?-u{KbtG8fssr# zcWii|;WIy93dG!T5){8Xr??B=Enz%kdxN&GAH8ws_M^g;EpxKDZ5BL9w~?-ZIE&CS zs=7Zxfh&$C7&cd9x&m~@eu)()S!>YHbn_mgvJX1C`5sgY(VJ1(3t;K^vU41{s+jgm z?9njBrw)5ow4a!Xy9M}xy#-3(*n^YA>j2sWDewtmWJM+rry!?B@To8+I0g(C_*!QD zIRZ}0x}CN_3xeYzY)j z>g!Q)hsmhB#|p+?Q_XGfYWA4W;1%Q7--o-31`2Ij_64YWKkU};GzXojyjygzJq~#) z)_1kujTeDVaU^BY(D!ztP^O8ARP96%yH4Yc=n*(ck)97>oN#ixoOlshP+~%W@h9HH zQ*l>~ld1jdM!=DEu`N}$J#B^{2J>vG&NWEIWu;i|o;s1U|QQh@v{IP1F9CRqL1PWTI<13>2VRGl_&0O@mnDHE*vrEyQFGGdoQ zo4+E-rzI76NQfKR$Wt>v;+$gT^^9LQ=PBm{$)=iPDEX|5gVK8*QtrC4g2FB;x?7T% zn=N32Hz{B;KP*=S>)UJhRbaWwG)rtfw!s9AF=`Vmyk6JY5{%9Z`n@y2(mCtv(p zuaja;u=(r~_q~fz8Fx*I)1wT@!tt?S&YO4dRtP}hP6&;`p^+r8x#hN9fedxIf!h}k z%klIFCmtAX@Gmj&E-x|mtXsn`cIxn3DBY?)JZOGAQlWoVBF}^A=hS0X;$Im8;`rCf z^oSAc9z$vl?d~=j9%Lyy7H(cPeiu=w<=TZ|o=QWJp%Jhk?a?AU{QTCLXTjOoRT=&3 zuPg!x4VHH(uqc1B_&ZBXrDb+0b?`=!!B*V9$%hPJ!Z7m-%=qYue;aVd~ew-qlt38v^??laPQlwsV|fPYloe3 zTKD@8EqPG9-K(;4Z&R{ppMHt)onau%()D`_X$m&mCmO1-0XU{rU^AAzf6Etd4FXF{ z+*D}N@%O!l&VTR+qTeOaKqC>-KEfNE6y&3t1SqZooSX&d z7h8ISv7crvhS*}K;e=KJ3tR*o#~F>Y=% z_}&E~miPKOY_0$@|I)mM#Pr>J{lnfn7cC6%BeL}$h}=#q09{1kSIbV(Hz{AR=dmXjNc|C8t-e!0w zvbNo757q8GN4A@?uZD-uspy&a)s=*CBx;)1-wpDRy~xR4CaDE^4R63D0mr~W5olf2 z1{xfm^u|Z<3G^)o!dnD_&tMiD#2chD+mLd_rzbj(MnLx+8$xJ=|CH~J+5-mU7&AqO z1@c+es&~M`8*>(8?yrJ7@ZxBSV$f4%!|qQ|)B!0{maihq{b`gTrK`i>PRxOX z4BlC^6@&#MsH|XFAin~p(KQ!a_U484d*j#qryqI^Uyjf-%Doxzq9OO#Rho2$S6z)8 z_s!Ani%+K->8qM{@~n-y>;2lJw9kRsL%sbgNebKrYXn3p(-~VR@I-nd16HE0a+=I~ zIR%X7kc?(V4x+ z&IfS9zQwEZa5w#r48R}?n0IMi z>o04o{8Qzwv-vwS-LJBSxbFf1r}y*sE*)1lWbxuu4%laO&-wdZXLfiPa2pgR&VGxX z!Y=Wsyd_7aC)~z}1;u=eKpi`|yq+LKhG*>EV?+iuYo0#nFI#DQ`?Vo(*1GHv#VJ)J zju9YV5?eT|^+{SUr;*shIVe2$78xA%-U<}r{t}zAoZCEgdxDqPQ@2YfBS=gq=;HMS zupFT(mjhYq)R=hhGqffQvJocE$PXnz6&AhWwJ&(2eN$)Zk_ZDlF5l_s0N!%&DZ@Y@ zk!ko$F4CGmlKnI>9W1U#dphk|=L=*5jP4&uD-Ql~*Gk57S7ts$9;2Ioh-@9H2PqDu z@uHtf_B0zN^?jJWPxemEKkuK3%ig?KW~!(6x?B?0P56FmV3R; z)zd$#F2LLCxG2m!c)bd4dt4u-M60ft{v%rAW~~P!^jAOAs#lO?V%!KA4pdOsjmK>+EDz3q-#iMq2pVC44zh=i>}4F}z6}J@ z<_6!-h@gAU)Fds~138sRW}7#G3@+yo;KY+p?=X6TS0!eMbVoKeO1ne`%|KTS$Q1T| z@x}Bef2ji+5yEf%+(*dmlqEJ&%+N92#2`#xTC;B4_{)jRgsbepqi!-Knb4J_vcth8 znCLvuoB3gO2Y<+v9cj9+^Jz5PEjm*zspbSCZ@+79SWwUbwY%bUFDymJGP`WWjX9cI zrn-N&dgyi@>ZTgFM*a5jj@x#54FQH%21uZIwCfc96z`XqIH;7`d@C@^HW^eB$_o4& z_&C;#Ob2Miq=~oZle(e1@V0zL*MN+Rptl>DJtHw5Uw0^phz56;b!sy!6~`tv$}K0D z-kLur!>)Msv{+mKP7|Bq8Lwhr!&90l-U=NCc-6hv>e62_b>Yj^2^M7Am@2XD8JsKMyHsY}f$~Zucuf<3_#x3T@_rC!{ zb3TKUNbtg0&JrGAA6=9b(PIkbpYw5!U=nObzO~@nn(YbkaI@;zZ=P9~~h9F~N_QQ%0j#q{AvwY3RqtRA8v<&n`B+^HsZ4ewxcw zL1>x1o;gN7gN|RaJ=h;?Ti2GeX+i^gd`-WnuTb$zgEk}st3iYAz}U?y-QphZmk;s{ zxd_t*{n+@lJWnmR+;9iz(<~DNk<`uLyRC}}b;mo5XGu4pZCbex1$!F!c3!{>-g*vU zH!x8LjJ&Ma&1bC1Y+;0Ez-xp881)JG#mqhTNwe#+gsu*pV1)6DPYECe8_2AQsX8`f zr`K}SlNEoBoPmCUg3wvA<)W+(ms*2x$?F|0)U|DHxSw_4lZ4{+@C<=rmEDcTDpP==F#?T59++H7i-F z2Myc?Y>Brf#3Y^YN+B;45F`6xleCvH%f!RT&^5cWqm0Wq#i`Jr&BUF=NShNsL_5r0 zZo9Y+t2O8 zyQyI5M|Dd1Cktb6Q%01Ho0urbdF~?Wd8N)c6H8SHSVrX~qq8pah~h$`z;Sg0d=(yn z551tOo7DFG>IURGNlv6aC5$e9nR1vS2%7V_S-HZHV1z+;1M+hO#_V&*pOzH158bKP zGo|gCiMBEZ@C&L2e84Sb{+h|U5@2jA*xgx4pNu~+k?ENVXUZJzG8F_&P^-SYYf)9b3b%N23x7Tw0erRsN< zNw3cac^AfrXCx0*ijS7-`c@3N7NPRxs??G^|FL@%MsL?A_#wL}o8e%cih^W~EaDnZ z;%xzY+7e?Q!e#|@O=Al{lyK#63^;G`WDRDh9ctvuUlXQsZRERty4>tIM}j7#BE?ry zpxyA1Z@A31wwy#&iYBUMF2C0!Pr(7JS%|H?mR){UW6_;@qx;jzxH7V>VI^9+%d-kq zYpkdhb)ga#Mv5G@Cn~~}fVRU8@f5UzFJdQRpdEg(!Cea@mswt481@C5 zbpL7p?c&>@uQ75hNC4xx^y1Wn%Kyw|apFI|2PftHY-1tC!$?vH1b}=Q7y)@#%EBEc z912VEOc%QH6P=5!t&nT>5Adh>t3TsM^vzB>Rte3Z5xCRu|DhXXBx~3& z?~q5(=>9{;y)dG$MZZp!CHJKoPK`swWg73!<%u68yOkvM#bdQ~Jeeag;~Cb+k1Hs7 zmE5YdtmnQ){ISRgTw#=Wrcv@qM_hZT!zaw>IH9vs&5lkZWqDz!JAES50H0Q_UxA zMruR|<*us;9V4b;y`XDVTlZkr_f56^Z-qN5alWTwiwPoelf&ve#7Uh@mi1u${i2k{ zoUF~`5>^BCCJ}$EjD&;cW%{OSqo#3Z^g#z=G@7S=3Zucm;EE@W*lDY0@P~FjR!k1CQ*>CMEaN81z>ud!8 zK0X5ZHJda((H8LGTY?_nS;9m^rG=<p>)RXjNZuR096Z>lFs=u00Pd5^48>b2Ad1&r#CvDk-k|!d^-KPIHb$ z`A(nDbq`PKu#sB}|0A|czdrm?CHl^Q;YMY*!p)%)j63-zMf+ou|6JkXmO1apoOb`! z?Fx}x7()gbO4h5AaAxtGP&g(H&wq^yYkp z^LDvyv5?u5XI-F}!yf(UdoQK2tHw|AtZrWs-l^N|!BRW=WC4d!PZG*J=RO+u#;s`z za#&Eg!0iWggeBqqNfhTG-jR1zToPIsuqO{#R_1fS3`l zDi)@c_Nl3clFNQmwF#&`i$3UeiK1V1aUq+FXY9V$(=zXtpl7Gg9&>*`PnC2y^mymp zL%A2GSa0thBQ?3}VGG+E#{7H-jXap$vB&6sPhL$> z#ijah=N?JT63Te=z30}FwwwurIkS+PGdpv*oVD|UEnog+a6F`jcX}(7(qfY2@V28A zAc>%ww8d`!oggR3%#Kg8?)QO;136x4$6nDzV^WV~d=x>=r)OU@T-S7Yh?33Zsk=Y% zboUP_!9ILZI7N3Ojj0~+UOq|bTRJtm5K&4xSm^@2nQQRM!yGkA-koyUQNKUTGcu+_ z9ZA>iI~Qpjmv=_H`O#b*xM-2bVMn$LtatExyVb-2(kxCHuU^_cBQF4*i}kos-H^;x zxYaK)WuVTdrI=OhZ^r+e5i)0R%+K`8_-sU{EXiwVbWj$C*E(*A-RPPheqXHL#~(@m z9&XzzulI78t~f&YmK+{;%Jf;VwzGYI(HAaSK+nB?$9e9n{#n1e{jThbNy~-7N;x0y z6j?d55upy};&PLD`B~t5$S=+KslbUUM zZV~5*za^D+`D+bZhwv#ZgsdR#bIUB)PWrfx#`jO#*Ti#H{r*@i{`*Qho~{0z{c4!) zMnlKtc|Y}y-06yaQKJ_S(P4Umu4pE&1oy)~-|r*kfz}Ds#!$k(w#TjIEeAvde7r;6 z$W=3*%d=ofXC^#xr5)a0>caE{>UdJ1*?D1Y{r!Vb_Iu*H=j_9bMqIkcMCiu%1XD@+ zQ`Z4qYeoy=8QDHnV0UW!LGS7YIC26-n%SLNNfWc*yZoF^jjjq4=!gtRH?)gWFe1ApWn@$Yh??gN4ZGbd2b7Ac za3(L`zK)4c=6bZC_*6X7S`vydh>@~4AFBX8NNzAKY)uGl)}44WJ5^m1%DyKR{GLk z3Kg&K2QtGsxC)WnL*#Y|(V;141X=TcXCEC(FoA7?;Poj}n`A zcXjNX{Y#Glb@gD6)s!jIS67@zT+bzd%pBB*hKwrcOdsK+`-6442;=^j)EX0Y^QY$M zX7xQK^Az!oAbX9Cd2H1Ie?il7!5}gRDm%gJUHdR21HyjFqS3>+oyd#On@C77uzS{J z7vVKW4OE|v%n^G^2h@KbrxQbAH#vtolxO{dx{47JfD?V?)kC}$zr3gEaKS^M&2}7g zR%uTxKFeS0EP8d<%q^CUD^}D;!**Y-@`phrJM*+Q!tWO})B44q*7TNi7(UGp9qtPL zUSuLr==CqYh(JEim(sRYuL2FKsz>JO7*#iv`_lZ9V+pqY-tS=E#VNa~xtKSy%V={v ztdUK?ftI=V5!MkP8l@zZ0(qJoo*GqPya}#|*RCCZ!AJNYGdTwh5Yl+l?b56@%{sz3 z5u9X3ln3!dl~o0qCF|X=e2?NzH2}_iq3$d&01@)bFwoKadce2R)PWH8f%_Ngv5Wj| zN6SJZ#fuWl$z>%mnNW#4AD^za=6hyh;ulL8s_9HuOIsC+Ch z<;VA?$CY*`YXwQWQLbeT3ajs->2(;@^F5Hsn&J~xxII+&!eH@r*(IvAvtsYYchJ6( z{cr^dI@nHq;tg_%YtmejBA4^E=);Qq&&vfjPeiTJg>VE-|F42hDYN20-kRc|75mez>l}?4@mf13 zpyW1R&PWm{NV+5cV5IFE{uE!RDX-A`M(w>pOk{ZGxq7*>V;`%s&wozMx!s=9^|t36 zZ7q=OyyZ~#tjZ)6x+*gZRZObj@Q`bH0uD=bWC$4wy2y9WXbxBM2XmUs9UVu(a<~&5 z5fEiUp?gIKl8C3r4d}4uG}Ft+21SQGb53;jhneLFFV7j+`F5PSwAQTAj4dAHD}T;?w28?z89vk`COg=L&Dd5s)(9=A6W84pwr5jCmQS=TQXM0#IpYiMo)Mca0@tVG9 zzhc|ya}oP9yZefKvA6v6QZpN%>I z_R)E58J${q2jC13CMi6}X9$mqjE00bKC2tVIt0JT|AfA8k=fn+-Sk!3ID6JRUT`mor$bME@Pdqj>$rQM;GyGH9z|a4H@eYR%Gz}O=kXq@ zpfGYj?j1J5s}Cff3d$|g+T+OnWn3$VWuMSkVw+=5^ZI^5KTgqOg-JSy2e(&k+=*`J z4#3F)2;(z)aUj47%jv+)LS@60)TZ}xzh~i{YL>1T4_bLmC*^-09&s2UOsaV2lf#R_ z@ER2|MhXYn*XJH%e#u?QBllVId=?c1y*`o^yR~h&bZ2en69en65_a>EYsvgNR;LC3 z@GXAnW)bk53!60C%p3*Dx{CboOk4(HPn#ey19a7WictQLbjMLp%$5rBblII`%p09D zSkJJw2nm{lNCQ{q+y#4XKHesczp=?TwqkENEC=7~z5mJgTWRM>=oHSgvUoJ6nI9#i zR1_mO=6}|%>urkyjEFw1ydR~oqO&BohoX%bl{MLpgs&?d&(-ZXv$lc-AA8^+j)fF?O{DLpQG zf%jZ6FHH+<9UZS)CkR}++GLWDy(y5Z)@EtJ$<;uw7jQK!krICR`)2#67Rm~A>h5=l zoTlr=9~aiq3Xgx(7cE&5UAr8T{m|YCWft|{bb5DdUCUEyC~lx?W)`Yz57<5n*e<@0 z38(v-X`MKkTjZG21HU(1w%{~N_pWIO3tRwwjguAn8Vl=opD+`N%L_0lkbus4_YiE; zihDWoAMl3TkFCuFB%Wy-zil%TcrS1~HH=VY^AqO=ul>Pz4x*!+9Z5M3qX||pX`tAt zi6}7{E?=S1knWLl?#`J^Cb1cjw7*A@Y&qN!=K6BsX>y+WL5uqqLuC?SnaVIVxKwCT^(re&0S} z*mfl`1d9$*apO>CH!m zgR|~K^KS$Bd(+oJqFM&w&;))rI9m`;YV)cWI9#n;_SYz}5%<}4WI_BCF`Mu&iQcj^ zT3Q3!8RxNOa+1egGI=VV(sK>X%c3vlW#~BZ(Z(sLN8!8zhL+Ku)c)Dp>m4JYL_Zcur{&FQ*Vq#~WvUyTdar;N_GS zNQbv7MP@|tuy0Qst#I~;I0bJ{PFTct&x65QtTV1Z$IjX)y{-Nca)XWSH)^gg;#Yh3 zLo&%0!`AU=Xqg)O5feXzvgN= zz-1ReZ4TNHby5_B-@Bzq7)CaS{E1M)YYJe@f*Y1vYp(pbS=iccK8!8z&0wvoBtgOb z(+_?Gw{5inTq|P%8-83D@}M=QDpVv6Wbu$w>Ab}JAYfn)Dvd`@z~8Gnxs>$z*=`Qn z^9lC~hTLdWRA&&nEXIv?XRJFYj0gIUu|FNC^|XWi^<YlaOn z?98BbkK2_9MU``Ii{J1?3(?ZTDr@1me0=@{S4%6Qdb?BP!hNgdr`gN|I zUYAGJ-WxA@A^V+g#beMYV{C2T`{j1kT6)Ht;` z;A<|;sBiuz-B#D_XAfk>e_}HBj9$;9HG7xR+IaWV7n2-yEboPJKZG(VO_ci{{c$SK zjlHx3Evn?~lc_Jy%&_9|Oz)k7_l5G&=m&Jpzk<#HaicQ7L%ZUa(#gD_I z!wi#PPPb_1)BGX3GHIwJ$>%bd8~k(mhab^`P6}!Ab{W(^=G^ECXIy(O{+(OHENg&6 zpW{r)Io*AbgFn3KZjWNdlC?i6{FFT@iLD-gRR68v{$q%8w^xlV1^vj~^4-}+3(;;n zh=Y-f)LqXm*Mty<-%?yw>jYY}pI>lYZdU9E3sLcG@2cI_DZKqQ1tAGEog3*qj6W{` zfqDK4Xm}(?S>ctc?#7DIwQSb`=zp9da_+~E1kFY)o303^sLs8B>^%>biGpU`FiYe53~Gmo8{Is zn?|E+$Q8q@XFttnGXodhG7-(&-*IxcXZh!|4zf{Y;V7~SHm0dcyGOuwk;-<>S5wLQ z297^}s|TEUuykaD3#&jJxWiqWVD4`zIE=plLNNFvovl8~oD9T1pi)rK>yP9^SnsFd z#E;TME@?NAWm`XL5@w9ZBNTi4_5QSfRGAWPnFG1&+Kn#v^;g#Ga_t!-(=Fy~NUhyZ zT9W!F-RZgq&Jo_A3*dSq(dLU7iqd2TN^LSAM)9t0>jC&kUMN>4PsNI=Xn{IOMQ79d zbx>jM{=;Jyywngj-N|jJv{$Jo@1ZYH>NRX);DjBvHVd{sJkfbb_--6b4vsP=~3O*Z>QE zqw@1TCUZI`y!t@@%>kVeil?$|QFuLThgi9&&)Jkap z7{wtj7+h22A`bwL?m!(Emfm@EBEC-Iz1NceZU6?JPB|%+*{Y^EfzZdL=v8w(y~0PSo^EW5KDzG-aq+S${V;3} zd$xL<#XLmM;@M+%yHVic76qeAp4``_#?o1>s(;K_c#_>(6zuhpp9Nme-o$I99VOE6 z(dk^rUQqWV>VwKNW@`{;{J>LNI-vx^V(`kCE;3ViFTP7U1*jd^!hjk}OwJr69d9I% z04KIeYn{>qamn=16c-jfggK)!dRP&m01}fLFIihXGE?xrUGpP$)gjS#ga2W;*R?;W z_@Yj#b}c@rzxvaieh*`rn`9@bispY7$j-UN`VWqj9E6FaPY*{*|1+?msF#;{tbc~NeEUi;pA}Vp-H^$ z5dHIpGEBlZyi>V#%-ZR&N@GH_boUvDytC*C(}|Y!!X@q#V(?IpwJufHYyWuuag%T~ zR@1vVl%=vbOlqxjDsog_4H`TjaB?}uWfJaGs_41p2;%vSVB1=R!g;sy@UU@;Lk>+} zMKAEEq|E`_iC$z&Wa%tqQd(pVTDY|h6sO~HTV5cm?a4=r#?c9jqQJUqK zCqM|CmOGF2rsXq3?x(3d2i?=JJOA;Ke?CJOUamdz^UX1H&zuY;_zKk??H%0>#(X#v zF9&0n^nYK0?+c8r>8nLbWzyWRolR)>dWKhqQX_5SwEkJc78ZBlEG_? zSrX_ZsI|6tZXM7zO|QhmK!4B($bkWR(q3)?}H|q z#DjRuW4xE3=V}ZWw;NbEm&5dTeq2fmivuXE&clMkRn_4h0(m8cGI;nGx}=BR^%yVT zV111sPsygbHGHXzq;8??99rQ++fG4J(HerjP0OU9%e{?qbzTiPw5Yi2>+AGO+1-e~ z*9TEParSpZ$c}q&X&yOuGQ3yz=J%xeNRlBn)~zp4Mbxp9ytr?UbBykS?C&CDa0qZA zoOB@Sml$ZxuorLwTW6*Byt`MqoIg92$1jXqYg=muY&s#|JGdP5T`DQ)LPC7ecV=e7 z+fS4lFqb{so61kJTu0)GO)F=#N}rDALnmGIU$3;Epq#*=o9hWvxgN(3Q8jZ}DEz%} zm5~KDX7fO7$B$?#BPyXeu>3?1><&W^==a-eh@<3VLZnLknY(uhXZsR*_@k7P@yj5PlCcQ z0+|#6?gJbIFM$og$|eCY5FqEY@(F4}Gd{Tj3`!Q``!QHZiKOb4F3v%Mk#EG>gU}h| zSj}sM1pjj}kDlnz+Ac7$)Nl1YNpX_#<2JaKb~#Gvj#bKQ>&H)@9-;i15Lk!Kl( zz_rowM^$Vr>?h)~Xlf}6QFQJyCLgwfU3C34Us&G|Up+?62V2Mm)H$-#fwB-e|7x0* zLxH}?Y5Q3-$oX67Wi&9rZp|e_q`LhAaoB50H28+pMCmF>hl(MPONhfj2K;&dgCq#o zr3WGrPLx^~TBTiT|DN8wh{w%F<#}gi*bk<6oJytv**s^x?nBE?ogg;oa55>|je;|J z$TzQ6NNK3l|LyU#K3cA?9PO*sz>Edgfta%3Kl5NAt6aAt^L~99=5VEW!nC)!Qj5`O zgZt7}=qDA@3s&G1b)Spenh|1{e93xL)~KoE3B@w5aBjowCVm$f;g$=qw~m0^a>49< zp$$KXTetdy3z-Vpygn>6dpJ#Df{R9TTUJYfB`;4xeu+^NTu8J@>x2J3&CY*>Pm2AgpLYG+35KLZBR~Ei z90R}cMga7e@PIPggA!8yk}x=a60^M5x0XaGOA7 zr{+9q?oZuLM0K`jGObFVKfm6T@7{LibFHTI`u(ZfxtPM+D7QA_GycBSed~$A4?FvR zM}(bs_&ju(jr)B*^5S!|;8d7Rq&q;~Lg1mm(Cmv{+_rAwUUn@w&Ya)8l^QVXe|$L|m42XgY& z6f)lv#?8<3!UIO@#>)grH{&$QUR1e#YEE`2ta6Y_eVRO0u0WU7^e)$RS2~kc9k1gs z&|E05>lx|y$DbnR>|GQHg#c5zS^cQU6tt)@d;kWvZ zF*~(KE|>NXQnyYJF+b;B{_E(^0jugWI6U#k?(M{Pds%rZ7bFI-yGHk~^&)@ctCse_G;%Z zG117kt@L-j9i1TY;!6XO>$?c_zK8q9_mZM=Q-W4s5=iB0!+yme_4EVlzYjxleM8?# zYz|;TEq1+WeTjIGRxBoh7F!kk>vKUj-v6;Fy$+r`!+ zX?rKpNJ+xeswt<&tzTkW_fq$LFXli+c0z{4Ur4EMnB3{*tX;c|eN=Y5BR;G^!YHz- zXRhFgzr^Ao0cu~i{&BC6E3LhrRiUjZs<#@OjIA&0yp5A)u3qMed?ktba(&4#e`0o%9 zvl2ih22jd+8=v3Q% zhTM3$UoyNCs6l1ALsaqR1ts%TCfP!ViTXCxap8`qPc3D4Euo(%*5SnWU)W5XASj1U zk-DrHVm^5NXFyzBOf?BIz~3BkOlNJ*$X#%@wHjRgE8Zv6swOfu5<|?&NgZ`co6zVdPe=W(U@AQGYbaCBd4ijeR zt3f-?1w2N^i|ui6u=4y%TDGO12ue$)M3qPlXeA4`O{3jg! zVMcB2%9PNjCx2pwBl!C-G2GE#Vm0TPzr^l={o*yl{x6hm{AAPP#i=W|7SgWm+B;>< zFgrPLVjV^p+-O7?z3BXGToIR1H`ZaqcVfBhRWEmBb*kRAeVXaD|Kj(7rm~S0xBkOM zf0Td4v(y&1)QHDvO@s{{V}kwLkyZAjzGTe#rC)$P9M`GxsUfVwj0wpCk1 zSj5(3%EI+;(0C8=KVj{6Z-QBs$si-ySU|Hl>XEq(qMDX!rw{3FOfzccZ~iN4o*3} z63&o*=m~Ejm^SluB@R3LF5mo-QjGC`y^vt(2>NH~ z@LC5vEgglTWzKgIhq3(}ynTbm|F?rk{8|$Nas=*gNf5oiW+^Sodl&QmU4CKenPjlK z%FWyT~|OKx5r4lOWtiTU(ApwLJ6lj7(HR zVOQrvUKd${VW+{LnPuu~USx$W7olF7yjWTt_T{4W$|cByEgg}a;6Iha{;IbfRsme# z^Pz!S4Ko*I)b|7V?`{~{``zlProAYbex)Ca-ZT1fp0NRdU`Xc}Q(V4fV|f~UCgeDj z>8Mn~qo!;=ltuatZ#WEB-TP8ac|-`SS{CUEi1~0mDU-8DIs_pF_%qa`=RKAmABJ6w zYF!a&j>ByN0h+$U=Pwx97zcN`ugDNv@cBx}eE8&C7781rGl0q8%r2Za@c)vU{=Is< zW`lO)N%c6%yGTwq9!nlxF48?W-1pY!^Ofd5{~wF&j(xu!OgVV!C$#^E2wUQ8vKdAh zajY5(@3cb?z}piYrTGh7%AW*j0NwkNBW0~q1rp;^65deKA|8g`eK<9-W#=0z5J8BI-!(Fc{2TBhU4oJXL1|<>`YIg@^@`la#j_j36-%AbX7TZ zS;2O{M$z-Sb6Z# zcW4}^CoTr7m^bQQe?Qc%9;6bX)H->j=&E-10chB=Y#jFSa4RW|u{lW~5+~natF*36 zb1oKxp+Cg5i4(~{OjSi&33X{aHE4t)Y_BMPVZ45q@L$X86m0Ob*1uQSdgK96?zfEoa%q1LY`0w}XU+eo3&}J7*k?~#JYD;T!pPoo0U4H%*>>38qqyO8x zH7E!6lyZbOP~{H0S(n3jAMk3xMZcRPu?~Ynvr^V?1f1V+*@Fu|dESYb_`vUPM{f6| zej2IqSR1_)BiDFJCsdIaD-*w>|GR9Ezae(aT~HnVD16ImGPPp-;1h1Jyt%E*eWvxJ zMO(6#EqMWjiHn~PQAhbG5JQ^z2psF#=Y-P))zdwsqpmnv74VN9WOVqqz&pD^Sb|3;GVH6vS_7KIK=H% zBx!oH+N4T8;fio-BwNDRSoxux`zih2KX+(}U8MI&quq+CWIuvN z^O{%Q$AxLszE9D7(PmK;)_ObF=d#A_JC|P^^8fn$@?=<@lkZ%|t}Vx;2p%J26(7Za z&gMWm)d6G7(@}an=P)1x&f!8bTO{bvbI*noB2o?FY{%-F`>Eevc=UaB9zAw#Y37Y+ z{L?TuutM<=b-3|5>13XF3=QouOx6*~9a)#tM$N3=DfDZOe61Qr9i?a#q2QXR8kGxV zbj{@fbj+um>vOM5b3q>Ct@cl=|I;Fx{QrMmUlS8?qI06IR)5`KE$GXdb9b!I{&Vxe z*`|)?HSd9K-!<`|u)S9tCJWs>o!6&0Tk#B7r|5IxvPEl&56-E!Gq#urq}JlVd0Hqa zqQG$rRvVtk2rykxRU{WQ@=8rVAr9d5wA**IPF=Vn zZ~E!GzmxK1wp^%^xWWroaD7}2*Z9Z2 z5f%nlGNPI@-2;CVrj+Ye3|5r+U8Dx;Sz?LF-X_s+6ZEk0Bj_QDqO(0ROdC^Hwddi7 zb_2)FzqZT2txw^Y&_<|GYbYEE#fNaWgSs~nl0)ChgWPjYgX!21BGY4#M>=?Z(uun+ zg$pOBy{W5Q{Mky0stKP;+3p->uU6>Yj4agjPWlVuc^9|eMSjRFnx0Wz%*uk@b+c{_J{2uE*w z36Zngdz>+tCchB2(nyy{ibocuX6o)EeqZaA*uv;i+hUv3ZdoSFqwcDfF?q|_O%t+r zorWw~Cso&E!Goe(tZ9ylMH$6fXx=E)dDC1Ngd%A*KN)?dvvsDY92sWyrMKcy)xNK> zRJ7~0@8rXvLc9^)NxCp%yfp6%?(fcta7D0xnX(fnF8BW5uIm4XJjE~n%u^)&AV{GO z17xaZx5uUSW7C6@69>JGZbd$BYfWEiwK8*jTu{!&o#_D~2UE;S|T#jTa94&lsJMtf1@oSv<@A9C{dZD*R;v~G0 zao(vdih;(Ry?vUugM3Tft;_E8@}CWh4;s#SZ7RN6E8q?O6g@8m8{vt7^Fyf2V7IlF zU52~g|F(86`Qp_6m#F588v^NcB za}V3SyH*E9RTVXswyKD^sG%XNEL0RV&q-AkQDUs9kX1F0t2MR8r9?!GHP)1xVwIYO zqD7LTrb^mM?-E)2Uhn%n``G(Ep1t1>&sT>JIda_j-`D@V&hz|Ly_N8Oe=7TZMEO3K zT>m%tFKaNldejlWaFxH$TUJhe7C&b-ulza`{^dyS-x9r)U_Zev@INms6>_?_)&iGT zzeur#n~gqvS#S~kn10V2diM|L4|@GTfK7r7549^SiUvzI(ociu-)ghr{yb=WwlM5rM=Dvxsm?z8Rs5wwbjxDaad7uLMLrF3 zS@|(w6hm-@m6;K>0z1D|dMfrJ=*L!w*U9=7`0)l|$lPl!#8m+SgGLgwFEi<5y9dgy zlP!hHg|6uEkLDKC8ubTgLZ){U!hNb7aWw<(NHw<(eiUX~YcGh%nQkUh%Mm#nGX%Xp z3%tA-ud!W9m~qW#5GY6`NZJP%Sae`gZEKBdBtz3TobWo3h)X|xswXLM!ylmEDCV!_ zULRj{z=deX_9(Subrw-R>5!Vx0TIQt%g+|uaHoy#YFc@=k->_Bok>p5R`mNBqN2s+ z+}h~Qo=#UiE-s=}7gYQH_lFPplY{CmivAZ`&>?Z~AGpx}WD@)bt@XcR;7R_sAgo9K zzr0h!|M&4P+RY@V^rr{||E2 z%`H7|YjgL9Jv(FeL&4@8S(x8PK{l>+^2!WX92*6chSa$Su|1NZKT=K0H-`2owQeySzS&t@Lf{t$lB!Wn3fjkBKhjM6kg61Rg z;e{btss^bQQO|Nf$Wd52ou=o2w{xhTF7hn$wFUv1mK85|BJKy1!QDK{CQh~~vzF=G z|Id+g93P$}?2UupI8Myn-mLhJQ%;c`m5ub}^FSW`dh~TmPndNgoh|?wtZWNJBRXz| zXGo}Rlbh8qL@#QWN+Np(ZF{F9>k;Cq4T*2<`|TnE9CBLYy+vLYm3G?&$2;KFBP|q8 z89s1mE=vk$*gZUmz>io9wZ1BPRy1Q5yK3-kI&bQ0saWjtauX<^#9r@Z^0#Qps zWT7q|CfbAbrI5Keu%FZ?c#?AmD0?{%B++*J1x2yuiPHyCmMmQA_hR=O?G4Q~&F>d2 z=!U;eJIsV1jM^=IEIv6RtVrRl53mM5dOjEr5yehsEoduFWDl6m5^88GK z5$b8qT}K4vYN*E;h$^aPC9FfQiHRofSIP<}r_L5jzRwEPLYwZ6=M|=pJS4Zl`kt`H zLCNPAP_8@1dBnW-nz1Z_A=B3TOQkVjJ2oMMBgd?#%Vu;5;kVq@T4=kxm}Te2xLGzm zMyfyd0u>93jtum|V@TFhYP(QfT=koa&)oWrUFv+ShhO&1QYaZ_Wo}@1cO}U&LEn|T zspI7ozB=NhjUS`oQwA)&a(rwFR&!;E5f^u-Gv&_WxBcNEQ+x6f_I6p?`qNq0GD=;6 zG>2@nDYd7`cF-tyeO!ds^oLhPC@x) zW#OyNK;3&S&5oaT-hF&V?IfuphG1=Z9cIIG4<9;XtGV3#Y>NPrzkFmU8gTu;9#v<+ zrTo-P&y-5*=h6onQf0m!HZ6s=7vewiqg#Kjy}vcWTXZoV-lugTR4D6y3w0x4qfO^0 zdUs=VM9sZB%E#wRtIQI7!Q6e(A|Sw19p_Y6?@PGaUH7^Ye4esvK_cpZqD0lQ{&Nb+ zU;3Z4YMDqHa-sAM;yTMW6Uwuam5k)adNHyRogdE13W1qxoT$ISk5uTKneO7XmPMTF1*2o&F;qGr5PsNq?yq12sO?VXc z!#%zG?M6kC0TpzEZJHW~CN-zaOPJXa{EQ;b(LV$_6|T9(ew93!T>EbGU!Rrg{}Y|P z+snNSl1rT_Urt_nHg)sY|A$-}k@JU>#pR1ReNh65r;Khp8$dtU^jBYo#^2SR9O7e8MR}`&tWJ~-@xG=rbpQD2&S=E@RR*#BF zzW2X7Q*I-;EuVt7YjViD60baTdXtt@yxBWu9sa3?WJ_@B7B$ca%W<;K@G-W{*@||V z0298R7GBk3Mb#AX1hE5RsN%Q5*YKRpDT1SM*r!a{H^{}^6)~`USsJSiV994y_COLD z;tKGXxjP`E4Fu4$crfY=u|K*b=p@$+Od5#i_gE#^h&XrG;}FLgI3hO=M&=i+~?I-wWLj(1-8;ZeW56(cD8Y3XF-7i{Rm>!r)zO-!Hu&wwOslkYL4^2 zA>AYXlZ}2V=0BnE`sdN?l!-1Kdg(EY?0}IR^L`pbHYE&`xlG$l)VaRvzVKEwdO*{jAF;LqC0pcyD}B4n}eSHf_yU? zUu)$bQuXErXdC)V!+2CkN9fI<8ivinu%^Rd3l!Fjg2P;DZG_x%rM|gRiI)5+q?4Zd zCW20uEBV^7Du?A9t(EJ`C;fZ$Sdsn9JSf>SSj)f_@-OcJ0-|w3jpNVYe}AuJyst;3 zm8uiqiZ!8MZ@-uq5GV$BuQkKapEBBf%yl#?33l3EQPd*HOok$9Cf7j7uGUV&+Ag^K z9NsD5BjH?8P4OV)Yl6Phvvjbgu*~3ykX}iFDm6; zpG#(M;5>J~Y{7%Da~5ukNcW%DcG_^ACTjv=>|H>=0vpJ3ej_D&k~?8^l{NIZ%YJ3| z1Tbw2L>(A{=Dy{}P=$KT`tK7^i?T&b+7O7L?7`ElWaKBXRu!myJe7?g)XUeL+W%Nj9OydE{2;3)21{M8e&Rp9B){scJE#xqJJ0XaJP81@k}VHwQmdSGRl z$bn^1uyDL5iRn8?7QQ&OXXPfy`x$no{=r$w*BG}&6DngOA}mlWaqpnmVC}rDd)GzX z9CD1_w3Tr!98*2x?}#dfe`neju}D0a8Cs(ufbp0hEW9 zg*exvSqz#&YV0|%Y=Cq>+Q|SIyUuVk`h-#(?haVvsmi9z({jr>N@tOFj?vJa+)wS^S zJDVNA>pOl9_}xiTTDnK>Nr%ZWhj?GRY=7e#rI{Rgd8s0D9}ZA*-d<%DtPkw_K51Ug z;H95eSoyTvtnj2usg5Jg8cU;(XW)*_-EN6Kad%&V%orfd(fd zItkQff9rhFRy;a*VK~=is@Bc{MZrXQd8hQlPwUmd=FofoPG!pQGepI)v&E}?Iu{`r zFu9uUo9<&BQ8ndQ^_4DjNn2B93g%R+q6(t;BBHw2t@MT$400BTmL&r1-w^_bJw|7N z#T&p-q&GONvs0#_VazLAZ)X2Fa>nQ+NXq&Z8zxKH!f>yE*Xe{9qF>38>6!@TdLh3O zWewD?;~rCLU&1pC!(&i^kA7IRP1VcC$>ZhS&eh7+S{~Q2SaN980G(V3K9{?TVlppf z_|0asweUuM4y7tT!!z{tzrttg<4X*RNIECy`t5V!p~d`LPN){83ATpyt6CH`qxn(N z_Qru7Ru`_>A+d*PQC zwqzvdzQ;tG&3r!~?daM~qiF?88_D5T=apg*nGiLtG6W{|Hc>2Mry*AqOg1U_L%symkYp zH6Z_o8ufhEdLfrz=RgvB^$=W6p#*e$VZR!QV{MqeeM%rb-N1{kE`F`M7BCqo(aV!s zn;m}wMUr~e*^jfVK{;jMoM83G5r4Yw>~VZuDWSHzLiVrVhastBSKm4f-3_KT1_-(T z2=6XZwlBkcjYes-LDoVYR!3@$EMHr^w)}P)`gib)b7*NJ_a_uDraJ$P12^szp07QT zuw$4IwOlZHJL1y%mFfdi85eosuV;JOf#AOc%b>^uHLNcaw!O=2ViQ?BNvd*y0xN4E zR-Tz3*JbNVnqT^0M5bAex{cUR?{z~?=oUzgUrVUDmiJ?rSk+tk(UNz8*!g%3qf;<0 zpKC7NCk}~9Y7BW;a<0a2SS!IwPDzoj__|kK@!R%}nvIjInvuSt%Fn5^kpOS`KQdg# z3ql4cIzA%?0WN0n^q)QoyV}pXCv_>M^EQmE?E^gaES0MWn1=h2{g6qsrNTX8@8?nH z_9;!(fHi0S@L8a_dE+;9a||4_$1jl$?@C9A*Oec>e|kUT)HUC@L3)$4j7+`ZgX$VZ zZ_irmV%O@PPpB}xnc#3pUwK-y?}@_LZM=9F;r!#?)#uxe^6<8To8T!VM zioaUos;j4qMLupChxnVejjn8z2b@K`tu!y+oI(YEkT2?Z@F;L=Q^@o0MY-m@!8-v} zUn+1ubCt1lkc2e*7KZ{YDv+8c2DT~IY%bYO8cfiv!j!ms@66R3`!=i8(^QY^W6#j zVOKicxSq!vC-*Y!caZas%t*w;gmad=+dJ3T2ivxtKmFy`=kFA5SA9Jy*>xaV^x#;- zr6R&fyppwMdU3guh5Qxj&bjT1o!ac~mdT4|!iA_aIUnppZPP0lqUAZytSU+}Ixl}m zJ1DTt>e&HXOU=CMylvy!+=4P#bZ!W>`nDj9D(E&Vzd|H56}Gh(PU?p)6TJG(W(69% zCC#cI#Lu=;zWcXTPIxRUMoWc-U(n!BqxuBsS^jK;oBSp%^!i)hrdqIF_Oo=4@==B3 zQB|bM59gX*VM)$j2L?rTh%qeD^tGY|1rk`L4h9E7qMbbq)St?at& z7!VMk5nfF}6%idW%tuh61RdRX^-?$L&{5iqdq1jn2;2VUMot z3j4(huwbd)&EA!MC5kN7%-2M}4*74-n#`l4ylJ(epQ`)b2AqEa(%8~xN7U@Wv6>3P z;83W_t2*lmSq9x08Ur4Ix1O^y*4cpnXwI2a-I05QQSk)Jo2;OQuD9P9@Pp;%Bd1u7 z5WD{BwkB&lJS@&BP;UvYG(aPT<^0F1iu(t7<%8f)BV;m}{h;UCKS!uZ%sR3WOOE@y z3;IA};-4ck4_x=N;>lm_?*4OR+bR|OLk;g?n=_r~yI%a`na6twlBX`M>Ww|Bo9+kzbzHgRU=r zP?u`(`N7S$unOw7)dJ+X60zoV9*&3&*rW*627QIbevjsMl)=>Jq6NT2N>T%;Ool9zGW%n|trHT3W_I5Xe?GLt>L zMfVK8;qVl7j6&RWWyklS-t-5UDQv;Y3<-q_N}H-#=u9k+JVPet=45(o@b&09@D~7n?SM zU~IU&-K=T4=obRhd4)`=KZ1!IDsA?SkChu2++K}j2=cpO)mX=P==X^#LycI8Cl`C z(oT?;Q;@&|zsZ3|Z9_XY>_L_~Wqf|USC&6kGj`JLIDsQFw1VuLF*JSMlsW*fPJi{qVob)4 zJM?Bvl=0w!hEAx5+1ZfbM1|`^UY{+GKYy#{GU`L(DZCqkD*Qw=1FiqWekuK_!{e+I zEi7jXP{Dl$-YjHZ&`THUaB<7bw@cSL=f1n66A@B#FrQ*)iUBhDQ^QomZVFCXt^Oj* ztdP}esaHEzNbB^xiivRd^2OwzYu&GFlw#5dkmsBos-uWedHzhlU9pbkQL~m?f##m8 z(^=YEch4)lO)ht&h*W|8TqO&$vrEk)8WMN2C>SN5C~dRJIMzpEdM!zzt(Jne*PJY=BR%&UnOuv*97@E&P>5nBYqdl z1K$svplPhAFCF~^Ct!4nqYB{Zmd^2&{)*it3=y05ouIk{A$P6 z% zG}p_O5@=tq-~ZR)%UDfMz?dkmN~G%}Tj%Acyltb+im6t+cu9EjO43ed(zcGma_=IHkSN zPwn%*x_z0Y=HTeop1ylw(Cb6>Y=)lV2tSyfse=S*%R%fi;7#Nk@4*OW}lq45-noff@K&qNHa0hoWm6>TzB`%E9mc6Q6sI0*~or zK(U%T4CBXv&)fpz4el&g0Ry8YQDRKk*|XfU{`==Lh_P=cHf11_u~KhY1j;{0j_wHx zBY{P0DpvukFrhQ*v}Pssro%UdV;NB9F&YtBB8jS%7Vx;|V=*(LrDy99Mc=%ytyO@u zEAfWQ_LS-0(LTiq^q8Cfz7QRr0 z2c{~?#FyIV@=X%-?<%fTR!`WK>)0X8S>ANnojuuJ^!&o&ZC3qGR+$T`qkgAbJu$)x zRLXaRDmieLZl9_grAN5w@EOPXz!a0uz6FFaN|Tq40=t57XdPZqHiyY@u7V3MlOc_d zo~TmM<}KJce{YNKBI9bBH9Ju!cn4P7mss?Ah@E*!v*dILdp_`1;H}m~v8{0Bp-m8o zP%zF{TqIX3Tis6gNlMW7BDXPFWl)c0-5FSuf{LjUrGokXvybh&kIv?zG9CeV6MlG8 za6zs5eb$d8uDV|=nB0g9i=AGUh*$;`*B_t7y|)=63&tw?!A@W=kfpg-Fv5)3dfx*b zFfw$KHI~1Edt9PP4eT;uIdbF~%4BIwDPp1-eF_2e_w7eCZOR-|RV$2KOby>ypRf5m zg$F~6;{IMlTjxH5Ki~N|5cB$iNf4nj(57?!Fi5kC<^)DnH~iK3pw!S?$MqfZTl?>4 zvzI2TW=hVl&etm5&vjP04~ljflGs13b5(wqb3Rr5%^~ZvECnacac@6tCa;v_^$3v< zYdM5je4O$mWUII*{*7r_ z?}J|U4UmutOW=^TP{@I-@qdn3_^w8BM1RoO#q@eHEC{aQ$bpoEry!HFR4`WIBjCL< zmx;Sqnu%-?+wNzD9t`}DJSIRJeYn1o&`qgjmNV|D{?+flBAYCkYAKUd8L*ITj+XD5 zd$q_k)553Krx18jiW^}}T)$UyV5P%rxPs;{d?CpPes*}F8_i3%|>Az zS8@#}paO1@mXf0U8M3D~7Gla+0<9VNtq%FQGCi|QpBFRzV1{gP$gq#D@jRC=Bv0;_ zgbWFjO@J_(lqn^YvcIi$OVq^{wCDRf9v(ve4A<{2FG1!_hkHruvJnP}SL5u$iQdW% zq?1%q(zE>mQjKumh+6&0*SCueay-m+z>YJYp`yAzq& zQoHngA+ub5o)=`*fWcO?V#o}T*{XbcX4N^H--I`NtVAX)w?1xwnt^gjG1J7fPkjnS zyd7Yql5B0u-0*HW2vf5J1m*zdpFA)-szfEZDK$4@9t9Pngo2QcM9&%9{O0&fW!mnW zidAHsGn_~ z|8pAid4YD$1LpItaAy_w_*n2%kHcV3fTjDTStYzI=fNEthHN4Zb^h07cIh-&KzLmp zxJS;|w<@KO>P5l0gH6RIOoSW|h^EVc4-MTpt|sC-?i7%a6J zOKRU`Nv?f=U3pkQmuzXEmWw;5jrg8&@1I~dd`gs3%M_xO={eZuLhs88!h>_890qK* zvH*p)?NX*N+W_R^`i?IuFk;IY=mfG9c%hlEo(6di&=A%L<;LE3mrBw?9o!9TDV4whvG~eKIP3P!2e(} zgqV^jli`*tFJHsT)f6szpW-1JnzV@wl2zPcwHHpT2c0{%!XgZ z36iVVcbb`aRxd*Ocf*2oAA_a=UA2cX*$nieFWVBxU=Gpc$&X_V0!oaU8>YUG2#O)N z!*U?FiK_|--(=k<$|mhVeJekc`J1yuff1XQX?23DTEvdlZ!@@8mn_tM<8Pd*>k{=E zmGnRG>%*ive01~J)ZPTUJM1%1f7;AX)ipG|{7~ejtA1})CUSJrYQequaAz=7wKToP4j$>=7`Lv^{lCM#KI0O5Xei&aUfH|yc<3Njgd7Glug+x=Ir zTKJI%U zWd5)0o72rHh1Jl>kzkPkus&rt z8afGa7I?ZMD1<%66qxHrr-RWZoUZ_S03?Ee>^jP#p)b8Hx#y(&XM-PoBfFyh=6#6s zOs6u&7Lq5xKy~0p&o9~TopaiUPRe~|03VeIcMJjRRTpNwGO9L)Hm{aHc0q;WowQez zi#-A#z=N|(6aj%pCvfbz{sut9fEa)rXzl@L-gX#WrB`f$~x>=&lT>6 z046`XpT(2e6?5P<6h#AhjP>cUm)~>lu%vO#zI_Cy!j9C6D^IfaMjhwq0UWEr+-Xbf z(E@0JXdMCc4M2jCCCw3?gsT3v>2~a4L15nN^34v?=vIuq!Wb(D7N#O_tEBapXk-ng zg^=#@L5IhEo>6W8lIXRbfmyIiRM^V#GJ6%iX`lFYrL~{Wv-xg))1~u4ih~%v{IUmTOr7(03iE}JYVvS zoz2Q0LdIWPfbD3ms7r3*#G3VBei*WMv6wp$L;GkmP*4Fo_62&Q8dEv`Mv^BBdY<;8 z_WQ{AlgRzHul4CZ`!=?;-gp!KLhzDko-)Exjer5Gu8JsePHMp{2WF*%i5*j_%`MYbW;^(Pd~0-TJ(Q(^qLC> zj(<>BqSW5;f`aDRUWiBC`}6P5KlCkmp7%mcg|{xZp{r=!|f8?R&r#+5E{ZiC1}qhC3C zOy@*w#DQ;x%*n&oWK?NlM^+jK$3(K*TUf}9e>-w*Scd-`c~fgnFCDOng9%&)4rLQ+ zRQHwzM`^H>5>X~BrOS{J=zF7K0*i~i*gA7LzogdG`GD^g(beF?^%pagYE0Bb7Pvkh z60qcTs#UbhZ=wvTkSu7-+$wXy45G7CG7)PnFr9A&2|7D2H)8%nf6b$zk=-!xZFR3f zui$-$i(AW;9A=hAnU1@VSy!X@O>_RFmnE`dd{4jbro@6hC2`L@!0InhHKbjdfuXVz zmVTKb->a8|rR}9?moE5HO3y{uEId+AF=#C;LoJ_+K+gxP)HSj0s)nKyZv!|A?o^i! zOC7A%isSHLJ9>zCj>)tS%PepIcTb%eZcAD`@>Rj`EBzG*xg9x6Q599w0%L-5-cg|} ze6kCM?&vxsV7G_5^V5F}n|ZdJ&(_~=z&10a(0*X{SrJ0+xOKC4o+H8fW!ZDE2i{Q( z(V`2_2?_s1v>)v8yB$w9$67LE7hd)Emojg*b6gqf3thnOV2LhstnlFWM~>KGF&4Tc z#A@DH@zmaIilrLBQqVhl$UlsZ%|yt*?&&PGXzl8lj$uT}XT<>r)jo)B1N30;l% zJ9KkJcsV(sZ1R7RhL3xKi-e+=R7D3?8EjrXTte7^d9 zV+LByTrLz)5J44yu89PR|!r&yR) z+6&8Ql|2=E0eAvF8E?iD5vM@hfx46nip={xdi2_U|8KKKDj4&DhXuN+MiLmQ-}H#|SDBZ>d-*T1QeG z3w12lt}->f@ioMjV4Zokf@lWMleA50LIrhO*a&aS>rowxnf4`e{B-;sbzS(J|mx7mm$orP)Q2&CV zOdYsf&WAzip9?cNo=~gQGLndwHjT8_ES7h_MwucyQse#XlgjKIHdk1aKST{A%wOS+ z{f8@9qTg=4s#NPa2XP{1k|ag^DRvgNBTQQfvMO zCME}fLe>JZsVAF8p5HPC1%Q=QEK--F{@}LZK zwo8Gm4O*yUN62*E2) zXKMXg;INu9xB{NSH4ZP=q5TKuqH!4zY}fS4D8tDm$}M)vA#}UI#Wcx2_+29yJM(-% zmKf2|P3ZF`&E+aOrz_En@FwTh-6lg1rdLV_MS-n@kWJaM;Fu?vxZH9gi9VjVECebz zvWI_i#Zj`L09PpcuaO$7?qgI-3-o5kv1|riyZA~4_Ucn)C7TVeef5+bbndxmBM9)* zW}CcL4u5rxD8>yK%HBbuZr9`g;q5{Yu4IH|LC76hUdKcVDO2MNRWKL zIaYb8OGcJ0JcRqIf|A{x2e0vg-FjHd^`8=sTBS>aUb*igc+yY*ARM@x}3f)w(c1=Mw=juYVwV={ldirokgvQMDz$9Mm-KylX z98@2);b$QuX%Lpuv7Cm55ixBD@CQ?c5oAYp96nPS+FmDN&jFf@Et?D(jl-(&T?xz& z6BdF9idfO0k9X92U|%7lo^#!cNgjalb-`FR{W83OPThvvZc2pu+4Z)`{-!N`3a9NC}^a@kD1KzM`?pqgziYHAb=;<>`ZN8IrGD{76)ijs-n#^YBPF4inhMPb08l( zwcG+r!J+vDrGXJH?;g#D2dt4?25)X1>Zv`l&4(4=k>?gIk%jE*A$f_A;!_S+lk)Bo zObtqRo)3!~Obm8t6f4g4c@g!dEUbIfcP_Ga*}YRRbO25S<85)+$dLsJi?HUo?Ri@C za$@XhKnEO79f3eb);!LY`QynVeRt4uh>1(^0mRBTbR(B^X0=tF`<+Hvvh=lFdi}8x zlKUtvTD-16HMHG+e^1ioMPK+9LyL3eBHZeBe>7CaJN_*y=6;u#6|J#-Un0oMxb>Wd z%OjV!F_CA8{X%>vz`~08CSppL!Sp>2Tt zE7l3vsK=Bjm!{6a_-kWjjWYbpF=@sf&8*ekl&tuO=Pgox%^9)W-zMwTPQb&PZdR-Psk|%5?%Pcn@P^xKKSrZI8kUN?$+Pvcocbtp>{{VE4(wST`y;5C$loc*3wb6Fa?-)sGVhz#V-F z*uLbr0$_XO3C@v;z!t$@mp%YFv7<<#X3{}zG(97i{qj=wP}5dx0ho%b+6jD_xfZq7 zUe3RJ2Qn6!Yg%x3gXny?bkQ!pI<&7?w4xy-Ev_8tXvde6zpIdJtEu*W_)J{0jH_`) zt}O2K=CH$bF+9tKzUXL(Kem__;ZVzmiBz8zOqAW+tN*bL`z|#+MzAUUj5gT;>2G$WFT4@Q=9oXGQnptsE^T2sAp&-4%PlFfCic{37-rp}2MDMGlqp7MsC$BG z9dj977W6E@a$VB2We=a|P(9=NP{zSO#`}HnAj)M}oJ~dUJUHRf@FIvW4huq}GWuzL zU`u3Zufs@8kAkp%SeV(C@l^_4)NSlpZRsTOacxqDwew>Nqde_hh~v+g>Rd&dvbT>d z!qfy*C`wa$z-8Vp9JU$<^9Mx=|DbF4F>K zCx-^eKaaZN-dn>Cm__C14Xx)qV<_6}oxKjrUOvO30T zSvz|NDgnF%?t97g#4e=@tmCASwm;&?VIj^BnT9(So&mOX3aDKH0_rykk{)w~F!lY* z%oivbWpdWdIWV~np93L<`6;E_18jmxAV$v9O-3P|5x|EY(>ZGh-E(`k$ctZUST<d<|qR= zrr6-pwZnAn-S?fn%OV^lB5;&$mDsfM&yhrf*6ipAo}}_-mTYRvfvnUL#|7-$Kddd^ ziap=NG_8Bt>KP?}b9!)96f$ZqB#E^!xmI#TO{3pi)7AHk&wPZ+dCv3A>fBsI;?;y)PS+Mmg7UcqsnY7U_}J3_m?OFwzLdow&iq} zeN^=Q0`R=(Bd0sHO#y|2x=seY> zL#Tokak(7g(mqY5ZVbI}z#HteY|qH;cQgAB5iQ)qB<_ID1Tav`XyO!MudDHkjhkv&@%4UuT z9zj<-Y&PDy1x0q-p8~tY4RSqyUcfJ7yy?ln;=!Y3OcMJU5O84Q|4{aq9xY5hEpj`W z*)z?)3w(=Y+Vwl8N?K11c1^|aN2h4fGh zI$8u((DHI*foSG3W-c9r($Bd|!rxd47LiX$XMPuT%4vL2A}b(CRgjIXQJxrKZU)&q zz6*~+4e^FI(IG=x+Zn)Svj991RDr= z)p4ohW^~40{d@#2pk6EhcMf-NchP-ih>}c3WhE|)K+iKrRs`|~>XX|1nd|+yV|%c) zr8CT1h?6XInvtq25dAwf9GxUdNYU;_(+-QNKaztkO*b1}V#h+BKgu2ndv1@I6HI+u z^VZgDvr1uo zoB2LIm*Y?J#DtRu8Jc{W&p1MSfGjKWw|2l+ri_5q0{Kf??CXampdO1sEFyqx^}bR*Kw*%4 zW^W7pakm=DPwk;4HupqY#}S#8b{%|b_6U@-n-!f5b}D14?|@2=*2T~qgMYa#jjDcNgjMo=+lxA9vKhkbkuf^)Ih6YRL3%0m?JS+y=jw!a|2#(cKL;F12 zbAc=M$*|^9aF9xO`$19lWw0CRhTgR0)U~Pa#mHVao*^4hDL-O#lmn$guQR$$`k8>C zs`f_&>>S|R>y{Hp+>E@|KD1CHHnVPkaZDM{r)p{tB&mvCn&Owg-Rc)LdzMYw#y{zw z93dp<_#&{)(g>N;6|F>w*1*(2ePD8ltYTH8p6gpMYBUq#Jkp>Ce-$uqbB08R+~?z; zpY&+YP<9N?+H?j{*5&~6FOCW;qxA7z;GZM4z@iK5<8nIJ0PA{K&gJ7+f(4WO44Xl+ z$i}RFK&-w2Y#@sdW4ks%Z+tb)fzi6f`dXU+hZ_Fh!EwMf+z!8reFD9H0d?Klk1I{3QP zC>}^c|9n!GRF;(5B{$F}Gj{Zaa@3_PF9l-*_ zFc@k)twaJ~5xH-j9wqfhqaXLEHL}(zNKy6F-yfmVYr8BfFzDYV<#5MP%L@;23;AOH zm7ncU9ikr-iRUXHR|XVN+umEXQ2S#;=rWG+q{o?oigbiQTG>6VgquCXC6xCMoO@Il=s z+J=4|9oa3m;hv0ODmxt31>u5VKtPrNVS74+m1>jemY9Azkwr)q8^SH>#){dbJsCtY z(+6pJtc9L}ALHJKZGlp<0;??!lCQwaphtLU?lasyDtBtXpw|qsA3Od&6hd}nTbC2J z4fp=Ny$jz28Aap$A6lapL3czoY+haZ8}IZF)tx+r1$Zju0C$fV?5v*qe8Hqj`u$OD2$*x z8c_N!A~rA9OyrkCsn~Z&k$;Zdho!<^Id9_+&cseGpW{f-)Dw(!09TehT|G4-ZCuEY z7{Vmc$9tC-nmj>W?7%QcISp9s@2DSyJt-h$a+L9lOKAZgLj`=zJg+Ss)~QVP$U@7~ z%Zl$yoBYw|IKa5-52bys~4xd8d+$nW4@ z5{Bg1vG8?`FR>C&d-!d9Vthhk2yR<#9oLeZ08~JfI8yzE3RxnD7FHm5^zbiMcsoQQ z?^3NKP;jWVc_&V(Hp-Vu}h z=g2+UXL>_BgjM!rXtI~dc2qG%g_ZrW|Hbh%%BA3Yk&mQZc)h;y@3JjoJFYa@Bm=P> z9HTT2*US^Ajm}&Et+Dn8{D7VMIgmo{oER&Q`Q`|e{l#%+A(LX2-26&K!WUy82Sdwe zx~{LhHYKEVg_ZkXbNgOk*-MvWpA4#1o6Gto-&=XCM6!opNadABh_fiX=+(xG#?%NZ z_}v!eRd1M;SFmW4c;9y)b)TL4%$6v5gPhE+aov6|QS5zTjXo2dtY7T<;LTdnMqX*I z#YUTSHVqZkOh_9%RVJJTh>5KNWPI(!LLIJo6XpmayF zF^BOgv6nH?y`?R+8T`%PRu;vlHPe>i1Vt`9!E{GUt23*>%Y?wu1Lc*c_qBDEmm;L zG^A@bf3_*ztb^lUqd;uu^5ZWG`+Bdc?k43D^C#$y+R*Yd!>_->!sf^!X7o0 zQa4vFng}3;;b#)n^R$0vKZzHt;|Mcw0)Q_CC(MO_E|&EKtU|>WnQ6P5vUDBL`U@k+ z*zK)<%;h_53n$elAdXI|alirm46qg*pq^ZM`a3dZzm}2`DEP;}3yOlpfk&Bbzf7q_ z__p@c=aro9?DG-lrIF?ar)lXUyzV?mo^%S%J0`$zFve1Y5a0K04xfsF3nxmI3!;V8f{A1$lPBPZH{!mj79fE^F)sJ<#oo@<&;l$>wsxXrQu3 z#F{=fAo~3SYc&ak!j-?3n6o}fZ>#S|_)qtHfo*lYF^;q)=gd4qhXFbfo~z`{{_r*b zauI&?$*3bAs@B6uXkFanN3z1;3xx9jAnrV)n%dWOk4q5&5k-+smW$p&sS(K%1QsBK z-iv^A0cjE-5S1oX3l?qpXc{HQv7BGo>-sE0b2r!UfWW8jrkw=g42&Pt6Kx-WOv6ax}?TZF)(~= zN5Gs;C*a~HK@uQ7oM(Vg*|zh5E8mVZLpADFqE>Zf5Q-Ei*pD%wHJfRExtt}aM-Y(9 zG)<2$1RFO@4-)B@*Smzk)2HI}rrKXDh#n4_i1!XK+nh_O>iZ-~s8vFnSruDZ@a2B& zo9KFfG5fC7%h#zBOJmV*nm>@A7}%6(Du?(g zha`Us6Z8icj?c?;S9q+)vkey;o1^)IDPw3kKkRMk=m8()P`VB$ZNK$cc8;>n9TfpKstK>zeQl zawjG0-!?>FJ@Vu*aKAhtV~#2^?Y!k@_NGSiT~LF!D=D)oA=gPTFOSaR%<;T;1MILd`DM{^@7$Z8coprjFVoZSTHl4n(-tq$H7FUiyNkb<)FzXJ3QR+o4+ zv>kWzR~i&w%ZpcQ+d5*e+DWhxe5|{@cpdSe^c>9r<#@PJ?+BV%^F1$rJ%(5$Ied;y ztFIo-Y<{ZYNS_A7GsNIxz*%uD6-q(S%iydiU+^&CR`vv?;x3UWN#wlF;uE8^XwrcV zJ#)K$c-j}rHf`|22h5YIqHqqb)AF(jm>Cr~=xB#Yn!)LN_{iIb`PYyCIv{zA+-zBE z=i#&bNpmCRC9fYrnpn&qw~N;jZlN!G#96v6Kd6@XRg9anHPE(rilz*SfbJ8)wl}}T zYmN#0N)o?XZ2MJ((b2Ggai{hk}gTznv^VxNw@(14`FK4Bx5|Sx7nh0s!+4jH?%tS)|t=?99+^ zkjsmf*x%MC{Rm|C0oABN+)rb;3od@e+>`R9ALZf3rS==RGx-D8>$s>1j(5+5DV@iJ ze;}L2wq08t8IT_TP%^By#prw9g-6ORZAzLH`50y+LTntv<-nn9z4H*8YSmKnkwWsP z$cQj2{z|zHfBsL&s@agK4V_$MLspn8T^x3E!=K=Cx+5td_n-$#pz|9?Ziaf0)@L_3 z(GFTDMbhEIdQu6W0>391>>HW9hF0~93cYl`!}HR0H<`+NpVRCg$68P@eV2U|S(ssDNY^Xl{YaL_wP4vB26`3UBi?a$DwuB&=wFYjXbRMJ0OdWia3081_&QpBWKT9~A1n4_HI z=#7^ueTEdxFoRl9A^H=94``Z&QcoU^wO?8Cip8;d&!470{m$gJomMPO5`F*#mfLKyiHjgC->?S9_@0B^K4T0Xe#<>VOtq_~*uQ%Ki-h|I16$(yu5g|f zS5VJ(2N3NV4UR489F2uiV*{ku6X1zxWJ8Xl@l6X<;wTV%F%iHmJrtbrRJ&~b;{e9E zGAt+}l$_OvDqENJhl93!oIYB<9O*uCgtn_mKY0WcD+nwQc^n;!@M#yITmaH&-gofP z_=wR6BJ(7YuyBgTHWnic`=P%JQMb90(&1fne38bVfFT-(^w{$}(lo%*L_TiU08_A9 zBQ=YdaRz1xR2`X@@d|Acgd?YhPGdvM9bduu(V8G(VT`7g3Y3mKa$r5}DV{4_fZ^vl zsFH2v*iQ|x~5aAKZEmb z_*72Zwd85F_qzs5hJ~32ga*@u1OwQfhb^`j|72)IqPE=q66db7aSqMnGNq#d@k;!l z#0vhkR7b2xYGOG~3g7QJPU7t!Ffk8P%w8Sht}qFXUBQ4&LM$zp(?~~4YCvo+gi=CB z0>?d&hSO#~9UL9rkeS5A^j6{mGgY87(hI2Odb5LKV_2yk#6fl|^2NjuG0H=n)Y#tk z#SP(OWM*`tz>$OzSt79k!3B%N8%~^lX+P-z4zuX7j1#RQF%-`2JD^P(?zE;}rj>`0 z>RJIu(5!M!fNz+Wd7OxSjz)`0rIhS#4@2&kq=HmJ!SZ;nNRGzG1V}<{1hN*xV{B1K zdXo3mKHCb6ooe|tOiJ5p>v5;(gZl<=_Jj*RleZAyFTDT81NQ%m%r3C{Y&l?GS8L4S z__q`*2C_z)Ue>D6@tvvp>EBFpAV8;AAAqe3((9~Pc;DvFsPDaP6pVq~{|B#^_ji1` zwFFHjjW@J)0=z;GYjSa+?h7(NkN(qDuyC3pu#Xr73egP^9}!Z=&w&|_eGc+(LM|3h z@@e{<%&YLRZV_S>1Y+9!DN*d3;(dMZcScW@K3tZq7R}cPkb9mljk319)=_JSoGkfT zE7$@ar&oh%Ibxp6-1ddilJ%v0HQrTC=f+Ft}G|Qc?j3abHLVc!qDke=3pz%J4=037dEOi{HL>P!I_Mpw>S`1o(@#$rq*-&HyKgZB=| zDj;qBe=%`6caM5NXO!<==gF@SDLFs$gI1h9`7m0&`yYII{Xa1Fz?FZ2q#s5S{aleX za)})w_}P&^iLvOlgT$8^zq4gO0|@@>0Ck_Q_e%xqMpAVG0GF*o4(~8T{v(|pz~uk- z#D))@F|p$jjk9#bi2e>F{?8AejzQjy`m-VUtO>J?*87cjH0AEt5$!H2X+FMS@e}V< zt>8JX8NO_fM*?-Ji=!d_4p1C`o4$j9v~F7Z=qWsPYIxr26uHKG=(m8L-dH#OGuq;0 zi)ga>4ZCwKr4=6PKc-gQ7Cg%-^6=Qo5BEjV=jXZXc`rSB{Lnq=>|^|iN{oo+(l@U! zO!@aUqC>hO1KNRf%Gtai;|o8Xo_WWgkb1Qx!R}ICjt_|0kaRcvmOn}yP?{>x8-Ifn zH4hbfZf!?7=mEH5gws}Y5D*67{Rs#IxEUES`H2DmwiG6pHgBD!^3|6dK2XMe#JxSZFEC;yoQ=DfpcpaCWzuF$>z6CFJfz>8n-;)#4V z?6TkxvJbqBm3@ZpKT;(AK*8IgOQ{NmBiJm{rd*00(SQqRro8w;9Rbx(gh#obK9q_+ zcwOjqGONIM^0({-?+!>?#b~P2Sh&+PBHR5NeG)zwZSc(WchBxI9g8vl20y;NqiV(t zLOAay*<`cH8@Ju~bYz}EE`y8XO^lVlZqs9+J4f-77zv&zt!aNO(rRY%K)sW+pv&^p zwk6BB@f}V^X2#05i9ay&^yl>G)9rtuosa$1N7rvmc0*kDT#+BXGui*yyz1#%i~saQ zfky;y90A_5(}j-*Y+sPTJty zp(%*|-niXCcP5n9Rvg?x)YFK(W&Qd*58xFYDE{@a5Q4+%>*Je|BE_*YuG9Mj=+}Tg2>0NfCbxqBT}rm-McX_!Q;kLFZ~A- zq6XYfm)XmbuiJP?zYQsTSmu%M@7po3m$!VdF!xl~*lr8?$_d=^9_fhNSo`>Umb z|9rE=tE#Vmozo1InFU`3Nh+r4yq6$iF-z z5W~~DMP9uejZ_I+shP$$12rtbb~>S2-0IU(=cHzgUY@6DB_&cC=p}$w&@&4AX$SP^ z@L|kQc(IhQ5mh--;3Fd4FuR((` zG!v-byG^bF^zheN`U*SFeZ|KkH+)wj*Y5L_V9f1N%~uh3`(Abne`8mD>vkhqbm-Ib z8)-j0uf#sfv=`09`>TZqmTxxFj^Bdu3r1f&d%*KK&KBweNYgvdrJR;RK*SDbJrR=}Ouo=*9E<DGrd<>qp!0LUfjih z3wy@=7ifZWm7T%y<~vi?GBpKgkpoMCbXx@5#_l#3-nAq!A(Co2yo~fPEIW)Y=wHAT$}6M(EyJIfYoGmr9)*JCu%m# zN)2;tfF+ew*V&vU-X5{-GW2#CrVLf-+*h_f@(QuLUJV?)erFP00phFP4K$arWWk|S zR?^j_J2(O4l0@f10)+buDeUtB<3b)$;X#|)k_2gaZefanS52K}6vf=(SecTG`ILL) z)}q?jM^sbPwvz0HPD}P$n%-S`_2qNG7SCMY;m-WK^%pZT`HtyD3nG~XY10{CYl~M$ z20mdeBdZ>MpgPFcH6jO;(AZD|XQ5uN+5ny0RIY{)7u@l6`1&I&sIG}4SH+`mwkKDY@{%Sq=byY zb;+E)(}*`Nv5n4>)aG*ENQGYG$QeR^oZEz!$m-#Et)m~3dN7Yubmvm@B4MkqXfcf;c~h!G>WW6^G$|V;`FdB&FF33E zv`*m6n2GX+(~+%|WS`-chS6;IQv=^hxp@Y;Vbt)n-_DAVb>KQuBwR+BB$_J_#+h+|fiyWLw$Aq!Ne7lAO zLQl%17*1MMSQdn&vDvO_XE!7lUyk!W(#5udgX5&yBrqiVhx#Gxy$2Wbk zNn@Sv-b>t4VgWEq%a!YW=0T}4mVOc1F;gK&ii|;g@fngrcPsY33yTDar<)@fbRNi8 zybZ{M#euO0lyoaU>JjZaK?rmt^A&uufs#^?lB9C&vVh;a@?tx$e4CueVQY3#;KA1UvKYSB zrWaX}?G5G!+D2R-3_L|MBSec0BnmSlG#Oz}wf|TR{tM(KJYS6#TMauSB%^rkkKUh|8!-x2LaS>R4Z26 zjxp$)=_2Fk7;zQ~d3-7Y{zSasR=SjvuCdzfnR@~6{BC0<0z9V;oa!fXvCpD>rdt1C z?cTFFsBazdt(nX107|+44O^1)-}7bvrs1;CWPc}me?Z`6kPM^Jss7fZj-zK$5%)6Y z6d2HbU^{xzr0F}8R*M(?u=zXF=a#>|1RCBO|tJ@8oC(P|N1?(3-oLW=Q zdzh&iclk-iC6Z3`NPX^808z{6;0p&tVC|*V8qM=-KK@wz#6VL_d+74lrz0>gEA`2w{ei!EvbpmsSu|OyK%+^>O3@27}AhFY}TM9Oa|H* zX*kv8My}DuRog%hVSaLA!alUYCv*N2@4h;L$Hh0{%c(~y=4qNY_;aLll$1+UCd)a` z+ps2O$kb<{9+g@b_>0Lqduv?p(<$^YsVY@oGFM);4)S9-x%ucdu%hd%q~00Wwff5# zD2Hp^j-s`%{fXiC5}>TynyHU0nmRyp2$iX?yn}xbg43A1DRn*1oG>N zHUku*ONo4?dySBB@r|YiQO=@!05iL~7F8i@Z;flYg>Tacm4C7hP+&sauM?Z1%A4-n zcVz>|Bb$;|%f4gip_;Xc^tOgudxe3wDJ?HQH~UVa%G%R>Mk$ZOQ8rl&=aHg?YkUi{ z$kr%7ZqM{h(R5RL4KCf&mf0S6`CuMm2FJ2V*E@y!?SZ_3*R3^G`pX-imf)^LzYb2h z`}Gg!ZCcfs^A~@4pTZR9YONt6_GGZA_vH^uK65!0y@m&C%LsZ41n(C$WmS+Uml@u^ z9WD1cA4g}x=K6M3)QJ!(mTbxlho4Mn1$iO|zCzM~spab!(ThImE<#%{)8{g>3q z;G4(!MT||Z6bckdji*UxC7Y*#U!|h?t@yJ{F_PNmsiI42yzdRyl-}1buJr|(=J*Cz z8yH?r!5E&mTrtlNHVGw|8*&voJ})14Fs>*<{@J&OkM=`=?9tFD^#cUZ{X%*E8TkKy zkHzRD!wn`i-_i!IX_U^FOqeh8mJ?pf2;{PT8XC2_ubCr~a4mm}vCRO&z?`smDSrfc z6wq+H)4@uaca<`su6R-M$7l%K+jZW$8{}^Tvi=v@BP!S3r<-SII%VQJ-wmBS6O?@X zw^)l0cRi$q>2=hn=Rdq>fBAc!#xdJpo7K&s+Lpjj^Nj^*jH3f5V14u2nveUA zJsvr(__Nc)hnokMw^QrxnPoMtc;?q1P|5HPB|ATTH_N;)BXrrVF*(PU8Y`e!*o>DxOK zr&ATVZL{aNeY?`6b1DZib0ker2CWUe7Ae`EzZSr2jq9_bvs!e!xG?4y7O~*W)Sv2Y zvge*Ryt484ook)$S0Zf?IpefssVtp{3tFHJRwC#Jnl=fSxCCL@7@1zM?SDb_>&Xwp zRX_KAJhgtW(LSxx#pmGOfmC3$mtQ#7TdpYB?K3G2hQTO#gDS(1xoOHv>QcSsHAPFJ zd)uRvN@#O#_dyfGpf80HxwGRdlEw<8_m^!Zt~#mayK7ydPQ-wyJiQF{l&*ON@&;93 zXS|_waX1ltBSw^rPZAyTFJ3uf?`mOpwk^kC7w(+(IfUm;x<$;LLqAg8e{a5Q3a`s7 zvR7_@yBmCD|MB^{k?nMj1jNTaoIWRMaxYIowjnj5z;Pt4#60Hc6+C;{OxJrGT-cV4 zpTS7>tsi7qctkcuhLt}jIj*gRc?H5Ir?a&uciuW`X9+}6?LIloIc)FLxXE`(Jsyjg zX>O?E1fsvRBEzl~8H`b~QGFO!GkN5mh$@TsH;KMfN zWekzGPxm$WS@=4Jraj>wCa~cUWyC2yy&e^MxorE=*D8O>Neh^~bE`+58C@$c_>`8(6UPR#!l7xH`L&UYrq0K_-> zj6X-v2mc7$c?Z~(SMWE#Gj-TNXnFd73|K$^qmKej`dSAlFH%PH)pReRB}C-2TE%#-+Y2muLl%t?hZcA^Iv7sjT>8jq0!AxBZ{$ ziiOV8fkCv%Xy<5qYt5vzzz5Z^RnzL@x%`cO;D~!*dgsqN_W$IcW8d+#e0m8DS^}F{ zTIuWHr8?79TsUA=#HI|I4uNj2j!uv>(wm@}VHSLv$hsjH)L6Nf%RbKD5Kq=P9V{i; z{6hzd8h$fT-09T^9!BZdK8>;ajVBrL88zIt3-6^+eHMGk}lZa`x~2Mb_St_W=p zCt5=4SQ0$R3(0T@;aU{IrWecBOBRP5TLt)OVlceZxH9CN-!x|es^w$oHMbcfps@kU z!h+3{R>v*JNDo7IZ*oUy9Pz{tDUKEu=JHqHK`JH)TaJsU@jqGL_gVBac^=xH{VcCG zGshim-+b>1=c$pfXq?TXWQ-V#ea`6_h3cf#!j)6Tt}pj|-BzzhF{b4Wtm<#Uu}5yO zCQ8Ye4aETv6n&i{vr`%@>9q_HwT>c4TIgCx$tO^amKK1$yI`9x+NheQNN<1}khY>_ zbjQD9+c*ipFso1Y>UB6(KOmD7%12YHQAnt_D<7{WuGvP6e^|bs<2{CYN>HROyfDpc zv{MeAej*#-wbh@mWLsD_{ju=cy5b-KdSX5J3p^9BCqqxx);#Qaf#gXlb>TEG1kOe8 zjD8SF>AdB-DaIfCr6hm6+b}+y*U-+%@JU(W&Y?tJ%oyIKUcU>O0jfuIxd1m>52bO- z^cSmA!;WNPJ{)4dcKDE@e$F5++^0DlnfmcVdx|)|P&cB#Go3ow@N6~Gt49Ffk5Fyw z$oI0y?B*nB7kX4hL1-iCMQ62uK$IB&4pw%woQIS75xU3H=hn-K_E~1Z6FVOYcpTU* z&dzE~s3jHFUa7X3=}KK2@9eXZu`z#t7LfBxxvdOqmmApEfE!o`Jvq5{Wk=&>tpOyE zt_gHX(p5uhyj0_3Xiy&75UHktSb|WcZ+0|ZtPWg4^3ohhYAJGnxhVb}f%7C4M1X9n zk&iD1ppR7P1)T;&e~=NB8Fq4;VlfT`i${Z(`ucn4HK+yTEBe%i#f}TT;-&ub1t&8~ zeOeN&&#w;)Tqs1v+cgFnovqa*XYX`imwc9YzElsGq*~jPR94*-;rl|Nj5@=RFdsg5 z!AaI$N$n1bwUeftuaZR`Yv+Y6kQfUnv~B&q5udTh6A-S&$6sLZ0?ncy%o!MZTfn#-s`-@RIoCwvoMB z;#7>L9GF_rbn=$*fIO1YuD(!F zmx?kpuaXdZPXJAHBnYWss}mXXHrGs$kdNynKoM zF2;Nm7A2o-#sKT78!FHP!Bq*<1 zx*URuFNrHGh$~UhrG}8s6DNi@=PF>WO95DNMi)YGZ+pfIN?y6&7Dm{f5?KoGxr*eE z`LMHQ4GxTF2u?G`r=dM!z%OWdD8e%cbDdqWT+O(nfsF8 z$6~E=HD>eF@ZeGD73_Ao#1@pdtv>TE^$0r1FfY zsvm1xIwk_J3pg{B4yO@^abJU`oXSN z=F`?p{ZcLAAduAK)Ma_)!xrw0<@PBZPKWbP2tK2wpm0x25t=Pu5<19Mkxd+!bPa%5w-HD0@CrkOJYMX6YJRY}%(%7N(~u z)xndySPy-hDZ3%AlKsl9Y@Z!%!L3|wlWKleZOgm+KUVY=M(0QeSXNeyS)}O<#$Dnc zGAXhpYE^4ofUw`J?vRyp3ZtzC1FvPwZk9ER%X;7C*7E1|5ESsCa z{2ag-qf9qb-N{!89bHvG20=j|qz<|%<3(scPLzn1@9=(yQ&~lF;6xW_d}Gy`5JC~p z&w)&HaLok7S!YE=7_ByHYU1(BS+vu7X^(4T14E?bBojUXkSwZP@+4`V{dAhJ= zS)~n@PybY@^poVelrRZUd<-Xbo3cZ4=}P+n5S1ZQA%VygNIgrR0#yRJX+vz?WTE5$ z&I#e8ZmqLGhdxdQ{T#b!m7YGYMRfSA0Pe-XOYj0 z!*ojmzi=QHaQqdI0oCg1sy>(6_yyX9tbSoIS?w!8#q^Hk> zfG^ZDl1ONqNgNl|de@ZPl)$(Mdw$eoZxeT3e?mo-d}RYfDy@ba<~yoRmMt3-lhp;Yo=1E$*|+5fpN?@-S4cFfY98i?Dk` z6ymp18hQKrVcyG_ccuwJ))6rxtlD?8mmZ)r#=32aFM@0%{BPd8TQ5BsTcvKb^;I+M z$YVNY0MX#FK{KPbgtZ$6g5caK6y-z$=*{-@J}n4Evuz#K)bR%r*%=P&O_EkuF~B- z1ueav=7=&m7Z-B#hPyVmVz!6P$z_ngnXAuaMd==Ayw;B~698}VCUpA1mW)gSDgn>z zp_@enP*RNS7(A*HewBKy3h<)!fgvdbD#|0^SH_!S4r*38)y9PS;7G-Y;AN|MzN!Wt zMU-sgJxs493K?sf1@c4AXqiQL8p)pT?>gVJc**KWTIEWjnVaIve*G&cKKd-Gg{98y zIk$Wb{pdibSNx)t3-*g?pZ;;F3inDgL-1A63}hlSH;io{FQlJ@*nkS3#=`f@Wj z54UpAt)JGWtDL5$ug-Lzw~3Lv?p7Sd+tnQ6NsglTt^~fYp?LzMY(?$!vcbj8P zSgwK8m{s8ioE0;Ks2N0jdgv3*)V6E)0z9bx1a1Tub+|LQp>m3*OJbyV_yO$v5Tbx7 zsb4nl9PIUzkbzTJuhaJ~zFH}jruqzUn7O&@3mJlkS)E2H+~?~fDs`S#G!7#;%tjA; zvLl*bKFQbG)HQ6g7SDdURl`+ERkGxE@i#QkQ1G0!$jRugb^x}_!f=-(t~CZLspIR4 zI|-nl-R}qtJh?z~1u=JcY-c#11u{(})(w4;`tcz$)tXa3sGFG)+YoQ1O=oq=C^-AetogqrJ0;?wHw zbqC|y$h_buU+XqB_g!NNFV?e)#q0g(p05UX)^1%&bKb!K}#B363B*Og@HJ86-O zIc0M-fm0>aH%hu;x2?F$T|2AfYsCv@U)z; zKj%C;IV!+$r4z)2y9BH212AGL=iS+WMFK~gPT5*sIOoMQ=3c`vvPH3tbz6zf<$+gi zCxXTT>9OZBM@t?sK4Q((n_RdbYX)O1X03K*<&S$=E2Y26;nnq2Dm9)M2x?A%9BCEW zCtT%xIs|*VyobU0nV05wpIFl=8cJuwX>>O5?ZRV^Ok;Yrlt^BE5kLl2zcY0J)CFZ> zykB6#ffxW0o)<9mD8OO#7n&}M+m9G$TFK1)y;vTYj6GGK%-MYxE=Ye)Ggw;w2=|X( zauHrVpv1_I1j^mff3&$LB;oLJOEvA60lw58*KE?0`26LD%ZG|_B41ync5uX@<2nho z=3kz_Ob#Wh!-rWP#d$avV(^t@HmvgHg8a_Kiix{eL%SI7I*rz$^MgU33XN~-$u|mh z_-Wq2SfMQBvHG(m>LzLGBN+Ax`tiU|$2*brE1QpxoT+1!)DedKI@mffWV}>qOmMGvGh7ilZU(8F9wAyE>Ml-X+}WyC$!I=o>Pp$_~eVYP@N}_jQsV@ zqAJnq=2ee+%}{JAYe8antoh9fE5R^(UdLjT^aT%j7mBQ|?Ih|^P@J>0a*l=pST*B< zmHp#V?fS~`QhKaDr^U3+)R8?ke+mVjkkOgXn0!eJtul+v&b^2VSK*7Y9BzRlNK2H$ zEE`}L5d%ms99{&Eg~Yadhc2B?R~N-l9m%5g?WFW-pHgqth4cQ5A7|Iq!10Y(ITX?N z9PDRk1ux@G+{*NC)3FJZEU- z7@m^X`t=ryb5r(}uW9#a0z^J1+OrGt**!26Jw z&_2%i!-H<c|DltQF9dsV6+;IjrK;)sF=OiL&a=;pQW2vWZ z&gY`dG+tM`gk+KYPTO5Nhtz734y<=Y zT<|L`9gU>?wL|_LPqVZ;XM&)|1Qx44b`Q&SM**$EHgfM$@?nQO7gklwO?tGl;!9}d zPz`_E>L?6jjo~$kBsdwuUbK|U=@m=oO7QGYzlU;LH=K4rOMg84%#Yf)w|H2UXQG(q ztAvsKm>{Ilb%mxyHO+&Ne)-NM#27}h#|XqsYq8RwztG}}8NmHO9Vnv~=+e>3o9@@i zD~UJ}nErKgDC7hd_u~KtW-SH3%7IMQ)DFa*C_u7J!6HJh*{@$m13!UdcHQSS8_OIH zvj#7-jss^xNy|BoJa~oSfs|4sPLu2fGS zW`NKRmm~hZA5avxU3cyX6(7|J4p%Yp6qm=KWS1}HG)dVCU;tuSl7Es z0XsDha=$6y=Fo<+KYEcv+83|v&g0x9WRI4bSS0+E1IHDE^Rs;>v9YMc9{uxdDxQSl zQ|``ejXzzel5mdaPd;1yei2@MbeDF8s)2=Hqd#r4Bk3%5^t`2skp>7EeFfOKg;(2s zfuAfW#njd#2y9=|X6XcvGY^?FjwVz-L%U8#GL#&B?FGkZ(&X@@`k7z$6j)((AI9pd zqAxCw4isaX(p6y?MB`+xT`s{rF~`}k7GE_8FYQxaZiYw(STO&m`16aju&OHn-f=j$I4GylBHZ%#+-(x00&G8M$ zSQpj?1UHN;Fdip9p~X)XFa@TQt3^6T_Dh5>O%a5&%S)D|8;2#GkI&KSk%4`AZbnD@(pKxdT1txg-5Mx|PH}#zf*14G zJe6meuMCX!f~*ZH2BrE5d2MA8>95%Rz%QAy=xD?nJ_vbV0c8-!&_E=+6fimn8Htd^ z=luP>E@v2h3;g>o4-fl$02PW+dUw}FoWLaGJdCFYa;`1fttF!e#JYAk`eqj8=abS`v|Q@g_<%`kf3fnCr(C@%P>L#gIf$B7W16~!XHFQ(nQW9Ucd ztSFE8lEHl+<1X7r+Er=%cJ;_fR9syirTiqfh>gAFE9z(3lbpP^8?NAbbU`Kbyajdd zTa`Wsc-&R?2JHx-6C4>EEFwN345{zPIt;cD($bLw5xaSgvuFD(O`Lj;Y}5nr9B8^n zykh-*yd7SEC?~@ju*ERgVF%swr>Jyd|0sidZT`}PTqs?Hh))cSHaEVvtxuw$5EZ`p zG2@q5O;Ur#D6qWlq@^|2ZzXZLQS_1oTfIP7QZdUYgVJhC^}$EpU*+b*R)>|VT-1WYp}_Qmo_MKBXjiOn`7 z$rPFh5m139AlVs;G2<;>U7V*GgMb#e9uPwt5uI}goYR3z5-Dv?2ngN@;m`5w=gKeD)RZ#4TkSGp+Sw-WqAv5Xn1|a&F~`36 zeZ5kc(vu8VgTS;n$y>r_%7vBJj17U9g%>D5VumoGg%CS36alCw2^!7jayDlCV(~e$ z8o+=(OzaopBn)>3o}Ob294@5E(wN<^RrmLA3jq{rY92x1k({Qk=q_A~G?Woo^)9?p zuJ~8HX}5f}GM&|o@oD%Hx0&muJPR0qm?-)I+U$#MFI--p&Aiv$Sy-81&COq4p~Grj zWKC8nl|S+2RUyuTg0RhnD*qJonx+gS zh7E4hV*%a$XqEkP@xqlz5KuK?r;TieJsyXzkY#`+%jPAbg3y}%Lbt;7m2MD+CbQqk z)clSIDlQNB!+SXNtzOrUd%d3YitP)BoNd$4deSmfNb>opt$ei;Wpvy{@4#QFNgLT} zk&oV%I{M|Eg-dJ9pdOp$H~OZBn1*yAZav=-o4JJ9wlFJiFETN}deis5%r;BBdtQG& z`RZujrspxCOr22Bh8nZLU8hXgVQ4CJ)ju~6>9xR@=6LrvTg*e z9p-$gy`|!E#>KdFK|sg?kXAuE>Y`FKL>rsiepn{l-nFvjc3F!N0%AVS7z6&$e-uT} zCo2p+Q{GWMNG zubs*F->3rr2R8^@a)L*d>@+j%rzkG)BR}OT&>> zLh*Xl2S`P@B|QUo4(^H6Z6>#WXX^F-{P9wI(-=UCQ>Ix{?O4+pMiBv!dVdBFwWIS_ zKl8-Kl&`xRN(;lD#dq!no(Ml*YJRVvq6Q_Y&AEV7kQMw~El?rlT!vLKxsth@x-8kn zcKKQM1+Di5$WG&de6v)im3Mq9-c{Fewyp2Pq}1eG;yYe-bc13eJ<(+_06ar9np!^~ zuiI2F1txc#Wk|ZPS5)UtFsEb4YCSlG@x4|TF})*>%aox$JA>D`Q!C@;*BngZ70Q?rM^qyI^?R?w3Suj;08wvKFzexhlOOskwHmf{nr3>4;uP zE$7#fB}7UL2X!APWh4lL$e=z%qT8iKNRoX+HjlG3DKUqcUfGnmoqw(6PMTlkx@wkD zAeTXiUsRBP@a~&=`(7y^KFxQ;c?cCIIa%n0GQW&A$u3p%fEgA^o86_l)W!O|SMo5) zYe4aHOK6P5l}cI!J3h{q2G{z{+4q&9)Ef&@vNK2OjnOf^h`NNwdrrc z0(lAESO%|yrR%cFmd*XBqLKu7thU4uG>`w%#9bc3e4k)|J2FmnBo45AFxyT(h-I z%ukI9mRAt;#@_^X_}{%9QLUS+fB~mqU+ix=!7V?WU@d;~lk>G6fA3Lcg#a?9r&XrmTu|D;{@fYQ>os)1&v!JwM?W@AIZ)}S0M=co7q$8OLx#8u?;I_CXL{cYIinmh zxof4l^0Ycbt8Jzjp-?S6HUJ0)08iGXhGqXI00eRAkaqY0uy17{8-2@`_^vp32|rcs z&* |Ht?0hUj`d^7=c|&56rk5O^j7eW(V?r%O_bU4TCm!s+_Qr@mF#z;%~WgG$OC zeOWU5Rix^I&oj0olY6yBR{m(-t9B;pCvN9oL+33ax*x|NU{So^nIgV9`5jz`G}HT^ zS$lT=`)NQ}1TbtPNTA+1c1sdIr3UWhU%J85I32%83Qwm2pj{gt)jXppo+&tV(b%E8 zSn5@L4Ud}YWXr6g^9}oHvR9*Ld;xrjV}9@R%0;yL(QFoaTXF{@Z=ZP}jusBED?^_$ zIs5GS7d)*kB^ApH)mInh)bbn^CUI{(7iE{s~oP5ddZ4SW5n(D>; ze#uO7Xw6{yN-AEqtPq3qu6loFOD(odOnu0Sx7F#L-JC=2HM#0+?CKuW$*C~*|=g8pt6 zfbTbGbk|DoGtn;qils%RBOw217b5d6aKtyxvF)>P^^t_2h|1IZJ-?d-&5&{8z1<5F{5GqiTc~!>X$)dkkwN4%}(0i&El_59c*Xs$`5#;iC4{ZGLjvMV6G8O zd)VK8`;xW#bEdkVGEJ6t?ki=rc*q3nB0 ztMyvf0;EUp3{rptXH}W+Oy(~oD0424Kp$5KHk;kRf%qjVhSan)UrI_`{PuA#1Ib+wDl^q@Umy!y_~B@1fchOMLK%XfxlR;+^=>qLvOpv+P)lxx&GR~z}}$u z_0;Oz7Mx6Or6iJVx{*mpb^snEN4-)(^QU3}x%fPU{56t?CO!@7&fuW_ANJlmsLA#1 z_tj;giIHAJSQdJdCJ>M;Q9y#UK##60um$;3%xF>OR5+`2oNIDqzj=eL3#iI zDM>`6NrIFJA)e>A_aE;$bKbMhe)r6o*>mRi2Q!d?l!xcO?)$o~@AdhH0GM+rjFvC` zYPdV=ev?l{-yyytfUCpoi^HhDnqJBLKt$yg~$5NIa!s4FHWiR?I`lZV_ef{OvPwLU=b^K84Ogrv=?1{&2u^+}=Dnj5Skk z+8W>QMHbL5S2!S?@rT6;P{&pJ#X;%N#TrSJD4uuXYfCY6rQd=&uLji5GCm_mF;G@I zKn3)ZlVJZ?%5lJ^$h z*)CjYp}Am2wb8wsHK&0do*9jW@=*)6T>Xj$XHFYD#?ELH&8?>+v3@zX{j4qWgqN1G zZ#JF=sOrNTeY8yxbUKi`P-X#I-0#on1YmigG=XB7MMAXP2NE(JyQs-_0L#`gY$Y(T z$)KD$pH?Oy5(RlP5h=Q64UI`ZVDWTBjP4tj=df|5E^ebp$YKt@#fdg)$^LcZ)4Xd! zM6{})L$ zw!8n`Aa7^Ys<=ogEWi47*-UUOKg!D0V6@w&#d-i6h_t^n_S?Yj!!jRcmCk0B&V9}g z4{VqXY-y})QdYuCmf}QlqS+ZqPL}d_0762>C=#EZA8o)Lydo#b@dgz2Ua>4^dNI)A zOv0Y38mIflT%C>>Rg1_P=Ims9v@a#MW6~iMA;PmN&c)uq4YT3G`hd^5)Lei z7kD_3{|RG%aE=3J0R>v5reHw3BI^s2OXZ$rhttv%!Hv6Vam^%B2POUFG+NP$%7I*9 zht9a6^2& zYxD2>=HF}T-|^<(vG~8fGaSVeR`;E%oA&=!s5v_Czp!<>5zs>v@9TX#27B_GPUja{-mBk^7I|Pl z+aA#Tk8ClxbnJq_A3V8odm>!6+Yw`+`1r1PuWRJ!Qr{ua?rax-0@8qdS3G3a9f=73 zbtD+F8T;zkK>7h7sllipk^T1*{rejI`#$|&@{9Wa zw^#!-T8}M55)t*`?>l1T&T{#HAr#@(fB+f@aBHbE%S~BkTLe}0?&c{b?P&h?fi^MG zs&lgCyt_bSu|;LdM56^5*PBea zioe-QMXIkk=Q}dDF9x)uCh+|BZ`oyyO{1}|Yb29&eKq~~1|!{?-GG6)i8Z6TCjYUN z)&30)mzMgw!!Z!QO}2_=Cqg2+4h4cUI_8_xT z%}ZoW*N3;srnH>s&^t!p{Q)fnXuK{d#@;7XvVE=g?2EPi&ha%rei!ISDl}W- z()sH`D%zd56_r=`T~bUG1>n?7yV8pJQk-PJ{Z21XeawvReuI$*YN7|5X)_v`%*`II z35%yI70}DMGZ)fA10M$^MN8C7=$#9oWp6&yg9yyB7jCkjpJ-1<=d=q>v>N%)&pENG z?&+qQ>>j|!NShHJuZv_=prtjAiiuranY&}%V}KQzEV;(93GfSD1F#cWxPwpD*3jbb z%aVb-O;tCtaWe95p}{&86Oto^ZZ>V2{;ON%UHB6lL@KtWP}vmaQTsecaC3vjjPJV~ zJ)qxl!bAye?7|)G5ObhBmRS%1{6Qa#PW2Q?Gk0qkasuohU0&jO5MHFyJcvy}(y{yq z7ylW>?rB}CN9h(*^2IhqVhx22v4%j!#M~09_dxrjp|`;S^d0o)-HT_Rc6$@T&GGsl ztc1K&V0oRrq^tmg3w~ko-BhT3rG?0;3%f5eV%5ylq9p{A35kTDz>~yDA~Yo<#iS8@ z8WxI!X|HmkSbYq{6D;7>06DR#OODNp2TDTeGx|ODxK*bQ^iR>M?8t5eKZnnkKJZgd z`A#dA*UP`Teqyn%E^TH=^}t^Q^LF6kxtB1^M8H@c`$2YasIM$F*!8ZsU3uZ`L-cB9 z^7AC9kj>r;y)VQ*J|)is561`fSc#=gkUp+DfDbReahL(|2hA+^A!-^aZEg#6@mfgVhGOr$C`%&U7=%L~^b`pfn))6#zFLh651-u;P`!Sfk&x!Okxy{Y|4|DcX%P@eg$SNpi4i&bbP```|)< zE5TN+$lKby6c^J+$6Fd5DABauRh`pBF&^O(9Q~b>mz?5M3Y?8Rdf%aR*X~larw6;l zYF^$K)*EXyu6cJ$L!p_#hg9}fbQg-xSkB14CGTqDNcH)Kt%>J|8tq0M*67+E{}z3U z#f>Fm?ogvop|8^kDXr&TBV{!jnLAC?d%CKKvzkwhJ*&S>7Rj)7(-(uJ*92%FAy2l2 z7fXow_3iGAbAo&=wd_n|Ng}0Jj5Q#r?9@fmQpu>ilr<+kPL>f@up9I0lakT%Gi~)= zR|Pvb5;U9#zj{~N;7?s*opG$BE9)DWS&zqEC{P{{iVF$IkxYTqJ+F)}D13~v?B`Q0 z_Q1fJ*o1(^z`KZNGqz9l$cdL$nCwRb^t(-Dz*U9UkGf8yZq^T4P^2Xi>c?gCOWO_) zNpZzxKbuKAZPj^uGamXdlv4q0xz#ZO7UHp$y`ZKS*sJpSN1#%9(>0Czeu@JF8vkqo zP5BvfkBTd;grG1(NSyv83~JNguU@-laUF<1IhatObXURV2gS?%hOuBV><(*MuOBF4 zj9J5RoJjh&j=LbCX*3V*R9&erT=zdwaYm)!26<27|Y2agRs6*aUbs@a~G&W zop{E6NniNn*9Z6u%25`k(}w8~K(xZKWOY0>QL8Ry09MX1dHS)Ovl4N@cn9b9NIgR4 z+kQ|tFxGmV-Uw8!B-w}_>?z%II-L^DL$AZ0o+d?=tvu?O4UZg;QE~i7Ci~XaQww5G zI&Zg;)oP^f>qz9$9wxm5XPwtT(vftn%cKtutK4BwI3lCU$0m5dp;!-h+HGfR-=)>; z1k~QK27T|!F_FKGVdYEmHqQvLWatXK(2bgb0~|7?-yCf24zf26%%g|fd8sCvm@CY_ z^}z^ce5VO7l>=uPb$6WOs&g#a5f$I|oHF`$&lypqEuOZ6Fi?b0J4vR-1=$eyeZZB< zxfbUAdgZb2(%?qZeUDr7(qTaoFs&@49l>h8`*yHSr3$c>5?7?)WPS5OUSj0VUHVU9 z2R+Jo+J!=eGMbaej_QxnayyCxPI|oChOC1U^_JdIo#%7FS=#0~XZdV{);AbIiCE$2 zlNe>7QI}4m0(5&t_IRhXL}DwF*?Z0@Fo1g|(9gBjYQlX}z1IGfmrQG&xv1**Ug@~FSD_BomN9n`J>>AahTg9&G;^zxTpVq9AK z5MQx2n(EAR!hXncyg3s$tm$oN4%LpSu>V3-2}$v@{?LVls>=>~eC?x{07|52C3NoY zB5QcP2EZk}`LYV-KmyQ$m7NWb^l`wVV$*LV~MK|0Ee7-`{XV!k(PH8 zlMKox{am22J&_V9Kezi7N721Z@_xPMT~lA7XkI4X@S9Lcm?79;D&JgBWk}srBuF(R zRtemIcM>KVz#rmIB)0K4VZ2dw!EZhqR$^qbnV0)lfdQGwu>v2NA)!LoNX_~23<946 zl;p+Jg#dJ(8#=GKz`!90dl!__&U08Cbk9Tu17=sUu8cuq~5M)sd+^}af>K`#DQ>#`Oraz1k-sQ*{-=Z2n?}%_w+Go@us^I5gwZw_| z9!H`LWPP;KT?86WT~eO$nhYkoHJt04ue9JxAi`25-zlqE=qbh(2m^Dc&F}oX9(pZI zJ5Z*`J9sf1fe(f^Z$@bmoW)Ep6X8ua_9L$y;<>V%>!T(npE+XEe-}!DW0##_ewX$ z&jbmuhbgt4zJ#@4M1ZVtdp@PPtkyT`uCw7#%EOOWkeUJJv5$8iOje%#(61?=rjAxp zd@L1r_T2Lx?})rSd_r>JBF)gbrm9wD+P4Hu{bt{7HvZ7Q?yyx$_wK5>vh#cTo4>0x zLY%;|WjK)ml^`ERfgOIJ z&rxGe#fA+!^nnknpO<)|Cl8!iHYJROlm4=j%)|CVzlc*asfS+)5-$k~`QegeWK8#0 z1Jr1Y%8~)1%17C-7mk8grnp9fPQ=b>n@;X!KaY7}+(R)Cu)k#$@ zKN9IM@LUynNloCqVyIc=^vgSb z@!c?#eM(zDl_0vr3$;|BiGTL_X?1V|xS$@baHc-JI5A9#E6EnIKg^Go<4(xR9cEyz zM@G^SqH<|meO6$@QyLJyFer8=;6(#Q#BUnw`7(zW=G7r zI-v0Mf?eNH8+7x}FqOZuu=OnT$1B3RQ@YIU%kQSLEVO%}--Wtv>twy%4}I0v>ggtS zZplp)->%-3CU==+8eFwiC4N>cwT-Hn&<(W3>b%_JzN9P}d&(3Vcj1a_V@uqm&6A1X zJOW3Y$AdZ5OO6!=jB%Hr0E2XQHB%Sru-%)}%kSOV0cfrSYC$G26cXF6T}(SoJg{Wf zQg)-Ib35HYorUg<6!Bk2PH)eiyYBLq#V7qemPiSSictTW7%&`1GA~sc7SZ&P@p4ES z{5GV)q>K>wcbKAD?W5QFMDl14&=sHDLYo$v20-^qoE+=AM=GIk8dRk4mV$TeK!7Ro z+7#JI*Gn6Xcs!Kju4XOiT+r0C{-x<^gRIExVIgF(vK=7j9Ndf^gz&d3VAPnJu^6!f ze~bwW(Y^UB892wHYZ1WlyrpX`fG#JDk+Pi;3c?s67YlWq*?Ws}Jm|jed7*J*ovJqO z%P?A-)vb5+6YA<^lNVMgqhY=Y6l$IzZYFc%%&c=6Q-w#WzS);}PQe^c)@n?ng z{Pp~UbU>6ITm0*Y9fDJi-IGVu{@_3kO5z*2%XB#=Fxk5*@assn9QO-u`X^WJaO7&o z9^}k_`~?8lmzNzLW&+C3ryGD)mmSXmL_hn_ejQn|0De^3jm84U$xu+x;c)lUc6B#( z>LYc+tortdRhVFAz-7{xhBwCcwqar=^#?a{My!m5tREzP=}}|$T}s;Ot%@|7AKIx7pmV4CnpvER#pkmZn=!W8*e9N-2z|!$YO_2ZR7lzRM0O7sFuOlju z!w(k!?E}7w#D0%H56mk-xUV{X0$Tp>(5uD8z&e^c28iuKSVqO503vy17}GF#&!@=c zQf#Sk>ga3B?7vQYuFBAKG(CHf=V-q4)~@KPW21g80i5H8$k6a;+^d=+MOw8tTp?)s zlLG>R);zIWhz|wn9JQeR=b4+kw8KDEPBpf!N7)kFEuGtxg>C!8>eM zFJ${9ge}v-NwnZNe!!0A<#H8Y0Nral2z6oqZ|o9K4G%ZoqZ2l+GN%@?Pg<|DEP#C_ z!Pt<=pe@I$ziwgiBy(KpX9E?@$9>*4#q|#Qyz1(0erI9H{Ic zJycvr?kb+i?-b#fv-Rj!2yyfkb*CTKrF%#XHQuQR>ld%7+iQhJh_TQ>x8Co)e>`)k zUcY@?-opBy{++|1#h_HoHF`TF@jfaK5&Lv?RkCp!P)iA9erORj7#4CjOwqtbC^*zmDdD~2d@=gXDboz?SJ!4E#(KhB>tW+|<7NX6%C)!f@S64?9j5#XI_t`Ce^ z06X69H!dQqZBsnVP}-?&;sA>XUkG_&n>;Bz{~1~1I&DRCrJU&bS}5Cee?J!AHo5us zv#0AN!LXnclYIxe<2^DV>p{w2W@eEN&4EVc^-{XkIECuhhliIKo0sM`Dw^*^Mzsxi zUa6l`?4KF+R948hp8nYe@H1Y2F z=}wL8dL;U7eOYn9qlP_^>7mKykNN^9mTK=c)fJXB1Pe~oa&pDT#PWE4%A4I+vTL?B zB1G!eJqk23i8Qh_XhAqmnZJ&Vyi%YxQJr7wY*|^In+$XJ(zf>09r#~~B1tBSA{n}- ze|&jS9x8QA@$eKQgU9~Ev4W)k%OVxwienG2VYhIcgA4Y? zCL0+5{9txdYezE)_(nG<(L|DMRKZ@RfU!Nc((I6KSxsgx<$&ZiCMU*@r^t#Gbxn0Y z{+^9%eYW-E)@Ze!6)(B^bxNPWXQfLUQJJ85Xx;fuD-g8s@u>S4&aDy$EuE-KS?w85 zZhz}EZWDN?;Ji&~W{N7jklz6{N^~pKbHf|770@gzGLpjW!$);S{Lk3 IRHjWWsa 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ƨt{O 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 Sfn@#v>tpesy*239=l=l<1=U^v literal 0 HcmV?d00001 diff --git a/x-pack/docs/en/ml/images/ml-customurl-edit.jpg b/x-pack/docs/en/ml/images/ml-customurl-edit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a209284c78669fb3d9341784641a2ffbd96f1674 GIT binary patch literal 123042 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 MNtn(6Zj!=Y4LNg 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

2fxHPL<{pSxO5F5Oc>=Kq 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�wcsj+^MM&i*~JTVm9So(W&R0n%0K}IqvV( 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

h{)5$ynTBnKL>Tg5pwTz`Zn*9)lV9m4BLx4 z<&J&D+z1bxqR#UV+u5RsTPW9zo9pHYZU?aT>OSV0PoR(W1gpJnXm)-L5bCj{aqSg$ z)tABt43r}Py~OFz4mb6bWSAEC-&PQl_;XK3h_!zhF+3r6>wP0Dlvx4C^MZ<$OB0%(p8q!&^MAH?(u9P{9N~_WfYs?&#rT3d zfL7snX^i=8i1)u3HR&trQ9Lk}N>sUhMHL&jF_2T!L{(>x#{cHOEoxX5jig?`4ldMG zwj*7-2i{jF^HA(A;oH1H-*>c&58u;x(+PtvQXehJIOd;YQ(B1JAAZ-hYQ~JYzR-GCQf?jF%g!dE|fS<-ZR8AHEuQ zXgYn;*!EJmuoOR@{SF%%sd;m7-tx8W=gWT(#$CUnwq|q2KcHmQE}PBLKQE2{9;YT| z|D0HJU_E%sYl;8-y#qY>J3ATUzkqeJuJkLa=nz;}hw{`n)`CaZzWJ&$?X2z{g+0_?87g=VE&nRWjLr$X@t}vBhT-ypvOhn zB_l=j0N;LeKo@g^}Ej1|EF2|?_kgWbISAo$~J=eI{beDi|_Vo From 31693c62e64a7cfcc9a6fe41521cd521d6095842 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Tue, 12 Jun 2018 19:46:08 +0300 Subject: [PATCH 29/71] [DOCS] Mark SQL feature as experimental --- x-pack/docs/en/sql/index.asciidoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/docs/en/sql/index.asciidoc b/x-pack/docs/en/sql/index.asciidoc index d2f9bba099384..1be44b2d05965 100644 --- a/x-pack/docs/en/sql/index.asciidoc +++ b/x-pack/docs/en/sql/index.asciidoc @@ -9,6 +9,9 @@ [partintro] -- + +experimental[] + X-Pack includes a SQL feature to execute SQL against Elasticsearch indices and return tabular results. There are four main components: From 76787732aa23652aea02d90ef54a8044ddc835d9 Mon Sep 17 00:00:00 2001 From: debadair Date: Tue, 12 Jun 2018 10:41:02 -0700 Subject: [PATCH 30/71] [DOCS] Added release highlights for 6.3 (#31256) * [DOCS] Added release highlights for 6.3 * [DOCS] Fixed typos and link to rollup section. * [DOCS] Fixed typo --- .../release-notes/highlights-6.3.0.asciidoc | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/reference/release-notes/highlights-6.3.0.asciidoc diff --git a/docs/reference/release-notes/highlights-6.3.0.asciidoc b/docs/reference/release-notes/highlights-6.3.0.asciidoc new file mode 100644 index 0000000000000..255aec49b20b4 --- /dev/null +++ b/docs/reference/release-notes/highlights-6.3.0.asciidoc @@ -0,0 +1,59 @@ +[[release-highlights-6.3.0]] +== 6.3.0 release highlights +++++ +6.3.0 +++++ + +Here are the highlights of features that were added in {es} 6.3.0. + +Refer to the <> for the full list +of changes in this release. + +NOTE: We want your feedback on the experimental features in this release! Let +us know what you’d like to see next and any problems you encounter. + +[float] +=== SQL +This experimental feature enables users who are familiar with SQL to +use SQL statements to query {es} indices. In addition to querying +through the SQL API, you can use the Translate API to see how SQL queries +are translated to native ELasticsearch queries. + +The included SQL CLI provides a simple way to submit SQL queries to {es}. +Similarly, the {es} JDBC driver enables you to connect your favorite +JDBC-compatible tool to {es}. + +For more information, see <>. + +[float] +=== Rollups +This experimental feature enables you to summarize and store historical data +so that is still available for analysis, but consumes significantly less +storage space. This is particularly useful when you're working with monitoring +and metrics data where it's not feasible to retain the raw data indefinitely. + +When you ask {es} to store a rollup of data, it also stores the underlying +statistics. For example, if you roll up an average, the sum and count are also +stored so that the average can be recomputed at query time. This enables you to +query both rolled up data and “live” data simultaneously using the standard +query DSL. + +For more information, see <>. + +[float] +=== Java 10 Support +Java 9 was a short-term release that reached EOL in March 2018. 6.3.0 introduces +support for Java 10, which is scheduled to reach EOL in September 2018. + +If you are not comfortable with the rapid release cycle of Java short term +versions (and EOL dates), you can continue to use Java 8. See the +https://www.elastic.co/support/matrix#matrix_jvm[support matrix] +for all of the JVM options for {es}. + +[float] +=== Improved trend modeling and periodicity testing for forecasting + +{stack-ov}/ml-overview.html#ml-forecasting[Forecasting] is now more reliable and +has greatly improved confidence intervals--particularly for longer time ranges. +These improvements also affect trend and seasonality modeling during anomaly +detection. From b2da83fa74b8c84015ff9177add6fa2c50d92727 Mon Sep 17 00:00:00 2001 From: Deb Adair Date: Tue, 12 Jun 2018 10:57:45 -0700 Subject: [PATCH 31/71] [DOCS] Fixed typo. --- docs/reference/release-notes/highlights-6.3.0.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/release-notes/highlights-6.3.0.asciidoc b/docs/reference/release-notes/highlights-6.3.0.asciidoc index 255aec49b20b4..94c89b5755713 100644 --- a/docs/reference/release-notes/highlights-6.3.0.asciidoc +++ b/docs/reference/release-notes/highlights-6.3.0.asciidoc @@ -17,7 +17,7 @@ us know what you’d like to see next and any problems you encounter. This experimental feature enables users who are familiar with SQL to use SQL statements to query {es} indices. In addition to querying through the SQL API, you can use the Translate API to see how SQL queries -are translated to native ELasticsearch queries. +are translated to native Elasticsearch queries. The included SQL CLI provides a simple way to submit SQL queries to {es}. Similarly, the {es} JDBC driver enables you to connect your favorite From 8cf7875a7914d941d094e8d3612a49855df2a435 Mon Sep 17 00:00:00 2001 From: James Baiera Date: Tue, 12 Jun 2018 14:58:53 -0400 Subject: [PATCH 32/71] Fix Netty 4 Server Transport tests. Again. --- .../transport/netty4/SecurityNetty4ServerTransportTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java index 3d9227319a870..90fc0d5cbad31 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport; import org.elasticsearch.xpack.core.ssl.SSLClientAuth; import org.elasticsearch.xpack.core.ssl.SSLService; +import org.elasticsearch.xpack.core.test.XPackTestCase; import org.junit.Before; import javax.net.ssl.SSLEngine; @@ -33,7 +34,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.mockito.Mockito.mock; -public class SecurityNetty4ServerTransportTests extends ESTestCase { +public class SecurityNetty4ServerTransportTests extends XPackTestCase { private Environment env; private SSLService sslService; From fdc6f4dcbe63151002e12e2a98737015e594f094 Mon Sep 17 00:00:00 2001 From: Sue Gallagher <36747279+Sue-Gallagher@users.noreply.github.com> Date: Fri, 8 Jun 2018 14:41:01 -0700 Subject: [PATCH 33/71] [DOCS] Added 'fail_on_unsupported_field' param to MLT. Closes #28008 (#31160) * [DOCS] Added 'fail_on_unsupported_field' param to MLT. Closes 28008 * [DOCS] Added 'fail_on_unsupported_field' param to MLT. Closes #28008 * [DOCS] Added 'fail_on_unsupported_field' param to MLT. Closes #28008 * [DOCS] Added 'fail_on_unsupported_field' param to MLT. Closes #28008 --- docs/reference/query-dsl/mlt-query.asciidoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/reference/query-dsl/mlt-query.asciidoc b/docs/reference/query-dsl/mlt-query.asciidoc index 868a7f21a212f..0c7252ff30b12 100644 --- a/docs/reference/query-dsl/mlt-query.asciidoc +++ b/docs/reference/query-dsl/mlt-query.asciidoc @@ -242,6 +242,13 @@ number of terms that must match. The syntax is the same as the <>. (Defaults to `"30%"`). +`fail_on_unsupported_field`:: +Controls whether the query should fail (throw an exception) if any of the +specified fields are not of the supported types +(`text` or `keyword'). Set this to `false` to ignore the field and continue +processing. Defaults to +`true`. + `boost_terms`:: Each term in the formed query could be further boosted by their tf-idf score. This sets the boost factor to use when using this feature. Defaults to From fe4ca0fdbfce4c75878942a03a447a2df83250b8 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 12 Jun 2018 19:13:02 -0400 Subject: [PATCH 34/71] Use armored input stream for reading public key (#31229) This was silly; Bouncy Castle has an armored input stream for reading keys in ASCII armor format. This means that we do not need to strip the header ourselves and base64 decode the key. This had problems anyway because of discrepancies in the padding that Bouncy Castle would produce and the JDK base64 decoder was expecting. Now that we armor input/output the whole way during tests, we fix all random failures in test cases too. --- .../plugins/InstallPluginCommand.java | 19 +++---------------- .../plugins/InstallPluginCommandTests.java | 2 -- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index 6a3f57c98d205..3c54afb92c7b7 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -23,6 +23,7 @@ import joptsimple.OptionSpec; import org.apache.lucene.search.spell.LevensteinDistance; import org.apache.lucene.util.CollectionUtil; +import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKey; @@ -47,7 +48,6 @@ import org.elasticsearch.env.Environment; import java.io.BufferedReader; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -74,7 +74,6 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Base64; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -543,8 +542,8 @@ void verifySignature(final Path zip, final String urlString) throws IOException, InputStream fin = pluginZipInputStream(zip); // sin is a URL stream to the signature corresponding to the downloaded plugin zip InputStream sin = urlOpenStream(ascUrl); - // pin is a input stream to the public key in ASCII-Armor format (RFC4880); the Armor data is in RFC2045 format - InputStream pin = getPublicKey()) { + // ain is a input stream to the public key in ASCII-Armor format (RFC4880) + InputStream ain = new ArmoredInputStream(getPublicKey())) { final JcaPGPObjectFactory factory = new JcaPGPObjectFactory(PGPUtil.getDecoderStream(sin)); final PGPSignature signature = ((PGPSignatureList) factory.nextObject()).get(0); @@ -555,18 +554,6 @@ void verifySignature(final Path zip, final String urlString) throws IOException, } // compute the signature of the downloaded plugin zip - final List lines = - new BufferedReader(new InputStreamReader(pin, StandardCharsets.UTF_8)).lines().collect(Collectors.toList()); - // skip armor headers and possible blank line - int index = 1; - for (; index < lines.size(); index++) { - if (lines.get(index).matches(".*: .*") == false && lines.get(index).matches("\\s*") == false) { - break; - } - } - final byte[] armoredData = - lines.subList(index, lines.size() - 1).stream().collect(Collectors.joining("\n")).getBytes(StandardCharsets.UTF_8); - final InputStream ain = Base64.getMimeDecoder().wrap(new ByteArrayInputStream(armoredData)); final PGPPublicKeyRingCollection collection = new PGPPublicKeyRingCollection(ain, new JcaKeyFingerprintCalculator()); final PGPPublicKey key = collection.getPublicKey(signature.getKeyID()); signature.init(new JcaPGPContentVerifierBuilderProvider().setProvider(new BouncyCastleProvider()), key); diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java index e9d0974c1438c..1db551934c768 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java @@ -23,7 +23,6 @@ import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import org.apache.lucene.util.LuceneTestCase; -import org.apache.lucene.util.LuceneTestCase.AwaitsFix; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.BCPGOutputStream; import org.bouncycastle.bcpg.HashAlgorithmTags; @@ -116,7 +115,6 @@ import static org.hamcrest.Matchers.not; @LuceneTestCase.SuppressFileSystems("*") -@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/30900") public class InstallPluginCommandTests extends ESTestCase { private InstallPluginCommand skipJarHellCommand; From 98254dcce752238d8bafde277e8a86e0b17fab7e Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Tue, 12 Jun 2018 19:26:39 -0400 Subject: [PATCH 35/71] Revert upgrade to Netty 4.1.25.Final (#31282) This reverts upgrading to Netty 4.1.25.Final until we have a cleaner solution to dealing with the object cleaner thread. --- .../ReindexFromRemoteWithAuthTests.java | 3 -- .../index/reindex/RetryTests.java | 3 -- .../test/ObjectCleanerThreadThreadFilter.java | 32 ---------------- modules/transport-netty4/build.gradle | 16 ++++---- .../netty-buffer-4.1.16.Final.jar.sha1 | 1 + .../netty-buffer-4.1.25.Final.jar.sha1 | 1 - .../netty-codec-4.1.16.Final.jar.sha1 | 1 + .../netty-codec-4.1.25.Final.jar.sha1 | 1 - .../netty-codec-http-4.1.16.Final.jar.sha1 | 1 + .../netty-codec-http-4.1.25.Final.jar.sha1 | 1 - .../netty-common-4.1.16.Final.jar.sha1 | 1 + .../netty-common-4.1.25.Final.jar.sha1 | 1 - .../netty-handler-4.1.16.Final.jar.sha1 | 1 + .../netty-handler-4.1.25.Final.jar.sha1 | 1 - .../netty-resolver-4.1.16.Final.jar.sha1 | 1 + .../netty-resolver-4.1.25.Final.jar.sha1 | 1 - .../netty-transport-4.1.16.Final.jar.sha1 | 1 + .../netty-transport-4.1.25.Final.jar.sha1 | 1 - .../plugin-metadata/plugin-security.policy | 2 - ...stCase.java => ESNetty4IntegTestCase.java} | 8 ++-- .../http/netty4/Netty4BadRequestTests.java | 4 +- .../http/netty4/Netty4HttpChannelTests.java | 4 +- .../netty4/Netty4HttpRequestSizeLimitIT.java | 4 +- .../Netty4HttpServerPipeliningTests.java | 5 ++- .../Netty4HttpServerTransportTests.java | 4 +- .../netty4/Netty4PipeliningDisabledIT.java | 4 +- .../netty4/Netty4PipeliningEnabledIT.java | 4 +- .../Netty4HttpPipeliningHandlerTests.java | 4 +- .../elasticsearch/test/Netty4TestCase.java | 26 ------------- .../test/ObjectCleanerThreadThreadFilter.java | 38 ------------------- .../netty4/ByteBufBytesReferenceTests.java | 3 -- .../transport/netty4/ESLoggingHandlerIT.java | 4 +- .../netty4/Netty4ScheduledPingTests.java | 4 +- .../Netty4SizeHeaderFrameDecoderTests.java | 4 +- .../transport/netty4/Netty4TransportIT.java | 4 +- ...Netty4TransportMultiPortIntegrationIT.java | 4 +- .../Netty4TransportPublishAddressIT.java | 4 +- .../netty4/NettyTransportMultiPortTests.java | 4 +- .../netty4/SimpleNetty4TransportTests.java | 3 -- .../smoketest/ESSmokeClientTestCase.java | 18 --------- .../elasticsearch/http/HttpSmokeTestCase.java | 14 ------- .../plugin-metadata/plugin-security.policy | 2 - .../AbstractLicensesIntegrationTestCase.java | 5 +-- .../license/StartBasicLicenseTests.java | 1 - .../test/ObjectCleanerThreadThreadFilter.java | 24 ------------ .../xpack/core/test/XPackIntegTestCase.java | 14 ------- .../core/test/XPackSingleNodeTestCase.java | 14 ------- .../xpack/core/test/XPackTestCase.java | 14 ------- .../xpack/ml/support/BaseMlIntegTestCase.java | 15 ++++---- .../plugin-metadata/plugin-security.policy | 2 - .../test/SecurityIntegTestCase.java | 5 +-- .../test/SecuritySingleNodeTestCase.java | 4 +- ...ecurityNetty4HttpServerTransportTests.java | 4 +- .../SecurityNetty4ServerTransportTests.java | 3 +- .../watcher/WatcherPluginDisableTests.java | 3 +- .../AbstractWatcherIntegrationTestCase.java | 4 +- .../xpack/security/audit/IndexAuditIT.java | 5 +-- .../MlNativeAutodetectIntegTestCase.java | 4 -- x-pack/qa/security-client-tests/build.gradle | 1 - .../qa/SecurityTransportClientIT.java | 10 ++--- .../build.gradle | 1 - .../example/realm/CustomRealmIT.java | 5 +-- .../example/role/CustomRolesProviderIT.java | 3 +- x-pack/qa/security-migrate-tests/build.gradle | 1 - .../xpack/security/MigrateToolTestCase.java | 4 -- x-pack/qa/smoke-test-plugins-ssl/build.gradle | 1 - .../SmokeTestMonitoringWithSecurityIT.java | 3 +- x-pack/qa/transport-client-tests/build.gradle | 1 - .../ml/client/ESXPackSmokeClientTestCase.java | 3 -- 69 files changed, 74 insertions(+), 323 deletions(-) delete mode 100644 modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.java create mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 create mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 delete mode 100644 modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 rename modules/transport-netty4/src/test/java/org/elasticsearch/{test/Netty4IntegTestCase.java => ESNetty4IntegTestCase.java} (90%) delete mode 100644 modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java delete mode 100644 modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java delete mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java delete mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java delete mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java delete mode 100644 x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java index 75da686c1af73..31077c405d8e1 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteWithAuthTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.lucene.util.SetOnce; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.ElasticsearchStatusException; @@ -42,7 +41,6 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; -import org.elasticsearch.index.reindex.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestStatus; @@ -66,7 +64,6 @@ import static org.elasticsearch.index.reindex.ReindexTestCase.matcher; import static org.hamcrest.Matchers.containsString; -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class ReindexFromRemoteWithAuthTests extends ESSingleNodeTestCase { private TransportAddress address; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java index 5562f8fd4a6b6..bd9642c2ed2e6 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/RetryTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.reindex; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.action.ActionFuture; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; @@ -34,7 +33,6 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.reindex.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.threadpool.ThreadPool; @@ -58,7 +56,6 @@ * Integration test for retry behavior. Useful because retrying relies on the way that the * rest of Elasticsearch throws exceptions and unit tests won't verify that. */ -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class RetryTests extends ESIntegTestCase { private static final int DOC_COUNT = 20; diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.java deleted file mode 100644 index 948f1ec7fd6f6..0000000000000 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/test/ObjectCleanerThreadThreadFilter.java +++ /dev/null @@ -1,32 +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.index.reindex.test; - -import com.carrotsearch.randomizedtesting.ThreadFilter; - -public class ObjectCleanerThreadThreadFilter implements ThreadFilter { - - @Override - public boolean reject(final Thread t) { - // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated - return "ObjectCleanerThread".equals(t.getName()); - } - -} diff --git a/modules/transport-netty4/build.gradle b/modules/transport-netty4/build.gradle index ed905a530c48e..5d4bcd7c10a84 100644 --- a/modules/transport-netty4/build.gradle +++ b/modules/transport-netty4/build.gradle @@ -34,13 +34,13 @@ compileTestJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-tr dependencies { // network stack - compile "io.netty:netty-buffer:4.1.25.Final" - compile "io.netty:netty-codec:4.1.25.Final" - compile "io.netty:netty-codec-http:4.1.25.Final" - compile "io.netty:netty-common:4.1.25.Final" - compile "io.netty:netty-handler:4.1.25.Final" - compile "io.netty:netty-resolver:4.1.25.Final" - compile "io.netty:netty-transport:4.1.25.Final" + compile "io.netty:netty-buffer:4.1.16.Final" + compile "io.netty:netty-codec:4.1.16.Final" + compile "io.netty:netty-codec-http:4.1.16.Final" + compile "io.netty:netty-common:4.1.16.Final" + compile "io.netty:netty-handler:4.1.16.Final" + compile "io.netty:netty-resolver:4.1.16.Final" + compile "io.netty:netty-transport:4.1.16.Final" } dependencyLicenses { @@ -161,6 +161,6 @@ thirdPartyAudit.excludes = [ 'org.conscrypt.AllocatedBuffer', 'org.conscrypt.BufferAllocator', - 'org.conscrypt.Conscrypt', + 'org.conscrypt.Conscrypt$Engines', 'org.conscrypt.HandshakeListener' ] diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 new file mode 100644 index 0000000000000..c546222971985 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-buffer-4.1.16.Final.jar.sha1 @@ -0,0 +1 @@ +63b5fa95c74785e16f2c30ce268bc222e35c8cb5 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 deleted file mode 100644 index 3ca0cbb45ec31..0000000000000 --- a/modules/transport-netty4/licenses/netty-buffer-4.1.25.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -f366d0cc87b158ca064d27507127e3cc4eb2f089 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 new file mode 100644 index 0000000000000..1e6c241ea0b17 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-4.1.16.Final.jar.sha1 @@ -0,0 +1 @@ +d84a1f21768b7309c2954521cf5a1f46c2309eb1 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 deleted file mode 100644 index 5e2bc85c548dd..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-4.1.25.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -3e465c75bead40d06b5b9c0612b37cf77c548887 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 new file mode 100644 index 0000000000000..71c33af1c5fc2 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-codec-http-4.1.16.Final.jar.sha1 @@ -0,0 +1 @@ +d64312378b438dfdad84267c599a053327c6f02a \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 deleted file mode 100644 index 58cb7fd987949..0000000000000 --- a/modules/transport-netty4/licenses/netty-codec-http-4.1.25.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -70888d3f2a829541378f68503ddd52c3193df35a \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 new file mode 100644 index 0000000000000..3edf5fcea59b3 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-common-4.1.16.Final.jar.sha1 @@ -0,0 +1 @@ +177a6b30cca92f6f5f9873c9befd681377a4c328 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 deleted file mode 100644 index 62f85f8965513..0000000000000 --- a/modules/transport-netty4/licenses/netty-common-4.1.25.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -e17d5c05c101fe14536ce3fb34b36c54e04791f6 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 new file mode 100644 index 0000000000000..cba27387268d1 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-handler-4.1.16.Final.jar.sha1 @@ -0,0 +1 @@ +fec0e63e7dd7f4eeef7ea8dc47a1ff32dfc7ebc2 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 deleted file mode 100644 index 5391f625a4df0..0000000000000 --- a/modules/transport-netty4/licenses/netty-handler-4.1.25.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -ecdfb8fe93a8b75db3ea8746d3437eed845c24bd \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 new file mode 100644 index 0000000000000..3571d2ecfdc48 --- /dev/null +++ b/modules/transport-netty4/licenses/netty-resolver-4.1.16.Final.jar.sha1 @@ -0,0 +1 @@ +f6eb553b53fb3a90a8ac1170697093fed82eae28 \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 deleted file mode 100644 index 8225fb799e3ff..0000000000000 --- a/modules/transport-netty4/licenses/netty-resolver-4.1.25.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -dc0965d00746b782b33f419b005cbc130973030d \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 new file mode 100644 index 0000000000000..e502d4c77084c --- /dev/null +++ b/modules/transport-netty4/licenses/netty-transport-4.1.16.Final.jar.sha1 @@ -0,0 +1 @@ +3c8ee2c4d4a1cbb947a5c184c7aeb2204260958b \ No newline at end of file diff --git a/modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 b/modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 deleted file mode 100644 index 1049ea4b98bc6..0000000000000 --- a/modules/transport-netty4/licenses/netty-transport-4.1.25.Final.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -19a6f1f649894b6705aa9d8cbcced188dff133b0 \ No newline at end of file diff --git a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy index 3775931efb150..32b2dc9bd1540 100644 --- a/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy +++ b/modules/transport-netty4/src/main/plugin-metadata/plugin-security.policy @@ -21,8 +21,6 @@ grant codeBase "${codebase.netty-common}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; - permission java.lang.RuntimePermission "setContextClassLoader"; - // netty makes and accepts socket connections permission java.net.SocketPermission "*", "accept,connect"; }; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4IntegTestCase.java b/modules/transport-netty4/src/test/java/org/elasticsearch/ESNetty4IntegTestCase.java similarity index 90% rename from modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4IntegTestCase.java rename to modules/transport-netty4/src/test/java/org/elasticsearch/ESNetty4IntegTestCase.java index c21b863d196b7..b38cda76c6980 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4IntegTestCase.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/ESNetty4IntegTestCase.java @@ -16,21 +16,19 @@ * specific language governing permissions and limitations * under the License. */ +package org.elasticsearch; -package org.elasticsearch.test; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.transport.netty4.Netty4Transport; import java.util.Arrays; import java.util.Collection; -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) -public abstract class Netty4IntegTestCase extends ESIntegTestCase { +public abstract class ESNetty4IntegTestCase extends ESIntegTestCase { @Override protected boolean ignoreExternalCluster() { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java index 8baf818975ed8..094f339059876 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4BadRequestTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; -import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; @@ -34,6 +33,7 @@ import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.junit.After; @@ -48,7 +48,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; -public class Netty4BadRequestTests extends Netty4TestCase { +public class Netty4BadRequestTests extends ESTestCase { private NetworkService networkService; private MockBigArrays bigArrays; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java index b903b25c78ea3..918e98fd2e7c0 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpChannelTests.java @@ -41,7 +41,6 @@ import io.netty.handler.codec.http.HttpVersion; import io.netty.util.Attribute; import io.netty.util.AttributeKey; -import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.ReleasablePagedBytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; @@ -65,6 +64,7 @@ import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.netty4.Netty4Utils; @@ -90,7 +90,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; -public class Netty4HttpChannelTests extends Netty4TestCase { +public class Netty4HttpChannelTests extends ESTestCase { private NetworkService networkService; private ThreadPool threadPool; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java index 550b14018236e..d99820bb86465 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpRequestSizeLimitIT.java @@ -20,7 +20,7 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; -import org.elasticsearch.test.Netty4IntegTestCase; +import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; @@ -46,7 +46,7 @@ * a single node "cluster". We also force test infrastructure to use the node client instead of the transport client for the same reason. */ @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numClientNodes = 0, numDataNodes = 1, transportClientRatio = 0) -public class Netty4HttpRequestSizeLimitIT extends Netty4IntegTestCase { +public class Netty4HttpRequestSizeLimitIT extends ESNetty4IntegTestCase { private static final ByteSizeValue LIMIT = new ByteSizeValue(2, ByteSizeUnit.KB); diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java index d7b72c38f7d06..1c7475379bb87 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java @@ -32,6 +32,7 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; +import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -44,7 +45,7 @@ import org.elasticsearch.http.NullDispatcher; import org.elasticsearch.http.netty4.pipelining.HttpPipelinedRequest; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; -import org.elasticsearch.test.Netty4TestCase; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.junit.After; @@ -68,7 +69,7 @@ /** * This test just tests, if he pipelining works in general with out any connection the Elasticsearch handler */ -public class Netty4HttpServerPipeliningTests extends Netty4TestCase { +public class Netty4HttpServerPipeliningTests extends ESTestCase { private NetworkService networkService; private ThreadPool threadPool; private MockBigArrays bigArrays; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java index 25bc94352a4ee..96b436ce7de43 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerTransportTests.java @@ -38,7 +38,6 @@ import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; -import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; @@ -60,6 +59,7 @@ import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.junit.After; @@ -94,7 +94,7 @@ /** * Tests for the {@link Netty4HttpServerTransport} class. */ -public class Netty4HttpServerTransportTests extends Netty4TestCase { +public class Netty4HttpServerTransportTests extends ESTestCase { private NetworkService networkService; private ThreadPool threadPool; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java index 17fafbde3cbbd..9f117d4ee21fc 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningDisabledIT.java @@ -19,13 +19,13 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; +import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; -import org.elasticsearch.test.Netty4IntegTestCase; import java.util.ArrayList; import java.util.Collection; @@ -36,7 +36,7 @@ import static org.hamcrest.Matchers.hasSize; @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numDataNodes = 1) -public class Netty4PipeliningDisabledIT extends Netty4IntegTestCase { +public class Netty4PipeliningDisabledIT extends ESNetty4IntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java index 678bcbc1d5089..cc3f22be453fb 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4PipeliningEnabledIT.java @@ -20,13 +20,13 @@ package org.elasticsearch.http.netty4; import io.netty.handler.codec.http.FullHttpResponse; +import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.http.HttpServerTransport; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; -import org.elasticsearch.test.Netty4IntegTestCase; import java.util.Collection; import java.util.Locale; @@ -35,7 +35,7 @@ import static org.hamcrest.Matchers.is; @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numDataNodes = 1) -public class Netty4PipeliningEnabledIT extends Netty4IntegTestCase { +public class Netty4PipeliningEnabledIT extends ESNetty4IntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java index 35f12bef6b6ef..ffb6c8fb3569d 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/pipelining/Netty4HttpPipeliningHandlerTests.java @@ -37,7 +37,7 @@ import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.QueryStringDecoder; import org.elasticsearch.common.Randomness; -import org.elasticsearch.test.Netty4TestCase; +import org.elasticsearch.test.ESTestCase; import org.junit.After; import java.nio.channels.ClosedChannelException; @@ -60,7 +60,7 @@ import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import static org.hamcrest.core.Is.is; -public class Netty4HttpPipeliningHandlerTests extends Netty4TestCase { +public class Netty4HttpPipeliningHandlerTests extends ESTestCase { private final ExecutorService executorService = Executors.newFixedThreadPool(randomIntBetween(4, 8)); private final Map waitingRequests = new ConcurrentHashMap<>(); diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java b/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java deleted file mode 100644 index df931d61992ef..0000000000000 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/test/Netty4TestCase.java +++ /dev/null @@ -1,26 +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.test; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; - -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) -public abstract class Netty4TestCase extends ESTestCase { -} diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java b/modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java deleted file mode 100644 index e47c536665d13..0000000000000 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/test/ObjectCleanerThreadThreadFilter.java +++ /dev/null @@ -1,38 +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.test; - -import com.carrotsearch.randomizedtesting.ThreadFilter; - -/** - * The Netty object cleaner thread is not closeable and it does not terminate in a timely manner. This means that thread leak control in - * tests will fail test suites when the object cleaner thread has not terminated. Since there is not a reliable way to terminate this thread - * we instead filter it out of thread leak control. - */ -public class ObjectCleanerThreadThreadFilter implements ThreadFilter { - - @Override - public boolean reject(final Thread t) { - // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated - return "ObjectCleanerThread".equals(t.getName()); - } - -} - diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java index 618d3ffe8f96a..4a41aaec952a0 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ByteBufBytesReferenceTests.java @@ -18,18 +18,15 @@ */ package org.elasticsearch.transport.netty4; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.common.bytes.AbstractBytesReferenceTestCase; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.ReleasableBytesStreamOutput; import java.io.IOException; -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class ByteBufBytesReferenceTests extends AbstractBytesReferenceTestCase { @Override diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java index 8ef8b28dc1497..acd71749e2333 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/ESLoggingHandlerIT.java @@ -20,7 +20,7 @@ package org.elasticsearch.transport.netty4; import org.apache.logging.log4j.Level; -import org.elasticsearch.test.Netty4IntegTestCase; +import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.action.admin.cluster.node.hotthreads.NodesHotThreadsRequest; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.test.ESIntegTestCase; @@ -29,7 +29,7 @@ @ESIntegTestCase.ClusterScope(numDataNodes = 2) @TestLogging(value = "org.elasticsearch.transport.netty4.ESLoggingHandler:trace") -public class ESLoggingHandlerIT extends Netty4IntegTestCase { +public class ESLoggingHandlerIT extends ESNetty4IntegTestCase { private MockLogAppender appender; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java index b4b33ba96211c..b967a7ea41069 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4ScheduledPingTests.java @@ -18,7 +18,6 @@ */ package org.elasticsearch.transport.netty4; -import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.lease.Releasables; @@ -27,6 +26,7 @@ import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.transport.MockTransportService; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; @@ -47,7 +47,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; -public class Netty4ScheduledPingTests extends Netty4TestCase { +public class Netty4ScheduledPingTests extends ESTestCase { public void testScheduledPing() throws Exception { ThreadPool threadPool = new TestThreadPool(getClass().getName()); diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java index 0819548907296..7343da6c3b11a 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4SizeHeaderFrameDecoderTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.transport.netty4; -import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Settings; @@ -29,6 +28,7 @@ import org.elasticsearch.common.util.MockPageCacheRecycler; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.mocksocket.MockSocket; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TcpTransport; import org.junit.After; @@ -47,7 +47,7 @@ * This test checks, if a HTTP look-alike request (starting with a HTTP method and a space) * actually returns text response instead of just dropping the connection */ -public class Netty4SizeHeaderFrameDecoderTests extends Netty4TestCase { +public class Netty4SizeHeaderFrameDecoderTests extends ESTestCase { private final Settings settings = Settings.builder() .put("node.name", "NettySizeHeaderFrameDecoderTests") diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java index a426c11db8245..b81c8efcb47ee 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportIT.java @@ -18,7 +18,7 @@ */ package org.elasticsearch.transport.netty4; -import org.elasticsearch.test.Netty4IntegTestCase; +import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -54,7 +54,7 @@ import static org.hamcrest.Matchers.is; @ClusterScope(scope = Scope.TEST, supportsDedicatedMasters = false, numDataNodes = 1) -public class Netty4TransportIT extends Netty4IntegTestCase { +public class Netty4TransportIT extends ESNetty4IntegTestCase { // static so we can use it in anonymous classes private static String channelProfileName = null; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java index ddef8abcf0181..52ad32efb5645 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportMultiPortIntegrationIT.java @@ -18,7 +18,7 @@ */ package org.elasticsearch.transport.netty4; -import org.elasticsearch.test.Netty4IntegTestCase; +import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; @@ -48,7 +48,7 @@ import static org.hamcrest.Matchers.lessThanOrEqualTo; @ClusterScope(scope = Scope.SUITE, supportsDedicatedMasters = false, numDataNodes = 1, numClientNodes = 0) -public class Netty4TransportMultiPortIntegrationIT extends Netty4IntegTestCase { +public class Netty4TransportMultiPortIntegrationIT extends ESNetty4IntegTestCase { private static int randomPort = -1; private static String randomPortRange; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java index dee104a73f7d7..922031d3c3dea 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/Netty4TransportPublishAddressIT.java @@ -19,7 +19,7 @@ package org.elasticsearch.transport.netty4; -import org.elasticsearch.test.Netty4IntegTestCase; +import org.elasticsearch.ESNetty4IntegTestCase; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.common.network.NetworkModule; @@ -41,7 +41,7 @@ * different ports on ipv4 and ipv6. */ @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0) -public class Netty4TransportPublishAddressIT extends Netty4IntegTestCase { +public class Netty4TransportPublishAddressIT extends ESNetty4IntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { return Settings.builder() diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java index 05d6d55ac42da..a49df3caaba4e 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/NettyTransportMultiPortTests.java @@ -18,7 +18,6 @@ */ package org.elasticsearch.transport.netty4; -import org.elasticsearch.test.Netty4TestCase; import org.elasticsearch.common.component.Lifecycle; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.network.NetworkService; @@ -28,6 +27,7 @@ import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.MockPageCacheRecycler; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; +import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.TestThreadPool; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TcpTransport; @@ -37,7 +37,7 @@ import static org.hamcrest.Matchers.is; -public class NettyTransportMultiPortTests extends Netty4TestCase { +public class NettyTransportMultiPortTests extends ESTestCase { private String host; diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java index 278b00965c9a5..efa296b6278af 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/transport/netty4/SimpleNetty4TransportTests.java @@ -19,8 +19,6 @@ package org.elasticsearch.transport.netty4; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; -import org.elasticsearch.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.Version; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; @@ -51,7 +49,6 @@ import static java.util.Collections.emptySet; import static org.hamcrest.Matchers.containsString; -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class SimpleNetty4TransportTests extends AbstractSimpleTransportTestCase { public static MockTransportService nettyFromThreadPool(Settings settings, ThreadPool threadPool, final Version version, diff --git a/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java index c34a2f44b73af..908e8e1c71114 100644 --- a/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java +++ b/qa/smoke-test-client/src/test/java/org/elasticsearch/smoketest/ESSmokeClientTestCase.java @@ -19,8 +19,6 @@ package org.elasticsearch.smoketest; -import com.carrotsearch.randomizedtesting.ThreadFilter; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -64,24 +62,8 @@ * then run JUnit. If you changed the default port, set "-Dtests.cluster=localhost:PORT" when running your test. */ @LuceneTestCase.SuppressSysoutChecks(bugUrl = "we log a lot on purpose") -@ThreadLeakFilters(filters = {ESSmokeClientTestCase.ObjectCleanerThreadThreadFilter.class}) public abstract class ESSmokeClientTestCase extends LuceneTestCase { - /** - * The Netty object cleaner thread is not closeable and it does not terminate in a timely manner. This means that thread leak control in - * tests will fail test suites when the object cleaner thread has not terminated. Since there is not a reliable way to terminate this - * thread we instead filter it out of thread leak control. - */ - public static class ObjectCleanerThreadThreadFilter implements ThreadFilter { - - @Override - public boolean reject(final Thread t) { - // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated - return "ObjectCleanerThread".equals(t.getName()); - } - - } - /** * Key used to eventually switch to using an external cluster and provide its transport addresses */ diff --git a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java index 8899284573bba..bb13d486a9adc 100644 --- a/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java +++ b/qa/smoke-test-http/src/test/java/org/elasticsearch/http/HttpSmokeTestCase.java @@ -18,13 +18,10 @@ */ package org.elasticsearch.http; -import com.carrotsearch.randomizedtesting.ThreadFilter; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.MockTcpTransportPlugin; import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.transport.nio.NioTransportPlugin; @@ -33,19 +30,8 @@ import java.util.Arrays; import java.util.Collection; -@ThreadLeakFilters(filters = {HttpSmokeTestCase.ObjectCleanerThreadThreadFilter.class}) public abstract class HttpSmokeTestCase extends ESIntegTestCase { - public static class ObjectCleanerThreadThreadFilter implements ThreadFilter { - - @Override - public boolean reject(final Thread t) { - // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated - return "ObjectCleanerThread".equals(t.getName()); - } - - } - private static String nodeTransportTypeKey; private static String nodeHttpTypeKey; private static String clientTypeKey; diff --git a/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy b/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy index 1f1bd66005693..0cd7a32bcc47b 100644 --- a/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy +++ b/x-pack/plugin/core/src/main/plugin-metadata/plugin-security.policy @@ -15,8 +15,6 @@ grant { grant codeBase "${codebase.netty-common}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; - - permission java.lang.RuntimePermission "setContextClassLoader"; }; grant codeBase "${codebase.netty-transport}" { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java index 57ba7a0c4674d..e9c9ba95bfd38 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/AbstractLicensesIntegrationTestCase.java @@ -3,7 +3,6 @@ * 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.license; import org.elasticsearch.analysis.common.CommonAnalysisPlugin; @@ -14,16 +13,16 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.XPackSettings; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.CountDownLatch; -public abstract class AbstractLicensesIntegrationTestCase extends XPackIntegTestCase { +public abstract class AbstractLicensesIntegrationTestCase extends ESIntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java index 3ca0ae7087875..55b14a4d79280 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/license/StartBasicLicenseTests.java @@ -3,7 +3,6 @@ * 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.license; import org.elasticsearch.client.Response; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java deleted file mode 100644 index e911920953ac2..0000000000000 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/ObjectCleanerThreadThreadFilter.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.test; - -import com.carrotsearch.randomizedtesting.ThreadFilter; - -/** - * The Netty object cleaner thread is not closeable and it does not terminate in a timely manner. This means that thread leak control in - * tests will fail test suites when the object cleaner thread has not terminated. Since there is not a reliable way to terminate this - * thread we instead filter it out of thread leak control. - */ -public class ObjectCleanerThreadThreadFilter implements ThreadFilter { - - @Override - public boolean reject(final Thread t) { - // TODO: replace with constant from Netty when https://github.com/netty/netty/pull/8014 is integrated - return "ObjectCleanerThread".equals(t.getName()); - } - -} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java deleted file mode 100644 index 87cf2d87f02a4..0000000000000 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackIntegTestCase.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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.test; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; -import org.elasticsearch.test.ESIntegTestCase; - -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) -public abstract class XPackIntegTestCase extends ESIntegTestCase { -} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java deleted file mode 100644 index a3cbd875503f5..0000000000000 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackSingleNodeTestCase.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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.test; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; -import org.elasticsearch.test.ESSingleNodeTestCase; - -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) -public abstract class XPackSingleNodeTestCase extends ESSingleNodeTestCase { -} diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java deleted file mode 100644 index ab7948eeae726..0000000000000 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/test/XPackTestCase.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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.test; - -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; -import org.elasticsearch.test.ESTestCase; - -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) -public abstract class XPackTestCase extends ESTestCase { -} diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java index e8a5193b04003..40b59e4dbec65 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/support/BaseMlIntegTestCase.java @@ -26,17 +26,19 @@ import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.discovery.TestZenDiscovery; import org.elasticsearch.xpack.core.XPackSettings; +import org.elasticsearch.xpack.core.ml.action.GetDatafeedsAction; +import org.elasticsearch.xpack.core.ml.action.GetJobsAction; +import org.elasticsearch.xpack.core.ml.action.util.QueryPage; +import org.elasticsearch.xpack.core.ml.client.MachineLearningClient; +import org.elasticsearch.xpack.ml.LocalStateMachineLearning; +import org.elasticsearch.xpack.ml.MachineLearning; import org.elasticsearch.xpack.core.ml.MachineLearningField; import org.elasticsearch.xpack.core.ml.action.CloseJobAction; import org.elasticsearch.xpack.core.ml.action.DeleteDatafeedAction; import org.elasticsearch.xpack.core.ml.action.DeleteJobAction; -import org.elasticsearch.xpack.core.ml.action.GetDatafeedsAction; import org.elasticsearch.xpack.core.ml.action.GetDatafeedsStatsAction; -import org.elasticsearch.xpack.core.ml.action.GetJobsAction; import org.elasticsearch.xpack.core.ml.action.GetJobsStatsAction; import org.elasticsearch.xpack.core.ml.action.StopDatafeedAction; -import org.elasticsearch.xpack.core.ml.action.util.QueryPage; -import org.elasticsearch.xpack.core.ml.client.MachineLearningClient; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig; import org.elasticsearch.xpack.core.ml.datafeed.DatafeedState; import org.elasticsearch.xpack.core.ml.job.config.AnalysisConfig; @@ -46,9 +48,6 @@ import org.elasticsearch.xpack.core.ml.job.config.Job; import org.elasticsearch.xpack.core.ml.job.config.JobState; import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.DataCounts; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; -import org.elasticsearch.xpack.ml.LocalStateMachineLearning; -import org.elasticsearch.xpack.ml.MachineLearning; import org.junit.After; import org.junit.Before; @@ -69,7 +68,7 @@ */ @ESIntegTestCase.ClusterScope(scope = ESIntegTestCase.Scope.TEST, numDataNodes = 0, numClientNodes = 0, transportClientRatio = 0, supportsDedicatedMasters = false) -public abstract class BaseMlIntegTestCase extends XPackIntegTestCase { +public abstract class BaseMlIntegTestCase extends ESIntegTestCase { @Override protected boolean ignoreExternalCluster() { diff --git a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy index f56affec02be0..857c2f6e472d5 100644 --- a/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy +++ b/x-pack/plugin/security/src/main/plugin-metadata/plugin-security.policy @@ -23,8 +23,6 @@ grant codeBase "${codebase.xmlsec-2.0.8.jar}" { grant codeBase "${codebase.netty-common}" { // for reading the system-wide configuration for the backlog of established sockets permission java.io.FilePermission "/proc/sys/net/core/somaxconn", "read"; - - permission java.lang.RuntimePermission "setContextClassLoader"; }; grant codeBase "${codebase.netty-transport}" { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java index bb5733c2e7423..aac16e7c7ab78 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecurityIntegTestCase.java @@ -3,7 +3,6 @@ * 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.test; import io.netty.util.ThreadDeathWatcher; @@ -41,9 +40,9 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.client.SecurityClient; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.elasticsearch.xpack.security.LocalStateSecurity; import org.elasticsearch.xpack.security.Security; + import org.elasticsearch.xpack.security.support.SecurityIndexManager; import org.junit.AfterClass; import org.junit.Before; @@ -76,7 +75,7 @@ * * @see SecuritySettingsSource */ -public abstract class SecurityIntegTestCase extends XPackIntegTestCase { +public abstract class SecurityIntegTestCase extends ESIntegTestCase { private static SecuritySettingsSource SECURITY_DEFAULT_SETTINGS; protected static SecureString BOOTSTRAP_PASSWORD = null; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java index 1c6f8e847d39c..cda627806e7b5 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/SecuritySingleNodeTestCase.java @@ -3,7 +3,6 @@ * 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.test; import io.netty.util.ThreadDeathWatcher; @@ -23,7 +22,6 @@ import org.elasticsearch.license.LicenseService; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.plugins.PluginInfo; -import org.elasticsearch.xpack.core.test.XPackSingleNodeTestCase; import org.elasticsearch.xpack.security.LocalStateSecurity; import org.junit.AfterClass; import org.junit.Before; @@ -51,7 +49,7 @@ * {@link SecurityIntegTestCase} due to simplicity and improved speed from not needing to start * multiple nodes and wait for the cluster to form. */ -public abstract class SecuritySingleNodeTestCase extends XPackSingleNodeTestCase { +public abstract class SecuritySingleNodeTestCase extends ESSingleNodeTestCase { private static SecuritySettingsSource SECURITY_DEFAULT_SETTINGS = null; private static CustomSecuritySettingsSource customSecuritySettingsSource = null; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java index d964f4f997bf1..3ef298f3f232d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4HttpServerTransportTests.java @@ -22,8 +22,6 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.ssl.SSLClientAuth; import org.elasticsearch.xpack.core.ssl.SSLService; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; -import org.elasticsearch.xpack.core.test.XPackTestCase; import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.junit.Before; @@ -40,7 +38,7 @@ import static org.hamcrest.Matchers.not; import static org.mockito.Mockito.mock; -public class SecurityNetty4HttpServerTransportTests extends XPackTestCase { +public class SecurityNetty4HttpServerTransportTests extends ESTestCase { private SSLService sslService; private Environment env; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java index 90fc0d5cbad31..3d9227319a870 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/netty4/SecurityNetty4ServerTransportTests.java @@ -21,7 +21,6 @@ import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport; import org.elasticsearch.xpack.core.ssl.SSLClientAuth; import org.elasticsearch.xpack.core.ssl.SSLService; -import org.elasticsearch.xpack.core.test.XPackTestCase; import org.junit.Before; import javax.net.ssl.SSLEngine; @@ -34,7 +33,7 @@ import static org.hamcrest.Matchers.notNullValue; import static org.mockito.Mockito.mock; -public class SecurityNetty4ServerTransportTests extends XPackTestCase { +public class SecurityNetty4ServerTransportTests extends ESTestCase { private Environment env; private SSLService sslService; diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java index d71b0575715fa..3227ba4c4a1e0 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/WatcherPluginDisableTests.java @@ -19,7 +19,6 @@ import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.XPackSettings; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.elasticsearch.xpack.watcher.execution.InternalWatchExecutor; import org.elasticsearch.xpack.watcher.test.LocalStateWatcher; @@ -32,7 +31,7 @@ import static org.hamcrest.Matchers.not; @ClusterScope(scope = SUITE, numClientNodes = 0, transportClientRatio = 0, maxNumDataNodes = 3) -public class WatcherPluginDisableTests extends XPackIntegTestCase { +public class WatcherPluginDisableTests extends ESIntegTestCase { @Override protected Settings nodeSettings(int nodeOrdinal) { return Settings.builder() diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java index 4cf5e3faa232d..4eb4bd1aa2c6e 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/AbstractWatcherIntegrationTestCase.java @@ -31,6 +31,7 @@ import org.elasticsearch.script.Script; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.InternalTestCluster; import org.elasticsearch.test.disruption.ServiceDisruptionScheme; @@ -39,7 +40,6 @@ import org.elasticsearch.xpack.core.XPackClient; import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.SecurityField; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.elasticsearch.xpack.core.watcher.WatcherState; import org.elasticsearch.xpack.core.watcher.client.WatcherClient; import org.elasticsearch.xpack.core.watcher.execution.ExecutionState; @@ -94,7 +94,7 @@ import static org.hamcrest.core.IsNot.not; @ClusterScope(scope = SUITE, numClientNodes = 0, transportClientRatio = 0, maxNumDataNodes = 3) -public abstract class AbstractWatcherIntegrationTestCase extends XPackIntegTestCase { +public abstract class AbstractWatcherIntegrationTestCase extends ESIntegTestCase { public static final String WATCHER_LANG = Script.DEFAULT_SCRIPT_LANG; diff --git a/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java b/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java index 26a98691f1323..3467316c24f6c 100644 --- a/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java +++ b/x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java @@ -6,7 +6,6 @@ package org.elasticsearch.xpack.security.audit; import com.carrotsearch.hppc.cursors.ObjectCursor; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.http.message.BasicHeader; import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateResponse; import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; @@ -25,9 +24,8 @@ import org.elasticsearch.test.TestCluster; import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.security.SecurityField; -import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; -import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; import org.elasticsearch.xpack.security.audit.index.IndexAuditTrail; +import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import java.io.IOException; import java.net.InetSocketAddress; @@ -40,7 +38,6 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public class IndexAuditIT extends ESIntegTestCase { private static final String USER = "test_user"; private static final String PASS = "x-pack-test-password"; diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java index a744f3ebb6380..f70efc72506d3 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/MlNativeAutodetectIntegTestCase.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.ml.integration; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.Client; @@ -35,7 +34,6 @@ import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.tasks.Task; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.SecuritySettingsSourceField; import org.elasticsearch.transport.Netty4Plugin; import org.elasticsearch.xpack.core.LocalStateCompositeXPackPlugin; @@ -86,7 +84,6 @@ import org.elasticsearch.xpack.core.ml.job.results.Result; import org.elasticsearch.xpack.core.security.SecurityField; import org.elasticsearch.xpack.core.security.authc.TokenMetaData; -import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; import java.io.IOException; import java.net.URISyntaxException; @@ -110,7 +107,6 @@ /** * Base class of ML integration tests that use a native autodetect process */ -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) abstract class MlNativeAutodetectIntegTestCase extends ESIntegTestCase { private List jobs = new ArrayList<>(); diff --git a/x-pack/qa/security-client-tests/build.gradle b/x-pack/qa/security-client-tests/build.gradle index 9706d554e95c9..4e517f4d3633e 100644 --- a/x-pack/qa/security-client-tests/build.gradle +++ b/x-pack/qa/security-client-tests/build.gradle @@ -3,7 +3,6 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') - testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java b/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java index 5342c2bd78095..519f365d515a0 100644 --- a/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java +++ b/x-pack/qa/security-client-tests/src/test/java/org/elasticsearch/xpack/security/qa/SecurityTransportClientIT.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.security.qa; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; @@ -16,11 +15,10 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.plugins.Plugin; -import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; +import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.xpack.core.XPackClientPlugin; +import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; import org.elasticsearch.xpack.core.security.SecurityField; -import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Collection; import java.util.Collections; @@ -34,9 +32,7 @@ /** * Integration tests that test a transport client with security being loaded that connect to an external cluster */ -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) -public class SecurityTransportClientIT extends XPackIntegTestCase { - +public class SecurityTransportClientIT extends ESIntegTestCase { static final String ADMIN_USER_PW = "test_user:x-pack-test-password"; static final String TRANSPORT_USER_PW = "transport:x-pack-test-password"; diff --git a/x-pack/qa/security-example-spi-extension/build.gradle b/x-pack/qa/security-example-spi-extension/build.gradle index 94a7bccca8034..b2fac075cb315 100644 --- a/x-pack/qa/security-example-spi-extension/build.gradle +++ b/x-pack/qa/security-example-spi-extension/build.gradle @@ -9,7 +9,6 @@ esplugin { dependencies { compileOnly project(path: xpackModule('core'), configuration: 'runtime') - testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java index 8f8f45f0448b6..65ec595a0d409 100644 --- a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java +++ b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/realm/CustomRealmIT.java @@ -3,7 +3,6 @@ * 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.example.realm; import org.apache.http.message.BasicHeader; @@ -20,9 +19,9 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.env.Environment; import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; import org.elasticsearch.xpack.core.XPackClientPlugin; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Collection; import java.util.Collections; @@ -33,7 +32,7 @@ /** * Integration test to test authentication with the custom realm */ -public class CustomRealmIT extends XPackIntegTestCase { +public class CustomRealmIT extends ESIntegTestCase { @Override protected Settings externalClusterClientSettings() { diff --git a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java index 85b34a9612f46..4e1fb72256086 100644 --- a/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java +++ b/x-pack/qa/security-example-spi-extension/src/test/java/org/elasticsearch/example/role/CustomRolesProviderIT.java @@ -18,7 +18,6 @@ import org.elasticsearch.xpack.core.XPackClientPlugin; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.client.SecurityClient; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import java.util.Collection; import java.util.Collections; @@ -32,7 +31,7 @@ /** * Integration test for custom roles providers. */ -public class CustomRolesProviderIT extends XPackIntegTestCase { +public class CustomRolesProviderIT extends ESIntegTestCase { private static final String TEST_USER = "test_user"; private static final String TEST_PWD = "change_me"; diff --git a/x-pack/qa/security-migrate-tests/build.gradle b/x-pack/qa/security-migrate-tests/build.gradle index de6f1c86b993f..7ccf6d2349b8b 100644 --- a/x-pack/qa/security-migrate-tests/build.gradle +++ b/x-pack/qa/security-migrate-tests/build.gradle @@ -3,7 +3,6 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') - testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackModule('security'), configuration: 'runtime') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java b/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java index e7f3a5ef48061..2987c1afc8daf 100644 --- a/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java +++ b/x-pack/qa/security-migrate-tests/src/test/java/org/elasticsearch/xpack/security/MigrateToolTestCase.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.security; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -14,10 +13,8 @@ import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; -import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; import org.elasticsearch.xpack.core.security.SecurityField; -import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -43,7 +40,6 @@ * then run JUnit. If you changed the default port, set "tests.cluster=localhost:PORT" when running * your test. */ -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) @LuceneTestCase.SuppressSysoutChecks(bugUrl = "we log a lot on purpose") public abstract class MigrateToolTestCase extends LuceneTestCase { diff --git a/x-pack/qa/smoke-test-plugins-ssl/build.gradle b/x-pack/qa/smoke-test-plugins-ssl/build.gradle index 7c1f7a8d0e558..595c562af3707 100644 --- a/x-pack/qa/smoke-test-plugins-ssl/build.gradle +++ b/x-pack/qa/smoke-test-plugins-ssl/build.gradle @@ -16,7 +16,6 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') - testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') } String outputDir = "${buildDir}/generated-resources/${project.name}" diff --git a/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java b/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java index 02c2faad2b085..f8d1dd5e2b717 100644 --- a/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java +++ b/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java @@ -17,7 +17,6 @@ import org.elasticsearch.xpack.core.action.XPackUsageResponse; import org.elasticsearch.xpack.core.monitoring.MonitoringFeatureSetUsage; import org.elasticsearch.xpack.core.security.SecurityField; -import org.elasticsearch.xpack.core.test.XPackIntegTestCase; import org.junit.After; import org.junit.Before; @@ -42,7 +41,7 @@ * then uses a transport client to check that the data have been correctly received and * indexed in the cluster. */ -public class SmokeTestMonitoringWithSecurityIT extends XPackIntegTestCase { +public class SmokeTestMonitoringWithSecurityIT extends ESIntegTestCase { private static final String USER = "test_user"; private static final String PASS = "x-pack-test-password"; private static final String MONITORING_PATTERN = ".monitoring-*"; diff --git a/x-pack/qa/transport-client-tests/build.gradle b/x-pack/qa/transport-client-tests/build.gradle index d179fee378c63..c864a9084cba8 100644 --- a/x-pack/qa/transport-client-tests/build.gradle +++ b/x-pack/qa/transport-client-tests/build.gradle @@ -3,7 +3,6 @@ apply plugin: 'elasticsearch.rest-test' dependencies { testCompile project(path: xpackModule('core'), configuration: 'runtime') - testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') testCompile project(path: xpackProject('transport-client').path, configuration: 'runtime') } diff --git a/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java b/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java index 4eb845ec9e90e..c77715431ec5e 100644 --- a/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java +++ b/x-pack/qa/transport-client-tests/src/test/java/org/elasticsearch/xpack/ml/client/ESXPackSmokeClientTestCase.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.xpack.ml.client; -import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; @@ -16,7 +15,6 @@ import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.client.PreBuiltXPackTransportClient; -import org.elasticsearch.xpack.core.test.ObjectCleanerThreadThreadFilter; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -48,7 +46,6 @@ * test. */ @LuceneTestCase.SuppressSysoutChecks(bugUrl = "we log a lot on purpose") -@ThreadLeakFilters(filters = {ObjectCleanerThreadThreadFilter.class}) public abstract class ESXPackSmokeClientTestCase extends LuceneTestCase { /** From 01ec669cb8347a62217dbe5848f40f3d0c671bdd Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 13 Jun 2018 07:25:19 +0200 Subject: [PATCH 36/71] Set analyzer version in PreBuiltAnalyzerProviderFactory (#31202) instead of lamda that creates the analyzer --- .../analysis/common/CommonAnalysisPlugin.java | 127 ++++-------------- .../PreBuiltAnalyzerProviderFactory.java | 17 ++- 2 files changed, 37 insertions(+), 107 deletions(-) diff --git a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java index 24dce7abcf370..722d75a9293f7 100644 --- a/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java +++ b/modules/analysis-common/src/main/java/org/elasticsearch/analysis/common/CommonAnalysisPlugin.java @@ -247,108 +247,31 @@ public Map> getTokenizers() { @Override public List getPreBuiltAnalyzerProviderFactories() { List analyzers = new ArrayList<>(); - analyzers.add(new PreBuiltAnalyzerProviderFactory("standard_html_strip", CachingStrategy.LUCENE, version -> { - Analyzer a = new StandardHtmlStripAnalyzer(CharArraySet.EMPTY_SET); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("pattern", CachingStrategy.ELASTICSEARCH, version -> { - Analyzer a = new PatternAnalyzer(Regex.compile("\\W+" /*PatternAnalyzer.NON_WORD_PATTERN*/, null), true, - CharArraySet.EMPTY_SET); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("snowball", CachingStrategy.LUCENE, version -> { - Analyzer a = new SnowballAnalyzer("English", StopAnalyzer.ENGLISH_STOP_WORDS_SET); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("arabic", CachingStrategy.LUCENE, version -> { - Analyzer a = new ArabicAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("armenian", CachingStrategy.LUCENE, version -> { - Analyzer a = new ArmenianAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("basque", CachingStrategy.LUCENE, version -> { - Analyzer a = new BasqueAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("bengali", CachingStrategy.LUCENE, version -> { - Analyzer a = new BengaliAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("brazilian", CachingStrategy.LUCENE, version -> { - Analyzer a = new BrazilianAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("bulgarian", CachingStrategy.LUCENE, version -> { - Analyzer a = new BulgarianAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("catalan", CachingStrategy.LUCENE, version -> { - Analyzer a = new CatalanAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("chinese", CachingStrategy.LUCENE, version -> { - // only for old indices, best effort - Analyzer a = new StandardAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("cjk", CachingStrategy.LUCENE, version -> { - Analyzer a = new CJKAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("czech", CachingStrategy.LUCENE, version -> { - Analyzer a = new CzechAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("danish", CachingStrategy.LUCENE, version -> { - Analyzer a = new DanishAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("dutch", CachingStrategy.LUCENE, version -> { - Analyzer a = new DutchAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("english", CachingStrategy.LUCENE, version -> { - Analyzer a = new EnglishAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("finnish", CachingStrategy.LUCENE, version -> { - Analyzer a = new FinnishAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("french", CachingStrategy.LUCENE, version -> { - Analyzer a = new FrenchAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("galician", CachingStrategy.LUCENE, version -> { - Analyzer a = new GalicianAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); - analyzers.add(new PreBuiltAnalyzerProviderFactory("german", CachingStrategy.LUCENE, version -> { - Analyzer a = new GermanAnalyzer(); - a.setVersion(version.luceneVersion); - return a; - })); + analyzers.add(new PreBuiltAnalyzerProviderFactory("standard_html_strip", CachingStrategy.LUCENE, + () -> new StandardHtmlStripAnalyzer(CharArraySet.EMPTY_SET))); + analyzers.add(new PreBuiltAnalyzerProviderFactory("pattern", CachingStrategy.ELASTICSEARCH, + () -> new PatternAnalyzer(Regex.compile("\\W+" /*PatternAnalyzer.NON_WORD_PATTERN*/, null), true, + CharArraySet.EMPTY_SET))); + analyzers.add(new PreBuiltAnalyzerProviderFactory("snowball", CachingStrategy.LUCENE, + () -> new SnowballAnalyzer("English", StopAnalyzer.ENGLISH_STOP_WORDS_SET))); + analyzers.add(new PreBuiltAnalyzerProviderFactory("arabic", CachingStrategy.LUCENE, ArabicAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("armenian", CachingStrategy.LUCENE, ArmenianAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("basque", CachingStrategy.LUCENE, BasqueAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("bengali", CachingStrategy.LUCENE, BengaliAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("brazilian", CachingStrategy.LUCENE, BrazilianAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("bulgarian", CachingStrategy.LUCENE, BulgarianAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("catalan", CachingStrategy.LUCENE, CatalanAnalyzer::new)); + // chinese analyzer: only for old indices, best effort + analyzers.add(new PreBuiltAnalyzerProviderFactory("chinese", CachingStrategy.LUCENE, StandardAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("cjk", CachingStrategy.LUCENE, CJKAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("czech", CachingStrategy.LUCENE, CzechAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("danish", CachingStrategy.LUCENE, DanishAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("dutch", CachingStrategy.LUCENE, DutchAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("english", CachingStrategy.LUCENE, EnglishAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("finnish", CachingStrategy.LUCENE, FinnishAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("french", CachingStrategy.LUCENE, FrenchAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("galician", CachingStrategy.LUCENE, GalicianAnalyzer::new)); + analyzers.add(new PreBuiltAnalyzerProviderFactory("german", CachingStrategy.LUCENE, GermanAnalyzer::new)); return analyzers; } diff --git a/server/src/main/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerProviderFactory.java b/server/src/main/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerProviderFactory.java index 9317f9fb1e4ab..1b15f65acf761 100644 --- a/server/src/main/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerProviderFactory.java +++ b/server/src/main/java/org/elasticsearch/index/analysis/PreBuiltAnalyzerProviderFactory.java @@ -33,6 +33,7 @@ import java.util.Collection; import java.util.List; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; public class PreBuiltAnalyzerProviderFactory extends PreConfiguredAnalysisComponent> implements Closeable { @@ -46,13 +47,17 @@ public class PreBuiltAnalyzerProviderFactory extends PreConfiguredAnalysisCompon PreBuiltAnalyzerProviderFactory(String name, PreBuiltAnalyzers preBuiltAnalyzer) { super(name, new PreBuiltAnalyzersDelegateCache(name, preBuiltAnalyzer)); this.create = preBuiltAnalyzer::getAnalyzer; - current = new PreBuiltAnalyzerProvider(name, AnalyzerScope.INDICES, preBuiltAnalyzer.getAnalyzer(Version.CURRENT)); + Analyzer analyzer = preBuiltAnalyzer.getAnalyzer(Version.CURRENT); + analyzer.setVersion(Version.CURRENT.luceneVersion); + current = new PreBuiltAnalyzerProvider(name, AnalyzerScope.INDICES, analyzer); } - public PreBuiltAnalyzerProviderFactory(String name, PreBuiltCacheFactory.CachingStrategy cache, Function create) { + public PreBuiltAnalyzerProviderFactory(String name, PreBuiltCacheFactory.CachingStrategy cache, Supplier create) { super(name, cache); - this.create = create; - this.current = new PreBuiltAnalyzerProvider(name, AnalyzerScope.INDICES, create.apply(Version.CURRENT)); + this.create = version -> create.get(); + Analyzer analyzer = create.get(); + analyzer.setVersion(Version.CURRENT.luceneVersion); + this.current = new PreBuiltAnalyzerProvider(name, AnalyzerScope.INDICES, analyzer); } @Override @@ -71,7 +76,9 @@ public AnalyzerProvider get(IndexSettings indexSettings, @Override protected AnalyzerProvider create(Version version) { assert Version.CURRENT.equals(version) == false; - return new PreBuiltAnalyzerProvider(getName(), AnalyzerScope.INDICES, create.apply(version)); + Analyzer analyzer = create.apply(version); + analyzer.setVersion(version.luceneVersion); + return new PreBuiltAnalyzerProvider(getName(), AnalyzerScope.INDICES, analyzer); } @Override From 9df86dc127c32fe341a59739a5b9ec16b1e33ad9 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 13 Jun 2018 09:22:17 +0200 Subject: [PATCH 37/71] Update checkstyle to 8.10.1 (#31269) --- .../org/elasticsearch/gradle/precommit/PrecommitTasks.groovy | 2 +- .../src/main/java/org/elasticsearch/transport/Transports.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/PrecommitTasks.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/PrecommitTasks.groovy index 09f0ad01578c9..3709805680d7a 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/PrecommitTasks.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/PrecommitTasks.groovy @@ -140,7 +140,7 @@ class PrecommitTasks { configProperties = [ suppressions: checkstyleSuppressions ] - toolVersion = 7.5 + toolVersion = '8.10.1' } project.tasks.withType(Checkstyle) { task -> diff --git a/server/src/main/java/org/elasticsearch/transport/Transports.java b/server/src/main/java/org/elasticsearch/transport/Transports.java index d07846835c23f..26235ebb44cdb 100644 --- a/server/src/main/java/org/elasticsearch/transport/Transports.java +++ b/server/src/main/java/org/elasticsearch/transport/Transports.java @@ -37,7 +37,7 @@ public enum Transports { * used in assertions to make sure that we do not call blocking code from * networking threads. */ - public static final boolean isTransportThread(Thread t) { + public static boolean isTransportThread(Thread t) { final String threadName = t.getName(); for (String s : Arrays.asList( HttpServerTransport.HTTP_SERVER_WORKER_THREAD_NAME_PREFIX, From dc393c83d1874d41f92d9297e657552a56bc243d Mon Sep 17 00:00:00 2001 From: David Turner Date: Wed, 13 Jun 2018 11:25:26 +0100 Subject: [PATCH 38/71] Ignore numeric shard count if waiting for ALL (#31265) Today, if GET /_cluster/health?wait_for_active_shards=all does not immediately succeed then it throws an exception due to an erroneous and unnecessary call to ActiveShardCount#enoughShardsActive(). This commit fixes this logic. Fixes #31151 --- .../health/TransportClusterHealthAction.java | 10 +++++----- .../health/TransportClusterHealthActionTests.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java index bd5912b9853ec..c67e2da2b9237 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthAction.java @@ -234,11 +234,11 @@ static int prepareResponse(final ClusterHealthRequest request, final ClusterHeal ActiveShardCount waitForActiveShards = request.waitForActiveShards(); assert waitForActiveShards.equals(ActiveShardCount.DEFAULT) == false : "waitForActiveShards must not be DEFAULT on the request object, instead it should be NONE"; - if (waitForActiveShards.equals(ActiveShardCount.ALL) - && response.getUnassignedShards() == 0 - && response.getInitializingShards() == 0) { - // if we are waiting for all shards to be active, then the num of unassigned and num of initializing shards must be 0 - waitForCounter++; + if (waitForActiveShards.equals(ActiveShardCount.ALL)) { + if (response.getUnassignedShards() == 0 && response.getInitializingShards() == 0) { + // if we are waiting for all shards to be active, then the num of unassigned and num of initializing shards must be 0 + waitForCounter++; + } } else if (waitForActiveShards.enoughShardsActive(response.getActiveShards())) { // there are enough active shards to meet the requirements of the request waitForCounter++; diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthActionTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthActionTests.java index cac5bed4033ac..8601687b04a23 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthActionTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/health/TransportClusterHealthActionTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.action.admin.cluster.health; import org.elasticsearch.Version; +import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; @@ -61,6 +62,20 @@ public void testWaitForInitializingShards() throws Exception { assertThat(TransportClusterHealthAction.prepareResponse(request, response, clusterState, null), equalTo(0)); } + public void testWaitForAllShards() { + final String[] indices = {"test"}; + final ClusterHealthRequest request = new ClusterHealthRequest(); + request.waitForActiveShards(ActiveShardCount.ALL); + + ClusterState clusterState = randomClusterStateWithInitializingShards("test", 1); + ClusterHealthResponse response = new ClusterHealthResponse("", indices, clusterState); + assertThat(TransportClusterHealthAction.prepareResponse(request, response, clusterState, null), equalTo(0)); + + clusterState = ClusterState.builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).build(); + response = new ClusterHealthResponse("", indices, clusterState); + assertThat(TransportClusterHealthAction.prepareResponse(request, response, clusterState, null), equalTo(1)); + } + ClusterState randomClusterStateWithInitializingShards(String index, final int initializingShards) { final IndexMetaData indexMetaData = IndexMetaData .builder(index) From bb11b9908218e0d38924cd7c58fa1a4b677a337a Mon Sep 17 00:00:00 2001 From: Dimitris Athanasiou Date: Wed, 13 Jun 2018 11:28:55 +0100 Subject: [PATCH 39/71] [TEST] Mute RecoveryIT.testHistoryUUIDIsGenerated Relates #31291 --- .../src/test/java/org/elasticsearch/upgrades/RecoveryIT.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java index 2b75e82cefb1c..f7bbe4847b959 100644 --- a/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java +++ b/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java @@ -49,6 +49,8 @@ * In depth testing of the recovery mechanism during a rolling restart. */ public class RecoveryIT extends AbstractRollingTestCase { + + @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/31291") public void testHistoryUUIDIsGenerated() throws Exception { final String index = "index_history_uuid"; if (CLUSTER_TYPE == ClusterType.OLD) { From a4cbcff644b8e1fd3cf63ba07f416daaecba6696 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 13 Jun 2018 12:40:22 +0200 Subject: [PATCH 40/71] Fix AntFixture waiting condition (#31272) The AntFixture waiting condition is evaluated to false but it should be true. --- .../gradle/test/AntFixture.groovy | 4 +- modules/reindex/build.gradle | 5 + modules/repository-url/build.gradle | 27 +- .../RepositoryURLClientYamlTestSuiteIT.java | 92 ++++++- .../repositories/url/URLFixture.java | 168 ++++++++++++ .../test/repository_url/10_basic.yml | 240 ++++++++++++++++-- .../test/repository_url/20_repository.yml | 15 ++ .../example/resthandler/ExampleFixtureIT.java | 24 +- .../azure/AzureStorageFixture.java | 15 +- .../gcs/GoogleCloudStorageFixture.java | 14 +- plugins/repository-hdfs/build.gradle | 5 + .../repositories/s3/AmazonS3Fixture.java | 14 +- .../main/java/example/ExampleTestFixture.java | 35 ++- 13 files changed, 611 insertions(+), 47 deletions(-) create mode 100644 modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy index 039bce052263c..8dcb862064ec9 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/AntFixture.groovy @@ -149,11 +149,11 @@ public class AntFixture extends AntTask implements Fixture { } // the process is started (has a pid) and is bound to a network interface - // so now wait undil the waitCondition has been met + // so now evaluates if the waitCondition is successful // TODO: change this to a loop? boolean success try { - success = waitCondition(this, ant) == false + success = waitCondition(this, ant) } catch (Exception e) { String msg = "Wait condition caught exception for ${name}" logger.error(msg, e) diff --git a/modules/reindex/build.gradle b/modules/reindex/build.gradle index 765d55dd095c7..8870e21858d18 100644 --- a/modules/reindex/build.gradle +++ b/modules/reindex/build.gradle @@ -121,6 +121,11 @@ if (Os.isFamily(Os.FAMILY_WINDOWS)) { baseDir, unzip.temporaryDir, version == '090' + waitCondition = { fixture, ant -> + // the fixture writes the ports file when Elasticsearch's HTTP service + // is ready, so we can just wait for the file to exist + return fixture.portsFile.exists() + } } integTest.dependsOn fixture integTestRunner { diff --git a/modules/repository-url/build.gradle b/modules/repository-url/build.gradle index 7008111ca9c54..62aad486ad804 100644 --- a/modules/repository-url/build.gradle +++ b/modules/repository-url/build.gradle @@ -16,12 +16,37 @@ * specific language governing permissions and limitations * under the License. */ +import org.elasticsearch.gradle.test.AntFixture esplugin { description 'Module for URL repository' classname 'org.elasticsearch.plugin.repository.url.URLRepositoryPlugin' } +forbiddenApisTest { + // we are using jdk-internal instead of jdk-non-portable to allow for com.sun.net.httpserver.* usage + bundledSignatures -= 'jdk-non-portable' + bundledSignatures += 'jdk-internal' +} + +// This directory is shared between two URL repositories and one FS repository in YAML integration tests +File repositoryDir = new File(project.buildDir, "shared-repository") + +/** A task to start the URLFixture which exposes the repositoryDir over HTTP **/ +task urlFixture(type: AntFixture) { + dependsOn testClasses + doFirst { + repositoryDir.mkdirs() + } + env 'CLASSPATH', "${ -> project.sourceSets.test.runtimeClasspath.asPath }" + executable = new File(project.runtimeJavaHome, 'bin/java') + args 'org.elasticsearch.repositories.url.URLFixture', baseDir, "${repositoryDir.absolutePath}" +} + integTestCluster { - setting 'repositories.url.allowed_urls', 'http://snapshot.test*' + dependsOn urlFixture + // repositoryDir is used by a FS repository to create snapshots + setting 'path.repo', "${repositoryDir.absolutePath}" + // repositoryDir is used by two URL repositories to restore snapshots + setting 'repositories.url.allowed_urls', "http://snapshot.test*,http://${ -> urlFixture.addressAndPort }" } diff --git a/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java b/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java index 238b14ce013ad..f33fa98f0e3be 100644 --- a/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java +++ b/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/RepositoryURLClientYamlTestSuiteIT.java @@ -21,9 +21,34 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; - +import org.apache.http.HttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.entity.NStringEntity; +import org.elasticsearch.client.Response; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.repositories.fs.FsRepository; +import org.elasticsearch.rest.RestStatus; import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.emptyMap; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.notNullValue; public class RepositoryURLClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { @@ -35,5 +60,70 @@ public RepositoryURLClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate public static Iterable parameters() throws Exception { return ESClientYamlSuiteTestCase.createParameters(); } + + /** + * This method registers 3 snapshot/restore repositories: + * - repository-fs: this FS repository is used to create snapshots. + * - repository-url: this URL repository is used to restore snapshots created using the previous repository. It uses + * the URLFixture to restore snapshots over HTTP. + * - repository-file: similar as the previous repository but using a file:// prefix instead of http://. + **/ + @Before + public void registerRepositories() throws IOException { + Response clusterSettingsResponse = client().performRequest("GET", "/_cluster/settings?include_defaults=true" + + "&filter_path=defaults.path.repo,defaults.repositories.url.allowed_urls"); + Map clusterSettings = entityAsMap(clusterSettingsResponse); + + @SuppressWarnings("unchecked") + List pathRepos = (List) XContentMapValues.extractValue("defaults.path.repo", clusterSettings); + assertThat(pathRepos, notNullValue()); + assertThat(pathRepos, hasSize(1)); + + final String pathRepo = pathRepos.get(0); + final URI pathRepoUri = PathUtils.get(pathRepo).toUri().normalize(); + + // Create a FS repository using the path.repo location + Response createFsRepositoryResponse = client().performRequest("PUT", "_snapshot/repository-fs", emptyMap(), + buildRepositorySettings(FsRepository.TYPE, Settings.builder().put("location", pathRepo).build())); + assertThat(createFsRepositoryResponse.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus())); + + // Create a URL repository using the file://{path.repo} URL + Response createFileRepositoryResponse = client().performRequest("PUT", "_snapshot/repository-file", emptyMap(), + buildRepositorySettings(URLRepository.TYPE, Settings.builder().put("url", pathRepoUri.toString()).build())); + assertThat(createFileRepositoryResponse.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus())); + + // Create a URL repository using the http://{fixture} URL + @SuppressWarnings("unchecked") + List allowedUrls = (List) XContentMapValues.extractValue("defaults.repositories.url.allowed_urls", clusterSettings); + for (String allowedUrl : allowedUrls) { + try { + InetAddress inetAddress = InetAddress.getByName(new URL(allowedUrl).getHost()); + if (inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress()) { + Response createUrlRepositoryResponse = client().performRequest("PUT", "_snapshot/repository-url", emptyMap(), + buildRepositorySettings(URLRepository.TYPE, Settings.builder().put("url", allowedUrl).build())); + assertThat(createUrlRepositoryResponse.getStatusLine().getStatusCode(), equalTo(RestStatus.OK.getStatus())); + break; + } + } catch (Exception e) { + logger.debug("Failed to resolve inet address for allowed URL [{}], skipping", allowedUrl); + } + } + } + + private static HttpEntity buildRepositorySettings(final String type, final Settings settings) throws IOException { + try (XContentBuilder builder = jsonBuilder()) { + builder.startObject(); + { + builder.field("type", type); + builder.startObject("settings"); + { + settings.toXContent(builder, ToXContent.EMPTY_PARAMS); + } + builder.endObject(); + } + builder.endObject(); + return new NStringEntity(Strings.toString(builder), ContentType.APPLICATION_JSON); + } + } } diff --git a/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java b/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java new file mode 100644 index 0000000000000..353a0d895c2c7 --- /dev/null +++ b/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java @@ -0,0 +1,168 @@ +/* + * 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.repositories.url; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.mocksocket.MockHttpServer; +import org.elasticsearch.rest.RestStatus; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Map; +import java.util.Objects; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonMap; + +/** + * This {@link URLFixture} exposes a filesystem directory over HTTP. It is used in repository-url + * integration tests to expose a directory created by a regular FS repository. + */ +public class URLFixture { + + public static void main(String[] args) throws Exception { + if (args == null || args.length != 2) { + throw new IllegalArgumentException("URLFixture "); + } + + final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + final HttpServer httpServer = MockHttpServer.createHttp(socketAddress, 0); + + try { + final Path workingDirectory = dir(args[0]); + /// Writes the PID of the current Java process in a `pid` file located in the working directory + writeFile(workingDirectory, "pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); + + final String addressAndPort = addressToString(httpServer.getAddress()); + // Writes the address and port of the http server in a `ports` file located in the working directory + writeFile(workingDirectory, "ports", addressAndPort); + + // Exposes the repository over HTTP + httpServer.createContext("/", new ResponseHandler(dir(args[1]))); + httpServer.start(); + + // Wait to be killed + Thread.sleep(Long.MAX_VALUE); + + } finally { + httpServer.stop(0); + } + } + + @SuppressForbidden(reason = "Paths#get is fine - we don't have environment here") + private static Path dir(final String dir) { + return Paths.get(dir); + } + + private static void writeFile(final Path dir, final String fileName, final String content) throws IOException { + final Path tempPidFile = Files.createTempFile(dir, null, null); + Files.write(tempPidFile, singleton(content)); + Files.move(tempPidFile, dir.resolve(fileName), StandardCopyOption.ATOMIC_MOVE); + } + + private static String addressToString(final SocketAddress address) { + final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; + if (inetSocketAddress.getAddress() instanceof Inet6Address) { + return "[" + inetSocketAddress.getHostString() + "]:" + inetSocketAddress.getPort(); + } else { + return inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); + } + } + + static class ResponseHandler implements HttpHandler { + + private final Path repositoryDir; + + ResponseHandler(final Path repositoryDir) { + this.repositoryDir = repositoryDir; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + Response response; + + final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); + if (userAgent != null && userAgent.startsWith("Apache Ant")) { + // This is a request made by the AntFixture, just reply "OK" + response = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); + + } else if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) { + String path = exchange.getRequestURI().toString(); + if (path.length() > 0 && path.charAt(0) == '/') { + path = path.substring(1); + } + + Path normalizedRepositoryDir = repositoryDir.normalize(); + Path normalizedPath = normalizedRepositoryDir.resolve(path).normalize(); + + if (normalizedPath.startsWith(normalizedRepositoryDir)) { + if (Files.exists(normalizedPath) && Files.isReadable(normalizedPath) && Files.isRegularFile(normalizedPath)) { + byte[] content = Files.readAllBytes(normalizedPath); + Map headers = singletonMap("Content-Length", String.valueOf(content.length)); + response = new Response(RestStatus.OK, headers, "application/octet-stream", content); + } else { + response = new Response(RestStatus.NOT_FOUND, emptyMap(), "text/plain; charset=utf-8", new byte[0]); + } + } else { + response = new Response(RestStatus.FORBIDDEN, emptyMap(), "text/plain; charset=utf-8", new byte[0]); + } + } else { + response = new Response(RestStatus.INTERNAL_SERVER_ERROR, emptyMap(), "text/plain; charset=utf-8", + "Unsupported HTTP method".getBytes(StandardCharsets.UTF_8)); + } + exchange.sendResponseHeaders(response.status.getStatus(), response.body.length); + if (response.body.length > 0) { + exchange.getResponseBody().write(response.body); + } + exchange.close(); + } + } + + /** + * Represents a HTTP Response. + */ + static class Response { + + final RestStatus status; + final Map headers; + final String contentType; + final byte[] body; + + Response(final RestStatus status, final Map headers, final String contentType, final byte[] body) { + this.status = Objects.requireNonNull(status); + this.headers = Objects.requireNonNull(headers); + this.contentType = Objects.requireNonNull(contentType); + this.body = Objects.requireNonNull(body); + } + } +} diff --git a/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yml b/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yml index 75e7873299869..d1633d9438d97 100644 --- a/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yml +++ b/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/10_basic.yml @@ -1,6 +1,108 @@ -# Integration tests for URL Repository component +# Integration tests for repository-url # -"URL Repository plugin loaded": +# This test is based on 3 repositories, all registered before this +# test is executed. The repository-fs is used to create snapshots +# in a shared directory on the filesystem. Then the test uses a URL +# repository with a "http://" prefix to test the restore of the +# snapshots. In order to do that it uses a URLFixture that exposes +# the content of the shared directory over HTTP. A second URL +# repository is used to test the snapshot restore but this time +# with a "file://" prefix. +setup: + + # Ensure that the FS repository is registered, so we can create + # snapshots that we later restore using the URL repository + - do: + snapshot.get_repository: + repository: repository-fs + + # Index documents + - do: + bulk: + refresh: true + body: + - index: + _index: docs + _type: doc + _id: 1 + - snapshot: one + - index: + _index: docs + _type: doc + _id: 2 + - snapshot: one + - index: + _index: docs + _type: doc + _id: 3 + - snapshot: one + + # Create a first snapshot using the FS repository + - do: + snapshot.create: + repository: repository-fs + snapshot: snapshot-one + wait_for_completion: true + + # Index more documents + - do: + bulk: + refresh: true + body: + - index: + _index: docs + _type: doc + _id: 4 + - snapshot: two + - index: + _index: docs + _type: doc + _id: 5 + - snapshot: two + - index: + _index: docs + _type: doc + _id: 6 + - snapshot: two + - index: + _index: docs + _type: doc + _id: 7 + - snapshot: two + + # Create a second snapshot + - do: + snapshot.create: + repository: repository-fs + snapshot: snapshot-two + wait_for_completion: true + + - do: + snapshot.get: + repository: repository-fs + snapshot: snapshot-one,snapshot-two + +--- +teardown: + + - do: + indices.delete: + index: docs + ignore_unavailable: true + + # Remove the snapshots + - do: + snapshot.delete: + repository: repository-fs + snapshot: snapshot-two + + - do: + snapshot.delete: + repository: repository-fs + snapshot: snapshot-one + +--- +"Module repository-url is loaded": - do: cluster.state: {} @@ -10,23 +112,129 @@ - do: nodes.info: {} - - match: { nodes.$master.modules.0.name: repository-url } + - match: { nodes.$master.modules.0.name: repository-url } --- -setup: +"Restore with repository-url using http://": + # Ensure that the URL repository is registered - do: - snapshot.create_repository: - repository: test_repo1 - body: - type: url - settings: - url: "http://snapshot.test1" + snapshot.get_repository: + repository: repository-url + + - match: { repository-url.type : "url" } + - match: { repository-url.settings.url: '/http://(.+):\d+/' } - do: - snapshot.create_repository: - repository: test_repo2 - body: - type: url - settings: - url: "http://snapshot.test2" + snapshot.get: + repository: repository-url + snapshot: snapshot-one,snapshot-two + + - is_true: snapshots + - match: { snapshots.0.state : SUCCESS } + - match: { snapshots.1.state : SUCCESS } + + # Delete the index + - do: + indices.delete: + index: docs + + # Restore the second snapshot + - do: + snapshot.restore: + repository: repository-url + snapshot: snapshot-two + wait_for_completion: true + + - do: + count: + index: docs + + - match: {count: 7} + + # Delete the index again + - do: + indices.delete: + index: docs + + # Restore the first snapshot + - do: + snapshot.restore: + repository: repository-url + snapshot: snapshot-one + wait_for_completion: true + + - do: + count: + index: docs + + - match: {count: 3} + + - do: + catch: /cannot delete snapshot from a readonly repository/ + snapshot.delete: + repository: repository-url + snapshot: snapshot-two + +--- +"Restore with repository-url using file://": + + # Ensure that the URL repository is registered + - do: + snapshot.get_repository: + repository: repository-file + + - match: { repository-file.type : "url" } + - match: { repository-file.settings.url: '/file://(.+)/' } + + - do: + snapshot.get: + repository: repository-file + snapshot: snapshot-one,snapshot-two + + - is_true: snapshots + - match: { snapshots.0.state : SUCCESS } + - match: { snapshots.1.state : SUCCESS } + + # Delete the index + - do: + indices.delete: + index: docs + + # Restore the second snapshot + - do: + snapshot.restore: + repository: repository-file + snapshot: snapshot-two + wait_for_completion: true + + - do: + count: + index: docs + + - match: {count: 7} + + # Delete the index again + - do: + indices.delete: + index: docs + + # Restore the first snapshot + - do: + snapshot.restore: + repository: repository-file + snapshot: snapshot-one + wait_for_completion: true + + - do: + count: + index: docs + + - match: {count: 3} + + - do: + catch: /cannot delete snapshot from a readonly repository/ + snapshot.delete: + repository: repository-file + snapshot: snapshot-one + diff --git a/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yml b/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yml index 39cfeee192c9b..904c4cb5acd99 100644 --- a/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yml +++ b/modules/repository-url/src/test/resources/rest-api-spec/test/repository_url/20_repository.yml @@ -14,3 +14,18 @@ repository: test_repo1 - is_true : test_repo1 + +--- +"Repository cannot be be registered": + + - do: + catch: /doesn't match any of the locations specified by path.repo or repositories.url.allowed_urls/ + snapshot.create_repository: + repository: test_repo2 + body: + type: url + settings: + url: "http://snapshot.unknown" + + + diff --git a/plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixtureIT.java b/plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixtureIT.java index 97fc6b241ea5a..522e67b512d04 100644 --- a/plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixtureIT.java +++ b/plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixtureIT.java @@ -23,25 +23,41 @@ import org.elasticsearch.test.ESTestCase; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.Socket; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.Objects; +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.Matchers.hasItems; public class ExampleFixtureIT extends ESTestCase { public void testExample() throws Exception { - final String stringAddress = Objects.requireNonNull(System.getProperty("external.address")); - final URL url = new URL("http://" + stringAddress); + final String externalAddress = System.getProperty("external.address"); + assertNotNull("External address must not be null", externalAddress); + final URL url = new URL("http://" + externalAddress); final InetAddress address = InetAddress.getByName(url.getHost()); try ( Socket socket = new MockSocket(address, url.getPort()); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)) ) { - assertEquals("TEST", reader.readLine()); + writer.write("GET / HTTP/1.1\r\n"); + writer.write("Host: elastic.co\r\n\r\n"); + writer.flush(); + + final List lines = new ArrayList<>(); + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + assertThat(lines, hasItems("HTTP/1.1 200 OK", "TEST")); } } } diff --git a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java b/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java index ebd8241e710ea..2f74c00ef92e2 100644 --- a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java +++ b/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java @@ -24,6 +24,8 @@ import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.Streams; import org.elasticsearch.mocksocket.MockHttpServer; +import org.elasticsearch.repositories.azure.AzureStorageTestServer.Response; +import org.elasticsearch.rest.RestStatus; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -39,6 +41,8 @@ import java.util.List; import java.util.Map; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; @@ -121,7 +125,16 @@ public void handle(HttpExchange exchange) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Streams.copy(exchange.getRequestBody(), out); - final AzureStorageTestServer.Response response = server.handle(method, path, query, headers, out.toByteArray()); + Response response = null; + + final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); + if (userAgent != null && userAgent.startsWith("Apache Ant")) { + // This is a request made by the AntFixture, just reply "OK" + response = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); + } else { + // Otherwise simulate a S3 response + response = server.handle(method, path, query, headers, out.toByteArray()); + } Map> responseHeaders = exchange.getResponseHeaders(); responseHeaders.put("Content-Type", singletonList(response.contentType)); diff --git a/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java b/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java index 1f5ec2f2dd819..978f8c4783197 100644 --- a/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java +++ b/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java @@ -25,6 +25,7 @@ import org.elasticsearch.core.internal.io.Streams; import org.elasticsearch.mocksocket.MockHttpServer; import org.elasticsearch.repositories.gcs.GoogleCloudStorageTestServer.Response; +import org.elasticsearch.rest.RestStatus; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -40,6 +41,8 @@ import java.util.List; import java.util.Map; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; @@ -120,7 +123,16 @@ public void handle(HttpExchange exchange) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Streams.copy(exchange.getRequestBody(), out); - final Response storageResponse = storageServer.handle(method, path, query, headers, out.toByteArray()); + Response storageResponse = null; + + final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); + if (userAgent != null && userAgent.startsWith("Apache Ant")) { + // This is a request made by the AntFixture, just reply "OK" + storageResponse = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); + } else { + // Otherwise simulate a S3 response + storageResponse = storageServer.handle(method, path, query, headers, out.toByteArray()); + } Map> responseHeaders = exchange.getResponseHeaders(); responseHeaders.put("Content-Type", singletonList(storageResponse.contentType)); diff --git a/plugins/repository-hdfs/build.gradle b/plugins/repository-hdfs/build.gradle index 3c94f4ace7759..304e0f4ae0e1f 100644 --- a/plugins/repository-hdfs/build.gradle +++ b/plugins/repository-hdfs/build.gradle @@ -116,6 +116,11 @@ for (String fixtureName : ['hdfsFixture', 'haHdfsFixture', 'secureHdfsFixture', dependsOn project.configurations.hdfsFixture 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 = [] diff --git a/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java b/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java index c8321e83d1390..cf123f85d98a9 100644 --- a/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java +++ b/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.io.Streams; import org.elasticsearch.mocksocket.MockHttpServer; import org.elasticsearch.repositories.s3.AmazonS3TestServer.Response; +import org.elasticsearch.rest.RestStatus; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -40,6 +41,8 @@ import java.util.List; import java.util.Map; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; @@ -122,7 +125,16 @@ public void handle(HttpExchange exchange) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Streams.copy(exchange.getRequestBody(), out); - final Response storageResponse = storageServer.handle(method, path, query, headers, out.toByteArray()); + Response storageResponse = null; + + final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); + if (userAgent != null && userAgent.startsWith("Apache Ant")) { + // This is a request made by the AntFixture, just reply "OK" + storageResponse = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); + } else { + // Otherwise simulate a S3 response + storageResponse = storageServer.handle(method, path, query, headers, out.toByteArray()); + } Map> responseHeaders = exchange.getResponseHeaders(); responseHeaders.put("Content-Type", singletonList(storageResponse.contentType)); diff --git a/test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java b/test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java index 603aba1fc639b..96103d8eaa900 100644 --- a/test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java +++ b/test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java @@ -19,14 +19,12 @@ package example; +import com.sun.net.httpserver.HttpServer; + import java.lang.management.ManagementFactory; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -41,9 +39,9 @@ public static void main(String args[]) throws Exception { throw new IllegalArgumentException("ExampleTestFixture "); } Path dir = Paths.get(args[0]); - AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel - .open() - .bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)); + + final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + final HttpServer httpServer = HttpServer.create(socketAddress, 0); // write pid file Path tmp = Files.createTempFile(dir, null, null); @@ -53,7 +51,7 @@ public static void main(String args[]) throws Exception { // write port file tmp = Files.createTempFile(dir, null, null); - InetSocketAddress bound = (InetSocketAddress) server.getLocalAddress(); + InetSocketAddress bound = httpServer.getAddress(); if (bound.getAddress() instanceof Inet6Address) { Files.write(tmp, Collections.singleton("[" + bound.getHostString() + "]:" + bound.getPort())); } else { @@ -61,21 +59,18 @@ public static void main(String args[]) throws Exception { } Files.move(tmp, dir.resolve("ports"), StandardCopyOption.ATOMIC_MOVE); + final byte[] response = "TEST\n".getBytes(StandardCharsets.UTF_8); + // go time - server.accept(null, new CompletionHandler() { - @Override - public void completed(AsynchronousSocketChannel socket, Void attachment) { - server.accept(null, this); - try (AsynchronousSocketChannel ch = socket) { - ch.write(ByteBuffer.wrap("TEST\n".getBytes(StandardCharsets.UTF_8))).get(); - } catch (Exception e) { - throw new RuntimeException(e); - } + httpServer.createContext("/", exchange -> { + try { + exchange.sendResponseHeaders(200, response.length); + exchange.getResponseBody().write(response); + } finally { + exchange.close(); } - - @Override - public void failed(Throwable exc, Void attachment) {} }); + httpServer.start(); // wait forever, until you kill me Thread.sleep(Long.MAX_VALUE); From 197aa9b5363636e9af04f5ef5abdf4abea0dbd28 Mon Sep 17 00:00:00 2001 From: Boaz Leskes Date: Wed, 13 Jun 2018 13:22:34 +0200 Subject: [PATCH 41/71] Log warnings when cluster state publication failed to some nodes (#31233) If the publishing of a cluster state to a node fails, we currently only log it as debug information and only on the master. This makes it hard to see the cause of (test) failures when logging is set to default levels. This PR adds a warn level log on the node receiving the cluster state when it fails to deserialise the cluster state and a warn level log on the master with a list of nodes for which publication failed. --- ...ingClusterStatePublishResponseHandler.java | 12 ++++++ .../zen/PublishClusterStateAction.java | 41 +++++++++++-------- ...usterStatePublishResponseHandlerTests.java | 19 +++++++-- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandler.java b/server/src/main/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandler.java index 72e59675c1bfa..8d5ef1926cdd5 100644 --- a/server/src/main/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandler.java +++ b/server/src/main/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandler.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import java.util.Collections; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -35,6 +36,7 @@ public class BlockingClusterStatePublishResponseHandler { private final CountDownLatch latch; private final Set pendingNodes; + private final Set failedNodes; /** * Creates a new BlockingClusterStatePublishResponseHandler @@ -44,6 +46,7 @@ public BlockingClusterStatePublishResponseHandler(Set publishingT this.pendingNodes = ConcurrentCollections.newConcurrentSet(); this.pendingNodes.addAll(publishingToNodes); this.latch = new CountDownLatch(pendingNodes.size()); + this.failedNodes = ConcurrentCollections.newConcurrentSet(); } /** @@ -64,6 +67,8 @@ public void onResponse(DiscoveryNode node) { public void onFailure(DiscoveryNode node, Exception e) { boolean found = pendingNodes.remove(node); assert found : "node [" + node + "] already responded or failed"; + boolean added = failedNodes.add(node); + assert added : "duplicate failures for " + node; latch.countDown(); } @@ -86,4 +91,11 @@ public DiscoveryNode[] pendingNodes() { // nulls if some nodes responded in the meanwhile return pendingNodes.toArray(new DiscoveryNode[0]); } + + /** + * returns a set of nodes for which publication has failed. + */ + public Set getFailedNodes() { + return Collections.unmodifiableSet(failedNodes); + } } diff --git a/server/src/main/java/org/elasticsearch/discovery/zen/PublishClusterStateAction.java b/server/src/main/java/org/elasticsearch/discovery/zen/PublishClusterStateAction.java index 382a42141d83a..cd87a41526313 100644 --- a/server/src/main/java/org/elasticsearch/discovery/zen/PublishClusterStateAction.java +++ b/server/src/main/java/org/elasticsearch/discovery/zen/PublishClusterStateAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.discovery.zen; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; @@ -41,6 +40,7 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.discovery.AckClusterStatePublishResponseHandler; import org.elasticsearch.discovery.BlockingClusterStatePublishResponseHandler; import org.elasticsearch.discovery.Discovery; @@ -207,6 +207,12 @@ private void innerPublish(final ClusterChangedEvent clusterChangedEvent, final S clusterState.version(), publishTimeout, pendingNodes); } } + // The failure is logged under debug when a sending failed. we now log a summary. + Set failedNodes = publishResponseHandler.getFailedNodes(); + if (failedNodes.isEmpty() == false) { + logger.warn("publishing cluster state with version [{}] failed for the following nodes: [{}]", + clusterChangedEvent.state().version(), failedNodes); + } } catch (InterruptedException e) { // ignore & restore interrupt Thread.currentThread().interrupt(); @@ -367,14 +373,14 @@ public static BytesReference serializeDiffClusterState(Diff diff, Version nodeVe protected void handleIncomingClusterStateRequest(BytesTransportRequest request, TransportChannel channel) throws IOException { Compressor compressor = CompressorFactory.compressor(request.bytes()); StreamInput in = request.bytes().streamInput(); - try { - if (compressor != null) { - in = compressor.streamInput(in); - } - in = new NamedWriteableAwareStreamInput(in, namedWriteableRegistry); - in.setVersion(request.version()); - synchronized (lastSeenClusterStateMutex) { - final ClusterState incomingState; + final ClusterState incomingState; + synchronized (lastSeenClusterStateMutex) { + try { + if (compressor != null) { + in = compressor.streamInput(in); + } + in = new NamedWriteableAwareStreamInput(in, namedWriteableRegistry); + in.setVersion(request.version()); // If true we received full cluster state - otherwise diffs if (in.readBoolean()) { incomingState = ClusterState.readFrom(in, transportService.getLocalNode()); @@ -391,14 +397,17 @@ protected void handleIncomingClusterStateRequest(BytesTransportRequest request, logger.debug("received diff for but don't have any local cluster state - requesting full state"); throw new IncompatibleClusterStateVersionException("have no local cluster state"); } - incomingClusterStateListener.onIncomingClusterState(incomingState); - lastSeenClusterState = incomingState; + } catch (IncompatibleClusterStateVersionException e) { + incompatibleClusterStateDiffReceivedCount.incrementAndGet(); + throw e; + } catch (Exception e) { + logger.warn("unexpected error while deserializing an incoming cluster state", e); + throw e; + } finally { + IOUtils.close(in); } - } catch (IncompatibleClusterStateVersionException e) { - incompatibleClusterStateDiffReceivedCount.incrementAndGet(); - throw e; - } finally { - IOUtils.close(in); + incomingClusterStateListener.onIncomingClusterState(incomingState); + lastSeenClusterState = incomingState; } channel.sendResponse(TransportResponse.Empty.INSTANCE); } diff --git a/server/src/test/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandlerTests.java b/server/src/test/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandlerTests.java index 6d0ee8a97821e..9504344236b86 100644 --- a/server/src/test/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandlerTests.java +++ b/server/src/test/java/org/elasticsearch/discovery/BlockingClusterStatePublishResponseHandlerTests.java @@ -85,10 +85,16 @@ public void testConcurrentAccess() throws InterruptedException { int firstRound = randomIntBetween(5, nodeCount - 1); Thread[] threads = new Thread[firstRound]; CyclicBarrier barrier = new CyclicBarrier(firstRound); + Set expectedFailures = new HashSet<>(); Set completedNodes = new HashSet<>(); for (int i = 0; i < threads.length; i++) { - completedNodes.add(allNodes[i]); - threads[i] = new Thread(new PublishResponder(randomBoolean(), allNodes[i], barrier, logger, handler)); + final DiscoveryNode node = allNodes[i]; + completedNodes.add(node); + final boolean fail = randomBoolean(); + if (fail) { + expectedFailures.add(node); + } + threads[i] = new Thread(new PublishResponder(fail, node, barrier, logger, handler)); threads[i].start(); } // wait on the threads to finish @@ -105,7 +111,12 @@ public void testConcurrentAccess() throws InterruptedException { barrier = new CyclicBarrier(secondRound); for (int i = 0; i < threads.length; i++) { - threads[i] = new Thread(new PublishResponder(randomBoolean(), allNodes[firstRound + i], barrier, logger, handler)); + final DiscoveryNode node = allNodes[firstRound + i]; + final boolean fail = randomBoolean(); + if (fail) { + expectedFailures.add(node); + } + threads[i] = new Thread(new PublishResponder(fail, node, barrier, logger, handler)); threads[i].start(); } // wait on the threads to finish @@ -114,6 +125,6 @@ public void testConcurrentAccess() throws InterruptedException { } assertTrue("expected handler not to timeout as all nodes responded", handler.awaitAllNodes(new TimeValue(10))); assertThat(handler.pendingNodes(), arrayWithSize(0)); - + assertThat(handler.getFailedNodes(), equalTo(expectedFailures)); } } From abc675140eaf5c6389d1a7232b8e689214e89f73 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Mon, 11 Jun 2018 11:06:28 -0400 Subject: [PATCH 42/71] HLRest: Add get index templates API (#31161) Relates #27205 --- .../elasticsearch/client/IndicesClient.java | 31 +++++++ .../client/RequestConverters.java | 11 +++ .../elasticsearch/client/IndicesClientIT.java | 51 +++++++++++ .../client/RequestConvertersTests.java | 20 +++++ .../IndicesClientDocumentationIT.java | 72 +++++++++++++++ .../high-level/indices/get_templates.asciidoc | 73 +++++++++++++++ .../high-level/supported-apis.asciidoc | 3 +- .../get/GetIndexTemplatesResponse.java | 17 +++- .../get/GetIndexTemplatesResponseTests.java | 89 +++++++++++++++++++ 9 files changed, 364 insertions(+), 3 deletions(-) create mode 100644 docs/java-rest/high-level/indices/get_templates.asciidoc create mode 100644 server/src/test/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponseTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java index fa7eb9ab9ec8a..59d2cf1392d30 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java @@ -54,6 +54,8 @@ import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse; @@ -1059,4 +1061,33 @@ public void putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest, Re restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, RequestConverters::putTemplate, options, PutIndexTemplateResponse::fromXContent, listener, emptySet()); } + + /** + * Gets index templates using the Index Templates API + * See Index Templates API + * on elastic.co + * @param getIndexTemplatesRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public GetIndexTemplatesResponse getTemplate(GetIndexTemplatesRequest getIndexTemplatesRequest, + RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest, RequestConverters::getTemplates, + options, GetIndexTemplatesResponse::fromXContent, emptySet()); + } + + /** + * Asynchronously gets index templates using the Index Templates API + * See Index Templates API + * on elastic.co + * @param getIndexTemplatesRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void getTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest, RequestConverters::getTemplates, + options, GetIndexTemplatesResponse::fromXContent, listener, emptySet()); + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 53992a051080b..eceaf67a41ad8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -55,6 +55,7 @@ import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeType; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.delete.DeleteRequest; @@ -841,6 +842,16 @@ static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) thro return request; } + static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) throws IOException { + String[] names = getIndexTemplatesRequest.names(); + String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addCommaSeparatedPathParts(names).build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + Params params = new Params(request); + params.withLocal(getIndexTemplatesRequest.local()); + params.withMasterTimeout(getIndexTemplatesRequest.masterNodeTimeout()); + return request; + } + private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException { BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef(); return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType)); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java index 87d5120116fb4..75cfd8e05deb5 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java @@ -60,6 +60,8 @@ import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeType; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse; import org.elasticsearch.action.index.IndexRequest; @@ -67,6 +69,7 @@ import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.broadcast.BroadcastResponse; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.common.ValidationException; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; @@ -89,6 +92,7 @@ import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractRawValues; import static org.elasticsearch.common.xcontent.support.XContentMapValues.extractValue; import static org.hamcrest.CoreMatchers.hasItem; +import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; @@ -999,4 +1003,51 @@ public void testPutTemplateBadRequests() throws Exception { () -> execute(unknownSettingTemplate, client.indices()::putTemplate, client.indices()::putTemplateAsync)); assertThat(unknownSettingError.getDetailedMessage(), containsString("unknown setting [index.this-setting-does-not-exist]")); } + + public void testGetIndexTemplate() throws Exception { + RestHighLevelClient client = highLevelClient(); + + PutIndexTemplateRequest putTemplate1 = new PutIndexTemplateRequest().name("template-1") + .patterns(Arrays.asList("pattern-1", "name-1")).alias(new Alias("alias-1")); + assertThat(execute(putTemplate1, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged(), + equalTo(true)); + PutIndexTemplateRequest putTemplate2 = new PutIndexTemplateRequest().name("template-2") + .patterns(Arrays.asList("pattern-2", "name-2")) + .settings(Settings.builder().put("number_of_shards", "2").put("number_of_replicas", "0")); + assertThat(execute(putTemplate2, client.indices()::putTemplate, client.indices()::putTemplateAsync).isAcknowledged(), + equalTo(true)); + + GetIndexTemplatesResponse getTemplate1 = execute(new GetIndexTemplatesRequest().names("template-1"), + client.indices()::getTemplate, client.indices()::getTemplateAsync); + assertThat(getTemplate1.getIndexTemplates(), hasSize(1)); + IndexTemplateMetaData template1 = getTemplate1.getIndexTemplates().get(0); + assertThat(template1.name(), equalTo("template-1")); + assertThat(template1.patterns(), contains("pattern-1", "name-1")); + assertTrue(template1.aliases().containsKey("alias-1")); + + GetIndexTemplatesResponse getTemplate2 = execute(new GetIndexTemplatesRequest().names("template-2"), + client.indices()::getTemplate, client.indices()::getTemplateAsync); + assertThat(getTemplate2.getIndexTemplates(), hasSize(1)); + IndexTemplateMetaData template2 = getTemplate2.getIndexTemplates().get(0); + assertThat(template2.name(), equalTo("template-2")); + assertThat(template2.patterns(), contains("pattern-2", "name-2")); + assertTrue(template2.aliases().isEmpty()); + assertThat(template2.settings().get("index.number_of_shards"), equalTo("2")); + assertThat(template2.settings().get("index.number_of_replicas"), equalTo("0")); + + GetIndexTemplatesRequest getBothRequest = new GetIndexTemplatesRequest(); + if (randomBoolean()) { + getBothRequest.names("*-1", "template-2"); + } else { + getBothRequest.names("template-*"); + } + GetIndexTemplatesResponse getBoth = execute(getBothRequest, client.indices()::getTemplate, client.indices()::getTemplateAsync); + assertThat(getBoth.getIndexTemplates(), hasSize(2)); + assertThat(getBoth.getIndexTemplates().stream().map(IndexTemplateMetaData::getName).toArray(), + arrayContainingInAnyOrder("template-1", "template-2")); + + ElasticsearchException notFound = expectThrows(ElasticsearchException.class, () -> execute( + new GetIndexTemplatesRequest().names("the-template-*"), client.indices()::getTemplate, client.indices()::getTemplateAsync)); + assertThat(notFound.status(), equalTo(RestStatus.NOT_FOUND)); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index 91532b7f2b7e1..f1a60d0d34b2c 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -57,6 +57,7 @@ import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeType; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.bulk.BulkRequest; import org.elasticsearch.action.bulk.BulkShardRequest; @@ -140,6 +141,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import static java.util.Collections.singletonMap; import static org.elasticsearch.client.RequestConverters.REQUEST_BODY_CONTENT_TYPE; @@ -1812,6 +1814,24 @@ public void testPutTemplateRequest() throws Exception { assertToXContentBody(putTemplateRequest, request.getEntity()); } + public void testGetTemplateRequest() throws Exception { + Map encodes = new HashMap<>(); + encodes.put("log", "log"); + encodes.put("1", "1"); + encodes.put("template#1", "template%231"); + encodes.put("template-*", "template-*"); + encodes.put("foo^bar", "foo%5Ebar"); + List names = randomSubsetOf(1, encodes.keySet()); + GetIndexTemplatesRequest getTemplatesRequest = new GetIndexTemplatesRequest().names(names.toArray(new String[0])); + Map expectedParams = new HashMap<>(); + setRandomMasterTimeout(getTemplatesRequest, expectedParams); + setRandomLocal(getTemplatesRequest, expectedParams); + Request request = RequestConverters.getTemplates(getTemplatesRequest); + assertThat(request.getEndpoint(), equalTo("/_template/" + names.stream().map(encodes::get).collect(Collectors.joining(",")))); + assertThat(request.getParameters(), equalTo(expectedParams)); + assertThat(request.getEntity(), nullValue()); + } + private static void assertToXContentBody(ToXContent expectedBody, HttpEntity actualEntity) throws IOException { BytesReference expectedBytes = XContentHelper.toXContent(expectedBody, REQUEST_BODY_CONTENT_TYPE, false); assertEquals(XContentType.JSON.mediaTypeWithoutParameters(), actualEntity.getContentType().getValue()); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java index 47c5c976fcc27..0114e58b7ca78 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java @@ -58,6 +58,8 @@ import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeType; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest; +import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse; import org.elasticsearch.action.support.ActiveShardCount; @@ -67,6 +69,7 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.SyncedFlushResponse; +import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.settings.Settings; @@ -82,11 +85,13 @@ import java.io.IOException; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; /** * This class is used to generate the Java Indices API documentation. @@ -1982,4 +1987,71 @@ public void onFailure(Exception e) { assertTrue(latch.await(30L, TimeUnit.SECONDS)); } + + public void testGetTemplates() throws Exception { + RestHighLevelClient client = highLevelClient(); + { + PutIndexTemplateRequest putRequest = new PutIndexTemplateRequest("my-template"); + putRequest.patterns(Arrays.asList("pattern-1", "log-*")); + putRequest.settings(Settings.builder().put("index.number_of_shards", 3).put("index.number_of_replicas", 1)); + putRequest.mapping("tweet", + "{\n" + + " \"tweet\": {\n" + + " \"properties\": {\n" + + " \"message\": {\n" + + " \"type\": \"text\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}", XContentType.JSON); + assertTrue(client.indices().putTemplate(putRequest, RequestOptions.DEFAULT).isAcknowledged()); + } + + // tag::get-templates-request + GetIndexTemplatesRequest request = new GetIndexTemplatesRequest("my-template"); // <1> + request.names("template-1", "template-2"); // <2> + request.names("my-*"); // <3> + // end::get-templates-request + + // tag::get-templates-request-masterTimeout + request.masterNodeTimeout(TimeValue.timeValueMinutes(1)); // <1> + request.masterNodeTimeout("1m"); // <2> + // end::get-templates-request-masterTimeout + + // tag::get-templates-execute + GetIndexTemplatesResponse getTemplatesResponse = client.indices().getTemplate(request, RequestOptions.DEFAULT); + // end::get-templates-execute + + // tag::get-templates-response + List templates = getTemplatesResponse.getIndexTemplates(); // <1> + // end::get-templates-response + + assertThat(templates, hasSize(1)); + assertThat(templates.get(0).name(), equalTo("my-template")); + + // tag::get-templates-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(GetIndexTemplatesResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::get-templates-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-templates-execute-async + client.indices().getTemplateAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::get-templates-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } } diff --git a/docs/java-rest/high-level/indices/get_templates.asciidoc b/docs/java-rest/high-level/indices/get_templates.asciidoc new file mode 100644 index 0000000000000..4a882bb53aa7d --- /dev/null +++ b/docs/java-rest/high-level/indices/get_templates.asciidoc @@ -0,0 +1,73 @@ +[[java-rest-high-get-templates]] +=== Get Templates API + +The Get Templates API allows to retrieve a list of index templates by name. + +[[java-rest-high-get-templates-request]] +==== Get Index Templates Request + +A `GetIndexTemplatesRequest` specifies one or several names of the index templates to get. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-templates-request] +-------------------------------------------------- +<1> A single index template name +<2> Multiple index template names +<3> An index template name using wildcard + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-templates-request-masterTimeout] +-------------------------------------------------- +<1> Timeout to connect to the master node as a `TimeValue` +<2> Timeout to connect to the master node as a `String` + +[[java-rest-high-get-templates-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-templates-execute] +-------------------------------------------------- + +[[java-rest-high-get-templates-async]] +==== Asynchronous Execution + +The asynchronous execution of a get index templates request requires a `GetTemplatesRequest` +instance and an `ActionListener` instance to be passed to the asynchronous +method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-templates-execute-async] +-------------------------------------------------- +<1> The `GetTemplatesRequest` to execute and the `ActionListener` to use when +the execution completes + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for `GetTemplatesResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-templates-execute-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of failure. The raised exception is provided as an argument + +[[java-rest-high-get-templates-response]] +==== Get Templates Response + +The returned `GetTemplatesResponse` consists a list of matching index templates. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-templates-response] +-------------------------------------------------- +<1> A list of matching index templates + diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 064cd401721ac..24f8fefff3430 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -99,8 +99,9 @@ include::indices/get_mappings.asciidoc[] include::indices/update_aliases.asciidoc[] include::indices/exists_alias.asciidoc[] include::indices/put_settings.asciidoc[] -include::indices/put_template.asciidoc[] include::indices/get_settings.asciidoc[] +include::indices/put_template.asciidoc[] +include::indices/get_templates.asciidoc[] == Cluster APIs diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java index 3c5fb36d6c6aa..2bdc966c74e59 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponse.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.ArrayList; @@ -34,9 +35,10 @@ public class GetIndexTemplatesResponse extends ActionResponse implements ToXContentObject { - private List indexTemplates; + private final List indexTemplates; GetIndexTemplatesResponse() { + indexTemplates = new ArrayList<>(); } GetIndexTemplatesResponse(List indexTemplates) { @@ -51,7 +53,7 @@ public List getIndexTemplates() { public void readFrom(StreamInput in) throws IOException { super.readFrom(in); int size = in.readVInt(); - indexTemplates = new ArrayList<>(size); + indexTemplates.clear(); for (int i = 0 ; i < size ; i++) { indexTemplates.add(0, IndexTemplateMetaData.readFrom(in)); } @@ -76,4 +78,15 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws builder.endObject(); return builder; } + + public static GetIndexTemplatesResponse fromXContent(XContentParser parser) throws IOException { + final List templates = new ArrayList<>(); + for (XContentParser.Token token = parser.nextToken(); token != XContentParser.Token.END_OBJECT; token = parser.nextToken()) { + if (token == XContentParser.Token.FIELD_NAME) { + final IndexTemplateMetaData templateMetaData = IndexTemplateMetaData.Builder.fromXContent(parser, parser.currentName()); + templates.add(templateMetaData); + } + } + return new GetIndexTemplatesResponse(templates); + } } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponseTests.java new file mode 100644 index 0000000000000..6cb26967d07fa --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/template/get/GetIndexTemplatesResponseTests.java @@ -0,0 +1,89 @@ +/* + * 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.action.admin.indices.template.get; + +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.hamcrest.Matchers.equalTo; + +public class GetIndexTemplatesResponseTests extends AbstractXContentTestCase { + @Override + protected GetIndexTemplatesResponse doParseInstance(XContentParser parser) throws IOException { + return GetIndexTemplatesResponse.fromXContent(parser); + } + + @Override + protected GetIndexTemplatesResponse createTestInstance() { + List templates = new ArrayList<>(); + int numTemplates = between(0, 10); + for (int t = 0; t < numTemplates; t++) { + IndexTemplateMetaData.Builder templateBuilder = IndexTemplateMetaData.builder("template-" + t); + templateBuilder.patterns(IntStream.range(0, between(1, 5)).mapToObj(i -> "pattern-" + i).collect(Collectors.toList())); + int numAlias = between(0, 5); + for (int i = 0; i < numAlias; i++) { + templateBuilder.putAlias(AliasMetaData.builder(randomAlphaOfLengthBetween(1, 10))); + } + if (randomBoolean()) { + templateBuilder.settings(Settings.builder().put("index.setting-1", randomLong())); + } + if (randomBoolean()) { + templateBuilder.order(randomInt()); + } + if (randomBoolean()) { + templateBuilder.version(between(0, 100)); + } + if (randomBoolean()) { + try { + templateBuilder.putMapping("doc", "{\"doc\":{\"properties\":{\"type\":\"text\"}}}"); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + templates.add(templateBuilder.build()); + } + return new GetIndexTemplatesResponse(templates); + } + + @Override + protected boolean supportsUnknownFields() { + // We can not inject anything at the top level because a GetIndexTemplatesResponse is serialized as a map + // from template name to template content. IndexTemplateMetaDataTests already covers situations where we + // inject arbitrary things inside the IndexTemplateMetaData. + return false; + } + + @Override + protected void assertEqualInstances(GetIndexTemplatesResponse expectedInstance, GetIndexTemplatesResponse newInstance) { + assertNotSame(newInstance, expectedInstance); + assertThat(new HashSet<>(newInstance.getIndexTemplates()), equalTo(new HashSet<>(expectedInstance.getIndexTemplates()))); + } +} From b31268c0486f10e8d4459ece3246bf1218862c3a Mon Sep 17 00:00:00 2001 From: olcbean <26058559+olcbean@users.noreply.github.com> Date: Tue, 12 Jun 2018 10:26:17 +0200 Subject: [PATCH 43/71] Add Get Aliases API to the high-level REST client (#28799) Given the weirdness of the response returned by the get alias API, we went for a client specific response, which allows us to hold the error message, exception and status returned as part of the response together with aliases. See #30536 . Relates to #27205 --- .../client/GetAliasesResponse.java | 199 +++++++++++++++ .../elasticsearch/client/IndicesClient.java | 29 +++ .../client/RequestConverters.java | 11 + .../client/RestHighLevelClient.java | 16 +- .../client/GetAliasesResponseTests.java | 175 +++++++++++++ .../elasticsearch/client/IndicesClientIT.java | 233 ++++++++++++++++-- .../client/RequestConvertersTests.java | 31 +++ .../IndicesClientDocumentationIT.java | 76 +++++- .../high-level/indices/get_alias.asciidoc | 94 +++++++ .../high-level/supported-apis.asciidoc | 2 + .../elasticsearch/ElasticsearchException.java | 2 +- .../indices/alias/get/GetAliasesResponse.java | 19 +- .../alias/get/TransportGetAliasesAction.java | 1 - .../cluster/metadata/AliasMetaData.java | 1 - .../admin/indices/RestGetAliasesAction.java | 19 +- .../alias/get/GetAliasesResponseTests.java | 139 +++++++++++ .../elasticsearch/test/XContentTestUtils.java | 2 +- 17 files changed, 1001 insertions(+), 48 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/GetAliasesResponse.java create mode 100644 client/rest-high-level/src/test/java/org/elasticsearch/client/GetAliasesResponseTests.java create mode 100644 docs/java-rest/high-level/indices/get_alias.asciidoc create mode 100644 server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponseTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/GetAliasesResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/GetAliasesResponse.java new file mode 100644 index 0000000000000..26bdcd2d26779 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/GetAliasesResponse.java @@ -0,0 +1,199 @@ +/* + * 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; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.common.xcontent.StatusToXContentObject; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.rest.RestStatus; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * Response obtained from the get aliases API. + * The format is pretty horrible as it holds aliases, but at the same time errors can come back through the status and error fields. + * Such errors are mostly 404 - NOT FOUND for aliases that were specified but not found. In such case the client won't throw exception + * so it allows to retrieve the returned aliases, while at the same time checking if errors were returned. + * There's also the case where an exception is returned, like for instance an {@link org.elasticsearch.index.IndexNotFoundException}. + * We would usually throw such exception, but we configure the client to not throw for 404 to support the case above, hence we also not + * throw in case an index is not found, although it is a hard error that doesn't come back with aliases. + */ +public class GetAliasesResponse extends ActionResponse implements StatusToXContentObject { + + private final RestStatus status; + private final String error; + private final ElasticsearchException exception; + + private final Map> aliases; + + GetAliasesResponse(RestStatus status, String error, Map> aliases) { + this.status = status; + this.error = error; + this.aliases = aliases; + this.exception = null; + } + + private GetAliasesResponse(RestStatus status, ElasticsearchException exception) { + this.status = status; + this.error = null; + this.aliases = Collections.emptyMap(); + this.exception = exception; + } + + @Override + public RestStatus status() { + return status; + } + + /** + * Return the possibly returned error, null otherwise + */ + public String getError() { + return error; + } + + /** + * Return the exception that may have been returned + */ + public ElasticsearchException getException() { + return exception; + } + + /** + * Return the requested aliases + */ + public Map> getAliases() { + return aliases; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + { + if (status != RestStatus.OK) { + builder.field("error", error); + builder.field("status", status.getStatus()); + } + + for (Map.Entry> entry : aliases.entrySet()) { + builder.startObject(entry.getKey()); + { + builder.startObject("aliases"); + { + for (final AliasMetaData alias : entry.getValue()) { + AliasMetaData.Builder.toXContent(alias, builder, ToXContent.EMPTY_PARAMS); + } + } + builder.endObject(); + } + builder.endObject(); + } + } + builder.endObject(); + return builder; + } + + /** + * Parse the get aliases response + */ + public static GetAliasesResponse fromXContent(XContentParser parser) throws IOException { + if (parser.currentToken() == null) { + parser.nextToken(); + } + ensureExpectedToken(Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation); + Map> aliases = new HashMap<>(); + + String currentFieldName; + Token token; + String error = null; + ElasticsearchException exception = null; + RestStatus status = RestStatus.OK; + + while (parser.nextToken() != Token.END_OBJECT) { + if (parser.currentToken() == Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + + if ("status".equals(currentFieldName)) { + if ((token = parser.nextToken()) != Token.FIELD_NAME) { + ensureExpectedToken(Token.VALUE_NUMBER, token, parser::getTokenLocation); + status = RestStatus.fromCode(parser.intValue()); + } + } else if ("error".equals(currentFieldName)) { + token = parser.nextToken(); + if (token == Token.VALUE_STRING) { + error = parser.text(); + } else if (token == Token.START_OBJECT) { + parser.nextToken(); + exception = ElasticsearchException.innerFromXContent(parser, true); + } else if (token == Token.START_ARRAY) { + parser.skipChildren(); + } + } else { + String indexName = parser.currentName(); + if (parser.nextToken() == Token.START_OBJECT) { + Set parseInside = parseAliases(parser); + aliases.put(indexName, parseInside); + } + } + } + } + if (exception != null) { + assert error == null; + assert aliases.isEmpty(); + return new GetAliasesResponse(status, exception); + } + return new GetAliasesResponse(status, error, aliases); + } + + private static Set parseAliases(XContentParser parser) throws IOException { + Set aliases = new HashSet<>(); + Token token; + String currentFieldName = null; + while ((token = parser.nextToken()) != Token.END_OBJECT) { + if (token == Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == Token.START_OBJECT) { + if ("aliases".equals(currentFieldName)) { + while (parser.nextToken() != Token.END_OBJECT) { + AliasMetaData fromXContent = AliasMetaData.Builder.fromXContent(parser); + aliases.add(fromXContent); + } + } else { + parser.skipChildren(); + } + } else if (token == Token.START_ARRAY) { + parser.skipChildren(); + } + } + return aliases; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java index 59d2cf1392d30..5d0376efce5f6 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java @@ -58,11 +58,13 @@ import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse; +import org.elasticsearch.rest.RestStatus; import java.io.IOException; import java.util.Collections; import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; /** * A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Indices API. @@ -978,6 +980,33 @@ public void rolloverAsync(RolloverRequest rolloverRequest, ActionListener Indices Aliases API on + * elastic.co + * @param getAliasesRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public GetAliasesResponse getAlias(GetAliasesRequest getAliasesRequest, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(getAliasesRequest, RequestConverters::getAlias, options, + GetAliasesResponse::fromXContent, singleton(RestStatus.NOT_FOUND.getStatus())); + } + + /** + * Asynchronously gets one or more aliases using the Get Index Aliases API. + * See Indices Aliases API on + * elastic.co + * @param getAliasesRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void getAliasAsync(GetAliasesRequest getAliasesRequest, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(getAliasesRequest, RequestConverters::getAlias, options, + GetAliasesResponse::fromXContent, listener, singleton(RestStatus.NOT_FOUND.getStatus())); + } + /** * Updates specific index level settings using the Update Indices Settings API. * See Update Indices Settings diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index eceaf67a41ad8..266acbf7394cf 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -852,6 +852,17 @@ static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) t return request; } + static Request getAlias(GetAliasesRequest getAliasesRequest) { + String[] indices = getAliasesRequest.indices() == null ? Strings.EMPTY_ARRAY : getAliasesRequest.indices(); + String[] aliases = getAliasesRequest.aliases() == null ? Strings.EMPTY_ARRAY : getAliasesRequest.aliases(); + String endpoint = endpoint(indices, "_alias", aliases); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + Params params = new Params(request); + params.withIndicesOptions(getAliasesRequest.indicesOptions()); + params.withLocal(getAliasesRequest.local()); + return request; + } + private static HttpEntity createEntity(ToXContent toXContent, XContentType xContentType) throws IOException { BytesRef source = XContentHelper.toXContent(toXContent, xContentType, false).toBytesRef(); return new ByteArrayEntity(source.bytes, source.offset, source.length, createContentType(xContentType)); 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 8980508c48738..0084ce0f90d74 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 @@ -1021,10 +1021,10 @@ protected final Resp performRequest(Req reques try { return responseConverter.apply(e.getResponse()); } catch (Exception innerException) { - //the exception is ignored as we now try to parse the response as an error. - //this covers cases like get where 404 can either be a valid document not found response, - //or an error for which parsing is completely different. We try to consider the 404 response as a valid one - //first. If parsing of the response breaks, we fall back to parsing it as an error. + // the exception is ignored as we now try to parse the response as an error. + // this covers cases like get where 404 can either be a valid document not found response, + // or an error for which parsing is completely different. We try to consider the 404 response as a valid one + // first. If parsing of the response breaks, we fall back to parsing it as an error. throw parseResponseException(e); } } @@ -1109,10 +1109,10 @@ public void onFailure(Exception exception) { try { actionListener.onResponse(responseConverter.apply(response)); } catch (Exception innerException) { - //the exception is ignored as we now try to parse the response as an error. - //this covers cases like get where 404 can either be a valid document not found response, - //or an error for which parsing is completely different. We try to consider the 404 response as a valid one - //first. If parsing of the response breaks, we fall back to parsing it as an error. + // the exception is ignored as we now try to parse the response as an error. + // this covers cases like get where 404 can either be a valid document not found response, + // or an error for which parsing is completely different. We try to consider the 404 response as a valid one + // first. If parsing of the response breaks, we fall back to parsing it as an error. actionListener.onFailure(parseResponseException(responseException)); } } else { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/GetAliasesResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/GetAliasesResponseTests.java new file mode 100644 index 0000000000000..5f3354ad2b95d --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/GetAliasesResponseTests.java @@ -0,0 +1,175 @@ +/* + * 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; + +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +public class GetAliasesResponseTests extends AbstractXContentTestCase { + + @Override + protected GetAliasesResponse createTestInstance() { + RestStatus status = randomFrom(RestStatus.OK, RestStatus.NOT_FOUND); + String errorMessage = RestStatus.OK == status ? null : randomAlphaOfLengthBetween(5, 10); + return new GetAliasesResponse(status, errorMessage, createIndicesAliasesMap(0, 5)); + } + + private static Map> createIndicesAliasesMap(int min, int max) { + Map> map = new HashMap<>(); + int indicesNum = randomIntBetween(min, max); + for (int i = 0; i < indicesNum; i++) { + String index = randomAlphaOfLength(5); + Set aliasMetaData = new HashSet<>(); + int aliasesNum = randomIntBetween(0, 3); + for (int alias = 0; alias < aliasesNum; alias++) { + aliasMetaData.add(createAliasMetaData()); + } + map.put(index, aliasMetaData); + } + return map; + } + + private static AliasMetaData createAliasMetaData() { + AliasMetaData.Builder builder = AliasMetaData.builder(randomAlphaOfLengthBetween(3, 10)); + if (randomBoolean()) { + builder.routing(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.searchRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.indexRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.filter("{\"term\":{\"year\":2016}}"); + } + return builder.build(); + } + + @Override + protected GetAliasesResponse doParseInstance(XContentParser parser) throws IOException { + return GetAliasesResponse.fromXContent(parser); + } + + @Override + protected Predicate getRandomFieldsExcludeFilter() { + return p -> p.equals("") // do not add elements at the top-level as any element at this level is parsed as a new index + || p.endsWith(".aliases") // do not add new alias + || p.contains(".filter"); // do not insert random data into AliasMetaData#filter + } + + @Override + protected boolean supportsUnknownFields() { + return true; + } + + @Override + protected void assertEqualInstances(GetAliasesResponse expectedInstance, GetAliasesResponse newInstance) { + assertEquals(expectedInstance.getAliases(), newInstance.getAliases()); + assertEquals(expectedInstance.status(), newInstance.status()); + assertEquals(expectedInstance.getError(), newInstance.getError()); + assertNull(expectedInstance.getException()); + assertNull(newInstance.getException()); + } + + public void testFromXContentWithElasticsearchException() throws IOException { + String xContent = + "{" + + " \"error\": {" + + " \"root_cause\": [" + + " {" + + " \"type\": \"index_not_found_exception\"," + + " \"reason\": \"no such index\"," + + " \"resource.type\": \"index_or_alias\"," + + " \"resource.id\": \"index\"," + + " \"index_uuid\": \"_na_\"," + + " \"index\": \"index\"" + + " }" + + " ]," + + " \"type\": \"index_not_found_exception\"," + + " \"reason\": \"no such index\"," + + " \"resource.type\": \"index_or_alias\"," + + " \"resource.id\": \"index\"," + + " \"index_uuid\": \"_na_\"," + + " \"index\": \"index\"" + + " }," + + " \"status\": 404" + + "}"; + + try (XContentParser parser = createParser(JsonXContent.jsonXContent, xContent)) { + GetAliasesResponse getAliasesResponse = GetAliasesResponse.fromXContent(parser); + assertThat(getAliasesResponse.getError(), nullValue()); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + } + + public void testFromXContentWithNoAliasFound() throws IOException { + String xContent = + "{" + + " \"error\": \"alias [aa] missing\"," + + " \"status\": 404" + + "}"; + try (XContentParser parser = createParser(JsonXContent.jsonXContent, xContent)) { + GetAliasesResponse getAliasesResponse = GetAliasesResponse.fromXContent(parser); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getError(), equalTo("alias [aa] missing")); + assertThat(getAliasesResponse.getException(), nullValue()); + } + } + + public void testFromXContentWithMissingAndFoundAlias() throws IOException { + String xContent = + "{" + + " \"error\": \"alias [something] missing\"," + + " \"status\": 404," + + " \"index\": {" + + " \"aliases\": {" + + " \"alias\": {}" + + " }" + + " }" + + "}"; + final String index = "index"; + try (XContentParser parser = createParser(JsonXContent.jsonXContent, xContent)) { + GetAliasesResponse response = GetAliasesResponse.fromXContent(parser); + assertThat(response.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(response.getError(), equalTo("alias [something] missing")); + assertThat(response.getAliases().size(), equalTo(1)); + assertThat(response.getAliases().get(index).size(), equalTo(1)); + AliasMetaData aliasMetaData = response.getAliases().get(index).iterator().next(); + assertThat(aliasMetaData.alias(), equalTo("alias")); + assertThat(response.getException(), nullValue()); + } + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java index 75cfd8e05deb5..4a782d76ab620 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java @@ -53,10 +53,10 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshResponse; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeType; @@ -68,6 +68,7 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.broadcast.BroadcastResponse; +import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.common.ValidationException; @@ -99,6 +100,8 @@ import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; public class IndicesClientIT extends ESRestHighLevelClientTestCase { @@ -321,28 +324,27 @@ public void testGetSettingsWithDefaultsFiltered() throws IOException { assertEquals(0, getSettingsResponse.getIndexToSettings().get("get_settings_index").size()); assertEquals(1, getSettingsResponse.getIndexToDefaultSettings().get("get_settings_index").size()); } + public void testPutMapping() throws IOException { - { - // Add mappings to index - String indexName = "mapping_index"; - createIndex(indexName, Settings.EMPTY); + // Add mappings to index + String indexName = "mapping_index"; + createIndex(indexName, Settings.EMPTY); - PutMappingRequest putMappingRequest = new PutMappingRequest(indexName); - putMappingRequest.type("type_name"); - XContentBuilder mappingBuilder = JsonXContent.contentBuilder(); - mappingBuilder.startObject().startObject("properties").startObject("field"); - mappingBuilder.field("type", "text"); - mappingBuilder.endObject().endObject().endObject(); - putMappingRequest.source(mappingBuilder); + PutMappingRequest putMappingRequest = new PutMappingRequest(indexName); + putMappingRequest.type("type_name"); + XContentBuilder mappingBuilder = JsonXContent.contentBuilder(); + mappingBuilder.startObject().startObject("properties").startObject("field"); + mappingBuilder.field("type", "text"); + mappingBuilder.endObject().endObject().endObject(); + putMappingRequest.source(mappingBuilder); - PutMappingResponse putMappingResponse = - execute(putMappingRequest, highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync, - highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync); - assertTrue(putMappingResponse.isAcknowledged()); + PutMappingResponse putMappingResponse = + execute(putMappingRequest, highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync, + highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync); + assertTrue(putMappingResponse.isAcknowledged()); - Map getIndexResponse = getAsMap(indexName); - assertEquals("text", XContentMapValues.extractValue(indexName + ".mappings.type_name.properties.field.type", getIndexResponse)); - } + Map getIndexResponse = getAsMap(indexName); + assertEquals("text", XContentMapValues.extractValue(indexName + ".mappings.type_name.properties.field.type", getIndexResponse)); } public void testGetMapping() throws IOException { @@ -850,6 +852,197 @@ public void testRollover() throws IOException { } } + public void testGetAlias() throws IOException { + { + createIndex("index1", Settings.EMPTY); + client().performRequest(HttpPut.METHOD_NAME, "/index1/_alias/alias1"); + + createIndex("index2", Settings.EMPTY); + client().performRequest(HttpPut.METHOD_NAME, "/index2/_alias/alias2"); + + createIndex("index3", Settings.EMPTY); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("alias1"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(1)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData, notNullValue()); + assertThat(aliasMetaData.alias(), equalTo("alias1")); + assertThat(aliasMetaData.getFilter(), nullValue()); + assertThat(aliasMetaData.getIndexRouting(), nullValue()); + assertThat(aliasMetaData.getSearchRouting(), nullValue()); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("alias*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(2)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("_all"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(2)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(2)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices("_all"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(3)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + assertThat(getAliasesResponse.getAliases().get("index3").size(), equalTo(0)); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices("ind*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(3)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + assertThat(getAliasesResponse.getAliases().get("index3").size(), equalTo(0)); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest(); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(3)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + assertThat(getAliasesResponse.getAliases().get("index3").size(), equalTo(0)); + } + } + + public void testGetAliasesNonExistentIndexOrAlias() throws IOException { + /* + * This test is quite extensive as this is the only way we can check that we haven't slid out of sync with the server + * because the server renders the xcontent in a spot that is difficult for us to access in a unit test. + */ + String alias = "alias"; + String index = "index"; + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest(alias); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getError(), equalTo("alias [" + alias + "] missing")); + } + createIndex(index, Settings.EMPTY); + client().performRequest(HttpPut.METHOD_NAME, index + "/_alias/" + alias); + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index, "non_existent_index"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index, "non_existent_index").aliases(alias); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices("non_existent_index*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.getAliases().size(), equalTo(0)); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index).aliases(alias, "non_existent_alias"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(1)); + assertThat(getAliasesResponse.getAliases().get(index).size(), equalTo(1)); + AliasMetaData aliasMetaData = getAliasesResponse.getAliases().get(index).iterator().next(); + assertThat(aliasMetaData, notNullValue()); + assertThat(aliasMetaData.alias(), equalTo(alias)); + /* + This is the above response in json format: + { + "error": "alias [something] missing", + "status": 404, + "index": { + "aliases": { + "alias": {} + } + } + } + */ + } + } + public void testIndexPutSettings() throws IOException { final Setting dynamicSetting = IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index f1a60d0d34b2c..04bf37d17b8a9 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -94,6 +94,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; @@ -1596,6 +1597,36 @@ public void testRollover() throws IOException { assertEquals(expectedParams, request.getParameters()); } + public void testGetAlias() { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest(); + + Map expectedParams = new HashMap<>(); + setRandomLocal(getAliasesRequest, expectedParams); + setRandomIndicesOptions(getAliasesRequest::indicesOptions, getAliasesRequest::indicesOptions, expectedParams); + + String[] indices = randomBoolean() ? null : randomIndicesNames(0, 2); + String[] aliases = randomBoolean() ? null : randomIndicesNames(0, 2); + getAliasesRequest.indices(indices); + getAliasesRequest.aliases(aliases); + + Request request = RequestConverters.getAlias(getAliasesRequest); + StringJoiner expectedEndpoint = new StringJoiner("/", "/", ""); + + if (false == CollectionUtils.isEmpty(indices)) { + expectedEndpoint.add(String.join(",", indices)); + } + expectedEndpoint.add("_alias"); + + if (false == CollectionUtils.isEmpty(aliases)) { + expectedEndpoint.add(String.join(",", aliases)); + } + + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + assertEquals(expectedEndpoint.toString(), request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertNull(request.getEntity()); + } + public void testIndexPutSettings() throws IOException { String[] indices = randomBoolean() ? null : randomIndicesNames(0, 2); UpdateSettingsRequest updateSettingsRequest = new UpdateSettingsRequest(indices); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java index 0114e58b7ca78..00cf2659d36d1 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java @@ -51,10 +51,10 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshResponse; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeType; @@ -66,10 +66,12 @@ import org.elasticsearch.action.support.DefaultShardOperationFailedException; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.ESRestHighLevelClientTestCase; +import org.elasticsearch.client.GetAliasesResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.SyncedFlushResponse; import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; +import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.settings.Settings; @@ -87,6 +89,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -1725,6 +1728,75 @@ public void onFailure(Exception e) { assertTrue(latch.await(30L, TimeUnit.SECONDS)); } + public void testGetAlias() throws Exception { + RestHighLevelClient client = highLevelClient(); + + { + CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("index").alias(new Alias("alias"))); + assertTrue(createIndexResponse.isAcknowledged()); + } + + { + // tag::get-alias-request + GetAliasesRequest request = new GetAliasesRequest(); + GetAliasesRequest requestWithAlias = new GetAliasesRequest("alias1"); + GetAliasesRequest requestWithAliases = + new GetAliasesRequest(new String[]{"alias1", "alias2"}); + // end::get-alias-request + + // tag::get-alias-request-alias + request.aliases("alias"); // <1> + // end::get-alias-request-alias + // tag::get-alias-request-indices + request.indices("index"); // <1> + // end::get-alias-request-indices + + // tag::get-alias-request-indicesOptions + request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1> + // end::get-alias-request-indicesOptions + + // tag::get-alias-request-local + request.local(true); // <1> + // end::get-alias-request-local + + // tag::get-alias-execute + GetAliasesResponse response = client.indices().getAlias(request, RequestOptions.DEFAULT); + // end::get-alias-execute + + // tag::get-alias-response + Map> aliases = response.getAliases(); // <1> + // end::get-alias-response + + assertThat(response.getAliases().get("index").size(), equalTo(1)); + assertThat(response.getAliases().get("index").iterator().next().alias(), equalTo("alias")); + + // tag::get-alias-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(GetAliasesResponse getAliasesResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::get-alias-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::get-alias-execute-async + client.indices().getAliasAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::get-alias-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + public void testIndexPutSettings() throws Exception { RestHighLevelClient client = highLevelClient(); diff --git a/docs/java-rest/high-level/indices/get_alias.asciidoc b/docs/java-rest/high-level/indices/get_alias.asciidoc new file mode 100644 index 0000000000000..77a336e0a024c --- /dev/null +++ b/docs/java-rest/high-level/indices/get_alias.asciidoc @@ -0,0 +1,94 @@ +[[java-rest-high-get-alias]] +=== Get Alias API + +[[java-rest-high-get-alias-request]] +==== Get Alias Request + +The Get Alias API uses `GetAliasesRequest` as its request object. +One or more aliases can be optionally provided either at construction +time or later on through the relevant setter method. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request] +-------------------------------------------------- + +==== Optional arguments +The following arguments can optionally be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-alias] +-------------------------------------------------- +<1> One or more aliases to retrieve + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-indices] +-------------------------------------------------- +<1> The index or indices that the alias is associated with + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-indicesOptions] +-------------------------------------------------- +<1> Setting `IndicesOptions` controls how unavailable indices are resolved and +how wildcard expressions are expanded when looking for aliases that belong to +specified indices. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-local] +-------------------------------------------------- +<1> The `local` flag (defaults to `false`) controls whether the aliases need +to be looked up in the local cluster state or in the cluster state held by +the elected master node + +[[java-rest-high-get-alias-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-execute] +-------------------------------------------------- + +[[java-rest-high-get-alias-async]] +==== Asynchronous Execution + +The asynchronous execution of a get alias request requires both a `GetAliasesRequest` +instance and an `ActionListener` instance to be passed to the asynchronous +method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-execute-async] +-------------------------------------------------- +<1> The `GetAliasesRequest` to execute and the `ActionListener` to use when +the execution completes + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for the `Boolean` response looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of failure. The raised exception is provided as an argument + +[[java-rest-high-get-alias-response]] +==== Get Alias Response + +The returned `GetAliasesResponse` allows to retrieve information about the +executed operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-response] +-------------------------------------------------- +<1> Retrieves a map of indices and their aliases \ 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 24f8fefff3430..465ae20619de3 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -80,6 +80,7 @@ Mapping Management:: Alias Management:: * <> * <> +* <> include::indices/create_index.asciidoc[] include::indices/delete_index.asciidoc[] @@ -98,6 +99,7 @@ include::indices/put_mapping.asciidoc[] include::indices/get_mappings.asciidoc[] include::indices/update_aliases.asciidoc[] include::indices/exists_alias.asciidoc[] +include::indices/get_alias.asciidoc[] include::indices/put_settings.asciidoc[] include::indices/get_settings.asciidoc[] include::indices/put_template.asciidoc[] diff --git a/server/src/main/java/org/elasticsearch/ElasticsearchException.java b/server/src/main/java/org/elasticsearch/ElasticsearchException.java index e0234d70bdc9c..a4f9892debec0 100644 --- a/server/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/server/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -420,7 +420,7 @@ public static ElasticsearchException fromXContent(XContentParser parser) throws return innerFromXContent(parser, false); } - private static ElasticsearchException innerFromXContent(XContentParser parser, boolean parseRootCauses) throws IOException { + public static ElasticsearchException innerFromXContent(XContentParser parser, boolean parseRootCauses) throws IOException { XContentParser.Token token = parser.currentToken(); ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java index 5a63ce8d86916..d0ad58b6e351c 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; public class GetAliasesResponse extends ActionResponse { @@ -42,7 +43,6 @@ public GetAliasesResponse(ImmutableOpenMap> aliases) GetAliasesResponse() { } - public ImmutableOpenMap> getAliases() { return aliases; } @@ -76,4 +76,21 @@ public void writeTo(StreamOutput out) throws IOException { } } } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GetAliasesResponse that = (GetAliasesResponse) o; + return Objects.equals(aliases, that.aliases); + } + + @Override + public int hashCode() { + return Objects.hash(aliases); + } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java index 7d6f73f0b0c21..43e4026833772 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java @@ -65,5 +65,4 @@ protected void masterOperation(GetAliasesRequest request, ClusterState state, Ac ImmutableOpenMap> result = state.metaData().findAliases(request.aliases(), concreteIndices); listener.onResponse(new GetAliasesResponse(result)); } - } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java b/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java index 92a3db82d6cc7..945c94bcd642d 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java @@ -348,5 +348,4 @@ public static AliasMetaData fromXContent(XContentParser parser) throws IOExcepti return builder.build(); } } - } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java index e5442c9a2f43f..b24729f50d5f4 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.rest.action.admin.indices; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; - import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; import org.elasticsearch.action.support.IndicesOptions; @@ -84,6 +83,8 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC getAliasesRequest.indicesOptions(IndicesOptions.fromRequest(request, getAliasesRequest.indicesOptions())); getAliasesRequest.local(request.paramAsBoolean("local", getAliasesRequest.local())); + //we may want to move this logic to TransportGetAliasesAction but it is based on the original provided aliases, which will + //not always be available there (they may get replaced so retrieving request.aliases is not quite the same). return channel -> client.admin().indices().getAliases(getAliasesRequest, new RestBuilderListener(channel) { @Override public RestResponse buildResponse(GetAliasesResponse response, XContentBuilder builder) throws Exception { @@ -127,9 +128,11 @@ public RestResponse buildResponse(GetAliasesResponse response, XContentBuilder b status = RestStatus.NOT_FOUND; final String message; if (difference.size() == 1) { - message = String.format(Locale.ROOT, "alias [%s] missing", toNamesString(difference.iterator().next())); + message = String.format(Locale.ROOT, "alias [%s] missing", + Strings.collectionToCommaDelimitedString(difference)); } else { - message = String.format(Locale.ROOT, "aliases [%s] missing", toNamesString(difference.toArray(new String[0]))); + message = String.format(Locale.ROOT, "aliases [%s] missing", + Strings.collectionToCommaDelimitedString(difference)); } builder.field("error", message); builder.field("status", status.getStatus()); @@ -158,14 +161,4 @@ public RestResponse buildResponse(GetAliasesResponse response, XContentBuilder b }); } - private static String toNamesString(final String... names) { - if (names == null || names.length == 0) { - return ""; - } else if (names.length == 1) { - return names[0]; - } else { - return Arrays.stream(names).collect(Collectors.joining(",")); - } - } - } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponseTests.java new file mode 100644 index 0000000000000..6c15c419d092c --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponseTests.java @@ -0,0 +1,139 @@ +/* + * 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.action.admin.indices.alias.get; + +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.cluster.metadata.AliasMetaData.Builder; +import org.elasticsearch.common.collect.ImmutableOpenMap; +import org.elasticsearch.test.AbstractStreamableTestCase; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class GetAliasesResponseTests extends AbstractStreamableTestCase { + + @Override + protected GetAliasesResponse createTestInstance() { + return createTestItem(); + } + + @Override + protected GetAliasesResponse createBlankInstance() { + return new GetAliasesResponse(); + } + + @Override + protected GetAliasesResponse mutateInstance(GetAliasesResponse response) { + return new GetAliasesResponse(mutateAliases(response.getAliases())); + } + + private static ImmutableOpenMap> mutateAliases(ImmutableOpenMap> aliases) { + if (aliases.isEmpty()) { + return createIndicesAliasesMap(1, 3).build(); + } + + if (randomBoolean()) { + ImmutableOpenMap.Builder> builder = ImmutableOpenMap.builder(aliases); + ImmutableOpenMap> list = createIndicesAliasesMap(1, 2).build(); + list.forEach(e -> builder.put(e.key, e.value)); + return builder.build(); + } + + Set indices = new HashSet<>(); + Iterator keys = aliases.keysIt(); + while (keys.hasNext()) { + indices.add(keys.next()); + } + + List indicesToBeModified = randomSubsetOf(randomIntBetween(1, indices.size()), indices); + ImmutableOpenMap.Builder> builder = ImmutableOpenMap.builder(); + + for (String index : indices) { + List list = new ArrayList<>(aliases.get(index)); + if (indicesToBeModified.contains(index)) { + if (randomBoolean() || list.isEmpty()) { + list.add(createAliasMetaData()); + } else { + int aliasIndex = randomInt(list.size() - 1); + AliasMetaData aliasMetaData = list.get(aliasIndex); + list.add(aliasIndex, mutateAliasMetaData(aliasMetaData)); + } + } + builder.put(index, list); + } + return builder.build(); + } + + private static GetAliasesResponse createTestItem() { + return new GetAliasesResponse(createIndicesAliasesMap(0, 5).build()); + } + + private static ImmutableOpenMap.Builder> createIndicesAliasesMap(int min, int max) { + ImmutableOpenMap.Builder> builder = ImmutableOpenMap.builder(); + int indicesNum = randomIntBetween(min, max); + for (int i = 0; i < indicesNum; i++) { + String index = randomAlphaOfLength(5); + List aliasMetaData = new ArrayList<>(); + int aliasesNum = randomIntBetween(0, 3); + for (int alias = 0; alias < aliasesNum; alias++) { + aliasMetaData.add(createAliasMetaData()); + } + builder.put(index, aliasMetaData); + } + return builder; + } + + public static AliasMetaData createAliasMetaData() { + Builder builder = AliasMetaData.builder(randomAlphaOfLengthBetween(3, 10)); + if (randomBoolean()) { + builder.routing(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.searchRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.indexRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.filter("{\"term\":{\"year\":2016}}"); + } + return builder.build(); + } + + public static AliasMetaData mutateAliasMetaData(AliasMetaData alias) { + boolean changeAlias = randomBoolean(); + AliasMetaData.Builder builder = AliasMetaData.builder(changeAlias ? randomAlphaOfLengthBetween(2, 5) : alias.getAlias()); + builder.searchRouting(alias.searchRouting()); + builder.indexRouting(alias.indexRouting()); + builder.filter(alias.filter()); + + if (false == changeAlias) { + if (randomBoolean()) { + builder.searchRouting(alias.searchRouting() + randomAlphaOfLengthBetween(1, 3)); + } else { + builder.indexRouting(alias.indexRouting() + randomAlphaOfLengthBetween(1, 3)); + } + } + return builder.build(); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java b/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java index 724a99f2c9425..7e80810d7dd27 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java @@ -60,7 +60,7 @@ public static Map convertToMap(ToXContent part) throws IOExcepti /** - * Compares to maps generated from XContentObjects. The order of elements in arrays is ignored. + * Compares two maps generated from XContentObjects. The order of elements in arrays is ignored. * * @return null if maps are equal or path to the element where the difference was found */ From 9d3f2787ebfb3b865a296fa7ae97571237de1568 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Tue, 12 Jun 2018 10:27:51 +0200 Subject: [PATCH 44/71] SyncedFlushResponse to implement ToXContentObject (#31155) The response currently implements ToXContentFragment although the only time it's used it is supposed to print out a complete object rather than a fragment. Note that this is the client version of the response, used only in the high-level client. --- .../client/SyncedFlushResponse.java | 23 +++++++++++-------- .../client/SyncedFlushResponseTests.java | 18 +++++++-------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/SyncedFlushResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/SyncedFlushResponse.java index 53f3f3358ba2f..ba42914d6ad62 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/SyncedFlushResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/SyncedFlushResponse.java @@ -21,25 +21,26 @@ import org.elasticsearch.action.ActionResponse; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ToXContentFragment; +import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentLocation; -import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser.Token; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; -import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; -import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; - import java.io.IOException; -import java.util.Map; -import java.util.HashMap; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; -import java.util.ArrayList; +import java.util.Map; -public class SyncedFlushResponse extends ActionResponse implements ToXContentFragment { +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +public class SyncedFlushResponse extends ActionResponse implements ToXContentObject { public static final String SHARDS_FIELD = "_shards"; @@ -86,6 +87,7 @@ ShardCounts getShardCounts() { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); builder.startObject(SHARDS_FIELD); totalCounts.toXContent(builder, params); builder.endObject(); @@ -96,6 +98,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws indexResult.toXContent(builder, params); builder.endObject(); } + builder.endObject(); return builder; } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SyncedFlushResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SyncedFlushResponseTests.java index bc8fc90dd75e6..0756cfa6bab10 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SyncedFlushResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SyncedFlushResponseTests.java @@ -18,14 +18,6 @@ */ package org.elasticsearch.client; -import java.io.IOException; -import java.util.Map; -import java.util.HashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.Set; -import java.util.HashSet; - import com.carrotsearch.hppc.ObjectIntHashMap; import com.carrotsearch.hppc.ObjectIntMap; import org.elasticsearch.cluster.routing.ShardRouting; @@ -42,6 +34,14 @@ import org.elasticsearch.indices.flush.SyncedFlushService; import org.elasticsearch.test.ESTestCase; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + public class SyncedFlushResponseTests extends ESTestCase { public void testXContentSerialization() throws IOException { @@ -55,9 +55,7 @@ public void testXContentSerialization() throws IOException { serverResponsebuilder.endObject(); XContentBuilder clientResponsebuilder = XContentBuilder.builder(xContentType.xContent()); assertNotNull(plan.result); - clientResponsebuilder.startObject(); plan.clientResult.toXContent(clientResponsebuilder, ToXContent.EMPTY_PARAMS); - clientResponsebuilder.endObject(); Map serverContentMap = convertFailureListToSet( serverResponsebuilder .generator() From 8953adc7922c5ccdcc3ff4a3837d6f01693c9fe6 Mon Sep 17 00:00:00 2001 From: javanna Date: Tue, 12 Jun 2018 10:59:14 +0200 Subject: [PATCH 45/71] Remove leftover usage of deprecated client API See #31200 & ##31069 --- .../org/elasticsearch/client/TasksClient.java | 4 ++-- .../elasticsearch/client/BulkProcessorIT.java | 3 ++- .../client/BulkProcessorRetryIT.java | 3 ++- .../client/RestHighLevelClientTests.java | 22 +++++++++---------- .../documentation/CRUDDocumentationIT.java | 14 +++++++----- .../IndicesClientDocumentationIT.java | 6 ++--- .../documentation/SearchDocumentationIT.java | 2 +- 7 files changed, 30 insertions(+), 24 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/TasksClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/TasksClient.java index f8f03d7f7d288..ebba636b8fa05 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/TasksClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/TasksClient.java @@ -84,7 +84,7 @@ public CancelTasksResponse cancel(CancelTasksRequest cancelTasksRequest, Request cancelTasksRequest, RequestConverters::cancelTasks, options, - parser -> CancelTasksResponse.fromXContent(parser), + CancelTasksResponse::fromXContent, emptySet() ); } @@ -103,7 +103,7 @@ public void cancelAsync(CancelTasksRequest cancelTasksRequest, RequestOptions op cancelTasksRequest, RequestConverters::cancelTasks, options, - parser -> CancelTasksResponse.fromXContent(parser), + CancelTasksResponse::fromXContent, listener, emptySet() ); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorIT.java index d41c47177f968..7605b1c715c74 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorIT.java @@ -56,7 +56,8 @@ public class BulkProcessorIT extends ESRestHighLevelClientTestCase { private static BulkProcessor.Builder initBulkProcessorBuilder(BulkProcessor.Listener listener) { - return BulkProcessor.builder(highLevelClient()::bulkAsync, listener); + return BulkProcessor.builder( + (request, bulkListener) -> highLevelClient().bulkAsync(request, RequestOptions.DEFAULT, bulkListener), listener); } public void testThatBulkProcessorCountIsCorrect() throws Exception { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorRetryIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorRetryIT.java index fe6aa6b1017ee..c20998eeb5826 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorRetryIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/BulkProcessorRetryIT.java @@ -48,7 +48,8 @@ public class BulkProcessorRetryIT extends ESRestHighLevelClientTestCase { private static final String TYPE_NAME = "type"; private static BulkProcessor.Builder initBulkProcessorBuilder(BulkProcessor.Listener listener) { - return BulkProcessor.builder(highLevelClient()::bulkAsync, listener); + return BulkProcessor.builder( + (request, bulkListener) -> highLevelClient().bulkAsync(request, RequestOptions.DEFAULT, bulkListener), listener); } public void testBulkRejectionLoadWithoutBackoff() throws Exception { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java index 307dd0afb5e07..ea8f9df665e81 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java @@ -189,12 +189,12 @@ public ActionRequestValidationException validate() { { ActionRequestValidationException actualException = expectThrows(ActionRequestValidationException.class, - () -> restHighLevelClient.performRequest(request, null, null, null)); + () -> restHighLevelClient.performRequest(request, null, RequestOptions.DEFAULT, null, null)); assertSame(validationException, actualException); } { TrackingActionListener trackingActionListener = new TrackingActionListener(); - restHighLevelClient.performRequestAsync(request, null, null, trackingActionListener, null); + restHighLevelClient.performRequestAsync(request, null, RequestOptions.DEFAULT, null, trackingActionListener, null); assertSame(validationException, trackingActionListener.exception.get()); } } @@ -308,13 +308,13 @@ public void testPerformRequestOnSuccess() throws IOException { Response mockResponse = new Response(REQUEST_LINE, new HttpHost("localhost", 9200), httpResponse); when(restClient.performRequest(any(Request.class))).thenReturn(mockResponse); { - Integer result = restHighLevelClient.performRequest(mainRequest, requestConverter, + Integer result = restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> response.getStatusLine().getStatusCode(), Collections.emptySet()); assertEquals(restStatus.getStatus(), result.intValue()); } { IOException ioe = expectThrows(IOException.class, () -> restHighLevelClient.performRequest(mainRequest, - requestConverter, response -> {throw new IllegalStateException();}, Collections.emptySet())); + requestConverter, RequestOptions.DEFAULT, response -> {throw new IllegalStateException();}, Collections.emptySet())); assertEquals("Unable to parse response body for Response{requestLine=GET / http/1.1, host=http://localhost:9200, " + "response=http/1.1 " + restStatus.getStatus() + " " + restStatus.name() + "}", ioe.getMessage()); } @@ -329,7 +329,7 @@ public void testPerformRequestOnResponseExceptionWithoutEntity() throws IOExcept ResponseException responseException = new ResponseException(mockResponse); when(restClient.performRequest(any(Request.class))).thenThrow(responseException); ElasticsearchException elasticsearchException = expectThrows(ElasticsearchException.class, - () -> restHighLevelClient.performRequest(mainRequest, requestConverter, + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> response.getStatusLine().getStatusCode(), Collections.emptySet())); assertEquals(responseException.getMessage(), elasticsearchException.getMessage()); assertEquals(restStatus, elasticsearchException.status()); @@ -347,7 +347,7 @@ public void testPerformRequestOnResponseExceptionWithEntity() throws IOException ResponseException responseException = new ResponseException(mockResponse); when(restClient.performRequest(any(Request.class))).thenThrow(responseException); ElasticsearchException elasticsearchException = expectThrows(ElasticsearchException.class, - () -> restHighLevelClient.performRequest(mainRequest, requestConverter, + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> response.getStatusLine().getStatusCode(), Collections.emptySet())); assertEquals("Elasticsearch exception [type=exception, reason=test error message]", elasticsearchException.getMessage()); assertEquals(restStatus, elasticsearchException.status()); @@ -364,7 +364,7 @@ public void testPerformRequestOnResponseExceptionWithBrokenEntity() throws IOExc ResponseException responseException = new ResponseException(mockResponse); when(restClient.performRequest(any(Request.class))).thenThrow(responseException); ElasticsearchException elasticsearchException = expectThrows(ElasticsearchException.class, - () -> restHighLevelClient.performRequest(mainRequest, requestConverter, + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> response.getStatusLine().getStatusCode(), Collections.emptySet())); assertEquals("Unable to parse response body", elasticsearchException.getMessage()); assertEquals(restStatus, elasticsearchException.status()); @@ -382,7 +382,7 @@ public void testPerformRequestOnResponseExceptionWithBrokenEntity2() throws IOEx ResponseException responseException = new ResponseException(mockResponse); when(restClient.performRequest(any(Request.class))).thenThrow(responseException); ElasticsearchException elasticsearchException = expectThrows(ElasticsearchException.class, - () -> restHighLevelClient.performRequest(mainRequest, requestConverter, + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> response.getStatusLine().getStatusCode(), Collections.emptySet())); assertEquals("Unable to parse response body", elasticsearchException.getMessage()); assertEquals(restStatus, elasticsearchException.status()); @@ -398,7 +398,7 @@ public void testPerformRequestOnResponseExceptionWithIgnores() throws IOExceptio ResponseException responseException = new ResponseException(mockResponse); when(restClient.performRequest(any(Request.class))).thenThrow(responseException); //although we got an exception, we turn it into a successful response because the status code was provided among ignores - assertEquals(Integer.valueOf(404), restHighLevelClient.performRequest(mainRequest, requestConverter, + assertEquals(Integer.valueOf(404), restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> response.getStatusLine().getStatusCode(), Collections.singleton(404))); } @@ -410,7 +410,7 @@ public void testPerformRequestOnResponseExceptionWithIgnoresErrorNoBody() throws ResponseException responseException = new ResponseException(mockResponse); when(restClient.performRequest(any(Request.class))).thenThrow(responseException); ElasticsearchException elasticsearchException = expectThrows(ElasticsearchException.class, - () -> restHighLevelClient.performRequest(mainRequest, requestConverter, + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> {throw new IllegalStateException();}, Collections.singleton(404))); assertEquals(RestStatus.NOT_FOUND, elasticsearchException.status()); assertSame(responseException, elasticsearchException.getCause()); @@ -427,7 +427,7 @@ public void testPerformRequestOnResponseExceptionWithIgnoresErrorValidBody() thr ResponseException responseException = new ResponseException(mockResponse); when(restClient.performRequest(any(Request.class))).thenThrow(responseException); ElasticsearchException elasticsearchException = expectThrows(ElasticsearchException.class, - () -> restHighLevelClient.performRequest(mainRequest, requestConverter, + () -> restHighLevelClient.performRequest(mainRequest, requestConverter, RequestOptions.DEFAULT, response -> {throw new IllegalStateException();}, Collections.singleton(404))); assertEquals(RestStatus.NOT_FOUND, elasticsearchException.status()); assertSame(responseException, elasticsearchException.getSuppressed()[0]); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java index f4b8636d72d78..8b8998baff581 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/CRUDDocumentationIT.java @@ -72,6 +72,7 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.arrayWithSize; @@ -755,7 +756,7 @@ public void onFailure(Exception e) { listener = new LatchedActionListener<>(listener, latch); // tag::bulk-execute-async - client.bulkAsync(request, listener); // <1> + client.bulkAsync(request, RequestOptions.DEFAULT, listener); // <1> // end::bulk-execute-async assertTrue(latch.await(30L, TimeUnit.SECONDS)); @@ -1007,8 +1008,9 @@ public void afterBulk(long executionId, BulkRequest request, Throwable failure) } }; - BulkProcessor bulkProcessor = - BulkProcessor.builder(client::bulkAsync, listener).build(); // <5> + BiConsumer> bulkConsumer = + (request, bulkListener) -> highLevelClient().bulkAsync(request, RequestOptions.DEFAULT, bulkListener); + BulkProcessor bulkProcessor = BulkProcessor.builder(bulkConsumer, listener).build(); // <5> // end::bulk-processor-init assertNotNull(bulkProcessor); @@ -1066,7 +1068,9 @@ public void afterBulk(long executionId, BulkRequest request, Throwable failure) // end::bulk-processor-listener // tag::bulk-processor-options - BulkProcessor.Builder builder = BulkProcessor.builder(client::bulkAsync, listener); + BiConsumer> bulkConsumer = + (request, bulkListener) -> highLevelClient().bulkAsync(request, RequestOptions.DEFAULT, bulkListener); + BulkProcessor.Builder builder = BulkProcessor.builder(bulkConsumer, listener); builder.setBulkActions(500); // <1> builder.setBulkSize(new ByteSizeValue(1L, ByteSizeUnit.MB)); // <2> builder.setConcurrentRequests(0); // <3> @@ -1189,7 +1193,7 @@ public void onFailure(Exception e) { listener = new LatchedActionListener<>(listener, latch); // tag::multi-get-execute-async - client.multiGetAsync(request, listener); // <1> + client.multiGetAsync(request, RequestOptions.DEFAULT, listener); // <1> // end::multi-get-execute-async assertTrue(latch.await(30L, TimeUnit.SECONDS)); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java index 00cf2659d36d1..725a59bb24d8b 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java @@ -576,7 +576,7 @@ public void testGetMapping() throws IOException { RestHighLevelClient client = highLevelClient(); { - CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter")); + CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter"), RequestOptions.DEFAULT); assertTrue(createIndexResponse.isAcknowledged()); PutMappingRequest request = new PutMappingRequest("twitter"); request.type("tweet"); @@ -589,7 +589,7 @@ public void testGetMapping() throws IOException { " }\n" + "}", // <1> XContentType.JSON); - PutMappingResponse putMappingResponse = client.indices().putMapping(request); + PutMappingResponse putMappingResponse = client.indices().putMapping(request, RequestOptions.DEFAULT); assertTrue(putMappingResponse.isAcknowledged()); } @@ -633,7 +633,7 @@ public void testGetMappingAsync() throws Exception { final RestHighLevelClient client = highLevelClient(); { - CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter")); + CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("twitter"), RequestOptions.DEFAULT); assertTrue(createIndexResponse.isAcknowledged()); PutMappingRequest request = new PutMappingRequest("twitter"); request.type("tweet"); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java index f4ce789d5106f..7445606758ea0 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SearchDocumentationIT.java @@ -628,7 +628,7 @@ public void onFailure(Exception e) { scrollListener = new LatchedActionListener<>(scrollListener, latch); // tag::search-scroll-execute-async - client.searchScrollAsync(scrollRequest, scrollListener); // <1> + client.searchScrollAsync(scrollRequest, RequestOptions.DEFAULT, scrollListener); // <1> // end::search-scroll-execute-async assertTrue(latch.await(30L, TimeUnit.SECONDS)); From 479a2eb6bf33a78f581d4eddf5ff74d41e1f4d28 Mon Sep 17 00:00:00 2001 From: Van0SS Date: Tue, 12 Jun 2018 07:34:06 -0400 Subject: [PATCH 46/71] REST high-level client: add Cluster Health API (#29331) Relates to #27205 --- .../elasticsearch/client/ClusterClient.java | 34 +++ .../client/RequestConverters.java | 61 ++++++ .../elasticsearch/client/ClusterClientIT.java | 138 ++++++++++++ .../client/RequestConvertersTests.java | 95 +++++++- .../ClusterClientDocumentationIT.java | 180 +++++++++++++++ .../high-level/cluster/health.asciidoc | 205 ++++++++++++++++++ .../high-level/supported-apis.asciidoc | 2 + docs/reference/cluster/health.asciidoc | 9 + .../cluster/health/ClusterHealthRequest.java | 28 +++ .../cluster/health/ClusterHealthResponse.java | 156 +++++++++++-- .../cluster/health/ClusterIndexHealth.java | 166 +++++++++++--- .../cluster/health/ClusterShardHealth.java | 109 +++++++++- .../cluster/health/ClusterStateHealth.java | 64 +++++- .../health/ClusterHealthResponsesTests.java | 115 +++++++++- .../health/ClusterIndexHealthTests.java | 143 +++++++++++- .../health/ClusterShardHealthTests.java | 111 ++++++++++ .../tasks/CancelTasksResponseTests.java | 3 +- .../tasks/ListTasksResponseTests.java | 3 +- .../test/AbstractSerializingTestCase.java | 10 +- .../AbstractStreamableXContentTestCase.java | 10 +- .../test/AbstractXContentTestCase.java | 20 +- 21 files changed, 1587 insertions(+), 75 deletions(-) create mode 100644 docs/java-rest/high-level/cluster/health.asciidoc create mode 100644 server/src/test/java/org/elasticsearch/cluster/health/ClusterShardHealthTests.java diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java index 488579785e0f7..1e25a40b0084a 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java @@ -21,12 +21,16 @@ import org.apache.http.Header; import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.elasticsearch.rest.RestStatus; import java.io.IOException; import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; /** * A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Cluster API. @@ -95,4 +99,34 @@ public void putSettingsAsync(ClusterUpdateSettingsRequest clusterUpdateSettingsR restHighLevelClient.performRequestAsyncAndParseEntity(clusterUpdateSettingsRequest, RequestConverters::clusterPutSettings, ClusterUpdateSettingsResponse::fromXContent, listener, emptySet(), headers); } + + /** + * Get cluster health using the Cluster Health API. + * See + * Cluster Health API on elastic.co + *

+ * If timeout occurred, {@link ClusterHealthResponse} will have isTimedOut() == true and status() == RestStatus.REQUEST_TIMEOUT + * @param healthRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public ClusterHealthResponse health(ClusterHealthRequest healthRequest, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(healthRequest, RequestConverters::clusterHealth, options, + ClusterHealthResponse::fromXContent, singleton(RestStatus.REQUEST_TIMEOUT.getStatus())); + } + + /** + * Asynchronously get cluster health using the Cluster Health API. + * See + * Cluster Health API on elastic.co + * If timeout occurred, {@link ClusterHealthResponse} will have isTimedOut() == true and status() == RestStatus.REQUEST_TIMEOUT + * @param healthRequest the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void healthAsync(ClusterHealthRequest healthRequest, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(healthRequest, RequestConverters::clusterHealth, options, + ClusterHealthResponse::fromXContent, listener, singleton(RestStatus.REQUEST_TIMEOUT.getStatus())); + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 266acbf7394cf..caeffdbe80c63 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -29,6 +29,7 @@ import org.apache.http.entity.ContentType; import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; @@ -74,7 +75,9 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.update.UpdateRequest; +import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.bytes.BytesReference; @@ -717,6 +720,28 @@ static Request listTasks(ListTasksRequest listTaskRequest) { return request; } + static Request clusterHealth(ClusterHealthRequest healthRequest) { + String[] indices = healthRequest.indices() == null ? Strings.EMPTY_ARRAY : healthRequest.indices(); + String endpoint = new EndpointBuilder() + .addPathPartAsIs("_cluster/health") + .addCommaSeparatedPathParts(indices) + .build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + + new Params(request) + .withWaitForStatus(healthRequest.waitForStatus()) + .withWaitForNoRelocatingShards(healthRequest.waitForNoRelocatingShards()) + .withWaitForNoInitializingShards(healthRequest.waitForNoInitializingShards()) + .withWaitForActiveShards(healthRequest.waitForActiveShards(), ActiveShardCount.NONE) + .withWaitForNodes(healthRequest.waitForNodes()) + .withWaitForEvents(healthRequest.waitForEvents()) + .withTimeout(healthRequest.timeout()) + .withMasterTimeout(healthRequest.masterNodeTimeout()) + .withLocal(healthRequest.local()) + .withLevel(healthRequest.level()); + return request; + } + static Request rollover(RolloverRequest rolloverRequest) throws IOException { String endpoint = new EndpointBuilder().addPathPart(rolloverRequest.getAlias()).addPathPartAsIs("_rollover") .addPathPart(rolloverRequest.getNewIndexName()).build(); @@ -1146,6 +1171,42 @@ Params withVerify(boolean verify) { } return this; } + + Params withWaitForStatus(ClusterHealthStatus status) { + if (status != null) { + return putParam("wait_for_status", status.name().toLowerCase(Locale.ROOT)); + } + return this; + } + + Params withWaitForNoRelocatingShards(boolean waitNoRelocatingShards) { + if (waitNoRelocatingShards) { + return putParam("wait_for_no_relocating_shards", Boolean.TRUE.toString()); + } + return this; + } + + Params withWaitForNoInitializingShards(boolean waitNoInitShards) { + if (waitNoInitShards) { + return putParam("wait_for_no_initializing_shards", Boolean.TRUE.toString()); + } + return this; + } + + Params withWaitForNodes(String waitForNodes) { + return putParam("wait_for_nodes", waitForNodes); + } + + Params withLevel(ClusterHealthRequest.Level level) { + return putParam("level", level.name().toLowerCase(Locale.ROOT)); + } + + Params withWaitForEvents(Priority waitForEvents) { + if (waitForEvents != null) { + return putParam("wait_for_events", waitForEvents.name().toLowerCase(Locale.ROOT)); + } + return this; + } } /** diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java index f1110163b2517..8e31d32ac7b3d 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ClusterClientIT.java @@ -20,8 +20,13 @@ package org.elasticsearch.client; import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.cluster.health.ClusterIndexHealth; +import org.elasticsearch.cluster.health.ClusterShardHealth; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; @@ -34,6 +39,7 @@ import java.util.HashMap; import java.util.Map; +import static java.util.Collections.emptyMap; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -108,4 +114,136 @@ public void testClusterUpdateSettingNonExistent() { assertThat(exception.getMessage(), equalTo( "Elasticsearch exception [type=illegal_argument_exception, reason=transient setting [" + setting + "], not recognized]")); } + + public void testClusterHealthGreen() throws IOException { + ClusterHealthRequest request = new ClusterHealthRequest(); + request.timeout("5s"); + ClusterHealthResponse response = execute(request, highLevelClient().cluster()::health, highLevelClient().cluster()::healthAsync); + + assertThat(response, notNullValue()); + assertThat(response.isTimedOut(), equalTo(false)); + assertThat(response.status(), equalTo(RestStatus.OK)); + assertThat(response.getStatus(), equalTo(ClusterHealthStatus.GREEN)); + assertNoIndices(response); + } + + public void testClusterHealthYellowClusterLevel() throws IOException { + createIndex("index", Settings.EMPTY); + createIndex("index2", Settings.EMPTY); + ClusterHealthRequest request = new ClusterHealthRequest(); + request.timeout("5s"); + request.level(ClusterHealthRequest.Level.CLUSTER); + ClusterHealthResponse response = execute(request, highLevelClient().cluster()::health, highLevelClient().cluster()::healthAsync); + + assertYellowShards(response); + assertThat(response.getIndices().size(), equalTo(0)); + } + + public void testClusterHealthYellowIndicesLevel() throws IOException { + createIndex("index", Settings.EMPTY); + createIndex("index2", Settings.EMPTY); + ClusterHealthRequest request = new ClusterHealthRequest(); + request.timeout("5s"); + request.level(ClusterHealthRequest.Level.INDICES); + ClusterHealthResponse response = execute(request, highLevelClient().cluster()::health, highLevelClient().cluster()::healthAsync); + + assertYellowShards(response); + assertThat(response.getIndices().size(), equalTo(2)); + for (Map.Entry entry : response.getIndices().entrySet()) { + assertYellowIndex(entry.getKey(), entry.getValue(), true); + } + } + + private static void assertYellowShards(ClusterHealthResponse response) { + assertThat(response, notNullValue()); + assertThat(response.isTimedOut(), equalTo(false)); + assertThat(response.status(), equalTo(RestStatus.OK)); + assertThat(response.getStatus(), equalTo(ClusterHealthStatus.YELLOW)); + assertThat(response.getActivePrimaryShards(), equalTo(10)); + assertThat(response.getNumberOfDataNodes(), equalTo(1)); + assertThat(response.getNumberOfNodes(), equalTo(1)); + assertThat(response.getActiveShards(), equalTo(10)); + assertThat(response.getDelayedUnassignedShards(), equalTo(0)); + assertThat(response.getInitializingShards(), equalTo(0)); + assertThat(response.getUnassignedShards(), equalTo(10)); + assertThat(response.getActiveShardsPercent(), equalTo(50d)); + } + + public void testClusterHealthYellowSpecificIndex() throws IOException { + createIndex("index", Settings.EMPTY); + createIndex("index2", Settings.EMPTY); + ClusterHealthRequest request = new ClusterHealthRequest("index"); + request.timeout("5s"); + ClusterHealthResponse response = execute(request, highLevelClient().cluster()::health, highLevelClient().cluster()::healthAsync); + + assertThat(response, notNullValue()); + assertThat(response.isTimedOut(), equalTo(false)); + assertThat(response.status(), equalTo(RestStatus.OK)); + assertThat(response.getStatus(), equalTo(ClusterHealthStatus.YELLOW)); + assertThat(response.getActivePrimaryShards(), equalTo(5)); + assertThat(response.getNumberOfDataNodes(), equalTo(1)); + assertThat(response.getNumberOfNodes(), equalTo(1)); + assertThat(response.getActiveShards(), equalTo(5)); + assertThat(response.getDelayedUnassignedShards(), equalTo(0)); + assertThat(response.getInitializingShards(), equalTo(0)); + assertThat(response.getUnassignedShards(), equalTo(5)); + assertThat(response.getActiveShardsPercent(), equalTo(50d)); + assertThat(response.getIndices().size(), equalTo(1)); + Map.Entry index = response.getIndices().entrySet().iterator().next(); + assertYellowIndex(index.getKey(), index.getValue(), false); + } + + private static void assertYellowIndex(String indexName, ClusterIndexHealth indexHealth, boolean emptyShards) { + assertThat(indexHealth, notNullValue()); + assertThat(indexHealth.getIndex(),equalTo(indexName)); + assertThat(indexHealth.getActivePrimaryShards(),equalTo(5)); + assertThat(indexHealth.getActiveShards(),equalTo(5)); + assertThat(indexHealth.getNumberOfReplicas(),equalTo(1)); + assertThat(indexHealth.getInitializingShards(),equalTo(0)); + assertThat(indexHealth.getUnassignedShards(),equalTo(5)); + assertThat(indexHealth.getRelocatingShards(),equalTo(0)); + assertThat(indexHealth.getStatus(), equalTo(ClusterHealthStatus.YELLOW)); + if (emptyShards) { + assertThat(indexHealth.getShards().size(), equalTo(0)); + } else { + assertThat(indexHealth.getShards().size(), equalTo(5)); + for (Map.Entry entry : indexHealth.getShards().entrySet()) { + assertYellowShard(entry.getKey(), entry.getValue()); + } + } + } + + private static void assertYellowShard(int shardId, ClusterShardHealth shardHealth) { + assertThat(shardHealth, notNullValue()); + assertThat(shardHealth.getShardId(), equalTo(shardId)); + assertThat(shardHealth.getStatus(), equalTo(ClusterHealthStatus.YELLOW)); + assertThat(shardHealth.getActiveShards(), equalTo(1)); + assertThat(shardHealth.getInitializingShards(), equalTo(0)); + assertThat(shardHealth.getUnassignedShards(), equalTo(1)); + assertThat(shardHealth.getRelocatingShards(), equalTo(0)); + } + + public void testClusterHealthNotFoundIndex() throws IOException { + ClusterHealthRequest request = new ClusterHealthRequest("notexisted-index"); + request.timeout("5s"); + ClusterHealthResponse response = execute(request, highLevelClient().cluster()::health, highLevelClient().cluster()::healthAsync); + + assertThat(response, notNullValue()); + assertThat(response.isTimedOut(), equalTo(true)); + assertThat(response.status(), equalTo(RestStatus.REQUEST_TIMEOUT)); + assertThat(response.getStatus(), equalTo(ClusterHealthStatus.RED)); + assertNoIndices(response); + } + + private static void assertNoIndices(ClusterHealthResponse response) { + assertThat(response.getIndices(), equalTo(emptyMap())); + assertThat(response.getActivePrimaryShards(), equalTo(0)); + assertThat(response.getNumberOfDataNodes(), equalTo(1)); + assertThat(response.getNumberOfNodes(), equalTo(1)); + assertThat(response.getActiveShards(), equalTo(0)); + assertThat(response.getDelayedUnassignedShards(), equalTo(0)); + assertThat(response.getInitializingShards(), equalTo(0)); + assertThat(response.getUnassignedShards(), equalTo(0)); + assertThat(response.getActiveShardsPercent(), equalTo(100d)); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index 04bf37d17b8a9..c40ecc4287c50 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -29,6 +29,7 @@ import org.apache.http.util.EntityUtils; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.DocWriteRequest; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksRequest; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksRequest; import org.elasticsearch.action.admin.cluster.repositories.delete.DeleteRepositoryRequest; @@ -83,8 +84,10 @@ import org.elasticsearch.action.support.replication.ReplicationRequest; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.client.RequestConverters.EndpointBuilder; +import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.CheckedFunction; +import org.elasticsearch.common.Priority; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; @@ -125,6 +128,7 @@ import org.elasticsearch.tasks.TaskId; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.RandomObjects; +import org.hamcrest.CoreMatchers; import java.io.IOException; import java.io.InputStream; @@ -1558,6 +1562,85 @@ public void testDeletePipeline() { assertEquals(expectedParams, expectedRequest.getParameters()); } + public void testClusterHealth() { + ClusterHealthRequest healthRequest = new ClusterHealthRequest(); + Map expectedParams = new HashMap<>(); + setRandomLocal(healthRequest, expectedParams); + String timeoutType = randomFrom("timeout", "masterTimeout", "both", "none"); + String timeout = randomTimeValue(); + String masterTimeout = randomTimeValue(); + switch (timeoutType) { + case "timeout": + healthRequest.timeout(timeout); + expectedParams.put("timeout", timeout); + // If Master Timeout wasn't set it uses the same value as Timeout + expectedParams.put("master_timeout", timeout); + break; + case "masterTimeout": + expectedParams.put("timeout", "30s"); + healthRequest.masterNodeTimeout(masterTimeout); + expectedParams.put("master_timeout", masterTimeout); + break; + case "both": + healthRequest.timeout(timeout); + expectedParams.put("timeout", timeout); + healthRequest.masterNodeTimeout(timeout); + expectedParams.put("master_timeout", timeout); + break; + case "none": + expectedParams.put("timeout", "30s"); + expectedParams.put("master_timeout", "30s"); + break; + default: + throw new UnsupportedOperationException(); + } + setRandomWaitForActiveShards(healthRequest::waitForActiveShards, ActiveShardCount.NONE, expectedParams); + if (randomBoolean()) { + ClusterHealthRequest.Level level = randomFrom(ClusterHealthRequest.Level.values()); + healthRequest.level(level); + expectedParams.put("level", level.name().toLowerCase(Locale.ROOT)); + } else { + expectedParams.put("level", "shards"); + } + if (randomBoolean()) { + Priority priority = randomFrom(Priority.values()); + healthRequest.waitForEvents(priority); + expectedParams.put("wait_for_events", priority.name().toLowerCase(Locale.ROOT)); + } + if (randomBoolean()) { + ClusterHealthStatus status = randomFrom(ClusterHealthStatus.values()); + healthRequest.waitForStatus(status); + expectedParams.put("wait_for_status", status.name().toLowerCase(Locale.ROOT)); + } + if (randomBoolean()) { + boolean waitForNoInitializingShards = randomBoolean(); + healthRequest.waitForNoInitializingShards(waitForNoInitializingShards); + if (waitForNoInitializingShards) { + expectedParams.put("wait_for_no_initializing_shards", Boolean.TRUE.toString()); + } + } + if (randomBoolean()) { + boolean waitForNoRelocatingShards = randomBoolean(); + healthRequest.waitForNoRelocatingShards(waitForNoRelocatingShards); + if (waitForNoRelocatingShards) { + expectedParams.put("wait_for_no_relocating_shards", Boolean.TRUE.toString()); + } + } + String[] indices = randomBoolean() ? null : randomIndicesNames(0, 5); + healthRequest.indices(indices); + + Request request = RequestConverters.clusterHealth(healthRequest); + assertThat(request, CoreMatchers.notNullValue()); + assertThat(request.getMethod(), equalTo(HttpGet.METHOD_NAME)); + assertThat(request.getEntity(), nullValue()); + if (indices != null && indices.length > 0) { + assertThat(request.getEndpoint(), equalTo("/_cluster/health/" + String.join(",", indices))); + } else { + assertThat(request.getEndpoint(), equalTo("/_cluster/health")); + } + assertThat(request.getParameters(), equalTo(expectedParams)); + } + public void testRollover() throws IOException { RolloverRequest rolloverRequest = new RolloverRequest(randomAlphaOfLengthBetween(3, 10), randomBoolean() ? null : randomAlphaOfLengthBetween(3, 10)); @@ -2155,12 +2238,12 @@ private static void setRandomWaitForActiveShards(Consumer sett } private static void setRandomUpdateAllTypes(Consumer setter, Map expectedParams) { - if (randomBoolean()) { - boolean updateAllTypes = randomBoolean(); - setter.accept(updateAllTypes); - if (updateAllTypes) { - expectedParams.put("update_all_types", Boolean.TRUE.toString()); - } + if (randomBoolean()){ + boolean updateAllTypes=randomBoolean(); + setter.accept(updateAllTypes); + if(updateAllTypes){ + expectedParams.put("update_all_types",Boolean.TRUE.toString()); + } } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java index 75902cf02babb..84a124f764b38 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/ClusterClientDocumentationIT.java @@ -21,17 +21,26 @@ import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.LatchedActionListener; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.support.ActiveShardCount; import org.elasticsearch.client.ESRestHighLevelClientTestCase; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.cluster.health.ClusterIndexHealth; +import org.elasticsearch.cluster.health.ClusterShardHealth; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; +import org.elasticsearch.common.Priority; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.indices.recovery.RecoverySettings; +import org.elasticsearch.rest.RestStatus; import java.io.IOException; import java.util.HashMap; @@ -40,6 +49,7 @@ import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; /** * This class is used to generate the Java Cluster API documentation. @@ -179,4 +189,174 @@ public void onFailure(Exception e) { } } + public void testClusterHealth() throws IOException { + RestHighLevelClient client = highLevelClient(); + client.indices().create(new CreateIndexRequest("index"), RequestOptions.DEFAULT); + { + // tag::health-request + ClusterHealthRequest request = new ClusterHealthRequest(); + // end::health-request + } + { + // tag::health-request-indices-ctr + ClusterHealthRequest request = new ClusterHealthRequest("index1", "index2"); + // end::health-request-indices-ctr + } + { + // tag::health-request-indices-setter + ClusterHealthRequest request = new ClusterHealthRequest(); + request.indices("index1", "index2"); + // end::health-request-indices-setter + } + ClusterHealthRequest request = new ClusterHealthRequest(); + + // tag::health-request-timeout + request.timeout(TimeValue.timeValueSeconds(50)); // <1> + request.timeout("50s"); // <2> + // end::health-request-timeout + + // tag::health-request-master-timeout + request.masterNodeTimeout(TimeValue.timeValueSeconds(20)); // <1> + request.masterNodeTimeout("20s"); // <2> + // end::health-request-master-timeout + + // tag::health-request-wait-status + request.waitForStatus(ClusterHealthStatus.YELLOW); // <1> + request.waitForYellowStatus(); // <2> + // end::health-request-wait-status + + // tag::health-request-wait-events + request.waitForEvents(Priority.NORMAL); // <1> + // end::health-request-wait-events + + // tag::health-request-level + request.level(ClusterHealthRequest.Level.SHARDS); // <1> + // end::health-request-level + + // tag::health-request-wait-relocation + request.waitForNoRelocatingShards(true); // <1> + // end::health-request-wait-relocation + + // tag::health-request-wait-initializing + request.waitForNoInitializingShards(true); // <1> + // end::health-request-wait-initializing + + // tag::health-request-wait-nodes + request.waitForNodes("2"); // <1> + request.waitForNodes(">=2"); // <2> + request.waitForNodes("le(2)"); // <3> + // end::health-request-wait-nodes + + // tag::health-request-wait-active + request.waitForActiveShards(ActiveShardCount.ALL); // <1> + request.waitForActiveShards(1); // <2> + // end::health-request-wait-active + + // tag::health-request-local + request.local(true); // <1> + // end::health-request-local + + // tag::health-execute + ClusterHealthResponse response = client.cluster().health(request, RequestOptions.DEFAULT); + // end::health-execute + + assertThat(response.isTimedOut(), equalTo(false)); + assertThat(response.status(), equalTo(RestStatus.OK)); + assertThat(response.getStatus(), equalTo(ClusterHealthStatus.YELLOW)); + assertThat(response, notNullValue()); + // tag::health-response-general + String clusterName = response.getClusterName(); // <1> + ClusterHealthStatus status = response.getStatus(); // <2> + // end::health-response-general + + // tag::health-response-request-status + boolean timedOut = response.isTimedOut(); // <1> + RestStatus restStatus = response.status(); // <2> + // end::health-response-request-status + + // tag::health-response-nodes + int numberOfNodes = response.getNumberOfNodes(); // <1> + int numberOfDataNodes = response.getNumberOfDataNodes(); // <2> + // end::health-response-nodes + + { + // tag::health-response-shards + int activeShards = response.getActiveShards(); // <1> + int activePrimaryShards = response.getActivePrimaryShards(); // <2> + int relocatingShards = response.getRelocatingShards(); // <3> + int initializingShards = response.getInitializingShards(); // <4> + int unassignedShards = response.getUnassignedShards(); // <5> + int delayedUnassignedShards = response.getDelayedUnassignedShards(); // <6> + double activeShardsPercent = response.getActiveShardsPercent(); // <7> + // end::health-response-shards + } + + // tag::health-response-task + TimeValue taskMaxWaitingTime = response.getTaskMaxWaitingTime(); // <1> + int numberOfPendingTasks = response.getNumberOfPendingTasks(); // <2> + int numberOfInFlightFetch = response.getNumberOfInFlightFetch(); // <3> + // end::health-response-task + + // tag::health-response-indices + Map indices = response.getIndices(); // <1> + // end::health-response-indices + + { + // tag::health-response-index + ClusterIndexHealth index = indices.get("index"); // <1> + ClusterHealthStatus indexStatus = index.getStatus(); + int numberOfShards = index.getNumberOfShards(); + int numberOfReplicas = index.getNumberOfReplicas(); + int activeShards = index.getActiveShards(); + int activePrimaryShards = index.getActivePrimaryShards(); + int initializingShards = index.getInitializingShards(); + int relocatingShards = index.getRelocatingShards(); + int unassignedShards = index.getUnassignedShards(); + // end::health-response-index + + // tag::health-response-shard-details + Map shards = index.getShards(); // <1> + ClusterShardHealth shardHealth = shards.get(0); + int shardId = shardHealth.getShardId(); + ClusterHealthStatus shardStatus = shardHealth.getStatus(); + int active = shardHealth.getActiveShards(); + int initializing = shardHealth.getInitializingShards(); + int unassigned = shardHealth.getUnassignedShards(); + int relocating = shardHealth.getRelocatingShards(); + boolean primaryActive = shardHealth.isPrimaryActive(); + // end::health-response-shard-details + } + } + + public void testClusterHealthAsync() throws Exception { + RestHighLevelClient client = highLevelClient(); + { + ClusterHealthRequest request = new ClusterHealthRequest(); + + // tag::health-execute-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(ClusterHealthResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::health-execute-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::health-execute-async + client.cluster().healthAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::health-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } } diff --git a/docs/java-rest/high-level/cluster/health.asciidoc b/docs/java-rest/high-level/cluster/health.asciidoc new file mode 100644 index 0000000000000..6c0f926f15f42 --- /dev/null +++ b/docs/java-rest/high-level/cluster/health.asciidoc @@ -0,0 +1,205 @@ +[[java-rest-high-cluster-health]] +=== Cluster Health API + +The Cluster Health API allows getting cluster health. + +[[java-rest-high-cluster-health-request]] +==== Cluster Health Request + +A `ClusterHealthRequest`: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request] +-------------------------------------------------- +There are no required parameters. By default, the client will check all indices and will not wait +for any events. + +==== Indices + +Indices which should be checked can be passed in the constructor: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-indices-ctr] +-------------------------------------------------- + +Or using the corresponding setter method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-indices-setter] +-------------------------------------------------- + +==== Other parameters + +Other parameters can be passed only through setter methods: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-timeout] +-------------------------------------------------- +<1> Timeout for the request as a `TimeValue`. Defaults to 30 seconds +<2> As a `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-master-timeout] +-------------------------------------------------- +<1> Timeout to connect to the master node as a `TimeValue`. Defaults to the same as `timeout` +<2> As a `String` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-wait-status] +-------------------------------------------------- +<1> The status to wait (e.g. `green`, `yellow`, or `red`). Accepts a `ClusterHealthStatus` value. +<2> Using predefined method + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-wait-events] +-------------------------------------------------- +<1> The priority of the events to wait for. Accepts a `Priority` value. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-level] +-------------------------------------------------- +<1> The level of detail of the returned health information. Accepts a `ClusterHealthRequest.Level` value. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-wait-relocation] +-------------------------------------------------- +<1> Wait for 0 relocating shards. Defaults to `false` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-wait-initializing] +-------------------------------------------------- +<1> Wait for 0 initializing shards. Defaults to `false` + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-wait-nodes] +-------------------------------------------------- +<1> Wait for `N` nodes in the cluster. Defaults to `0` +<2> Using `>=N`, `<=N`, `>N` and ` Using `ge(N)`, `le(N)`, `gt(N)`, `lt(N)` notation + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-wait-active] +-------------------------------------------------- + +<1> Wait for all shards to be active in the cluster +<2> Wait for `N` shards to be active in the cluster + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-request-local] +-------------------------------------------------- +<1> Non-master node can be used for this request. Defaults to `false` + +[[java-rest-high-cluster-health-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-execute] +-------------------------------------------------- + +[[java-rest-high-cluster-health-async]] +==== Asynchronous Execution + +The asynchronous execution of a cluster health request requires both the +`ClusterHealthRequest` instance and an `ActionListener` instance to be +passed to the asynchronous method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-execute-async] +-------------------------------------------------- +<1> The `ClusterHealthRequest` to execute and the `ActionListener` to use +when the execution completes + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for `ClusterHealthResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-execute-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of a failure. The raised exception is provided as an argument + +[[java-rest-high-cluster-health-response]] +==== Cluster Health Response + +The returned `ClusterHealthResponse` contains the next information about the +cluster: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-general] +-------------------------------------------------- +<1> Name of the cluster +<2> Cluster status (`green`, `yellow` or `red`) + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-request-status] +-------------------------------------------------- +<1> Whether request was timed out while processing +<2> Status of the request (`OK` or `REQUEST_TIMEOUT`). Other errors will be thrown as exceptions + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-nodes] +-------------------------------------------------- +<1> Number of nodes in the cluster +<2> Number of data nodes in the cluster + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-shards] +-------------------------------------------------- +<1> Number of active shards +<2> Number of primary active shards +<3> Number of relocating shards +<4> Number of initializing shards +<5> Number of unassigned shards +<6> Number of unassigned shards that are currently being delayed +<7> Percent of active shards + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-task] +-------------------------------------------------- +<1> Maximum wait time of all tasks in the queue +<2> Number of currently pending tasks +<3> Number of async fetches that are currently ongoing + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-indices] +-------------------------------------------------- +<1> Detailed information about indices in the cluster + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-index] +-------------------------------------------------- +<1> Detailed information about a specific index + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/ClusterClientDocumentationIT.java[health-response-shard-details] +-------------------------------------------------- +<1> Detailed information about a specific shard \ 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 465ae20619de3..b33c2421b06d3 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -110,8 +110,10 @@ include::indices/get_templates.asciidoc[] The Java High Level REST Client supports the following Cluster APIs: * <> +* <> include::cluster/put_settings.asciidoc[] +include::cluster/health.asciidoc[] == Ingest APIs The Java High Level REST Client supports the following Ingest APIs: diff --git a/docs/reference/cluster/health.asciidoc b/docs/reference/cluster/health.asciidoc index 6cc99a25476d9..4e6f89d84946b 100644 --- a/docs/reference/cluster/health.asciidoc +++ b/docs/reference/cluster/health.asciidoc @@ -104,10 +104,19 @@ The cluster health API accepts the following request parameters: Alternatively, it is possible to use `ge(N)`, `le(N)`, `gt(N)` and `lt(N)` notation. +`wait_for_events`:: + Can be one of `immediate`, `urgent`, `high`, `normal`, `low`, `languid`. + Wait until all currently queued events with the given priority are processed. + `timeout`:: A time based parameter controlling how long to wait if one of the wait_for_XXX are provided. Defaults to `30s`. +`master_timeout`:: + A time based parameter controlling how long to wait if the master has not been + discovered yet or disconnected. + If not provided, uses the same value as `timeout`. + `local`:: If `true` returns the local node information and does not provide the state from master node. Default: `false`. diff --git a/server/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java b/server/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java index 04c03fa00a41e..3deca380691f5 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthRequest.java @@ -33,6 +33,7 @@ import org.elasticsearch.common.unit.TimeValue; import java.io.IOException; +import java.util.Objects; import java.util.concurrent.TimeUnit; public class ClusterHealthRequest extends MasterNodeReadRequest implements IndicesRequest.Replaceable { @@ -45,6 +46,11 @@ public class ClusterHealthRequest extends MasterNodeReadRequest PARSER = + new ConstructingObjectParser<>("cluster_health_response", true, + parsedObjects -> { + int i = 0; + // ClusterStateHealth fields + int numberOfNodes = (int) parsedObjects[i++]; + int numberOfDataNodes = (int) parsedObjects[i++]; + int activeShards = (int) parsedObjects[i++]; + int relocatingShards = (int) parsedObjects[i++]; + int activePrimaryShards = (int) parsedObjects[i++]; + int initializingShards = (int) parsedObjects[i++]; + int unassignedShards = (int) parsedObjects[i++]; + double activeShardsPercent = (double) parsedObjects[i++]; + String statusStr = (String) parsedObjects[i++]; + ClusterHealthStatus status = ClusterHealthStatus.fromString(statusStr); + @SuppressWarnings("unchecked") List indexList = (List) parsedObjects[i++]; + final Map indices; + if (indexList == null || indexList.isEmpty()) { + indices = emptyMap(); + } else { + indices = new HashMap<>(indexList.size()); + for (ClusterIndexHealth indexHealth : indexList) { + indices.put(indexHealth.getIndex(), indexHealth); + } + } + ClusterStateHealth stateHealth = new ClusterStateHealth(activePrimaryShards, activeShards, relocatingShards, + initializingShards, unassignedShards, numberOfNodes, numberOfDataNodes, activeShardsPercent, status, + indices); + + // ClusterHealthResponse fields + String clusterName = (String) parsedObjects[i++]; + int numberOfPendingTasks = (int) parsedObjects[i++]; + int numberOfInFlightFetch = (int) parsedObjects[i++]; + int delayedUnassignedShards = (int) parsedObjects[i++]; + long taskMaxWaitingTimeMillis = (long) parsedObjects[i++]; + boolean timedOut = (boolean) parsedObjects[i]; + return new ClusterHealthResponse(clusterName, numberOfPendingTasks, numberOfInFlightFetch, delayedUnassignedShards, + TimeValue.timeValueMillis(taskMaxWaitingTimeMillis), timedOut, stateHealth); + }); + + private static final ObjectParser.NamedObjectParser INDEX_PARSER = + (XContentParser parser, Void context, String index) -> ClusterIndexHealth.innerFromXContent(parser, index); + + static { + // ClusterStateHealth fields + PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_NODES)); + PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_DATA_NODES)); + PARSER.declareInt(constructorArg(), new ParseField(ACTIVE_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(RELOCATING_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(ACTIVE_PRIMARY_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(INITIALIZING_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(UNASSIGNED_SHARDS)); + PARSER.declareDouble(constructorArg(), new ParseField(ACTIVE_SHARDS_PERCENT_AS_NUMBER)); + PARSER.declareString(constructorArg(), new ParseField(STATUS)); + // Can be absent if LEVEL == 'cluster' + PARSER.declareNamedObjects(optionalConstructorArg(), INDEX_PARSER, new ParseField(INDICES)); + + // ClusterHealthResponse fields + PARSER.declareString(constructorArg(), new ParseField(CLUSTER_NAME)); + PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_PENDING_TASKS)); + PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_IN_FLIGHT_FETCH)); + PARSER.declareInt(constructorArg(), new ParseField(DELAYED_UNASSIGNED_SHARDS)); + PARSER.declareLong(constructorArg(), new ParseField(TASK_MAX_WAIT_TIME_IN_QUEUE_IN_MILLIS)); + PARSER.declareBoolean(constructorArg(), new ParseField(TIMED_OUT)); + } + private String clusterName; private int numberOfPendingTasks = 0; private int numberOfInFlightFetch = 0; @@ -60,11 +156,23 @@ public ClusterHealthResponse(String clusterName, String[] concreteIndices, Clust this.numberOfPendingTasks = numberOfPendingTasks; this.numberOfInFlightFetch = numberOfInFlightFetch; this.delayedUnassignedShards = delayedUnassignedShards; + this.taskMaxWaitingTime = taskMaxWaitingTime; + this.clusterStateHealth = new ClusterStateHealth(clusterState, concreteIndices); + this.clusterHealthStatus = clusterStateHealth.getStatus(); + } + + /** + * For XContent Parser and serialization tests + */ + ClusterHealthResponse(String clusterName, int numberOfPendingTasks, int numberOfInFlightFetch, int delayedUnassignedShards, + TimeValue taskMaxWaitingTime, boolean timedOut, ClusterStateHealth clusterStateHealth) { this.clusterName = clusterName; this.numberOfPendingTasks = numberOfPendingTasks; this.numberOfInFlightFetch = numberOfInFlightFetch; + this.delayedUnassignedShards = delayedUnassignedShards; this.taskMaxWaitingTime = taskMaxWaitingTime; - this.clusterStateHealth = new ClusterStateHealth(clusterState, concreteIndices); + this.timedOut = timedOut; + this.clusterStateHealth = clusterStateHealth; this.clusterHealthStatus = clusterStateHealth.getStatus(); } @@ -210,25 +318,6 @@ public RestStatus status() { return isTimedOut() ? RestStatus.REQUEST_TIMEOUT : RestStatus.OK; } - private static final String CLUSTER_NAME = "cluster_name"; - private static final String STATUS = "status"; - private static final String TIMED_OUT = "timed_out"; - private static final String NUMBER_OF_NODES = "number_of_nodes"; - private static final String NUMBER_OF_DATA_NODES = "number_of_data_nodes"; - private static final String NUMBER_OF_PENDING_TASKS = "number_of_pending_tasks"; - private static final String NUMBER_OF_IN_FLIGHT_FETCH = "number_of_in_flight_fetch"; - private static final String DELAYED_UNASSIGNED_SHARDS = "delayed_unassigned_shards"; - private static final String TASK_MAX_WAIT_TIME_IN_QUEUE = "task_max_waiting_in_queue"; - private static final String TASK_MAX_WAIT_TIME_IN_QUEUE_IN_MILLIS = "task_max_waiting_in_queue_millis"; - private static final String ACTIVE_SHARDS_PERCENT_AS_NUMBER = "active_shards_percent_as_number"; - private static final String ACTIVE_SHARDS_PERCENT = "active_shards_percent"; - private static final String ACTIVE_PRIMARY_SHARDS = "active_primary_shards"; - private static final String ACTIVE_SHARDS = "active_shards"; - private static final String RELOCATING_SHARDS = "relocating_shards"; - private static final String INITIALIZING_SHARDS = "initializing_shards"; - private static final String UNASSIGNED_SHARDS = "unassigned_shards"; - private static final String INDICES = "indices"; - @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -254,13 +343,36 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (outputIndices) { builder.startObject(INDICES); for (ClusterIndexHealth indexHealth : clusterStateHealth.getIndices().values()) { - builder.startObject(indexHealth.getIndex()); indexHealth.toXContent(builder, params); - builder.endObject(); } builder.endObject(); } builder.endObject(); return builder; } + + public static ClusterHealthResponse fromXContent(XContentParser parser) { + return PARSER.apply(parser, null); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClusterHealthResponse that = (ClusterHealthResponse) o; + return Objects.equals(clusterName, that.clusterName) && + numberOfPendingTasks == that.numberOfPendingTasks && + numberOfInFlightFetch == that.numberOfInFlightFetch && + delayedUnassignedShards == that.delayedUnassignedShards && + Objects.equals(taskMaxWaitingTime, that.taskMaxWaitingTime) && + timedOut == that.timedOut && + Objects.equals(clusterStateHealth, that.clusterStateHealth) && + clusterHealthStatus == that.clusterHealthStatus; + } + + @Override + public int hashCode() { + return Objects.hash(clusterName, numberOfPendingTasks, numberOfInFlightFetch, delayedUnassignedShards, taskMaxWaitingTime, + timedOut, clusterStateHealth, clusterHealthStatus); + } } diff --git a/server/src/main/java/org/elasticsearch/cluster/health/ClusterIndexHealth.java b/server/src/main/java/org/elasticsearch/cluster/health/ClusterIndexHealth.java index 75c564c20385e..c1a52f2ffc548 100644 --- a/server/src/main/java/org/elasticsearch/cluster/health/ClusterIndexHealth.java +++ b/server/src/main/java/org/elasticsearch/cluster/health/ClusterIndexHealth.java @@ -22,19 +22,82 @@ import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.IndexShardRoutingTable; +import org.elasticsearch.common.ParseField; 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.ObjectParser; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; + +import static java.util.Collections.emptyMap; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; public final class ClusterIndexHealth implements Iterable, Writeable, ToXContentFragment { + private static final String STATUS = "status"; + private static final String NUMBER_OF_SHARDS = "number_of_shards"; + private static final String NUMBER_OF_REPLICAS = "number_of_replicas"; + private static final String ACTIVE_PRIMARY_SHARDS = "active_primary_shards"; + private static final String ACTIVE_SHARDS = "active_shards"; + private static final String RELOCATING_SHARDS = "relocating_shards"; + private static final String INITIALIZING_SHARDS = "initializing_shards"; + private static final String UNASSIGNED_SHARDS = "unassigned_shards"; + private static final String SHARDS = "shards"; + + private static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("cluster_index_health", true, + (parsedObjects, index) -> { + int i = 0; + int numberOfShards = (int) parsedObjects[i++]; + int numberOfReplicas = (int) parsedObjects[i++]; + int activeShards = (int) parsedObjects[i++]; + int relocatingShards = (int) parsedObjects[i++]; + int initializingShards = (int) parsedObjects[i++]; + int unassignedShards = (int) parsedObjects[i++]; + int activePrimaryShards = (int) parsedObjects[i++]; + String statusStr = (String) parsedObjects[i++]; + ClusterHealthStatus status = ClusterHealthStatus.fromString(statusStr); + @SuppressWarnings("unchecked") List shardList = (List) parsedObjects[i]; + final Map shards; + if (shardList == null || shardList.isEmpty()) { + shards = emptyMap(); + } else { + shards = new HashMap<>(shardList.size()); + for (ClusterShardHealth shardHealth : shardList) { + shards.put(shardHealth.getShardId(), shardHealth); + } + } + return new ClusterIndexHealth(index, numberOfShards, numberOfReplicas, activeShards, relocatingShards, + initializingShards, unassignedShards, activePrimaryShards, status, shards); + }); + + public static final ObjectParser.NamedObjectParser SHARD_PARSER = + (XContentParser p, String indexIgnored, String shardId) -> ClusterShardHealth.innerFromXContent(p, Integer.valueOf(shardId)); + + static { + PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(NUMBER_OF_REPLICAS)); + PARSER.declareInt(constructorArg(), new ParseField(ACTIVE_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(RELOCATING_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(INITIALIZING_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(UNASSIGNED_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(ACTIVE_PRIMARY_SHARDS)); + PARSER.declareString(constructorArg(), new ParseField(STATUS)); + // Can be absent if LEVEL == 'indices' or 'cluster' + PARSER.declareNamedObjects(optionalConstructorArg(), SHARD_PARSER, new ParseField(SHARDS)); + } private final String index; private final int numberOfShards; @@ -45,13 +108,14 @@ public final class ClusterIndexHealth implements Iterable, W private final int unassignedShards; private final int activePrimaryShards; private final ClusterHealthStatus status; - private final Map shards = new HashMap<>(); + private final Map shards; public ClusterIndexHealth(final IndexMetaData indexMetaData, final IndexRoutingTable indexRoutingTable) { this.index = indexMetaData.getIndex().getName(); this.numberOfShards = indexMetaData.getNumberOfShards(); this.numberOfReplicas = indexMetaData.getNumberOfReplicas(); + shards = new HashMap<>(); for (IndexShardRoutingTable shardRoutingTable : indexRoutingTable) { int shardId = shardRoutingTable.shardId().id(); shards.put(shardId, new ClusterShardHealth(shardId, shardRoutingTable)); @@ -104,12 +168,31 @@ public ClusterIndexHealth(final StreamInput in) throws IOException { status = ClusterHealthStatus.fromValue(in.readByte()); int size = in.readVInt(); + shards = new HashMap<>(size); for (int i = 0; i < size; i++) { ClusterShardHealth shardHealth = new ClusterShardHealth(in); - shards.put(shardHealth.getId(), shardHealth); + shards.put(shardHealth.getShardId(), shardHealth); } } + /** + * For XContent Parser and serialization tests + */ + ClusterIndexHealth(String index, int numberOfShards, int numberOfReplicas, int activeShards, int relocatingShards, + int initializingShards, int unassignedShards, int activePrimaryShards, ClusterHealthStatus status, + Map shards) { + this.index = index; + this.numberOfShards = numberOfShards; + this.numberOfReplicas = numberOfReplicas; + this.activeShards = activeShards; + this.relocatingShards = relocatingShards; + this.initializingShards = initializingShards; + this.unassignedShards = unassignedShards; + this.activePrimaryShards = activePrimaryShards; + this.status = status; + this.shards = shards; + } + public String getIndex() { return index; } @@ -173,19 +256,9 @@ public void writeTo(final StreamOutput out) throws IOException { } } - private static final String STATUS = "status"; - private static final String NUMBER_OF_SHARDS = "number_of_shards"; - private static final String NUMBER_OF_REPLICAS = "number_of_replicas"; - private static final String ACTIVE_PRIMARY_SHARDS = "active_primary_shards"; - private static final String ACTIVE_SHARDS = "active_shards"; - private static final String RELOCATING_SHARDS = "relocating_shards"; - private static final String INITIALIZING_SHARDS = "initializing_shards"; - private static final String UNASSIGNED_SHARDS = "unassigned_shards"; - private static final String SHARDS = "shards"; - private static final String PRIMARY_ACTIVE = "primary_active"; - @Override public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException { + builder.startObject(getIndex()); builder.field(STATUS, getStatus().name().toLowerCase(Locale.ROOT)); builder.field(NUMBER_OF_SHARDS, getNumberOfShards()); builder.field(NUMBER_OF_REPLICAS, getNumberOfReplicas()); @@ -197,22 +270,65 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa if ("shards".equals(params.param("level", "indices"))) { builder.startObject(SHARDS); - for (ClusterShardHealth shardHealth : shards.values()) { - builder.startObject(Integer.toString(shardHealth.getId())); - - builder.field(STATUS, shardHealth.getStatus().name().toLowerCase(Locale.ROOT)); - builder.field(PRIMARY_ACTIVE, shardHealth.isPrimaryActive()); - builder.field(ACTIVE_SHARDS, shardHealth.getActiveShards()); - builder.field(RELOCATING_SHARDS, shardHealth.getRelocatingShards()); - builder.field(INITIALIZING_SHARDS, shardHealth.getInitializingShards()); - builder.field(UNASSIGNED_SHARDS, shardHealth.getUnassignedShards()); - - builder.endObject(); + shardHealth.toXContent(builder, params); } - builder.endObject(); } + builder.endObject(); return builder; } + + public static ClusterIndexHealth innerFromXContent(XContentParser parser, String index) { + return PARSER.apply(parser, index); + } + + public static ClusterIndexHealth fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + XContentParser.Token token = parser.nextToken(); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); + String index = parser.currentName(); + ClusterIndexHealth parsed = innerFromXContent(parser, index); + ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.nextToken(), parser::getTokenLocation); + return parsed; + } + + @Override + public String toString() { + return "ClusterIndexHealth{" + + "index='" + index + '\'' + + ", numberOfShards=" + numberOfShards + + ", numberOfReplicas=" + numberOfReplicas + + ", activeShards=" + activeShards + + ", relocatingShards=" + relocatingShards + + ", initializingShards=" + initializingShards + + ", unassignedShards=" + unassignedShards + + ", activePrimaryShards=" + activePrimaryShards + + ", status=" + status + + ", shards.size=" + (shards == null ? "null" : shards.size()) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClusterIndexHealth that = (ClusterIndexHealth) o; + return Objects.equals(index, that.index) && + numberOfShards == that.numberOfShards && + numberOfReplicas == that.numberOfReplicas && + activeShards == that.activeShards && + relocatingShards == that.relocatingShards && + initializingShards == that.initializingShards && + unassignedShards == that.unassignedShards && + activePrimaryShards == that.activePrimaryShards && + status == that.status && + Objects.equals(shards, that.shards); + } + + @Override + public int hashCode() { + return Objects.hash(index, numberOfShards, numberOfReplicas, activeShards, relocatingShards, initializingShards, unassignedShards, + activePrimaryShards, status, shards); + } } diff --git a/server/src/main/java/org/elasticsearch/cluster/health/ClusterShardHealth.java b/server/src/main/java/org/elasticsearch/cluster/health/ClusterShardHealth.java index 12131b11f3f66..1d3a3dcee7b95 100644 --- a/server/src/main/java/org/elasticsearch/cluster/health/ClusterShardHealth.java +++ b/server/src/main/java/org/elasticsearch/cluster/health/ClusterShardHealth.java @@ -24,13 +24,54 @@ import org.elasticsearch.cluster.routing.ShardRouting; import org.elasticsearch.cluster.routing.UnassignedInfo; import org.elasticsearch.cluster.routing.UnassignedInfo.AllocationStatus; +import org.elasticsearch.common.ParseField; +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.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.ToXContentFragment; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; +import java.util.Locale; +import java.util.Objects; -public final class ClusterShardHealth implements Writeable { +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +public final class ClusterShardHealth implements Writeable, ToXContentFragment { + private static final String STATUS = "status"; + private static final String ACTIVE_SHARDS = "active_shards"; + private static final String RELOCATING_SHARDS = "relocating_shards"; + private static final String INITIALIZING_SHARDS = "initializing_shards"; + private static final String UNASSIGNED_SHARDS = "unassigned_shards"; + private static final String PRIMARY_ACTIVE = "primary_active"; + + public static final ConstructingObjectParser PARSER = + new ConstructingObjectParser<>("cluster_shard_health", true, + (parsedObjects, shardId) -> { + int i = 0; + boolean primaryActive = (boolean) parsedObjects[i++]; + int activeShards = (int) parsedObjects[i++]; + int relocatingShards = (int) parsedObjects[i++]; + int initializingShards = (int) parsedObjects[i++]; + int unassignedShards = (int) parsedObjects[i++]; + String statusStr = (String) parsedObjects[i]; + ClusterHealthStatus status = ClusterHealthStatus.fromString(statusStr); + return new ClusterShardHealth(shardId, status, activeShards, relocatingShards, initializingShards, unassignedShards, + primaryActive); + }); + + static { + PARSER.declareBoolean(constructorArg(), new ParseField(PRIMARY_ACTIVE)); + PARSER.declareInt(constructorArg(), new ParseField(ACTIVE_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(RELOCATING_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(INITIALIZING_SHARDS)); + PARSER.declareInt(constructorArg(), new ParseField(UNASSIGNED_SHARDS)); + PARSER.declareString(constructorArg(), new ParseField(STATUS)); + } private final int shardId; private final ClusterHealthStatus status; @@ -88,7 +129,21 @@ public ClusterShardHealth(final StreamInput in) throws IOException { primaryActive = in.readBoolean(); } - public int getId() { + /** + * For XContent Parser and serialization tests + */ + ClusterShardHealth(int shardId, ClusterHealthStatus status, int activeShards, int relocatingShards, int initializingShards, + int unassignedShards, boolean primaryActive) { + this.shardId = shardId; + this.status = status; + this.activeShards = activeShards; + this.relocatingShards = relocatingShards; + this.initializingShards = initializingShards; + this.unassignedShards = unassignedShards; + this.primaryActive = primaryActive; + } + + public int getShardId() { return shardId; } @@ -155,4 +210,54 @@ public static ClusterHealthStatus getInactivePrimaryHealth(final ShardRouting sh } } + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(Integer.toString(getShardId())); + builder.field(STATUS, getStatus().name().toLowerCase(Locale.ROOT)); + builder.field(PRIMARY_ACTIVE, isPrimaryActive()); + builder.field(ACTIVE_SHARDS, getActiveShards()); + builder.field(RELOCATING_SHARDS, getRelocatingShards()); + builder.field(INITIALIZING_SHARDS, getInitializingShards()); + builder.field(UNASSIGNED_SHARDS, getUnassignedShards()); + builder.endObject(); + return builder; + } + + static ClusterShardHealth innerFromXContent(XContentParser parser, Integer shardId) { + return PARSER.apply(parser, shardId); + } + + public static ClusterShardHealth fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + XContentParser.Token token = parser.nextToken(); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); + String shardIdStr = parser.currentName(); + ClusterShardHealth parsed = innerFromXContent(parser, Integer.valueOf(shardIdStr)); + ensureExpectedToken(XContentParser.Token.END_OBJECT, parser.nextToken(), parser::getTokenLocation); + return parsed; + } + + @Override + public String toString() { + return Strings.toString(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ClusterShardHealth)) return false; + ClusterShardHealth that = (ClusterShardHealth) o; + return shardId == that.shardId && + activeShards == that.activeShards && + relocatingShards == that.relocatingShards && + initializingShards == that.initializingShards && + unassignedShards == that.unassignedShards && + primaryActive == that.primaryActive && + status == that.status; + } + + @Override + public int hashCode() { + return Objects.hash(shardId, status, activeShards, relocatingShards, initializingShards, unassignedShards, primaryActive); + } } diff --git a/server/src/main/java/org/elasticsearch/cluster/health/ClusterStateHealth.java b/server/src/main/java/org/elasticsearch/cluster/health/ClusterStateHealth.java index 8aeb110c37007..6d36f4dec1d26 100644 --- a/server/src/main/java/org/elasticsearch/cluster/health/ClusterStateHealth.java +++ b/server/src/main/java/org/elasticsearch/cluster/health/ClusterStateHealth.java @@ -33,6 +33,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; + public final class ClusterStateHealth implements Iterable, Writeable { @@ -45,7 +47,7 @@ public final class ClusterStateHealth implements Iterable, W private final int unassignedShards; private final double activeShardsPercent; private final ClusterHealthStatus status; - private final Map indices = new HashMap<>(); + private final Map indices; /** * Creates a new ClusterStateHealth instance considering the current cluster state and all indices in the cluster. @@ -65,7 +67,7 @@ public ClusterStateHealth(final ClusterState clusterState) { public ClusterStateHealth(final ClusterState clusterState, final String[] concreteIndices) { numberOfNodes = clusterState.nodes().getSize(); numberOfDataNodes = clusterState.nodes().getDataNodes().size(); - + indices = new HashMap<>(); for (String index : concreteIndices) { IndexRoutingTable indexRoutingTable = clusterState.routingTable().index(index); IndexMetaData indexMetaData = clusterState.metaData().index(index); @@ -134,6 +136,7 @@ public ClusterStateHealth(final StreamInput in) throws IOException { numberOfDataNodes = in.readVInt(); status = ClusterHealthStatus.fromValue(in.readByte()); int size = in.readVInt(); + indices = new HashMap<>(size); for (int i = 0; i < size; i++) { ClusterIndexHealth indexHealth = new ClusterIndexHealth(in); indices.put(indexHealth.getIndex(), indexHealth); @@ -141,6 +144,24 @@ public ClusterStateHealth(final StreamInput in) throws IOException { activeShardsPercent = in.readDouble(); } + /** + * For ClusterHealthResponse's XContent Parser + */ + public ClusterStateHealth(int activePrimaryShards, int activeShards, int relocatingShards, int initializingShards, int unassignedShards, + int numberOfNodes, int numberOfDataNodes, double activeShardsPercent, ClusterHealthStatus status, + Map indices) { + this.activePrimaryShards = activePrimaryShards; + this.activeShards = activeShards; + this.relocatingShards = relocatingShards; + this.initializingShards = initializingShards; + this.unassignedShards = unassignedShards; + this.numberOfNodes = numberOfNodes; + this.numberOfDataNodes = numberOfDataNodes; + this.activeShardsPercent = activeShardsPercent; + this.status = status; + this.indices = indices; + } + public int getActiveShards() { return activeShards; } @@ -202,4 +223,43 @@ public void writeTo(final StreamOutput out) throws IOException { } out.writeDouble(activeShardsPercent); } + + @Override + public String toString() { + return "ClusterStateHealth{" + + "numberOfNodes=" + numberOfNodes + + ", numberOfDataNodes=" + numberOfDataNodes + + ", activeShards=" + activeShards + + ", relocatingShards=" + relocatingShards + + ", activePrimaryShards=" + activePrimaryShards + + ", initializingShards=" + initializingShards + + ", unassignedShards=" + unassignedShards + + ", activeShardsPercent=" + activeShardsPercent + + ", status=" + status + + ", indices.size=" + (indices == null ? "null" : indices.size()) + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClusterStateHealth that = (ClusterStateHealth) o; + return numberOfNodes == that.numberOfNodes && + numberOfDataNodes == that.numberOfDataNodes && + activeShards == that.activeShards && + relocatingShards == that.relocatingShards && + activePrimaryShards == that.activePrimaryShards && + initializingShards == that.initializingShards && + unassignedShards == that.unassignedShards && + Double.compare(that.activeShardsPercent, activeShardsPercent) == 0 && + status == that.status && + Objects.equals(indices, that.indices); + } + + @Override + public int hashCode() { + return Objects.hash(numberOfNodes, numberOfDataNodes, activeShards, relocatingShards, activePrimaryShards, initializingShards, + unassignedShards, activeShardsPercent, status, indices); + } } diff --git a/server/src/test/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthResponsesTests.java b/server/src/test/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthResponsesTests.java index d0d452df478a9..8c1438815250a 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthResponsesTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/cluster/health/ClusterHealthResponsesTests.java @@ -21,26 +21,38 @@ import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.health.ClusterHealthStatus; +import org.elasticsearch.cluster.health.ClusterIndexHealth; +import org.elasticsearch.cluster.health.ClusterIndexHealthTests; import org.elasticsearch.cluster.health.ClusterStateHealth; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.AbstractStreamableXContentTestCase; import org.hamcrest.Matchers; import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.function.Predicate; +import java.util.regex.Pattern; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; -public class ClusterHealthResponsesTests extends ESTestCase { +public class ClusterHealthResponsesTests extends AbstractStreamableXContentTestCase { + private final ClusterHealthRequest.Level level = randomFrom(ClusterHealthRequest.Level.values()); - public void testIsTimeout() throws IOException { + public void testIsTimeout() { ClusterHealthResponse res = new ClusterHealthResponse(); for (int i = 0; i < 5; i++) { res.setTimedOut(randomBoolean()); @@ -89,4 +101,101 @@ ClusterHealthResponse maybeSerialize(ClusterHealthResponse clusterHealth) throws } return clusterHealth; } + + @Override + protected ClusterHealthResponse doParseInstance(XContentParser parser) { + return ClusterHealthResponse.fromXContent(parser); + } + + @Override + protected ClusterHealthResponse createBlankInstance() { + return new ClusterHealthResponse(); + } + + @Override + protected ClusterHealthResponse createTestInstance() { + int indicesSize = randomInt(20); + Map indices = new HashMap<>(indicesSize); + if ("indices".equals(level) || "shards".equals(level)) { + for (int i = 0; i < indicesSize; i++) { + String indexName = randomAlphaOfLengthBetween(1, 5) + i; + indices.put(indexName, ClusterIndexHealthTests.randomIndexHealth(indexName, level)); + } + } + ClusterStateHealth stateHealth = new ClusterStateHealth(randomInt(100), randomInt(100), randomInt(100), + randomInt(100), randomInt(100), randomInt(100), randomInt(100), + randomDoubleBetween(0d, 100d, true), randomFrom(ClusterHealthStatus.values()), indices); + + return new ClusterHealthResponse(randomAlphaOfLengthBetween(1, 10), randomInt(100), randomInt(100), randomInt(100), + TimeValue.timeValueMillis(randomInt(10000)), randomBoolean(), stateHealth); + } + + @Override + protected ToXContent.Params getToXContentParams() { + return new ToXContent.MapParams(Collections.singletonMap("level", level.name().toLowerCase(Locale.ROOT))); + } + + @Override + protected boolean supportsUnknownFields() { + return true; + } + + // Ignore all paths which looks like "indices.RANDOMINDEXNAME.shards" + private static final Pattern SHARDS_IN_XCONTENT = Pattern.compile("^indices\\.\\w+\\.shards$"); + + @Override + protected Predicate getRandomFieldsExcludeFilter() { + return field -> "indices".equals(field) || SHARDS_IN_XCONTENT.matcher(field).find(); + } + + @Override + protected ClusterHealthResponse mutateInstance(ClusterHealthResponse instance) { + String mutate = randomFrom("clusterName", "numberOfPendingTasks","numberOfInFlightFetch", "delayedUnassignedShards", + "taskMaxWaitingTime", "timedOut", "clusterStateHealth"); + switch (mutate) { + case "clusterName": + return new ClusterHealthResponse(instance.getClusterName() + randomAlphaOfLengthBetween(2, 5), + instance.getNumberOfPendingTasks(), instance.getNumberOfInFlightFetch(), + instance.getDelayedUnassignedShards(), instance.getTaskMaxWaitingTime(), + instance.isTimedOut(), instance.getClusterStateHealth()); + case "numberOfPendingTasks": + return new ClusterHealthResponse(instance.getClusterName(), + instance.getNumberOfPendingTasks() + between(1, 10), instance.getNumberOfInFlightFetch(), + instance.getDelayedUnassignedShards(), instance.getTaskMaxWaitingTime(), + instance.isTimedOut(), instance.getClusterStateHealth()); + case "numberOfInFlightFetch": + return new ClusterHealthResponse(instance.getClusterName(), + instance.getNumberOfPendingTasks(), instance.getNumberOfInFlightFetch() + between(1, 10), + instance.getDelayedUnassignedShards(), instance.getTaskMaxWaitingTime(), + instance.isTimedOut(), instance.getClusterStateHealth()); + case "delayedUnassignedShards": + return new ClusterHealthResponse(instance.getClusterName(), + instance.getNumberOfPendingTasks(), instance.getNumberOfInFlightFetch(), + instance.getDelayedUnassignedShards() + between(1, 10), instance.getTaskMaxWaitingTime(), + instance.isTimedOut(), instance.getClusterStateHealth()); + case "taskMaxWaitingTime": + + return new ClusterHealthResponse(instance.getClusterName(), + instance.getNumberOfPendingTasks(), instance.getNumberOfInFlightFetch(), + instance.getDelayedUnassignedShards(), new TimeValue(instance.getTaskMaxWaitingTime().millis() + between(1, 10)), + instance.isTimedOut(), instance.getClusterStateHealth()); + case "timedOut": + return new ClusterHealthResponse(instance.getClusterName(), + instance.getNumberOfPendingTasks(), instance.getNumberOfInFlightFetch(), + instance.getDelayedUnassignedShards(), instance.getTaskMaxWaitingTime(), + instance.isTimedOut() == false, instance.getClusterStateHealth()); + case "clusterStateHealth": + ClusterStateHealth state = instance.getClusterStateHealth(); + ClusterStateHealth newState = new ClusterStateHealth(state.getActivePrimaryShards() + between(1, 10), + state.getActiveShards(), state.getRelocatingShards(), state.getInitializingShards(), state.getUnassignedShards(), + state.getNumberOfNodes(), state.getNumberOfDataNodes(), state.getActiveShardsPercent(), state.getStatus(), + state.getIndices()); + return new ClusterHealthResponse(instance.getClusterName(), + instance.getNumberOfPendingTasks(), instance.getNumberOfInFlightFetch(), + instance.getDelayedUnassignedShards(), instance.getTaskMaxWaitingTime(), + instance.isTimedOut(), newState); + default: + throw new UnsupportedOperationException(); + } + } } diff --git a/server/src/test/java/org/elasticsearch/cluster/health/ClusterIndexHealthTests.java b/server/src/test/java/org/elasticsearch/cluster/health/ClusterIndexHealthTests.java index 215f28f727587..851ab63297a21 100644 --- a/server/src/test/java/org/elasticsearch/cluster/health/ClusterIndexHealthTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/health/ClusterIndexHealthTests.java @@ -19,29 +19,45 @@ package org.elasticsearch.cluster.health; import org.elasticsearch.Version; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.routing.IndexRoutingTable; import org.elasticsearch.cluster.routing.RoutingTableGenerator; -import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractSerializingTestCase; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import static org.hamcrest.CoreMatchers.equalTo; -public class ClusterIndexHealthTests extends ESTestCase { +public class ClusterIndexHealthTests extends AbstractSerializingTestCase { + private final ClusterHealthRequest.Level level = randomFrom(ClusterHealthRequest.Level.SHARDS, ClusterHealthRequest.Level.INDICES); + public void testClusterIndexHealth() { RoutingTableGenerator routingTableGenerator = new RoutingTableGenerator(); int numberOfShards = randomInt(3) + 1; int numberOfReplicas = randomInt(4); - IndexMetaData indexMetaData = IndexMetaData.builder("test1").settings(settings(Version.CURRENT)).numberOfShards(numberOfShards).numberOfReplicas(numberOfReplicas).build(); + IndexMetaData indexMetaData = IndexMetaData.builder("test1").settings(settings(Version.CURRENT)) + .numberOfShards(numberOfShards).numberOfReplicas(numberOfReplicas).build(); RoutingTableGenerator.ShardCounter counter = new RoutingTableGenerator.ShardCounter(); IndexRoutingTable indexRoutingTable = routingTableGenerator.genIndexRoutingTable(indexMetaData, counter); ClusterIndexHealth indexHealth = new ClusterIndexHealth(indexMetaData, indexRoutingTable); - logger.info("index status: {}, expected {}", indexHealth.getStatus(), counter.status()); assertIndexHealth(indexHealth, counter, indexMetaData); } - - private void assertIndexHealth(ClusterIndexHealth indexHealth, RoutingTableGenerator.ShardCounter counter, IndexMetaData indexMetaData) { + private void assertIndexHealth(ClusterIndexHealth indexHealth, RoutingTableGenerator.ShardCounter counter, + IndexMetaData indexMetaData) { assertThat(indexHealth.getStatus(), equalTo(counter.status())); assertThat(indexHealth.getNumberOfShards(), equalTo(indexMetaData.getNumberOfShards())); assertThat(indexHealth.getNumberOfReplicas(), equalTo(indexMetaData.getNumberOfReplicas())); @@ -57,4 +73,119 @@ private void assertIndexHealth(ClusterIndexHealth indexHealth, RoutingTableGener assertThat(totalShards, equalTo(indexMetaData.getNumberOfShards() * (1 + indexMetaData.getNumberOfReplicas()))); } + + @Override + protected ClusterIndexHealth createTestInstance() { + return randomIndexHealth(randomAlphaOfLengthBetween(1, 10), level); + } + + public static ClusterIndexHealth randomIndexHealth(String indexName, ClusterHealthRequest.Level level) { + Map shards = new HashMap<>(); + if (level == ClusterHealthRequest.Level.SHARDS) { + for (int i = 0; i < randomInt(5); i++) { + shards.put(i, ClusterShardHealthTests.randomShardHealth(i)); + } + } + return new ClusterIndexHealth(indexName, randomInt(1000), randomInt(1000), randomInt(1000), randomInt(1000), + randomInt(1000), randomInt(1000), randomInt(1000), randomFrom(ClusterHealthStatus.values()), shards); + } + + @Override + protected Writeable.Reader instanceReader() { + return ClusterIndexHealth::new; + } + + @Override + protected ClusterIndexHealth doParseInstance(XContentParser parser) throws IOException { + return ClusterIndexHealth.fromXContent(parser); + } + + @Override + protected ToXContent.Params getToXContentParams() { + return new ToXContent.MapParams(Collections.singletonMap("level", level.name().toLowerCase(Locale.ROOT))); + } + + @Override + protected boolean supportsUnknownFields() { + return true; + } + + // Ignore all paths which looks like "RANDOMINDEXNAME.shards" + private static final Pattern SHARDS_IN_XCONTENT = Pattern.compile("^\\w+\\.shards$"); + + @Override + protected Predicate getRandomFieldsExcludeFilter() { + return field -> "".equals(field) || SHARDS_IN_XCONTENT.matcher(field).find(); + } + @Override + protected ClusterIndexHealth mutateInstance(ClusterIndexHealth instance) throws IOException { + String mutate = randomFrom("index", "numberOfShards", "numberOfReplicas", "activeShards", "relocatingShards", + "initializingShards", "unassignedShards", "activePrimaryShards", "status", "shards"); + switch (mutate) { + case "index": + return new ClusterIndexHealth(instance.getIndex() + randomAlphaOfLengthBetween(2, 5), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), instance.getStatus(), instance.getShards()); + case "numberOfShards": + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards() + between(1, 10), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), instance.getStatus(), instance.getShards()); + case "numberOfReplicas": + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas() + between(1, 10), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), instance.getStatus(), instance.getShards()); + case "activeShards": + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards() + between(1, 10), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), instance.getStatus(), instance.getShards()); + case "relocatingShards": + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards() + between(1, 10), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), instance.getStatus(), instance.getShards()); + case "initializingShards": + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards() + between(1, 10), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), instance.getStatus(), instance.getShards()); + case "unassignedShards": + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards() + between(1, 10), + instance.getActivePrimaryShards(), instance.getStatus(), instance.getShards()); + case "activePrimaryShards": + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards() + between(1, 10), instance.getStatus(), instance.getShards()); + case "status": + ClusterHealthStatus status = randomFrom( + Arrays.stream(ClusterHealthStatus.values()).filter( + value -> !value.equals(instance.getStatus()) + ).collect(Collectors.toList()) + ); + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), status, instance.getShards()); + case "shards": + Map map; + if (instance.getShards().isEmpty()) { + map = Collections.singletonMap(0, ClusterShardHealthTests.randomShardHealth(0)); + } else { + map = new HashMap<>(instance.getShards()); + map.remove(map.keySet().iterator().next()); + } + return new ClusterIndexHealth(instance.getIndex(), instance.getNumberOfShards(), + instance.getNumberOfReplicas(), instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.getActivePrimaryShards(), instance.getStatus(), map); + default: + throw new UnsupportedOperationException(); + } + } } diff --git a/server/src/test/java/org/elasticsearch/cluster/health/ClusterShardHealthTests.java b/server/src/test/java/org/elasticsearch/cluster/health/ClusterShardHealthTests.java new file mode 100644 index 0000000000000..6ee0fc1ee0a67 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/cluster/health/ClusterShardHealthTests.java @@ -0,0 +1,111 @@ +/* + * 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.cluster.health; + +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.test.AbstractSerializingTestCase; + +import java.io.IOException; +import java.util.Arrays; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class ClusterShardHealthTests extends AbstractSerializingTestCase { + + @Override + protected ClusterShardHealth doParseInstance(XContentParser parser) throws IOException { + return ClusterShardHealth.fromXContent(parser); + } + + @Override + protected ClusterShardHealth createTestInstance() { + return randomShardHealth(randomInt(1000)); + } + + static ClusterShardHealth randomShardHealth(int id) { + return new ClusterShardHealth(id, randomFrom(ClusterHealthStatus.values()), randomInt(1000), randomInt(1000), + randomInt(1000), randomInt(1000), randomBoolean()); + } + + @Override + protected Writeable.Reader instanceReader() { + return ClusterShardHealth::new; + } + + @Override + protected boolean supportsUnknownFields() { + return true; + } + + @Override + protected Predicate getRandomFieldsExcludeFilter() { + //don't inject random fields at the root, which contains arbitrary shard ids + return ""::equals; + } + + @Override + protected ClusterShardHealth mutateInstance(final ClusterShardHealth instance) { + String mutate = randomFrom("shardId", "status", "activeShards", "relocatingShards", "initializingShards", + "unassignedShards", "primaryActive"); + switch (mutate) { + case "shardId": + return new ClusterShardHealth(instance.getShardId() + between(1, 10), instance.getStatus(), + instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.isPrimaryActive()); + case "status": + ClusterHealthStatus status = randomFrom( + Arrays.stream(ClusterHealthStatus.values()).filter( + value -> !value.equals(instance.getStatus()) + ).collect(Collectors.toList()) + ); + return new ClusterShardHealth(instance.getShardId(), status, + instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.isPrimaryActive()); + case "activeShards": + return new ClusterShardHealth(instance.getShardId(), instance.getStatus(), + instance.getActiveShards() + between(1, 10), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.isPrimaryActive()); + case "relocatingShards": + return new ClusterShardHealth(instance.getShardId(), instance.getStatus(), + instance.getActiveShards(), instance.getRelocatingShards() + between(1, 10), + instance.getInitializingShards(), instance.getUnassignedShards(), instance.isPrimaryActive()); + case "initializingShards": + return new ClusterShardHealth(instance.getShardId(), instance.getStatus(), + instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards() + between(1, 10), instance.getUnassignedShards(), + instance.isPrimaryActive()); + case "unassignedShards": + return new ClusterShardHealth(instance.getShardId(), instance.getStatus(), + instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards() + between(1, 10), + instance.isPrimaryActive()); + case "primaryActive": + return new ClusterShardHealth(instance.getShardId(), instance.getStatus(), + instance.getActiveShards(), instance.getRelocatingShards(), + instance.getInitializingShards(), instance.getUnassignedShards(), + instance.isPrimaryActive() == false); + default: + throw new UnsupportedOperationException(); + } + } +} diff --git a/server/src/test/java/org/elasticsearch/tasks/CancelTasksResponseTests.java b/server/src/test/java/org/elasticsearch/tasks/CancelTasksResponseTests.java index 3233edefb30d4..56b92bb1e25c6 100644 --- a/server/src/test/java/org/elasticsearch/tasks/CancelTasksResponseTests.java +++ b/server/src/test/java/org/elasticsearch/tasks/CancelTasksResponseTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.action.admin.cluster.node.tasks.cancel.CancelTasksResponse; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.AbstractXContentTestCase; @@ -96,7 +97,7 @@ public void testFromXContentWithFailures() throws IOException { boolean assertToXContentEquivalence = false; AbstractXContentTestCase.testFromXContent(NUMBER_OF_TEST_RUNS, instanceSupplier, supportsUnknownFields, Strings.EMPTY_ARRAY, getRandomFieldsExcludeFilter(), this::createParser, this::doParseInstance, - this::assertEqualInstances, assertToXContentEquivalence); + this::assertEqualInstances, assertToXContentEquivalence, ToXContent.EMPTY_PARAMS); } private static CancelTasksResponse createTestInstanceWithFailures() { diff --git a/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java b/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java index 4862278fac111..23ce53b6c35c9 100644 --- a/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java +++ b/server/src/test/java/org/elasticsearch/tasks/ListTasksResponseTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.action.TaskOperationFailure; import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.test.AbstractXContentTestCase; @@ -156,7 +157,7 @@ public void testFromXContentWithFailures() throws IOException { boolean assertToXContentEquivalence = false; AbstractXContentTestCase.testFromXContent(NUMBER_OF_TEST_RUNS, instanceSupplier, supportsUnknownFields, Strings.EMPTY_ARRAY, getRandomFieldsExcludeFilter(), this::createParser, this::doParseInstance, - this::assertEqualInstances, assertToXContentEquivalence); + this::assertEqualInstances, assertToXContentEquivalence, ToXContent.EMPTY_PARAMS); } private static ListTasksResponse createTestInstanceWithFailures() { diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java index a725c967973d4..6ec32f6654fff 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractSerializingTestCase.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; @@ -35,7 +36,7 @@ public abstract class AbstractSerializingTestCase getRandomFieldsExcludeFilter() { protected String[] getShuffleFieldsExceptions() { return Strings.EMPTY_ARRAY; } + + /** + * Params that have to be provided when calling calling {@link ToXContent#toXContent(XContentBuilder, ToXContent.Params)} + */ + protected ToXContent.Params getToXContentParams() { + return ToXContent.EMPTY_PARAMS; + } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractStreamableXContentTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractStreamableXContentTestCase.java index 0c165f92e3998..4c9d2f7f95231 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractStreamableXContentTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractStreamableXContentTestCase.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; @@ -35,7 +36,7 @@ public abstract class AbstractStreamableXContentTestCase getRandomFieldsExcludeFilter() { protected String[] getShuffleFieldsExceptions() { return Strings.EMPTY_ARRAY; } + + /** + * Params that have to be provided when calling calling {@link ToXContent#toXContent(XContentBuilder, ToXContent.Params)} + */ + protected ToXContent.Params getToXContentParams() { + return ToXContent.EMPTY_PARAMS; + } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java index 983897049c767..fd5700c68a981 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractXContentTestCase.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContent; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; @@ -48,12 +49,13 @@ public static void testFromXContent(int numberOfTestRuns, createParserFunction, CheckedFunction parseFunction, BiConsumer assertEqualsConsumer, - boolean assertToXContentEquivalence) throws IOException { + boolean assertToXContentEquivalence, + ToXContent.Params toXContentParams) throws IOException { for (int runs = 0; runs < numberOfTestRuns; runs++) { T testInstance = instanceSupplier.get(); XContentType xContentType = randomFrom(XContentType.values()); - BytesReference shuffled = toShuffledXContent(testInstance, xContentType, ToXContent.EMPTY_PARAMS, false, createParserFunction, - shuffleFieldsExceptions); + BytesReference shuffled = toShuffledXContent(testInstance, xContentType, toXContentParams,false, + createParserFunction, shuffleFieldsExceptions); BytesReference withRandomFields; if (supportsUnknownFields) { // we add a few random fields to check that parser is lenient on new fields @@ -65,7 +67,8 @@ public static void testFromXContent(int numberOfTestRuns, T parsed = parseFunction.apply(parser); assertEqualsConsumer.accept(testInstance, parsed); if (assertToXContentEquivalence) { - assertToXContentEquivalent(shuffled, XContentHelper.toXContent(parsed, xContentType, false), xContentType); + assertToXContentEquivalent(shuffled, XContentHelper.toXContent(parsed, xContentType, toXContentParams, false), + xContentType); } } } @@ -77,7 +80,7 @@ public static void testFromXContent(int numberOfTestRuns, public final void testFromXContent() throws IOException { testFromXContent(NUMBER_OF_TEST_RUNS, this::createTestInstance, supportsUnknownFields(), getShuffleFieldsExceptions(), getRandomFieldsExcludeFilter(), this::createParser, this::parseInstance, this::assertEqualInstances, - assertToXContentEquivalence()); + assertToXContentEquivalence(), getToXContentParams()); } /** @@ -127,4 +130,11 @@ protected Predicate getRandomFieldsExcludeFilter() { protected String[] getShuffleFieldsExceptions() { return Strings.EMPTY_ARRAY; } + + /** + * Params that have to be provided when calling calling {@link ToXContent#toXContent(XContentBuilder, ToXContent.Params)} + */ + protected ToXContent.Params getToXContentParams() { + return ToXContent.EMPTY_PARAMS; + } } From ab06b13544a9b115c7862465709e3a3508fae22f Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Wed, 13 Jun 2018 10:16:46 -0400 Subject: [PATCH 47/71] Add notion of internal index settings (#31286) We have some use cases for an index setting to only be manageable by dedicated APIs rather than be updateable via the update settings API. This commit adds the notion of an internal index setting. Such settings can be set on create index requests, they can not be changed via the update settings API, yet they can be changed by action on behalf of or triggered by the user via dedicated APIs. --- .../MetaDataUpdateSettingsService.java | 6 +- .../settings/AbstractScopedSettings.java | 60 ++++- .../common/settings/Setting.java | 20 +- .../common/settings/ScopedSettingsTests.java | 24 ++ .../common/settings/SettingTests.java | 7 + .../indices/settings/UpdateSettingsIT.java | 205 +++++++++++++++++- 6 files changed, 311 insertions(+), 11 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java index ce5ad12a53d6a..38766c08e08a3 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataUpdateSettingsService.java @@ -82,8 +82,10 @@ public void updateSettings(final UpdateSettingsClusterStateUpdateRequest request Settings.Builder settingsForOpenIndices = Settings.builder(); final Set skippedSettings = new HashSet<>(); - indexScopedSettings.validate(normalizedSettings.filter(s -> Regex.isSimpleMatchPattern(s) == false /* don't validate wildcards */), - false); //don't validate dependencies here we check it below never allow to change the number of shards + indexScopedSettings.validate( + normalizedSettings.filter(s -> Regex.isSimpleMatchPattern(s) == false), // don't validate wildcards + false, // don't validate dependencies here we check it below never allow to change the number of shards + true); // validate internal index settings for (String key : normalizedSettings.keySet()) { Setting setting = indexScopedSettings.get(key); boolean isWildcard = setting == null && Regex.isSimpleMatchPattern(key); diff --git a/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java index e8bb946c8a795..eb4e294642417 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java @@ -282,6 +282,18 @@ public final void validate(final Settings settings, final boolean validateDepend validate(settings, validateDependencies, false, false); } + /** + * Validates that all settings are registered and valid. + * + * @param settings the settings to validate + * @param validateDependencies true if dependent settings should be validated + * @param validateInternalIndex true if internal index settings should be validated + * @see Setting#getSettingsDependencies(String) + */ + public final void validate(final Settings settings, final boolean validateDependencies, final boolean validateInternalIndex) { + validate(settings, validateDependencies, false, false, validateInternalIndex); + } + /** * Validates that all settings are registered and valid. * @@ -296,6 +308,25 @@ public final void validate( final boolean validateDependencies, final boolean ignorePrivateSettings, final boolean ignoreArchivedSettings) { + validate(settings, validateDependencies, ignorePrivateSettings, ignoreArchivedSettings, false); + } + + /** + * Validates that all settings are registered and valid. + * + * @param settings the settings + * @param validateDependencies true if dependent settings should be validated + * @param ignorePrivateSettings true if private settings should be ignored during validation + * @param ignoreArchivedSettings true if archived settings should be ignored during validation + * @param validateInternalIndex true if index internal settings should be validated + * @see Setting#getSettingsDependencies(String) + */ + public final void validate( + final Settings settings, + final boolean validateDependencies, + final boolean ignorePrivateSettings, + final boolean ignoreArchivedSettings, + final boolean validateInternalIndex) { final List exceptions = new ArrayList<>(); for (final String key : settings.keySet()) { // settings iterate in deterministic fashion if (isPrivateSetting(key) && ignorePrivateSettings) { @@ -305,7 +336,7 @@ public final void validate( continue; } try { - validate(key, settings, validateDependencies); + validate(key, settings, validateDependencies, validateInternalIndex); } catch (final RuntimeException ex) { exceptions.add(ex); } @@ -314,9 +345,27 @@ public final void validate( } /** - * Validates that the setting is valid + * Validates that the settings is valid. + * + * @param key the key of the setting to validate + * @param settings the settings + * @param validateDependencies true if dependent settings should be validated + * @throws IllegalArgumentException if the setting is invalid */ - void validate(String key, Settings settings, boolean validateDependencies) { + void validate(final String key, final Settings settings, final boolean validateDependencies) { + validate(key, settings, validateDependencies, false); + } + + /** + * Validates that the settings is valid. + * + * @param key the key of the setting to validate + * @param settings the settings + * @param validateDependencies true if dependent settings should be validated + * @param validateInternalIndex true if internal index settings should be validated + * @throws IllegalArgumentException if the setting is invalid + */ + void validate(final String key, final Settings settings, final boolean validateDependencies, final boolean validateInternalIndex) { Setting setting = getRaw(key); if (setting == null) { LevensteinDistance ld = new LevensteinDistance(); @@ -356,6 +405,11 @@ void validate(String key, Settings settings, boolean validateDependencies) { } } } + // the only time that validateInternalIndex should be true is if this call is coming via the update settings API + if (validateInternalIndex && setting.getProperties().contains(Setting.Property.InternalIndex)) { + throw new IllegalArgumentException( + "can not update internal setting [" + setting.getKey() + "]; this setting is managed via a dedicated API"); + } } setting.get(settings); } diff --git a/server/src/main/java/org/elasticsearch/common/settings/Setting.java b/server/src/main/java/org/elasticsearch/common/settings/Setting.java index f45f4bda9c9fe..7f3906ff5a251 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/Setting.java +++ b/server/src/main/java/org/elasticsearch/common/settings/Setting.java @@ -120,7 +120,13 @@ public enum Property { * Mark this setting as not copyable during an index resize (shrink or split). This property can only be applied to settings that * also have {@link Property#IndexScope}. */ - NotCopyableOnResize + NotCopyableOnResize, + + /** + * Indicates an index-level setting that is managed internally. Such a setting can only be added to an index on index creation but + * can not be updated via the update API. + */ + InternalIndex } private final Key key; @@ -152,14 +158,18 @@ private Setting(Key key, @Nullable Setting fallbackSetting, Function properties, final Property property) { + if (properties.contains(property) && properties.contains(Property.IndexScope) == false) { + throw new IllegalArgumentException("non-index-scoped setting [" + key + "] can not have property [" + property + "]"); + } + } + /** * Creates a new Setting instance * @param key the settings key for this setting. diff --git a/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java b/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java index f00768651f917..2376d5663402e 100644 --- a/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java +++ b/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java @@ -876,4 +876,28 @@ public void testFinalSettingUpdateFail() { Settings.builder().put(currentSettings), Settings.builder(), "node")); assertThat(exc.getMessage(), containsString("final node setting [some.final.group.foo]")); } + + public void testInternalIndexSettingsFailsValidation() { + final Setting indexInternalSetting = Setting.simpleString("index.internal", Property.InternalIndex, Property.IndexScope); + final IndexScopedSettings indexScopedSettings = + new IndexScopedSettings(Settings.EMPTY, Collections.singleton(indexInternalSetting)); + final IllegalArgumentException e = expectThrows( + IllegalArgumentException.class, + () -> { + final Settings settings = Settings.builder().put("index.internal", "internal").build(); + indexScopedSettings.validate(settings, false, /* validateInternalIndex */ true); + }); + final String message = "can not update internal setting [index.internal]; this setting is managed via a dedicated API"; + assertThat(e, hasToString(containsString(message))); + } + + public void testInternalIndexSettingsSkipValidation() { + final Setting internalIndexSetting = Setting.simpleString("index.internal", Property.InternalIndex, Property.IndexScope); + final IndexScopedSettings indexScopedSettings = + new IndexScopedSettings(Settings.EMPTY, Collections.singleton(internalIndexSetting)); + // nothing should happen, validation should not throw an exception + final Settings settings = Settings.builder().put("index.internal", "internal").build(); + indexScopedSettings.validate(settings, false, /* validateInternalIndex */ false); + } + } diff --git a/server/src/test/java/org/elasticsearch/common/settings/SettingTests.java b/server/src/test/java/org/elasticsearch/common/settings/SettingTests.java index 1ab92526e3130..c32037f44525e 100644 --- a/server/src/test/java/org/elasticsearch/common/settings/SettingTests.java +++ b/server/src/test/java/org/elasticsearch/common/settings/SettingTests.java @@ -735,6 +735,13 @@ public void testRejectNonIndexScopedNotCopyableOnResizeSetting() { assertThat(e, hasToString(containsString("non-index-scoped setting [foo.bar] can not have property [NotCopyableOnResize]"))); } + public void testRejectNonIndexScopedIndexInternalSetting() { + final IllegalArgumentException e = expectThrows( + IllegalArgumentException.class, + () -> Setting.simpleString("foo.bar", Property.InternalIndex)); + assertThat(e, hasToString(containsString("non-index-scoped setting [foo.bar] can not have property [InternalIndex]"))); + } + public void testTimeValue() { final TimeValue random = TimeValue.parseTimeValue(randomTimeValue(), "test"); diff --git a/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java b/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java index 51c073c607e22..8093e7d38a14d 100644 --- a/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java +++ b/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java @@ -19,19 +19,40 @@ package org.elasticsearch.indices.settings; +import org.elasticsearch.action.Action; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.action.ActionRequest; +import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.master.MasterNodeRequest; +import org.elasticsearch.action.support.master.TransportMasterNodeAction; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.ClusterStateUpdateTask; +import org.elasticsearch.cluster.block.ClusterBlockException; import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; +import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Priority; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexModule; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.engine.VersionConflictEngineException; import org.elasticsearch.indices.IndicesService; +import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportService; +import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -46,6 +67,7 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertThrows; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.nullValue; public class UpdateSettingsIT extends ESIntegTestCase { @@ -79,7 +101,12 @@ public void testInvalidDynamicUpdate() { @Override protected Collection> nodePlugins() { - return Arrays.asList(DummySettingPlugin.class, FinalSettingPlugin.class); + return Arrays.asList(DummySettingPlugin.class, FinalSettingPlugin.class, InternalIndexSettingsPlugin.class); + } + + @Override + protected Collection> transportClientPlugins() { + return Collections.singletonList(InternalIndexSettingsPlugin.class); } public static class DummySettingPlugin extends Plugin { @@ -124,6 +151,151 @@ public List> getSettings() { } } + public static class InternalIndexSettingsPlugin extends Plugin implements ActionPlugin { + + public static final Setting INDEX_INTERNAL_SETTING = + Setting.simpleString("index.internal", Setting.Property.IndexScope, Setting.Property.InternalIndex); + + @Override + public List> getSettings() { + return Collections.singletonList(INDEX_INTERNAL_SETTING); + } + + public static class UpdateInternalIndexAction + extends Action { + + private static final UpdateInternalIndexAction INSTANCE = new UpdateInternalIndexAction(); + private static final String NAME = "indices:admin/settings/update-internal-index"; + + public UpdateInternalIndexAction() { + super(NAME); + } + + static class Request extends MasterNodeRequest { + + private String index; + private String value; + + Request() { + + } + + Request(final String index, final String value) { + this.index = index; + this.value = value; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public void readFrom(final StreamInput in) throws IOException { + super.readFrom(in); + index = in.readString(); + value = in.readString(); + } + + @Override + public void writeTo(final StreamOutput out) throws IOException { + super.writeTo(out); + out.writeString(index); + out.writeString(value); + } + + } + + static class Response extends ActionResponse { + + } + + @Override + public Response newResponse() { + return new Response(); + } + + } + + public static class TransportUpdateInternalIndexAction + extends TransportMasterNodeAction { + + @Inject + public TransportUpdateInternalIndexAction( + final Settings settings, + final TransportService transportService, + final ClusterService clusterService, + final ThreadPool threadPool, + final ActionFilters actionFilters, + final IndexNameExpressionResolver indexNameExpressionResolver) { + super( + settings, + UpdateInternalIndexAction.NAME, + transportService, + clusterService, + threadPool, + actionFilters, + indexNameExpressionResolver, + UpdateInternalIndexAction.Request::new); + } + + @Override + protected String executor() { + return ThreadPool.Names.SAME; + } + + @Override + protected UpdateInternalIndexAction.Response newResponse() { + return new UpdateInternalIndexAction.Response(); + } + + @Override + protected void masterOperation( + final UpdateInternalIndexAction.Request request, + final ClusterState state, + final ActionListener listener) throws Exception { + clusterService.submitStateUpdateTask("update-index-internal", new ClusterStateUpdateTask() { + @Override + public ClusterState execute(final ClusterState currentState) throws Exception { + final MetaData.Builder builder = MetaData.builder(currentState.metaData()); + final IndexMetaData.Builder imdBuilder = IndexMetaData.builder(currentState.metaData().index(request.index)); + final Settings.Builder settingsBuilder = + Settings.builder() + .put(currentState.metaData().index(request.index).getSettings()) + .put("index.internal", request.value); + imdBuilder.settings(settingsBuilder); + builder.put(imdBuilder.build(), true); + return ClusterState.builder(currentState).metaData(builder).build(); + } + + @Override + public void clusterStateProcessed(final String source, final ClusterState oldState, final ClusterState newState) { + listener.onResponse(new UpdateInternalIndexAction.Response()); + } + + @Override + public void onFailure(final String source, final Exception e) { + listener.onFailure(e); + } + + }); + } + + @Override + protected ClusterBlockException checkBlock(UpdateInternalIndexAction.Request request, ClusterState state) { + return null; + } + + } + + @Override + public List> getActions() { + return Collections.singletonList( + new ActionHandler<>(UpdateInternalIndexAction.INSTANCE, TransportUpdateInternalIndexAction.class)); + } + + } + public void testUpdateDependentClusterSettings() { IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> client().admin().cluster().prepareUpdateSettings().setPersistentSettings(Settings.builder() @@ -474,4 +646,35 @@ public void testUpdateSettingsWithBlocks() { } } + public void testUpdateInternalIndexSettingViaSettingsAPI() { + final Settings settings = Settings.builder().put("index.internal", "internal").build(); + createIndex("test", settings); + final GetSettingsResponse response = client().admin().indices().prepareGetSettings("test").get(); + assertThat(response.getSetting("test", "index.internal"), equalTo("internal")); + // we can not update the setting via the update settings API + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> client().admin() + .indices() + .prepareUpdateSettings("test") + .setSettings(Settings.builder().put("index.internal", "internal-update")) + .get()); + final String message = "can not update internal setting [index.internal]; this setting is managed via a dedicated API"; + assertThat(e, hasToString(containsString(message))); + final GetSettingsResponse responseAfterAttemptedUpdate = client().admin().indices().prepareGetSettings("test").get(); + assertThat(responseAfterAttemptedUpdate.getSetting("test", "index.internal"), equalTo("internal")); + } + + public void testUpdateInternalIndexSettingViaDedicatedAPI() { + final Settings settings = Settings.builder().put("index.internal", "internal").build(); + createIndex("test", settings); + final GetSettingsResponse response = client().admin().indices().prepareGetSettings("test").get(); + assertThat(response.getSetting("test", "index.internal"), equalTo("internal")); + client().execute( + InternalIndexSettingsPlugin.UpdateInternalIndexAction.INSTANCE, + new InternalIndexSettingsPlugin.UpdateInternalIndexAction.Request("test", "internal-update")) + .actionGet(); + final GetSettingsResponse responseAfterUpdate = client().admin().indices().prepareGetSettings("test").get(); + assertThat(responseAfterUpdate.getSetting("test", "index.internal"), equalTo("internal-update")); + } + } From 60a0b143c24f5d321fd4a6af40c0c280492bfcb3 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 13 Jun 2018 17:31:38 +0200 Subject: [PATCH 48/71] 6.3 release notes. --- docs/reference/release-notes/6.3.asciidoc | 524 ++++++++++++++++++++-- 1 file changed, 478 insertions(+), 46 deletions(-) diff --git a/docs/reference/release-notes/6.3.asciidoc b/docs/reference/release-notes/6.3.asciidoc index a2fc2ad52145e..5cc49df6cccea 100644 --- a/docs/reference/release-notes/6.3.asciidoc +++ b/docs/reference/release-notes/6.3.asciidoc @@ -1,47 +1,29 @@ -//// -// To add a release, copy and paste the following text, uncomment the relevant -// sections, and add a link to the new section in the list of releases at the -// top of the page. Note that release subheads must be floated and sections -// cannot be empty. -// TEMPLATE - -// [[release-notes-n.n.n]] -// == {es} n.n.n - -//[float] -[[breaking-n.n.n]] -//=== Breaking Changes +[[release-notes-6.3.0]] +== 6.3.0 Release Notes -//[float] -//=== Breaking Java Changes +coming[6.3.0] -//[float] -//=== Deprecations +Also see <>. -//[float] -//=== New Features +[[breaking-6.3.0]] +[float] +=== Breaking changes -//[float] -//=== Enhancements +Core:: +* Rename the bulk thread pool to write thread pool {pull}29593[#29593] -//[float] -//=== Regressions +Packaging:: +* Create keystore on package install {pull}28928[#28928] -//[float] -//=== Known Issues -//// +Plugins:: +* Remove silent batch mode from install plugin {pull}29359[#29359] -[[release-notes-6.3.0]] -== {es} version 6.3.0 +Search:: +* Fail _search request with trailing tokens {pull}29428[#29428] (issue: {issue}28995[#28995]) -coming[6.3.0] +Task Management:: +* Remove metadata customs that can break serialization {pull}30945[#30945] (issues: {issue}30731[#30731], {issue}30857[#30857]) -[float] -[[breaking-6.3.0]] -=== Breaking Changes - -[float] -=== Deprecations Monitoring:: * By default when you install {xpack}, monitoring is enabled but data collection is disabled. To enable data collection, use the new @@ -54,24 +36,357 @@ Security:: with an SPI based extension mechanism that is installed and built as an elasticsearch plugin. -//[float] -//=== Breaking Java Changes -//[float] -//=== Deprecations -//[float] -//=== New Features +[[breaking-java-6.3.0]] +[float] +=== Breaking Java changes + +Aggregations:: +* Add a shallow copy method to aggregation builders {pull}28430[#28430] (issue: {issue}27782[#27782]) + + + +[[deprecation-6.3.0]] +[float] +=== Deprecations + +Analysis:: +* Deprecate use of `htmlStrip` as name for HtmlStripCharFilter {pull}27429[#27429] + +Core:: +* Deprecate the index thread pool {pull}29540[#29540] +* Add deprecation message for config prompt {pull}28000[#28000] (issue: {issue}27216[#27216]) + +Mapping:: +* Deprecate the `update_all_types` option. {pull}28284[#28284] + +Network:: +* Networking: Deprecate http.enabled setting {pull}29591[#29591] (issue: {issue}12792[#12792]) +* Deprecate large max content length truncation {pull}29339[#29339] (issue: {issue}29337[#29337]) + +REST API:: +* REST : deprecate `field_data` for Clear Indices Cache API {pull}28943[#28943] (issue: {issue}17804[#17804]) + +Search:: +* Deprecate slicing on `_uid`. {pull}29353[#29353] + +Stats:: +* Deprecate the suggest metrics {pull}29627[#29627] (issue: {issue}29589[#29589]) + + + +[[feature-6.3.0]] +[float] +=== New features + +Aggregations:: +* Adds the ability to specify a format on composite date_histogram source {pull}28310[#28310] (issue: {issue}27923[#27923]) +* Calculate sum in Kahan summation algorithm in aggregations (#27807) {pull}27848[#27848] (issue: {issue}27807[#27807]) +* Adds a new auto-interval date histogram {pull}26659[#26659] (issue: {issue}9572[#9572]) + +Discovery-Plugins:: +* Update ec2 secure settings {pull}29134[#29134] + +Geo:: +* Add Z value support to geo_point and geo_shape {pull}25738[#25738] (issue: {issue}22917[#22917]) + +Ingest:: +* Add ingest-attachment support for per document `indexed_chars` limit {pull}28977[#28977] (issue: {issue}28942[#28942]) + +Java High Level REST Client:: +* Add ranking evaluation API to High Level Rest Client {pull}28357[#28357] +* Add Indices Aliases API to the high level REST client {pull}27876[#27876] (issue: {issue}27205[#27205]) + +Java Low Level REST Client:: +* Client: Wrap synchronous exceptions {pull}28919[#28919] (issue: {issue}28399[#28399]) + +Network:: +* Introduce client feature tracking {pull}31020[#31020] (issue: {issue}30731[#30731]) + +REST API:: +* REST: Include suppressed exceptions on failures {pull}29115[#29115] (issue: {issue}23392[#23392]) + +Ranking:: +* Add indices options support to _rank_eval {pull}29386[#29386] + +Scripting:: +* Painless execute api {pull}29164[#29164] + +Search:: +* Search - new flag: allow_partial_search_results {pull}27906[#27906] (issue: {issue}27435[#27435]) + +Snapshot/Restore:: +* Update s3 secure settings {pull}28517[#28517] + +Task Management:: +* Add new setting to disable persistent tasks allocations {pull}29137[#29137] + + +[[enhancement-6.3.0]] [float] === Enhancements +Aggregations:: +* Build global ordinals terms bucket from matching ordinals {pull}30166[#30166] (issue: {issue}30117[#30117]) +* Reject query if top hits result window exceeds index max result window {pull}29199[#29199] (issue: {issue}29190[#29190]) +* Optimize the composite aggregation for match_all and range queries {pull}28745[#28745] (issue: {issue}28688[#28688]) +* Always return the after_key in composite aggregation response {pull}28358[#28358] +* Upgrade t-digest to 3.2 {pull}28305[#28305] (issue: {issue}28295[#28295]) + +Core:: +* Implement Iterator#remove for Cache values iter {pull}29633[#29633] +* Introduce analyze thread pool {pull}29541[#29541] +* Add useful message when no input from terminal {pull}29369[#29369] (issues: {issue}29359[#29359], {issue}29365[#29365]) +* Improve exception handling on TransportMasterNodeAction {pull}29314[#29314] (issue: {issue}1[#1]) +* Add generic array support to AbstractObjectParser {pull}28552[#28552] +* Introduce secure security manager to project {pull}28453[#28453] +* XContent: Factor deprecation handling into callback {pull}28449[#28449] (issue: {issue}27955[#27955]) +* Add settings to control size and count of warning headers in responses {pull}28427[#28427] (issue: {issue}28301[#28301]) +* Trim down usages of `ShardOperationFailedException` interface {pull}28312[#28312] (issue: {issue}27799[#27799]) +* Enforce that java.io.tmpdir exists on startup {pull}28217[#28217] +* Add Writeable.Reader support to TransportResponseHandler {pull}28010[#28010] (issue: {issue}26315[#26315]) + +Discovery-Plugins:: +* Add information when master node left to DiscoveryNodes' shortSummary() {pull}28197[#28197] (issue: {issue}28169[#28169]) + +Distributed:: +* Only log warning when actually failing shards {pull}28558[#28558] (issue: {issue}28534[#28534]) +* Allows failing shards without marking as stale {pull}28054[#28054] (issue: {issue}24841[#24841]) + +Engine:: +* Never leave stale delete tombstones in version map {pull}29619[#29619] +* Avoid side-effect in VersionMap when assertion enabled {pull}29585[#29585] +* Enforce access to translog via engine {pull}29542[#29542] +* ElasticsearchMergePolicy should extend from MergePolicyWrapper {pull}29476[#29476] +* Track Lucene operations in engine explicitly {pull}29357[#29357] +* Allow _update and upsert to read from the transaction log {pull}29264[#29264] (issue: {issue}26802[#26802]) +* Move trimming unsafe commits from the Engine constructor to Store {pull}29260[#29260] (issue: {issue}28245[#28245]) +* Add primary term to translog header {pull}29227[#29227] +* Fold EngineDiskUtils into Store, for better lock semantics {pull}29156[#29156] (issue: {issue}28245[#28245]) +* Do not renew sync-id if all shards are sealed {pull}29103[#29103] (issue: {issue}27838[#27838]) +* Prune only gc deletes below the local checkpoint {pull}28790[#28790] +* Do not optimize append-only operation if normal operation with higher seq# was seen {pull}28787[#28787] +* Try if tombstone is eligable for pruning before locking on it's key {pull}28767[#28767] +* Simplify Engine.Searcher creation {pull}28728[#28728] +* Revisit deletion policy after release the last snapshot {pull}28627[#28627] (issue: {issue}28140[#28140]) +* Index shard should roll generation via the engine {pull}28537[#28537] +* Add lower bound for translog flush threshold {pull}28382[#28382] (issues: {issue}23779[#23779], {issue}28350[#28350]) +* Untangle Engine Constructor logic {pull}28245[#28245] +* Clean up commits when global checkpoint advanced {pull}28140[#28140] (issue: {issue}10708[#10708]) +* Replicate writes only to fully initialized shards {pull}28049[#28049] +* Track deletes only in the tombstone map instead of maintaining as copy {pull}27868[#27868] + +Geo:: +* Add null_value support to geo_point type {pull}29451[#29451] (issue: {issue}12998[#12998]) + +Highlighting:: +* Limit analyzed text for highlighting (improvements) {pull}28907[#28907] (issues: {issue}16764[#16764], {issue}27934[#27934]) +* Limit analyzed text for highlighting (improvements) {pull}28808[#28808] (issues: {issue}16764[#16764], {issue}27934[#27934]) + +Ingest:: +* Reduce heap-memory usage of ingest-geoip plugin {pull}28963[#28963] (issue: {issue}28782[#28782]) +* Forbid trappy methods from java.time {pull}28476[#28476] +* version set in ingest pipeline {pull}27573[#27573] (issue: {issue}27242[#27242]) + +Java High Level REST Client:: +* Remove flatSettings support from request classes {pull}29560[#29560] +* REST high-level client: add support for Indices Update Settings API [take 2] {pull}29327[#29327] (issue: {issue}27205[#27205]) +* REST high-level client: add force merge API {pull}28896[#28896] (issue: {issue}27205[#27205]) +* REST high-level client: add support for Indices Update Settings API {pull}28892[#28892] (issue: {issue}27205[#27205]) +* REST high-level client: add clear cache API {pull}28866[#28866] (issue: {issue}27205[#27205]) +* REST high-level client: add flush API {pull}28852[#28852] (issue: {issue}27205[#27205]) +* REST high-level client: add support for Rollover Index API {pull}28698[#28698] (issue: {issue}27205[#27205]) +* Add Cluster Put Settings API to the high level REST client {pull}28633[#28633] (issue: {issue}27205[#27205]) +* REST high-level Client: add missing final modifiers {pull}28572[#28572] +* REST high-level client: add support for split and shrink index API {pull}28425[#28425] (issue: {issue}27205[#27205]) +* Java high-level REST : minor code clean up {pull}28409[#28409] +* High level rest client : code clean up {pull}28386[#28386] +* REST high-level client: add support for exists alias {pull}28332[#28332] (issue: {issue}27205[#27205]) +* Added Put Mapping API to high-level Rest client (#27205) {pull}27869[#27869] (issue: {issue}27205[#27205]) +* Add Refresh API for RestHighLevelClient {pull}27799[#27799] (issue: {issue}27205[#27205]) +* Add support for indices exists to REST high level client {pull}27384[#27384] + +License:: +* Require acknowledgement to start_trial license {pull}30135[#30135] (issue: {issue}30134[#30134]) + +Logging:: +* Fix missing node id prefix in startup logs {pull}29534[#29534] +* Do not swallow fail to convert exceptions {pull}29043[#29043] (issue: {issue}19573[#19573]) +* Add total hits to the search slow log {pull}29034[#29034] (issue: {issue}20648[#20648]) +* Remove interning from prefix logger {pull}29031[#29031] (issue: {issue}16831[#16831]) +* Log template creation and deletion {pull}29027[#29027] (issue: {issue}10795[#10795]) +* Disallow logger methods with Object parameter {pull}28969[#28969] + +Mapping:: +* Restrict Document list access in ParseContext {pull}29463[#29463] +* Check presence of multi-types before validating new mapping {pull}29316[#29316] (issue: {issue}29313[#29313]) +* Validate regular expressions in dynamic templates. {pull}29013[#29013] (issue: {issue}24749[#24749]) + Machine Learning:: * Synchronize long and short tests for periodicity {ml-pull}62[#62] * Improvements to trend modelling and periodicity testing for forecasting {ml-pull}7[#7] (issue: {ml-issue}5[#5]) +* [ML] Hide internal Job update options from the REST API {pull}30537[#30537] (issue: {issue}30512[#30512]) + +Packaging:: +* Configure heap dump path for archive packages {pull}29130[#29130] (issue: {issue}26755[#26755]) +* Configure error file for archive packages {pull}29129[#29129] (issues: {issue}29028[#29028], {issue}29032[#29032]) +* Put JVM crash logs in the default log directory {pull}29028[#29028] (issue: {issue}13982[#13982]) +* Stop sourcing scripts during installation/removal {pull}28918[#28918] (issue: {issue}14630[#14630]) + +Plugins:: +* Ensure that azure stream has socket privileges (#28751) {pull}28773[#28773] (issue: {issue}28662[#28662]) +* Plugins: Remove intermediate "elasticsearch" directory within plugin zips {pull}28589[#28589] +* Plugins: Store elasticsearch and java versions in PluginInfo {pull}28556[#28556] +* Plugins: Use one confirmation of all meta plugin permissions {pull}28366[#28366] +* Replace jvm-example by two plugin examples {pull}28339[#28339] +* Improve error message when installing an offline plugin {pull}28298[#28298] (issue: {issue}27401[#27401]) +REST API:: +* REST : Split `RestUpgradeAction` into two actions {pull}29124[#29124] (issue: {issue}29062[#29062]) +* Change BroadcastResponse from ToXContentFragment to ToXContentObject {pull}28878[#28878] (issues: {issue}27799[#27799], {issue}3889[#3889]) +* Remove AcknowledgedRestListener in favour of RestToXContentListener {pull}28724[#28724] (issue: {issue}3889[#3889]) +* Standardize underscore requirements in parameters {pull}27040[#27040] (issue: {issue}26886[#26886]) + +Ranking:: +* RankEvalRequest should implement IndicesRequest {pull}29188[#29188] +* Move indices field from RankEvalSpec to RankEvalRequest {pull}28341[#28341] +* Simplify RankEvalResponse output {pull}28266[#28266] + +Recovery:: +* Require translogUUID when reading global checkpoint {pull}28587[#28587] (issue: {issue}28435[#28435]) +* Do not ignore shard not-available exceptions in replication {pull}28571[#28571] (issues: {issue}28049[#28049], {issue}28534[#28534]) +* Make primary-replica resync failures less lenient {pull}28534[#28534] (issues: {issue}24841[#24841], {issue}28049[#28049], {issue}28054[#28054]) +* Synced-flush should not seal index of out of sync replicas {pull}28464[#28464] (issue: {issue}10032[#10032]) +* Don't refresh shard on activation {pull}28013[#28013] (issue: {issue}26055[#26055]) + +Rollup:: +* Allow rollup job creation only if cluster is x-pack ready {pull}30963[#30963] (issue: {issue}30743[#30743]) + +Scripting:: +* Modify Painless grammar to support right brackets as statement delimiters {pull}29566[#29566] + +Search:: +* Add support to match_phrase query for zero_terms_query. {pull}29598[#29598] (issue: {issue}29344[#29344]) +* Improve similarity integration. {pull}29187[#29187] (issues: {issue}23208[#23208], {issue}29035[#29035]) +* Store offsets in index prefix fields when stored in the parent field {pull}29067[#29067] (issue: {issue}28994[#28994]) +* Add QueryBuilders.matchNoneQuery(), #28679 {pull}28680[#28680] +* Adds SpanGapQueryBuilder. Feature #27862 {pull}28636[#28636] (issue: {issue}27862[#27862]) +* Provide a better error message for the case when all shards failed {pull}28333[#28333] +* Add ability to index prefixes on text fields {pull}28290[#28290] (issue: {issue}28222[#28222]) +* Add index_prefix option to text fields {pull}28222[#28222] + +Settings:: +* Enhance error for out of bounds byte size settings {pull}29338[#29338] (issue: {issue}29337[#29337]) +* Settings: Reimplement keystore format to use FIPS compliant algorithms {pull}28255[#28255] + +Snapshot/Restore:: +* Do not fail snapshot when deleting a missing snapshotted file {pull}30332[#30332] (issue: {issue}28322[#28322]) +* Update secure settings for the repository azure repository plugin {pull}29319[#29319] (issue: {issue}29135[#29135]) +* Use client settings in repository-gcs {pull}28575[#28575] + +Stats:: +* Add periodic flush count to flush stats {pull}29360[#29360] (issue: {issue}29125[#29125]) +* Enable selecting adaptive selection stats {pull}28721[#28721] +* Add translog files age to Translog Stats (#28613) {pull}28613[#28613] (issue: {issue}28189[#28189]) + +Task Management:: +* Make Persistent Tasks implementations version and feature aware {pull}31045[#31045] (issues: {issue}30731[#30731], {issue}31020[#31020]) + +Transport API:: +* Add remote cluster client {pull}29495[#29495] +* Java api clean-up : consistency for `shards_acknowledged` getters {pull}27819[#27819] (issue: {issue}27784[#27784]) + +Watcher:: +* Move watcher-history version setting to _meta field {pull}30832[#30832] (issue: {issue}30731[#30731]) +* Only allow x-pack metadata if all nodes are ready {pull}30743[#30743] (issues: {issue}30728[#30728], {issue}30731[#30731]) + +ZenDiscovery:: +* Add support for skippable named writeables {pull}30948[#30948] + + + +[[bug-6.3.0]] [float] -=== Bug Fixes +=== Bug fixes + +Aggregations:: +* Fix date and ip sources in the composite aggregation {pull}29370[#29370] +* Pass through script params in scripted metric agg {pull}29154[#29154] (issue: {issue}28819[#28819]) +* Force depth_first mode execution for terms aggregation under a nested context {pull}28421[#28421] (issue: {issue}28394[#28394]) +* StringTerms.Bucket.getKeyAsNumber detection type {pull}28118[#28118] (issue: {issue}28012[#28012]) + +Allocation:: +* Move allocation awareness attributes to list setting {pull}30626[#30626] (issue: {issue}30617[#30617]) +* Grammar matters.. {pull}29462[#29462] +* Don't break allocation if resize source index is missing {pull}29311[#29311] (issue: {issue}26931[#26931]) +* Add check when trying to reroute a shard to a non-data discovery node {pull}28886[#28886] + +Audit:: +* Fix audit index template upgrade loop {pull}30779[#30779] + +Authentication:: +* Security: cleanup code in file stores {pull}30348[#30348] +* Security: fix TokenMetaData equals and hashcode {pull}30347[#30347] + +Authorization:: +* Security: reduce garbage during index resolution {pull}30180[#30180] + +CRUD:: +* Bulk operation fail to replicate operations when a mapping update times out {pull}30244[#30244] + +Core:: +* Create default ES_TMPDIR on Windows {pull}30325[#30325] (issues: {issue}27609[#27609], {issue}28217[#28217]) +* Core: Pick inner most parse exception as root cause {pull}30270[#30270] (issues: {issue}29373[#29373], {issue}30261[#30261]) +* Fix the version ID for v5.6.10 (backport to 6.x). {pull}29571[#29571] +* Fix the version ID for v5.6.10. {pull}29570[#29570] +* Fix EsAbortPolicy to conform to API {pull}29075[#29075] (issue: {issue}19508[#19508]) +* Remove special handling for _all in nodes info {pull}28971[#28971] (issue: {issue}28797[#28797]) +* Handle throws on tasks submitted to thread pools {pull}28667[#28667] +* Fix size blocking queue to not lie about its weight {pull}28557[#28557] (issue: {issue}28547[#28547]) +* Further minor bug fixes found by lgtm.com {pull}27772[#27772] + +Engine:: +* Add an escape hatch to increase the maximum amount of memory that IndexWriter gets. {pull}31132[#31132] (issue: {issue}31105[#31105]) +* Avoid self-deadlock in the translog {pull}29520[#29520] (issues: {issue}29401[#29401], {issue}29509[#29509]) +* Close translog writer if exception on write channel {pull}29401[#29401] (issue: {issue}29390[#29390]) +* Harden periodically check to avoid endless flush loop {pull}29125[#29125] (issues: {issue}1[#1], {issue}2[#2], {issue}28350[#28350], {issue}29097[#29097], {issue}3[#3]) +* Avoid class cast exception from index writer {pull}28989[#28989] +* Maybe die before failing engine {pull}28973[#28973] (issues: {issue}27265[#27265], {issue}28967[#28967]) +* Never block on key in `LiveVersionMap#pruneTombstones` {pull}28736[#28736] (issue: {issue}28714[#28714]) +* Inc store reference before refresh {pull}28656[#28656] +* Replica recovery could go into an endless flushing loop {pull}28350[#28350] + +Geo:: +* Fix overflow error in parsing of long geohashes {pull}29418[#29418] (issue: {issue}24616[#24616]) +* Fix bwc in GeoDistanceQuery serialization {pull}29325[#29325] (issues: {issue}22876[#22876], {issue}29301[#29301]) +* Allow using distance measure in the geo context precision {pull}29273[#29273] (issue: {issue}24807[#24807]) +* Fix incorrect geohash for lat 90, lon 180 {pull}29256[#29256] (issue: {issue}22163[#22163]) +* [GEO] Fix points_only indexing failure for GeoShapeFieldMapper {pull}28774[#28774] (issues: {issue}27415[#27415], {issue}28744[#28744]) + +Index APIs:: +* Propagate mapping.single_type setting on shrinked index {pull}29202[#29202] +* Fix Parsing Bug with Update By Query for Stored Scripts {pull}29039[#29039] (issue: {issue}28002[#28002]) + +Ingest:: +* Don't allow referencing the pattern bank name in the pattern bank {pull}29295[#29295] (issue: {issue}29257[#29257]) +* Continue registering pipelines after one pipeline parse failure. {pull}28752[#28752] (issue: {issue}28269[#28269]) +* Guard accessDeclaredMembers for Tika on JDK 10 {pull}28603[#28603] (issue: {issue}28602[#28602]) +* Fix for bug that prevents pipelines to load that use stored scripts after a restart {pull}28588[#28588] + +Java High Level REST Client:: +* Bulk processor#awaitClose to close scheduler {pull}29263[#29263] +* REST high-level client: encode path parts {pull}28663[#28663] (issue: {issue}28625[#28625]) +* Fix parsing of script fields {pull}28395[#28395] (issue: {issue}28380[#28380]) +* Move to POST when calling API to retrieve which support request body {pull}28342[#28342] (issue: {issue}28326[#28326]) + +Java Low Level REST Client:: +* REST client: hosts marked dead for the first time should not be immediately retried {pull}29230[#29230] + +License:: +* Do not serialize basic license exp in x-pack info {pull}30848[#30848] +* Require acknowledgement to start_trial license {pull}30198[#30198] (issue: {issue}30134[#30134]) Machine Learning:: * By-fields should respect model_plot_config.terms {ml-pull}86[#86] (issue: {issue}30004[#30004]) @@ -86,12 +401,129 @@ Machine Learning:: * Fail start up if state is missing {ml-pull}4[#4] * Do not log incorrect model memory limit {ml-pull}3[#3] +Mapping:: +* Ignore null value for range field (#27845) {pull}28116[#28116] (issue: {issue}27845[#27845]) +* Fix a type check that is always false {pull}27726[#27726] + +Network:: +* Fix handling of bad requests {pull}29249[#29249] (issues: {issue}21974[#21974], {issue}28909[#28909]) + +Packaging:: +* Fix #29057 CWD to ES_HOME does not change drive {pull}29086[#29086] +* Allow overriding JVM options in Windows service {pull}29044[#29044] (issue: {issue}23484[#23484]) +* CLI: Close subcommands in MultiCommand {pull}28954[#28954] +* Delay path expansion on Windows {pull}28753[#28753] (issues: {issue}27675[#27675], {issue}28748[#28748]) +* Fix using relative custom config path {pull}28700[#28700] (issue: {issue}27610[#27610]) +* Disable console logging in the Windows service {pull}28618[#28618] (issue: {issue}20422[#20422]) + +Percolator:: +* Fixed bug when non percolator docs end up in the search hits {pull}29447[#29447] (issue: {issue}29429[#29429]) +* Fixed a msm accounting error that can occur during analyzing a percolator query {pull}29415[#29415] (issue: {issue}29393[#29393]) +* Fix more query extraction bugs. {pull}29388[#29388] (issues: {issue}28353[#28353], {issue}29376[#29376]) +* Fix some query extraction bugs. {pull}29283[#29283] +* Fix percolator query analysis for function_score query {pull}28854[#28854] +* Improved percolator's random candidate query duel test {pull}28840[#28840] +* Do not take duplicate query extractions into account for minimum_should_match attribute {pull}28353[#28353] (issue: {issue}28315[#28315]) + +Plugins:: +* Plugins: Fix native controller confirmation for non-meta plugin {pull}29434[#29434] +* Plugins: Fix module name conflict check for meta plugins {pull}29146[#29146] +* Ensure that azure stream has socket privileges {pull}28751[#28751] (issue: {issue}28662[#28662]) +* Fix handling of mandatory meta plugins {pull}28710[#28710] (issue: {issue}28022[#28022]) +* Fix the ability to remove old plugin {pull}28540[#28540] (issue: {issue}28538[#28538]) + +REST API:: +* Respect accept header on no handler {pull}30383[#30383] (issue: {issue}30329[#30329]) +* Protect against NPE in RestNodesAction {pull}29059[#29059] +* REST api specs : remove unsupported `wait_for_merge` param {pull}28959[#28959] (issue: {issue}27158[#27158]) +* Rest api specs : remove unsupported parameter `parent_node` {pull}28841[#28841] +* Rest api specs : remove a common param from nodes.usage.json {pull}28835[#28835] (issue: {issue}28226[#28226]) +* Missing `timeout` parameter from the REST API spec JSON files (#28200) {pull}28328[#28328] + +Ranking:: +* Fix NDCG for empty search results {pull}29267[#29267] + +Recovery:: +* Cancelling a peer recovery on the source can leak a primary permit {pull}30318[#30318] +* ReplicationTracker.markAllocationIdAsInSync may hang if allocation is cancelled {pull}30316[#30316] +* Do not log warn shard not-available exception in replication {pull}30205[#30205] (issues: {issue}28049[#28049], {issue}28571[#28571]) +* Fix outgoing NodeID {pull}28779[#28779] (issue: {issue}28777[#28777]) +* Fsync directory after cleanup {pull}28604[#28604] (issue: {issue}28435[#28435]) + Security:: * Reduces the number of object allocations made by {security} when resolving the indices and aliases for a request ({pull}30180[#30180]) * Respects accept header on requests with no handler ({pull}30383[#30383]) -//[float] -//=== Regressions +SQL:: +* SQL: Verify GROUP BY ordering on grouped columns {pull}30585[#30585] (issue: {issue}29900[#29900]) +* SQL: Fix parsing of dates with milliseconds {pull}30419[#30419] (issue: {issue}30002[#30002]) +* SQL: Fix bug caused by empty composites {pull}30343[#30343] (issue: {issue}30292[#30292]) +* SQL: Correct error message {pull}30138[#30138] (issue: {issue}30016[#30016]) +* SQL: Add BinaryMathProcessor to named writeables list {pull}30127[#30127] (issue: {issue}30014[#30014]) + +Scripting:: +* Correct class to name string conversion {pull}28997[#28997] +* Painless: Fix For Loop NullPointerException {pull}28506[#28506] (issue: {issue}28501[#28501]) +* Scripts: Fix security for deprecation warning {pull}28485[#28485] (issue: {issue}28408[#28408]) + +Search:: +* Ensure that index_prefixes settings cannot be changed {pull}30967[#30967] +* Fix TermsSetQueryBuilder.doEquals() method {pull}29629[#29629] (issue: {issue}29620[#29620]) +* Fix binary doc values fetching in _search {pull}29567[#29567] (issues: {issue}26775[#26775], {issue}29565[#29565]) +* Fixes query_string query equals timezone check {pull}29406[#29406] (issue: {issue}29403[#29403]) +* Fixed quote_field_suffix in query_string {pull}29332[#29332] (issue: {issue}29324[#29324]) +* Search: Validate script query is run with a single script {pull}29304[#29304] +* Propagate ignore_unmapped to inner_hits {pull}29261[#29261] (issue: {issue}29071[#29071]) +* Restore tiebreaker for cross fields query {pull}28935[#28935] (issues: {issue}25115[#25115], {issue}28933[#28933]) +* Fix (simple)_query_string to ignore removed terms {pull}28871[#28871] (issues: {issue}28855[#28855], {issue}28856[#28856]) +* Search option terminate_after does not handle post_filters and aggregations correctly {pull}28459[#28459] (issue: {issue}28411[#28411]) +* Fix AIOOB on indexed geo_shape query {pull}28458[#28458] (issue: {issue}28456[#28456]) + +Settings:: +* Archive unknown or invalid settings on updates {pull}28888[#28888] (issue: {issue}28609[#28609]) +* Settings: Introduce settings updater for a list of settings {pull}28338[#28338] (issue: {issue}28047[#28047]) + +Snapshot/Restore:: +* Delete temporary blobs before creating index file {pull}30528[#30528] (issues: {issue}30332[#30332], {issue}30507[#30507]) +* Fix NPE when using deprecated Azure settings {pull}28769[#28769] (issues: {issue}23518[#23518], {issue}28299[#28299]) + +Stats:: +* Fix AdaptiveSelectionStats serialization bug {pull}28718[#28718] (issue: {issue}28713[#28713]) + +Suggesters:: +* Fix merging logic of Suggester Options {pull}29514[#29514] + +Transport API:: +* Fix interoperability with < 6.3 transport clients {pull}30971[#30971] (issue: {issue}30731[#30731]) + +Watcher:: +* Watcher: Prevent triggering watch when using activate API {pull}30613[#30613] + +ZenDiscovery:: +* Fsync state file before exposing it {pull}30929[#30929] +* Do not return metadata customs by default {pull}30857[#30857] (issue: {issue}30731[#30731]) +* Use correct cluster state version for node fault detection {pull}30810[#30810] + + + +[[regression-6.3.0]] +[float] +=== Regressions + +Snapshot/Restore:: +* S3 repo plugin populate SettingsFilter {pull}30652[#30652] + + + +[[upgrade-6.3.0]] +[float] +=== Upgrades + +Network:: +* Update Netty to 4.1.16.Final {pull}28345[#28345] + +Search:: +* Upgrade to lucene-7.3.1 {pull}30729[#30729] + + -//[float] -//=== Known Issues From 76ecf1d2bf68b4febaba5f167f4580bc8ef28b7a Mon Sep 17 00:00:00 2001 From: lcawl Date: Wed, 13 Jun 2018 08:59:31 -0700 Subject: [PATCH 49/71] [DOCS] Removes coming tag from 6.3.0 release notes --- docs/reference/release-notes/6.3.asciidoc | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/docs/reference/release-notes/6.3.asciidoc b/docs/reference/release-notes/6.3.asciidoc index 5cc49df6cccea..c0e8ed4c89695 100644 --- a/docs/reference/release-notes/6.3.asciidoc +++ b/docs/reference/release-notes/6.3.asciidoc @@ -1,8 +1,6 @@ [[release-notes-6.3.0]] == 6.3.0 Release Notes -coming[6.3.0] - Also see <>. [[breaking-6.3.0]] @@ -12,6 +10,13 @@ Also see <>. Core:: * Rename the bulk thread pool to write thread pool {pull}29593[#29593] +Monitoring:: +* By default when you install {xpack}, monitoring is enabled but data collection +is disabled. To enable data collection, use the new +`xpack.monitoring.collection.enabled` setting. You can update this setting by +using the <>. For more +information, see <>. + Packaging:: * Create keystore on package install {pull}28928[#28928] @@ -21,21 +26,13 @@ Plugins:: Search:: * Fail _search request with trailing tokens {pull}29428[#29428] (issue: {issue}28995[#28995]) -Task Management:: -* Remove metadata customs that can break serialization {pull}30945[#30945] (issues: {issue}30731[#30731], {issue}30857[#30857]) - -Monitoring:: -* By default when you install {xpack}, monitoring is enabled but data collection -is disabled. To enable data collection, use the new -`xpack.monitoring.collection.enabled` setting. You can update this setting by -using the <>. For more -information, see <>. - Security:: * The legacy `XPackExtension` extension mechanism has been removed and replaced with an SPI based extension mechanism that is installed and built as an elasticsearch plugin. +Task Management:: +* Remove metadata customs that can break serialization {pull}30945[#30945] (issues: {issue}30731[#30731], {issue}30857[#30857]) [[breaking-java-6.3.0]] From 9258ea0d857192016809afe972b224f005b97f6e Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Wed, 13 Jun 2018 17:24:32 +0100 Subject: [PATCH 50/71] Removes experimental tag from scripted_metric aggregation (#31298) --- .../aggregations/metrics/scripted-metric-aggregation.asciidoc | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/reference/aggregations/metrics/scripted-metric-aggregation.asciidoc b/docs/reference/aggregations/metrics/scripted-metric-aggregation.asciidoc index daa86969e4556..1a4d6d4774c49 100644 --- a/docs/reference/aggregations/metrics/scripted-metric-aggregation.asciidoc +++ b/docs/reference/aggregations/metrics/scripted-metric-aggregation.asciidoc @@ -1,8 +1,6 @@ [[search-aggregations-metrics-scripted-metric-aggregation]] === Scripted Metric Aggregation -experimental[] - A metric aggregation that executes using scripts to provide a metric output. Example: From ef9eaeaf4cbe984256ceee4dfbc4a636a8fab26f Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Wed, 13 Jun 2018 11:31:04 -0400 Subject: [PATCH 51/71] [Rollup] Metric config parser must use builder so validation runs (#31159) The parser for the Metric config was directly instantiating the config object, rather than using the builder. That means it was bypassing the validation logic built into the builder, and would allow users to create invalid metric configs (like using unsupported metrics). The job would later blow up and abort due to bad configs, but this isn't immediately obvious to the user since the PutJob API succeeded. --- .../xpack/core/rollup/job/MetricConfig.java | 11 ++++--- .../core/rollup/job/RollupJobConfig.java | 2 +- .../job/MetricsConfigSerializingTests.java | 4 +-- .../rest-api-spec/test/rollup/put_job.yml | 30 +++++++++++++++++++ 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java index f26c67935edff..67b83646c4237 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/MetricConfig.java @@ -12,7 +12,7 @@ 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.ObjectParser; import org.elasticsearch.common.xcontent.ToXContentFragment; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.mapper.NumberFieldMapper; @@ -75,12 +75,11 @@ public class MetricConfig implements Writeable, ToXContentFragment { MAPPER_TYPES = types; } - public static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( - NAME, a -> new MetricConfig((String)a[0], (List) a[1])); + public static final ObjectParser PARSER = new ObjectParser<>(NAME, MetricConfig.Builder::new); static { - PARSER.declareString(ConstructingObjectParser.constructorArg(), FIELD); - PARSER.declareStringArray(ConstructingObjectParser.constructorArg(), METRICS); + PARSER.declareString(MetricConfig.Builder::setField, FIELD); + PARSER.declareStringArray(MetricConfig.Builder::setMetrics, METRICS); } MetricConfig(String name, List metrics) { @@ -257,4 +256,4 @@ public MetricConfig build() { return new MetricConfig(field, metrics); } } -} \ No newline at end of file +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/RollupJobConfig.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/RollupJobConfig.java index 3818ebcf44758..422ecdd5fd9fb 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/RollupJobConfig.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/rollup/job/RollupJobConfig.java @@ -63,7 +63,7 @@ public class RollupJobConfig implements NamedWriteable, ToXContentObject { static { PARSER.declareString(RollupJobConfig.Builder::setId, RollupField.ID); PARSER.declareObject(RollupJobConfig.Builder::setGroupConfig, (p, c) -> GroupConfig.PARSER.apply(p,c).build(), GROUPS); - PARSER.declareObjectArray(RollupJobConfig.Builder::setMetricsConfig, MetricConfig.PARSER, METRICS); + PARSER.declareObjectArray(RollupJobConfig.Builder::setMetricsConfig, (p, c) -> MetricConfig.PARSER.apply(p, c).build(), METRICS); PARSER.declareString((params, val) -> params.setTimeout(TimeValue.parseTimeValue(val, TIMEOUT.getPreferredName())), TIMEOUT); PARSER.declareString(RollupJobConfig.Builder::setIndexPattern, INDEX_PATTERN); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/MetricsConfigSerializingTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/MetricsConfigSerializingTests.java index 92a0976f532b7..9b330e7165093 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/MetricsConfigSerializingTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/rollup/job/MetricsConfigSerializingTests.java @@ -24,7 +24,7 @@ public class MetricsConfigSerializingTests extends AbstractSerializingTestCase { @Override protected MetricConfig doParseInstance(XContentParser parser) throws IOException { - return MetricConfig.PARSER.apply(parser, null); + return MetricConfig.PARSER.apply(parser, null).build(); } @Override @@ -36,7 +36,7 @@ protected Writeable.Reader instanceReader() { protected MetricConfig createTestInstance() { return ConfigTestHelpers.getMetricConfig().build(); } - + public void testValidateNoMapping() throws IOException { ActionRequestValidationException e = new ActionRequestValidationException(); Map> responseMap = new HashMap<>(); diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/rollup/put_job.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/rollup/put_job.yml index 717be0d6b250f..98ef9b32e3d29 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/rollup/put_job.yml +++ b/x-pack/plugin/src/test/resources/rest-api-spec/test/rollup/put_job.yml @@ -188,3 +188,33 @@ setup: ] } +--- +"Unknown Metric": + + - do: + catch: /Unsupported metric \[does_not_exist\]/ + headers: + Authorization: "Basic eF9wYWNrX3Jlc3RfdXNlcjp4LXBhY2stdGVzdC1wYXNzd29yZA==" # run as x_pack_rest_user, i.e. the test setup superuser + xpack.rollup.put_job: + id: foo + body: > + { + "index_pattern": "foo", + "rollup_index": "foo_rollup", + "cron": "*/30 * * * * ?", + "page_size" :10, + "groups" : { + "date_histogram": { + "field": "the_field", + "interval": "1h" + } + }, + "metrics": [ + { + "field": "value_field", + "metrics": ["min", "max", "sum", "does_not_exist"] + } + ] + } + + From 1bdcadd7251991f69deab0877cf98ad3927078da Mon Sep 17 00:00:00 2001 From: Dimitris Athanasiou Date: Wed, 13 Jun 2018 17:52:46 +0100 Subject: [PATCH 52/71] Add unreleased version 6.3.1 --- server/src/main/java/org/elasticsearch/Version.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/Version.java b/server/src/main/java/org/elasticsearch/Version.java index 23201c5df1c6b..9565f247d53b4 100644 --- a/server/src/main/java/org/elasticsearch/Version.java +++ b/server/src/main/java/org/elasticsearch/Version.java @@ -162,10 +162,10 @@ public class Version implements Comparable, ToXContentFragment { public static final Version V_6_2_3 = new Version(V_6_2_3_ID, LUCENE_7_2_1); public static final int V_6_2_4_ID = 6020499; public static final Version V_6_2_4 = new Version(V_6_2_4_ID, LUCENE_7_2_1); - public static final int V_6_2_5_ID = 6020599; - public static final Version V_6_2_5 = new Version(V_6_2_5_ID, LUCENE_7_2_1); public static final int V_6_3_0_ID = 6030099; public static final Version V_6_3_0 = new Version(V_6_3_0_ID, org.apache.lucene.util.Version.LUCENE_7_3_1); + public static final int V_6_3_1_ID = 6030199; + public static final Version V_6_3_1 = new Version(V_6_3_1_ID, org.apache.lucene.util.Version.LUCENE_7_3_1); public static final int V_6_4_0_ID = 6040099; public static final Version V_6_4_0 = new Version(V_6_4_0_ID, org.apache.lucene.util.Version.LUCENE_7_4_0); @@ -184,10 +184,10 @@ public static Version fromId(int id) { switch (id) { case V_6_4_0_ID: return V_6_4_0; + case V_6_3_1_ID: + return V_6_3_1; case V_6_3_0_ID: return V_6_3_0; - case V_6_2_5_ID: - return V_6_2_5; case V_6_2_4_ID: return V_6_2_4; case V_6_2_3_ID: From c922e0ba02b872869bce711313dcc9ad5d2042ee Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Wed, 13 Jun 2018 09:33:06 -0400 Subject: [PATCH 53/71] Test: Remove broken yml test feature (#31255) The `requires_replica` yaml test feature hasn't worked for years. This is what happens if you try to use it: ``` > Throwable #1: java.lang.NullPointerException > at __randomizedtesting.SeedInfo.seed([E6602FB306244B12:6E341069A8D826EA]:0) > at org.elasticsearch.test.rest.yaml.Features.areAllSupported(Features.java:58) > at org.elasticsearch.test.rest.yaml.section.SkipSection.skip(SkipSection.java:144) > at org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase.test(ESClientYamlSuiteTestCase.java:321) ``` None of our tests use it. --- .../main/java/org/elasticsearch/test/rest/yaml/Features.java | 5 ----- 1 file changed, 5 deletions(-) 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 ab9be65514a96..3168543b5554b 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 @@ -19,8 +19,6 @@ package org.elasticsearch.test.rest.yaml; -import org.elasticsearch.test.ESIntegTestCase; - import java.util.Arrays; import java.util.List; @@ -54,9 +52,6 @@ private Features() { */ public static boolean areAllSupported(List features) { for (String feature : features) { - if ("requires_replica".equals(feature) && ESIntegTestCase.cluster().numDataNodes() >= 2) { - continue; - } if (!SUPPORTED.contains(feature)) { return false; } From 9e3c36495f0c6387066d17be9e7d6efb74578b02 Mon Sep 17 00:00:00 2001 From: David Kyle Date: Wed, 13 Jun 2018 18:09:16 +0100 Subject: [PATCH 54/71] Fix compilation error in UpdateSettingsIT (#31304) --- .../indices/settings/UpdateSettingsIT.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java b/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java index 8093e7d38a14d..1579e2dcdf1ab 100644 --- a/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java +++ b/server/src/test/java/org/elasticsearch/indices/settings/UpdateSettingsIT.java @@ -27,8 +27,10 @@ import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; import org.elasticsearch.action.support.ActionFilters; +import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder; import org.elasticsearch.action.support.master.MasterNodeRequest; import org.elasticsearch.action.support.master.TransportMasterNodeAction; +import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.ClusterStateUpdateTask; import org.elasticsearch.cluster.block.ClusterBlockException; @@ -162,7 +164,8 @@ public List> getSettings() { } public static class UpdateInternalIndexAction - extends Action { + extends Action { private static final UpdateInternalIndexAction INSTANCE = new UpdateInternalIndexAction(); private static final String NAME = "indices:admin/settings/update-internal-index"; @@ -171,6 +174,11 @@ public UpdateInternalIndexAction() { super(NAME); } + @Override + public RequestBuilder newRequestBuilder(ElasticsearchClient client) { + return new RequestBuilder(client, this); + } + static class Request extends MasterNodeRequest { private String index; @@ -215,6 +223,12 @@ public Response newResponse() { return new Response(); } + static class RequestBuilder extends MasterNodeOperationRequestBuilder { + + protected RequestBuilder(ElasticsearchClient client, Action action) { + super(client, action, new Request()); + } + } } public static class TransportUpdateInternalIndexAction From c63cbc1e02efc31e4aa9a2d032f32f1b029d5f87 Mon Sep 17 00:00:00 2001 From: Jay Modi Date: Wed, 13 Jun 2018 12:09:55 -0600 Subject: [PATCH 55/71] Security: fix token bwc with pre 6.0.0-beta2 (#31254) This commit fixes a backwards compatibility bug in the token service that causes token decoding to fail when there is a pre 6.0.0-beta2 node in the cluster. The token encoding is actually the culprit as a version check is missing around the serialization of the key hash bytes. This value was added in 6.0.0-beta2 and cannot be sent to nodes that do not know about this value. The version check has been added and the token service unit tests have been enhanced to randomly run with some 5.6.x nodes in the cluster service. Additionally, a small change was made to the way we check to see if the token metadata needs to be installed. Previously we would pass the metadata to the install method and check that the token metadata is null. This null check is now done prior to checking if the metadata can be installed. Relates #30743 Closes #31195 --- .../xpack/security/authc/TokenService.java | 63 ++++++++-------- .../security/authc/TokenServiceTests.java | 72 +++++++++++++++---- x-pack/qa/rolling-upgrade/build.gradle | 1 - .../TokenBackwardsCompatibilityIT.java | 5 +- 4 files changed, 90 insertions(+), 51 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java index 99680da2bba6a..4aa4c866c8976 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/TokenService.java @@ -9,7 +9,6 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; import org.elasticsearch.cluster.ClusterStateUpdateTask; -import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.Priority; import org.elasticsearch.core.internal.io.IOUtils; import org.apache.lucene.util.UnicodeUtil; @@ -1041,7 +1040,9 @@ public String getUserTokenString(UserToken userToken) throws IOException, Genera KeyAndCache keyAndCache = keyCache.activeKeyCache; Version.writeVersion(userToken.getVersion(), out); out.writeByteArray(keyAndCache.getSalt().bytes); - out.writeByteArray(keyAndCache.getKeyHash().bytes); + if (userToken.getVersion().onOrAfter(Version.V_6_0_0_beta2)) { + out.writeByteArray(keyAndCache.getKeyHash().bytes); + } final byte[] initializationVector = getNewInitializationVector(); out.writeByteArray(initializationVector); try (CipherOutputStream encryptedOutput = @@ -1369,16 +1370,18 @@ private void initialize(ClusterService clusterService) { return; } + TokenMetaData custom = event.state().custom(TokenMetaData.TYPE); if (state.nodes().isLocalNodeElectedMaster()) { - if (XPackPlugin.isReadyForXPackCustomMetadata(state)) { - installTokenMetadata(state.metaData()); - } else { - logger.debug("cannot add token metadata to cluster as the following nodes might not understand the metadata: {}", - () -> XPackPlugin.nodesNotReadyForXPackCustomMetadata(state)); + if (custom == null) { + if (XPackPlugin.isReadyForXPackCustomMetadata(state)) { + installTokenMetadata(); + } else { + logger.debug("cannot add token metadata to cluster as the following nodes might not understand the metadata: {}", + () -> XPackPlugin.nodesNotReadyForXPackCustomMetadata(state)); + } } } - TokenMetaData custom = event.state().custom(TokenMetaData.TYPE); if (custom != null && custom.equals(getTokenMetaData()) == false) { logger.info("refresh keys"); try { @@ -1394,33 +1397,31 @@ private void initialize(ClusterService clusterService) { // to prevent too many cluster state update tasks to be queued for doing the same update private final AtomicBoolean installTokenMetadataInProgress = new AtomicBoolean(false); - private void installTokenMetadata(MetaData metaData) { - if (metaData.custom(TokenMetaData.TYPE) == null) { - if (installTokenMetadataInProgress.compareAndSet(false, true)) { - clusterService.submitStateUpdateTask("install-token-metadata", new ClusterStateUpdateTask(Priority.URGENT) { - @Override - public ClusterState execute(ClusterState currentState) { - XPackPlugin.checkReadyForXPackCustomMetadata(currentState); + private void installTokenMetadata() { + if (installTokenMetadataInProgress.compareAndSet(false, true)) { + clusterService.submitStateUpdateTask("install-token-metadata", new ClusterStateUpdateTask(Priority.URGENT) { + @Override + public ClusterState execute(ClusterState currentState) { + XPackPlugin.checkReadyForXPackCustomMetadata(currentState); - if (currentState.custom(TokenMetaData.TYPE) == null) { - return ClusterState.builder(currentState).putCustom(TokenMetaData.TYPE, getTokenMetaData()).build(); - } else { - return currentState; - } + if (currentState.custom(TokenMetaData.TYPE) == null) { + return ClusterState.builder(currentState).putCustom(TokenMetaData.TYPE, getTokenMetaData()).build(); + } else { + return currentState; } + } - @Override - public void onFailure(String source, Exception e) { - installTokenMetadataInProgress.set(false); - logger.error("unable to install token metadata", e); - } + @Override + public void onFailure(String source, Exception e) { + installTokenMetadataInProgress.set(false); + logger.error("unable to install token metadata", e); + } - @Override - public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { - installTokenMetadataInProgress.set(false); - } - }); - } + @Override + public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) { + installTokenMetadataInProgress.set(false); + } + }); } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java index 07276e33b4efe..dbce97e31def4 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenServiceTests.java @@ -26,6 +26,9 @@ import org.elasticsearch.action.update.UpdateAction; import org.elasticsearch.action.update.UpdateRequestBuilder; import org.elasticsearch.client.Client; +import org.elasticsearch.cluster.ClusterState; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.node.DiscoveryNodes; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.MockSecureSettings; import org.elasticsearch.common.Strings; @@ -44,6 +47,7 @@ import org.elasticsearch.test.ClusterServiceUtils; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.EqualsHashCodeTestUtils; +import org.elasticsearch.test.VersionUtils; import org.elasticsearch.threadpool.FixedExecutorBuilder; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.XPackSettings; @@ -53,6 +57,7 @@ import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.watcher.watch.ClockMock; import org.elasticsearch.xpack.security.support.SecurityIndexManager; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -66,6 +71,7 @@ import java.time.temporal.ChronoUnit; import java.util.Base64; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -91,6 +97,7 @@ public class TokenServiceTests extends ESTestCase { private Client client; private SecurityIndexManager securityIndex; private ClusterService clusterService; + private boolean mixedCluster; private Settings tokenServiceEnabledSettings = Settings.builder() .put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build(); @@ -141,6 +148,25 @@ public void setupClient() { return null; }).when(securityIndex).prepareIndexIfNeededThenExecute(any(Consumer.class), any(Runnable.class)); this.clusterService = ClusterServiceUtils.createClusterService(threadPool); + this.mixedCluster = randomBoolean(); + if (mixedCluster) { + Version version = VersionUtils.randomVersionBetween(random(), Version.V_5_6_0, Version.V_5_6_10); + logger.info("adding a node with version [{}] to the cluster service", version); + ClusterState updatedState = ClusterState.builder(clusterService.state()) + .nodes(DiscoveryNodes.builder(clusterService.state().nodes()) + .add(new DiscoveryNode("56node", ESTestCase.buildNewFakeTransportAddress(), Collections.emptyMap(), + EnumSet.allOf(DiscoveryNode.Role.class), version)) + .build()) + .build(); + ClusterServiceUtils.setState(clusterService, updatedState); + } + } + + @After + public void stopClusterService() { + if (clusterService != null) { + clusterService.close(); + } } @BeforeClass @@ -172,7 +198,7 @@ public void testAttachAndGetToken() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) { @@ -183,11 +209,12 @@ public void testAttachAndGetToken() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); anotherService.getAndValidateToken(requestContext, future); UserToken fromOtherService = future.get(); - assertEquals(authentication, fromOtherService.getAuthentication()); + assertAuthenticationEquals(authentication, fromOtherService.getAuthentication()); } } public void testRotateKey() throws Exception { + assumeFalse("internally managed keys do not work in a mixed cluster", mixedCluster); TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService); Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null); PlainActionFuture> tokenFuture = new PlainActionFuture<>(); @@ -203,7 +230,7 @@ public void testRotateKey() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } rotateKeys(tokenService); @@ -211,7 +238,7 @@ public void testRotateKey() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } PlainActionFuture> newTokenFuture = new PlainActionFuture<>(); @@ -240,6 +267,7 @@ private void rotateKeys(TokenService tokenService) { } public void testKeyExchange() throws Exception { + assumeFalse("internally managed keys do not work in a mixed cluster", mixedCluster); TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService); int numRotations = 0;randomIntBetween(1, 5); for (int i = 0; i < numRotations; i++) { @@ -261,7 +289,7 @@ public void testKeyExchange() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); otherTokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } rotateKeys(tokenService); @@ -272,11 +300,12 @@ public void testKeyExchange() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); otherTokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } } public void testPruneKeys() throws Exception { + assumeFalse("internally managed keys do not work in a mixed cluster", mixedCluster); TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, securityIndex, clusterService); Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null); PlainActionFuture> tokenFuture = new PlainActionFuture<>(); @@ -292,7 +321,7 @@ public void testPruneKeys() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } TokenMetaData metaData = tokenService.pruneKeys(randomIntBetween(0, 100)); tokenService.refreshMetaData(metaData); @@ -306,7 +335,7 @@ public void testPruneKeys() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } PlainActionFuture> newTokenFuture = new PlainActionFuture<>(); @@ -332,7 +361,7 @@ public void testPruneKeys() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } } @@ -353,7 +382,7 @@ public void testPassphraseWorks() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); } try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) { @@ -454,7 +483,7 @@ public void testTokenExpiry() throws Exception { // the clock is still frozen, so the cookie should be valid PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); - assertEquals(authentication, future.get().getAuthentication()); + assertAuthenticationEquals(authentication, future.get().getAuthentication()); } final TimeValue defaultExpiration = TokenService.TOKEN_EXPIRATION.get(Settings.EMPTY); @@ -464,7 +493,7 @@ public void testTokenExpiry() throws Exception { clock.fastForwardSeconds(fastForwardAmount); PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); - assertEquals(authentication, future.get().getAuthentication()); + assertAuthenticationEquals(authentication, future.get().getAuthentication()); } try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) { @@ -473,7 +502,7 @@ public void testTokenExpiry() throws Exception { clock.rewind(TimeValue.timeValueNanos(clock.instant().getNano())); // trim off nanoseconds since don't store them in the index PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); - assertEquals(authentication, future.get().getAuthentication()); + assertAuthenticationEquals(authentication, future.get().getAuthentication()); } try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) { @@ -569,7 +598,7 @@ public void testIndexNotAvailable() throws Exception { PlainActionFuture future = new PlainActionFuture<>(); tokenService.getAndValidateToken(requestContext, future); UserToken serialized = future.get(); - assertEquals(authentication, serialized.getAuthentication()); + assertAuthenticationEquals(authentication, serialized.getAuthentication()); when(securityIndex.isAvailable()).thenReturn(false); when(securityIndex.indexExists()).thenReturn(true); @@ -601,6 +630,7 @@ public void testDecodePre6xToken() throws GeneralSecurityException, ExecutionExc assertWarnings("[xpack.security.authc.token.passphrase] setting was deprecated in Elasticsearch and will be removed in a future" + " release! See the breaking changes documentation for the next major version."); } + public void testGetAuthenticationWorksWithExpiredToken() throws Exception { TokenService tokenService = new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, securityIndex, clusterService); @@ -611,7 +641,7 @@ public void testGetAuthenticationWorksWithExpiredToken() throws Exception { PlainActionFuture>> authFuture = new PlainActionFuture<>(); tokenService.getAuthenticationAndMetaData(userTokenString, authFuture); Authentication retrievedAuth = authFuture.actionGet().v1(); - assertEquals(authentication, retrievedAuth); + assertAuthenticationEquals(authentication, retrievedAuth); } private void mockGetTokenFromId(UserToken userToken) { @@ -638,4 +668,16 @@ public static void mockGetTokenFromId(UserToken userToken, Client client) { return Void.TYPE; }).when(client).get(any(GetRequest.class), any(ActionListener.class)); } + + private void assertAuthenticationEquals(Authentication expected, Authentication actual) { + if (mixedCluster) { + assertNotNull(expected); + assertNotNull(actual); + assertEquals(expected.getUser(), actual.getUser()); + assertEquals(expected.getAuthenticatedBy(), actual.getAuthenticatedBy()); + assertEquals(expected.getLookedUpBy(), actual.getLookedUpBy()); + } else { + assertEquals(expected, actual); + } + } } diff --git a/x-pack/qa/rolling-upgrade/build.gradle b/x-pack/qa/rolling-upgrade/build.gradle index e53c34f42e042..54033b0422c72 100644 --- a/x-pack/qa/rolling-upgrade/build.gradle +++ b/x-pack/qa/rolling-upgrade/build.gradle @@ -146,7 +146,6 @@ subprojects { if (version.onOrAfter('6.0.0') == false) { // this is needed since in 5.6 we don't bootstrap the token service if there is no explicit initial password keystoreSetting 'xpack.security.authc.token.passphrase', 'xpack_token_passphrase' - setting 'xpack.security.authc.token.enabled', 'true' } dependsOn copyTestNodeKeystore extraConfigFile 'testnode.jks', new File(outputDir + '/testnode.jks') diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java index 2ba388e9852dc..d5e87cca5cfc2 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/TokenBackwardsCompatibilityIT.java @@ -74,9 +74,6 @@ public void testMixedCluster() throws Exception { assumeTrue("the master must be on the latest version before we can write", isMasterOnLatestVersion()); assumeFalse("can't be run twice because it invalidates a token so we skip the first attempt", Booleans.parseBoolean(System.getProperty("tests.first_round"))); - Version upgradeFromVersion = Version.fromString(System.getProperty("tests.upgrade_from_version")); - assumeFalse("this test fails for unknown reasons when run before 5.6.0", - upgradeFromVersion.before(Version.V_6_0_0)); Response getResponse = client().performRequest("GET", "token_backwards_compatibility_it/doc/old_cluster_token2"); assertOK(getResponse); @@ -124,7 +121,7 @@ public void testMixedCluster() throws Exception { } public void testUpgradedCluster() throws Exception { - assumeTrue("this test should only run against the mixed cluster", CLUSTER_TYPE == ClusterType.UPGRADED); + assumeTrue("this test should only run against the upgraded cluster", CLUSTER_TYPE == ClusterType.UPGRADED); Response getResponse = client().performRequest("GET", "token_backwards_compatibility_it/doc/old_cluster_token2"); assertOK(getResponse); Map source = (Map) entityAsMap(getResponse).get("_source"); From dc671e008d14bb5f71dead2e165ed6c79fc6778d Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 13 Jun 2018 21:11:39 +0200 Subject: [PATCH 56/71] Add missing release notes. --- docs/reference/release-notes/6.3.asciidoc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/reference/release-notes/6.3.asciidoc b/docs/reference/release-notes/6.3.asciidoc index c0e8ed4c89695..1c14668e099df 100644 --- a/docs/reference/release-notes/6.3.asciidoc +++ b/docs/reference/release-notes/6.3.asciidoc @@ -132,6 +132,9 @@ Aggregations:: * Always return the after_key in composite aggregation response {pull}28358[#28358] * Upgrade t-digest to 3.2 {pull}28305[#28305] (issue: {issue}28295[#28295]) +CRUD:: +* Don't swallow exceptions on replication {pull}31179[#31179] (issue: {issue}28571[#28571]) + Core:: * Implement Iterator#remove for Cache values iter {pull}29633[#29633] * Introduce analyze thread pool {pull}29541[#29541] @@ -224,6 +227,7 @@ Mapping:: Machine Learning:: * Synchronize long and short tests for periodicity {ml-pull}62[#62] * Improvements to trend modelling and periodicity testing for forecasting {ml-pull}7[#7] (issue: {ml-issue}5[#5]) +* [ML] Clean left behind model state docs {pull}30659[#30659] (issue: {issue}30551[#30551]) * [ML] Hide internal Job update options from the REST API {pull}30537[#30537] (issue: {issue}30512[#30512]) Packaging:: @@ -265,7 +269,8 @@ Scripting:: * Modify Painless grammar to support right brackets as statement delimiters {pull}29566[#29566] Search:: -* Add support to match_phrase query for zero_terms_query. {pull}29598[#29598] (issue: {issue}29344[#29344]) +* Improve explanation in rescore {pull}30629[#30629] (issue: {issue}28725[#28725]) +* Add support to match_phrase query for zero_terms_query. {pull}29598[#29598] (issue: {issue}29344[#29344]) * Improve similarity integration. {pull}29187[#29187] (issues: {issue}23208[#23208], {issue}29035[#29035]) * Store offsets in index prefix fields when stored in the parent field {pull}29067[#29067] (issue: {issue}28994[#28994]) * Add QueryBuilders.matchNoneQuery(), #28679 {pull}28680[#28680] @@ -324,6 +329,8 @@ Audit:: * Fix audit index template upgrade loop {pull}30779[#30779] Authentication:: +* Security: fix dynamic mapping updates with aliases {pull}30787[#30787] (issue: {issue}30597[#30597]) +* [Security] Include an empty json object in an json array when FLS filters out all fields {pull}30709[#30709] (issue: {issue}30624[#30624]) * Security: cleanup code in file stores {pull}30348[#30348] * Security: fix TokenMetaData equals and hashcode {pull}30347[#30347] @@ -399,6 +406,7 @@ Machine Learning:: * Do not log incorrect model memory limit {ml-pull}3[#3] Mapping:: +* Delay _uid field data deprecation warning {pull}30651[#30651] (issue: {issue}30625[#30625]) * Ignore null value for range field (#27845) {pull}28116[#28116] (issue: {issue}27845[#27845]) * Fix a type check that is always false {pull}27726[#27726] @@ -423,6 +431,7 @@ Percolator:: * Do not take duplicate query extractions into account for minimum_should_match attribute {pull}28353[#28353] (issue: {issue}28315[#28315]) Plugins:: +* Template upgrades should happen in a system context {pull}30621[#30621] (issue: {issue}30603[#30603]) * Plugins: Fix native controller confirmation for non-meta plugin {pull}29434[#29434] * Plugins: Fix module name conflict check for meta plugins {pull}29146[#29146] * Ensure that azure stream has socket privileges {pull}28751[#28751] (issue: {issue}28662[#28662]) From e3baaa81e4689a1f50bbbc88e81f24a6ba653d91 Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Wed, 13 Jun 2018 15:42:20 -0400 Subject: [PATCH 57/71] [Docs] All Rollup docs experimental, agg limitations, clarify DeleteJob (#31299) - All rollup pages should be marked as experimental instead of just the top page - While the job config docs state which aggregations are allowed, adding a section which specifically details this in one place is more convenient for the user - Add a clarification that the DeleteJob API does not delete the rollup data, just the rollup job. --- .../en/rest-api/rollup/delete-job.asciidoc | 28 +++++++++++++++++++ .../docs/en/rest-api/rollup/get-job.asciidoc | 2 ++ .../docs/en/rest-api/rollup/put-job.asciidoc | 2 ++ .../en/rest-api/rollup/rollup-caps.asciidoc | 2 ++ .../rollup/rollup-job-config.asciidoc | 2 ++ .../en/rest-api/rollup/rollup-search.asciidoc | 2 ++ .../en/rest-api/rollup/start-job.asciidoc | 2 ++ .../docs/en/rest-api/rollup/stop-job.asciidoc | 2 ++ x-pack/docs/en/rollup/api-quickref.asciidoc | 2 ++ x-pack/docs/en/rollup/index.asciidoc | 4 ++- x-pack/docs/en/rollup/overview.asciidoc | 2 ++ .../en/rollup/rollup-agg-limitations.asciidoc | 24 ++++++++++++++++ .../en/rollup/rollup-getting-started.asciidoc | 2 ++ .../rollup/rollup-search-limitations.asciidoc | 6 ++-- .../en/rollup/understanding-groups.asciidoc | 2 ++ 15 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 x-pack/docs/en/rollup/rollup-agg-limitations.asciidoc diff --git a/x-pack/docs/en/rest-api/rollup/delete-job.asciidoc b/x-pack/docs/en/rest-api/rollup/delete-job.asciidoc index 056a4470480a0..b795e0b28c760 100644 --- a/x-pack/docs/en/rest-api/rollup/delete-job.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/delete-job.asciidoc @@ -5,9 +5,37 @@ Delete Job ++++ +experimental[] + This API deletes an existing rollup job. The job can be started or stopped, in both cases it will be deleted. Attempting to delete a non-existing job will throw an exception +.Deleting the job does not delete rolled up data +********************************** +When a job is deleted, that only removes the process that is actively monitoring and rolling up data. +It does not delete any previously rolled up data. This is by design; a user may wish to roll up a static dataset. Because +the dataset is static, once it has been fully rolled up there is no need to keep the indexing Rollup job around (as there +will be no new data). So the job may be deleted, leaving behind the rolled up data for analysis. + +If you wish to also remove the rollup data, and the rollup index only contains the data for a single job, you can simply +delete the whole rollup index. If the rollup index stores data from several jobs, you must issue a Delete-By-Query that +targets the Rollup job's ID in the rollup index: + + +[source,js] +-------------------------------------------------- +POST my_rollup_index/_delete_by_query +{ + "query": { + "term": { + "_rollup.id": "the_rollup_job_id" + } + } +} +-------------------------------------------------- +// NOTCONSOLE + +********************************** ==== Request `DELETE _xpack/rollup/job/` diff --git a/x-pack/docs/en/rest-api/rollup/get-job.asciidoc b/x-pack/docs/en/rest-api/rollup/get-job.asciidoc index 4482a87527930..7a7db9258b88a 100644 --- a/x-pack/docs/en/rest-api/rollup/get-job.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/get-job.asciidoc @@ -5,6 +5,8 @@ Get Job ++++ +experimental[] + This API returns the configuration, stats and status of rollup jobs. The API can return the details for a single job, or for all jobs. diff --git a/x-pack/docs/en/rest-api/rollup/put-job.asciidoc b/x-pack/docs/en/rest-api/rollup/put-job.asciidoc index 2cc869e1e3467..1449acadc636d 100644 --- a/x-pack/docs/en/rest-api/rollup/put-job.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/put-job.asciidoc @@ -5,6 +5,8 @@ Create Job ++++ +experimental[] + This API enables you to create a rollup job. The job will be created in a `STOPPED` state, and must be started with the <>. diff --git a/x-pack/docs/en/rest-api/rollup/rollup-caps.asciidoc b/x-pack/docs/en/rest-api/rollup/rollup-caps.asciidoc index 5a4dab69d937f..270ad005144ac 100644 --- a/x-pack/docs/en/rest-api/rollup/rollup-caps.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/rollup-caps.asciidoc @@ -5,6 +5,8 @@ Get Rollup Caps ++++ +experimental[] + This API returns the rollup capabilities that have been configured for an index or index pattern. This API is useful because a rollup job is often configured to rollup only a subset of fields from the source index. Furthermore, only certain aggregations can be configured for various fields, leading to a limited subset of functionality depending on diff --git a/x-pack/docs/en/rest-api/rollup/rollup-job-config.asciidoc b/x-pack/docs/en/rest-api/rollup/rollup-job-config.asciidoc index 85f1a57caa763..ef0ea6f00f7ce 100644 --- a/x-pack/docs/en/rest-api/rollup/rollup-job-config.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/rollup-job-config.asciidoc @@ -2,6 +2,8 @@ [[rollup-job-config]] === Rollup Job Configuration +experimental[] + The Rollup Job Configuration contains all the details about how the rollup job should run, when it indexes documents, and what future queries will be able to execute against the rollup index. diff --git a/x-pack/docs/en/rest-api/rollup/rollup-search.asciidoc b/x-pack/docs/en/rest-api/rollup/rollup-search.asciidoc index 557953fefb231..470cbc4eaf57d 100644 --- a/x-pack/docs/en/rest-api/rollup/rollup-search.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/rollup-search.asciidoc @@ -5,6 +5,8 @@ Rollup Search ++++ +experimental[] + The Rollup Search endpoint allows searching rolled-up data using the standard query DSL. The Rollup Search endpoint is needed because, internally, rolled-up documents utilize a different document structure than the original data. The Rollup Search endpoint rewrites standard query DSL into a format that matches the rollup documents, then takes the response diff --git a/x-pack/docs/en/rest-api/rollup/start-job.asciidoc b/x-pack/docs/en/rest-api/rollup/start-job.asciidoc index b8eccd5fbce82..9a0a0a7e4f01c 100644 --- a/x-pack/docs/en/rest-api/rollup/start-job.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/start-job.asciidoc @@ -5,6 +5,8 @@ Start Job ++++ +experimental[] + This API starts an existing, stopped rollup job. If the job does not exist an exception will be thrown. Starting an already started job has no action. diff --git a/x-pack/docs/en/rest-api/rollup/stop-job.asciidoc b/x-pack/docs/en/rest-api/rollup/stop-job.asciidoc index 9da3872a10b00..6050740270503 100644 --- a/x-pack/docs/en/rest-api/rollup/stop-job.asciidoc +++ b/x-pack/docs/en/rest-api/rollup/stop-job.asciidoc @@ -5,6 +5,8 @@ Stop Job ++++ +experimental[] + This API stops an existing, started rollup job. If the job does not exist an exception will be thrown. Stopping an already stopped job has no action. diff --git a/x-pack/docs/en/rollup/api-quickref.asciidoc b/x-pack/docs/en/rollup/api-quickref.asciidoc index 1ae6de4ee011c..10aed1b572d38 100644 --- a/x-pack/docs/en/rollup/api-quickref.asciidoc +++ b/x-pack/docs/en/rollup/api-quickref.asciidoc @@ -1,6 +1,8 @@ [[rollup-api-quickref]] == API Quick Reference +experimental[] + Most {rollup} endpoints have the following base: [source,js] diff --git a/x-pack/docs/en/rollup/index.asciidoc b/x-pack/docs/en/rollup/index.asciidoc index 69cd872e59669..9ac89341bfe99 100644 --- a/x-pack/docs/en/rollup/index.asciidoc +++ b/x-pack/docs/en/rollup/index.asciidoc @@ -18,7 +18,8 @@ for analysis, but at a fraction of the storage cost of raw data. * <> * <> * <> -* <> +* <> +* <> -- @@ -27,4 +28,5 @@ include::overview.asciidoc[] include::api-quickref.asciidoc[] include::rollup-getting-started.asciidoc[] include::understanding-groups.asciidoc[] +include::rollup-agg-limitations.asciidoc[] include::rollup-search-limitations.asciidoc[] \ No newline at end of file diff --git a/x-pack/docs/en/rollup/overview.asciidoc b/x-pack/docs/en/rollup/overview.asciidoc index cee244a2ec241..a3f29f23bd107 100644 --- a/x-pack/docs/en/rollup/overview.asciidoc +++ b/x-pack/docs/en/rollup/overview.asciidoc @@ -1,6 +1,8 @@ [[rollup-overview]] == Overview +experimental[] + Time-based data (documents that are predominantly identified by their timestamp) often have associated retention policies to manage data growth. For example, your system may be generating 500,000 documents every second. That will generate 43 million documents per day, and nearly 16 billion documents a year. diff --git a/x-pack/docs/en/rollup/rollup-agg-limitations.asciidoc b/x-pack/docs/en/rollup/rollup-agg-limitations.asciidoc new file mode 100644 index 0000000000000..cd20622d93c8d --- /dev/null +++ b/x-pack/docs/en/rollup/rollup-agg-limitations.asciidoc @@ -0,0 +1,24 @@ +[[rollup-agg-limitations]] +== Rollup Aggregation Limitations + +experimental[] + +There are some limitations to how fields can be rolled up / aggregated. This page highlights the major limitations so that +you are aware of them. + +[float] +=== Limited aggregation components + +The Rollup functionality allows fields to be grouped with the following aggregations: + +- Date Histogram aggregation +- Histogram aggregation +- Terms aggregation + +And the following metrics are allowed to be specified for numeric fields: + +- Min aggregation +- Max aggregation +- Sum aggregation +- Average aggregation +- Value Count aggregation \ No newline at end of file diff --git a/x-pack/docs/en/rollup/rollup-getting-started.asciidoc b/x-pack/docs/en/rollup/rollup-getting-started.asciidoc index cf96d67454083..24f68dddd8101 100644 --- a/x-pack/docs/en/rollup/rollup-getting-started.asciidoc +++ b/x-pack/docs/en/rollup/rollup-getting-started.asciidoc @@ -1,6 +1,8 @@ [[rollup-getting-started]] == Getting Started +experimental[] + To use the Rollup feature, you need to create one or more "Rollup Jobs". These jobs run continuously in the background and rollup the index or indices that you specify, placing the rolled documents in a secondary index (also of your choosing). diff --git a/x-pack/docs/en/rollup/rollup-search-limitations.asciidoc b/x-pack/docs/en/rollup/rollup-search-limitations.asciidoc index de47404a29da3..57ba23eebccbe 100644 --- a/x-pack/docs/en/rollup/rollup-search-limitations.asciidoc +++ b/x-pack/docs/en/rollup/rollup-search-limitations.asciidoc @@ -1,6 +1,8 @@ [[rollup-search-limitations]] == Rollup Search Limitations +experimental[] + While we feel the Rollup function is extremely flexible, the nature of summarizing data means there will be some limitations. Once live data is thrown away, you will always lose some flexibility. @@ -100,8 +102,8 @@ The Rollup functionality allows `query`'s in the search request, but with a limi - MatchAll Query - Any compound query (Boolean, Boosting, ConstantScore, etc) -Furthermore, these queries can only use fields that were also saved in the rollup job. If you wish to filter on a keyword `hostname` field, -that field must have been configured in the rollup job under a `terms` grouping. +Furthermore, these queries can only use fields that were also saved in the rollup job as a `group`. +If you wish to filter on a keyword `hostname` field, that field must have been configured in the rollup job under a `terms` grouping. If you attempt to use an unsupported query, or the query references a field that wasn't configured in the rollup job, an exception will be thrown. We expect the list of support queries to grow over time as more are implemented. diff --git a/x-pack/docs/en/rollup/understanding-groups.asciidoc b/x-pack/docs/en/rollup/understanding-groups.asciidoc index d6eef54fab87e..f57f905ae04c8 100644 --- a/x-pack/docs/en/rollup/understanding-groups.asciidoc +++ b/x-pack/docs/en/rollup/understanding-groups.asciidoc @@ -1,6 +1,8 @@ [[rollup-understanding-groups]] == Understanding Groups +experimental[] + To preserve flexibility, Rollup Jobs are defined based on how future queries may need to use the data. Traditionally, systems force the admin to make decisions about what metrics to rollup and on what interval. E.g. The average of `cpu_time` on an hourly basis. This is limiting; if, at a future date, the admin wishes to see the average of `cpu_time` on an hourly basis _and partitioned by `host_name`_, From f8b086034e71a26c990eb8200a64a428f55c6dcc Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 13 Jun 2018 22:06:43 +0200 Subject: [PATCH 58/71] Fix version detection. --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 85567aed2ce5f..fb7c0d0901508 100644 --- a/build.gradle +++ b/build.gradle @@ -121,7 +121,7 @@ task verifyVersions { new URL('https://repo1.maven.org/maven2/org/elasticsearch/elasticsearch/maven-metadata.xml').openStream().withStream { s -> xml = new XmlParser().parse(s) } - Set knownVersions = new TreeSet<>(xml.versioning.versions.version.collect { it.text() }.findAll { it ==~ /\d\.\d\.\d/ }.collect { Version.fromString(it) }) + Set knownVersions = new TreeSet<>(xml.versioning.versions.version.collect { it.text() }.findAll { it ==~ /\d+\.\d+\.\d+/ }.collect { Version.fromString(it) }) // Limit the known versions to those that should be index compatible, and are not future versions knownVersions = knownVersions.findAll { it.major >= bwcVersions.currentVersion.major - 1 && it.before(VersionProperties.elasticsearch) } From 2e00c37f05b9444c0242e0578289fb74a27d5e94 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 13 Jun 2018 22:21:09 +0200 Subject: [PATCH 59/71] Add 5.6.11 version constant. --- server/src/main/java/org/elasticsearch/Version.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/Version.java b/server/src/main/java/org/elasticsearch/Version.java index 9565f247d53b4..b54a7717231c2 100644 --- a/server/src/main/java/org/elasticsearch/Version.java +++ b/server/src/main/java/org/elasticsearch/Version.java @@ -120,6 +120,8 @@ public class Version implements Comparable, ToXContentFragment { public static final Version V_5_6_9 = new Version(V_5_6_9_ID, org.apache.lucene.util.Version.LUCENE_6_6_1); public static final int V_5_6_10_ID = 5061099; public static final Version V_5_6_10 = new Version(V_5_6_10_ID, org.apache.lucene.util.Version.LUCENE_6_6_1); + public static final int V_5_6_11_ID = 5061199; + public static final Version V_5_6_11 = new Version(V_5_6_11_ID, org.apache.lucene.util.Version.LUCENE_6_6_1); public static final int V_6_0_0_alpha1_ID = 6000001; public static final Version V_6_0_0_alpha1 = new Version(V_6_0_0_alpha1_ID, org.apache.lucene.util.Version.LUCENE_7_0_0); public static final int V_6_0_0_alpha2_ID = 6000002; @@ -224,6 +226,8 @@ public static Version fromId(int id) { return V_6_0_0_alpha2; case V_6_0_0_alpha1_ID: return V_6_0_0_alpha1; + case V_5_6_11_ID: + return V_5_6_11; case V_5_6_10_ID: return V_5_6_10; case V_5_6_9_ID: From e6815128891845d5f4e2655d6df5163f874903f3 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 13 Jun 2018 23:08:18 +0300 Subject: [PATCH 60/71] SQL: Whitelist SQL utility class for better scripting (#30681) Add SQL class for reusing code inside SQL functions within Painless Fix #29832 --- x-pack/plugin/sql/build.gradle | 3 +- .../licenses/antlr4-runtime-4.5.3.jar.sha1 | 1 - .../sql/licenses/antlr4-runtime-LICENSE.txt | 26 --------- .../sql/licenses/antlr4-runtime-NOTICE.txt | 0 .../scalar/datetime/DateTimeFunction.java | 57 +++++++------------ .../scalar/script/ScriptTemplate.java | 3 +- .../whitelist/InternalSqlScriptUtils.java | 22 +++++++ .../sql/plugin/SqlPainlessExtension.java | 35 ++++++++++++ ...asticsearch.painless.spi.PainlessExtension | 1 + .../xpack/sql/plugin/sql_whitelist.txt | 12 ++++ 10 files changed, 94 insertions(+), 66 deletions(-) delete mode 100644 x-pack/plugin/sql/licenses/antlr4-runtime-4.5.3.jar.sha1 delete mode 100644 x-pack/plugin/sql/licenses/antlr4-runtime-LICENSE.txt delete mode 100644 x-pack/plugin/sql/licenses/antlr4-runtime-NOTICE.txt create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java create mode 100644 x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.java create mode 100644 x-pack/plugin/sql/src/main/resources/META-INF/services/org.elasticsearch.painless.spi.PainlessExtension create mode 100644 x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt diff --git a/x-pack/plugin/sql/build.gradle b/x-pack/plugin/sql/build.gradle index c52413aa4e1d6..8b406235985c6 100644 --- a/x-pack/plugin/sql/build.gradle +++ b/x-pack/plugin/sql/build.gradle @@ -5,7 +5,7 @@ esplugin { name 'x-pack-sql' description 'The Elasticsearch plugin that powers SQL for Elasticsearch' classname 'org.elasticsearch.xpack.sql.plugin.SqlPlugin' - extendedPlugins = ['x-pack-core'] + extendedPlugins = ['x-pack-core', 'lang-painless'] } configurations { @@ -20,6 +20,7 @@ integTest.enabled = false dependencies { compileOnly "org.elasticsearch.plugin:x-pack-core:${version}" + compileOnly project(':modules:lang-painless') compile project('sql-proto') compile "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}" compile "org.antlr:antlr4-runtime:4.5.3" diff --git a/x-pack/plugin/sql/licenses/antlr4-runtime-4.5.3.jar.sha1 b/x-pack/plugin/sql/licenses/antlr4-runtime-4.5.3.jar.sha1 deleted file mode 100644 index 535955b7d6826..0000000000000 --- a/x-pack/plugin/sql/licenses/antlr4-runtime-4.5.3.jar.sha1 +++ /dev/null @@ -1 +0,0 @@ -2609e36f18f7e8d593cc1cddfb2ac776dc96b8e0 \ No newline at end of file diff --git a/x-pack/plugin/sql/licenses/antlr4-runtime-LICENSE.txt b/x-pack/plugin/sql/licenses/antlr4-runtime-LICENSE.txt deleted file mode 100644 index 95d0a2554f686..0000000000000 --- a/x-pack/plugin/sql/licenses/antlr4-runtime-LICENSE.txt +++ /dev/null @@ -1,26 +0,0 @@ -[The "BSD license"] -Copyright (c) 2015 Terence Parr, Sam Harwell -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/x-pack/plugin/sql/licenses/antlr4-runtime-NOTICE.txt b/x-pack/plugin/sql/licenses/antlr4-runtime-NOTICE.txt deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java index 77a6db3009d0c..606728222787b 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/datetime/DateTimeFunction.java @@ -51,8 +51,18 @@ public abstract class DateTimeFunction extends UnaryScalarFunction { protected final NodeInfo info() { return NodeInfo.create(this, ctorForInfo(), field(), timeZone()); } + protected abstract NodeInfo.NodeCtor2 ctorForInfo(); + @Override + protected TypeResolution resolveType() { + if (field().dataType() == DataType.DATE) { + return TypeResolution.TYPE_RESOLVED; + } + return new TypeResolution("Function [" + functionName() + "] cannot be applied on a non-date expression ([" + + Expressions.name(field()) + "] of type [" + field().dataType().esType + "])"); + } + public TimeZone timeZone() { return timeZone; } @@ -69,18 +79,12 @@ public Object fold() { return null; } - ZonedDateTime time = ZonedDateTime.ofInstant( - Instant.ofEpochMilli(folded.getMillis()), ZoneId.of(timeZone.getID())); - return time.get(chronoField()); + return dateTimeChrono(folded.getMillis(), timeZone.getID(), chronoField().name()); } - @Override - protected TypeResolution resolveType() { - if (field().dataType() == DataType.DATE) { - return TypeResolution.TYPE_RESOLVED; - } - return new TypeResolution("Function [" + functionName() + "] cannot be applied on a non-date expression ([" - + Expressions.name(field()) + "] of type [" + field().dataType().esType + "])"); + public static Integer dateTimeChrono(long millis, String tzId, String chronoName) { + ZonedDateTime time = ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of(tzId)); + return Integer.valueOf(time.get(ChronoField.valueOf(chronoName))); } @Override @@ -88,28 +92,11 @@ protected ScriptTemplate asScriptFrom(FieldAttribute field) { ParamsBuilder params = paramsBuilder(); String template = null; - if (TimeZone.getTimeZone("UTC").equals(timeZone)) { - // TODO: it would be nice to be able to externalize the extract function and reuse the script across all extractors - template = formatTemplate("doc[{}].value.get" + extractFunction() + "()"); - params.variable(field.name()); - } else { - // TODO ewwww - /* - * This uses the Java 8 time API because Painless doesn't whitelist creation of new - * Joda classes. - * - * The actual script is - * ZonedDateTime.ofInstant(Instant.ofEpochMilli(.value.millis), - * ZoneId.of()).get(ChronoField.get(MONTH_OF_YEAR)) - */ - - template = formatTemplate("ZonedDateTime.ofInstant(Instant.ofEpochMilli(doc[{}].value.millis), " - + "ZoneId.of({})).get(ChronoField.valueOf({}))"); - params.variable(field.name()) - .variable(timeZone.getID()) - .variable(chronoField().name()); - } - + template = formatTemplate("{sql}.dateTimeChrono(doc[{}].value.millis, {}, {})"); + params.variable(field.name()) + .variable(timeZone.getID()) + .variable(chronoField().name()); + return new ScriptTemplate(template, params.build(), dataType()); } @@ -119,10 +106,6 @@ protected ScriptTemplate asScriptFrom(AggregateFunctionAttribute aggregate) { throw new UnsupportedOperationException(); } - protected String extractFunction() { - return getClass().getSimpleName(); - } - /** * Used for generating the painless script version of this function when the time zone is not UTC */ @@ -164,4 +147,4 @@ public boolean equals(Object obj) { public int hashCode() { return Objects.hash(field(), timeZone); } -} +} \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java index 323ccc4e072c6..35b7680dcca78 100644 --- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/script/ScriptTemplate.java @@ -7,6 +7,7 @@ import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptType; +import org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils; import org.elasticsearch.xpack.sql.type.DataType; import org.elasticsearch.xpack.sql.util.StringUtils; @@ -92,6 +93,6 @@ public String toString() { } public static String formatTemplate(String template) { - return template.replace("{}", "params.%s"); + return template.replace("{sql}", InternalSqlScriptUtils.class.getSimpleName()).replace("{}", "params.%s"); } } diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java new file mode 100644 index 0000000000000..802aa4a7c09bb --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/function/scalar/whitelist/InternalSqlScriptUtils.java @@ -0,0 +1,22 @@ +/* + * 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.sql.expression.function.scalar.whitelist; + +import org.elasticsearch.xpack.sql.expression.function.scalar.datetime.DateTimeFunction; + +/** + * Whitelisted class for SQL scripts. + * Acts as a registry of the various static methods used internally by the scalar functions + * (to simplify the whitelist definition). + */ +public final class InternalSqlScriptUtils { + + private InternalSqlScriptUtils() {} + + public static Integer dateTimeChrono(long millis, String tzId, String chronoName) { + return DateTimeFunction.dateTimeChrono(millis, tzId, chronoName); + } +} diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.java new file mode 100644 index 0000000000000..426d725ac79d8 --- /dev/null +++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/plugin/SqlPainlessExtension.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.sql.plugin; + +import org.elasticsearch.painless.spi.PainlessExtension; +import org.elasticsearch.painless.spi.Whitelist; +import org.elasticsearch.painless.spi.WhitelistLoader; +import org.elasticsearch.script.FilterScript; +import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.script.SearchScript; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.singletonList; + +public class SqlPainlessExtension implements PainlessExtension { + + private static final Whitelist WHITELIST = WhitelistLoader.loadFromResourceFiles(SqlPainlessExtension.class, "sql_whitelist.txt"); + + @Override + public Map, List> getContextWhitelists() { + Map, List> whitelist = new HashMap<>(); + List list = singletonList(WHITELIST); + whitelist.put(FilterScript.CONTEXT, list); + whitelist.put(SearchScript.AGGS_CONTEXT, list); + whitelist.put(SearchScript.CONTEXT, list); + whitelist.put(SearchScript.SCRIPT_SORT_CONTEXT, list); + return whitelist; + } +} diff --git a/x-pack/plugin/sql/src/main/resources/META-INF/services/org.elasticsearch.painless.spi.PainlessExtension b/x-pack/plugin/sql/src/main/resources/META-INF/services/org.elasticsearch.painless.spi.PainlessExtension new file mode 100644 index 0000000000000..5f2f571f015d9 --- /dev/null +++ b/x-pack/plugin/sql/src/main/resources/META-INF/services/org.elasticsearch.painless.spi.PainlessExtension @@ -0,0 +1 @@ +org.elasticsearch.xpack.sql.plugin.SqlPainlessExtension \ No newline at end of file diff --git a/x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt b/x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt new file mode 100644 index 0000000000000..8dae4f8c0d1d6 --- /dev/null +++ b/x-pack/plugin/sql/src/main/resources/org/elasticsearch/xpack/sql/plugin/sql_whitelist.txt @@ -0,0 +1,12 @@ +# +# 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. +# + +# This file contains a whitelist for SQL specific utilities available inside SQL scripting + +class org.elasticsearch.xpack.sql.expression.function.scalar.whitelist.InternalSqlScriptUtils { + + Integer dateTimeChrono(long, String, String) +} \ No newline at end of file From d72a6bde8d7e8a52e646f4fc9531469d2647e0e3 Mon Sep 17 00:00:00 2001 From: Tal Levy Date: Wed, 13 Jun 2018 13:35:41 -0700 Subject: [PATCH 61/71] move security ingest processors to a sub ingest directory (#31306) It makes sense to introduce new Security ingest processors (example: #31087), and this change would give them a good place to be written. --- .../main/java/org/elasticsearch/xpack/security/Security.java | 2 +- .../xpack/security/ingest}/SetSecurityUserProcessor.java | 2 +- .../ingest}/SetSecurityUserProcessorFactoryTests.java | 4 ++-- .../SetSecurityUserProcessorTests.java | 5 ++--- 4 files changed, 6 insertions(+), 7 deletions(-) rename x-pack/plugin/{core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol => security/src/main/java/org/elasticsearch/xpack/security/ingest}/SetSecurityUserProcessor.java (98%) rename x-pack/plugin/{core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol => security/src/test/java/org/elasticsearch/xpack/security/ingest}/SetSecurityUserProcessorFactoryTests.java (94%) rename x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/{authz/accesscontrol => ingest}/SetSecurityUserProcessorTests.java (97%) 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 300cedeb1caf2..cb714afc30c44 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 @@ -117,7 +117,6 @@ import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.xpack.core.security.authz.accesscontrol.SecurityIndexSearcherWrapper; -import org.elasticsearch.xpack.core.security.authz.accesscontrol.SetSecurityUserProcessor; import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions; import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; @@ -177,6 +176,7 @@ import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; import org.elasticsearch.xpack.security.authz.store.FileRolesStore; import org.elasticsearch.xpack.security.authz.store.NativeRolesStore; +import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor; import org.elasticsearch.xpack.security.rest.SecurityRestFilter; import org.elasticsearch.xpack.security.rest.action.RestAuthenticateAction; import org.elasticsearch.xpack.security.rest.action.oauth2.RestGetTokenAction; diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SetSecurityUserProcessor.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java similarity index 98% rename from x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SetSecurityUserProcessor.java rename to x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java index 051a077646320..15ac88b4d9462 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SetSecurityUserProcessor.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java @@ -3,7 +3,7 @@ * 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.authz.accesscontrol; +package org.elasticsearch.xpack.security.ingest; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.ingest.AbstractProcessor; diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SetSecurityUserProcessorFactoryTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorFactoryTests.java similarity index 94% rename from x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SetSecurityUserProcessorFactoryTests.java rename to x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorFactoryTests.java index 483d3de2beaa3..19da9b18ea650 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SetSecurityUserProcessorFactoryTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorFactoryTests.java @@ -3,11 +3,11 @@ * 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.authz.accesscontrol; +package org.elasticsearch.xpack.security.ingest; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.xpack.core.security.authz.accesscontrol.SetSecurityUserProcessor.Property; +import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor.Property; import java.util.Arrays; import java.util.EnumSet; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SetSecurityUserProcessorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorTests.java similarity index 97% rename from x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SetSecurityUserProcessorTests.java rename to x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorTests.java index c9ef169a375a5..26c59a1ef5470 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/accesscontrol/SetSecurityUserProcessorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorTests.java @@ -3,7 +3,7 @@ * 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.security.authz.accesscontrol; +package org.elasticsearch.xpack.security.ingest; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; @@ -11,9 +11,8 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.AuthenticationField; -import org.elasticsearch.xpack.core.security.authz.accesscontrol.SetSecurityUserProcessor; -import org.elasticsearch.xpack.core.security.authz.accesscontrol.SetSecurityUserProcessor.Property; import org.elasticsearch.xpack.core.security.user.User; +import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor.Property; import java.util.Collections; import java.util.EnumSet; From 2b8f2526c457948a758640bf56b3f660acdd0117 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 13 Jun 2018 23:37:51 +0300 Subject: [PATCH 62/71] Use quotes in the call invocation (#31249) Adding quotes around call invocation as paths can contain spaces that otherwise would cause the command to fail --- distribution/src/bin/elasticsearch-cli.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/src/bin/elasticsearch-cli.bat b/distribution/src/bin/elasticsearch-cli.bat index b668a7c06c272..e17ade3b74af1 100644 --- a/distribution/src/bin/elasticsearch-cli.bat +++ b/distribution/src/bin/elasticsearch-cli.bat @@ -2,7 +2,7 @@ call "%~dp0elasticsearch-env.bat" || exit /b 1 if defined ES_ADDITIONAL_SOURCES ( for %%a in ("%ES_ADDITIONAL_SOURCES:;=","%") do ( - call %~dp0%%a + call "%~dp0%%a" ) ) From 3faef737d573e6df6d04c35843902d7ba3f5248c Mon Sep 17 00:00:00 2001 From: lcawl Date: Wed, 13 Jun 2018 13:37:35 -0700 Subject: [PATCH 63/71] [DOCS] Shortens ML API intros --- x-pack/docs/en/rest-api/ml/close-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/delete-calendar-event.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/delete-calendar-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/delete-calendar.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/delete-datafeed.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/delete-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/delete-snapshot.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/flush-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/forecast.asciidoc | 3 +-- x-pack/docs/en/rest-api/ml/get-bucket.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-calendar-event.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-calendar.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-category.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-datafeed-stats.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-datafeed.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-influencer.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-job-stats.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-overall-buckets.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-record.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/get-snapshot.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/open-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/post-calendar-event.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/post-data.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/preview-datafeed.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/put-calendar-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/put-calendar.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/put-datafeed.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/put-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/revert-snapshot.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/start-datafeed.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/stop-datafeed.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/update-datafeed.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/update-job.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/update-snapshot.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/validate-detector.asciidoc | 2 +- x-pack/docs/en/rest-api/ml/validate-job.asciidoc | 2 +- 37 files changed, 37 insertions(+), 38 deletions(-) diff --git a/x-pack/docs/en/rest-api/ml/close-job.asciidoc b/x-pack/docs/en/rest-api/ml/close-job.asciidoc index 3e612f5171da1..8e7e8eb0ce850 100644 --- a/x-pack/docs/en/rest-api/ml/close-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/close-job.asciidoc @@ -5,7 +5,7 @@ Close Jobs ++++ -This API enables you to close one or more jobs. +Closes one or more jobs. A job can be opened and closed multiple times throughout its lifecycle. A closed job cannot receive data or perform analysis diff --git a/x-pack/docs/en/rest-api/ml/delete-calendar-event.asciidoc b/x-pack/docs/en/rest-api/ml/delete-calendar-event.asciidoc index b6f3c644acfea..73458f3179197 100644 --- a/x-pack/docs/en/rest-api/ml/delete-calendar-event.asciidoc +++ b/x-pack/docs/en/rest-api/ml/delete-calendar-event.asciidoc @@ -5,7 +5,7 @@ Delete Events from Calendar ++++ -This API enables you to delete scheduled events from a calendar. +Deletes scheduled events from a calendar. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/delete-calendar-job.asciidoc b/x-pack/docs/en/rest-api/ml/delete-calendar-job.asciidoc index 54fe9ebdaba9b..94388c0c4b680 100644 --- a/x-pack/docs/en/rest-api/ml/delete-calendar-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/delete-calendar-job.asciidoc @@ -5,7 +5,7 @@ Delete Jobs from Calendar ++++ -This API enables you to delete jobs from a calendar. +Deletes jobs from a calendar. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/delete-calendar.asciidoc b/x-pack/docs/en/rest-api/ml/delete-calendar.asciidoc index 37b3ae3c87b36..f7673b545748b 100644 --- a/x-pack/docs/en/rest-api/ml/delete-calendar.asciidoc +++ b/x-pack/docs/en/rest-api/ml/delete-calendar.asciidoc @@ -5,7 +5,7 @@ Delete Calendar ++++ -This API enables you to delete a calendar. +Deletes a calendar. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/delete-datafeed.asciidoc b/x-pack/docs/en/rest-api/ml/delete-datafeed.asciidoc index de529267f4f7c..db4fd5c177aed 100644 --- a/x-pack/docs/en/rest-api/ml/delete-datafeed.asciidoc +++ b/x-pack/docs/en/rest-api/ml/delete-datafeed.asciidoc @@ -5,7 +5,7 @@ Delete {dfeeds-cap} ++++ -This API enables you to delete an existing {dfeed}. +Deletes an existing {dfeed}. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/delete-job.asciidoc b/x-pack/docs/en/rest-api/ml/delete-job.asciidoc index 7aaba59e122eb..c01b08545b638 100644 --- a/x-pack/docs/en/rest-api/ml/delete-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/delete-job.asciidoc @@ -5,7 +5,7 @@ Delete Jobs ++++ -This API enables you to delete an existing anomaly detection job. +Deletes an existing anomaly detection job. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/delete-snapshot.asciidoc b/x-pack/docs/en/rest-api/ml/delete-snapshot.asciidoc index b63e37a1b454b..2ab0116fe74d9 100644 --- a/x-pack/docs/en/rest-api/ml/delete-snapshot.asciidoc +++ b/x-pack/docs/en/rest-api/ml/delete-snapshot.asciidoc @@ -5,7 +5,7 @@ Delete Model Snapshots ++++ -This API enables you to delete an existing model snapshot. +Deletes an existing model snapshot. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/flush-job.asciidoc b/x-pack/docs/en/rest-api/ml/flush-job.asciidoc index 2a65c5284fcf4..934a2d81b1778 100644 --- a/x-pack/docs/en/rest-api/ml/flush-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/flush-job.asciidoc @@ -5,7 +5,7 @@ Flush Jobs ++++ -This API forces any buffered data to be processed by the job. +Forces any buffered data to be processed by the job. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/forecast.asciidoc b/x-pack/docs/en/rest-api/ml/forecast.asciidoc index 9e3e48a2e7b38..169debef7b6cb 100644 --- a/x-pack/docs/en/rest-api/ml/forecast.asciidoc +++ b/x-pack/docs/en/rest-api/ml/forecast.asciidoc @@ -5,8 +5,7 @@ Forecast Jobs ++++ -This API uses historical behavior to predict the future behavior of a time -series. +Predict the future behavior of a time series by using historical behavior. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-bucket.asciidoc b/x-pack/docs/en/rest-api/ml/get-bucket.asciidoc index 9a20d4fc15e52..95b05ff7f5dd2 100644 --- a/x-pack/docs/en/rest-api/ml/get-bucket.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-bucket.asciidoc @@ -5,7 +5,7 @@ Get Buckets ++++ -This API enables you to retrieve job results for one or more buckets. +Retrieves job results for one or more buckets. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-calendar-event.asciidoc b/x-pack/docs/en/rest-api/ml/get-calendar-event.asciidoc index 1a10ad68d7f22..e89173c3382d9 100644 --- a/x-pack/docs/en/rest-api/ml/get-calendar-event.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-calendar-event.asciidoc @@ -5,7 +5,7 @@ Get Scheduled Events ++++ -This API enables you to retrieve information about the scheduled events in +Retrieves information about the scheduled events in calendars. diff --git a/x-pack/docs/en/rest-api/ml/get-calendar.asciidoc b/x-pack/docs/en/rest-api/ml/get-calendar.asciidoc index 245d570947276..ae95fd9968893 100644 --- a/x-pack/docs/en/rest-api/ml/get-calendar.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-calendar.asciidoc @@ -5,7 +5,7 @@ Get Calendars ++++ -This API enables you to retrieve configuration information for calendars. +Retrieves configuration information for calendars. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-category.asciidoc b/x-pack/docs/en/rest-api/ml/get-category.asciidoc index 9e69083355bbb..13f274133c0d1 100644 --- a/x-pack/docs/en/rest-api/ml/get-category.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-category.asciidoc @@ -5,7 +5,7 @@ Get Categories ++++ -This API enables you to retrieve job results for one or more categories. +Retrieves job results for one or more categories. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-datafeed-stats.asciidoc b/x-pack/docs/en/rest-api/ml/get-datafeed-stats.asciidoc index 6c5b3af650b9a..2869e8222f86f 100644 --- a/x-pack/docs/en/rest-api/ml/get-datafeed-stats.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-datafeed-stats.asciidoc @@ -5,7 +5,7 @@ Get {dfeed-cap} Statistics ++++ -This API enables you to retrieve usage information for {dfeeds}. +Retrieves usage information for {dfeeds}. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-datafeed.asciidoc b/x-pack/docs/en/rest-api/ml/get-datafeed.asciidoc index 8d582ed672aff..0fa51773fd162 100644 --- a/x-pack/docs/en/rest-api/ml/get-datafeed.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-datafeed.asciidoc @@ -5,7 +5,7 @@ Get {dfeeds-cap} ++++ -This API enables you to retrieve configuration information for {dfeeds}. +Retrieves configuration information for {dfeeds}. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-influencer.asciidoc b/x-pack/docs/en/rest-api/ml/get-influencer.asciidoc index 6c49e66e944ac..bffd2b8e09633 100644 --- a/x-pack/docs/en/rest-api/ml/get-influencer.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-influencer.asciidoc @@ -5,7 +5,7 @@ Get Influencers ++++ -This API enables you to retrieve job results for one or more influencers. +Retrieves job results for one or more influencers. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-job-stats.asciidoc b/x-pack/docs/en/rest-api/ml/get-job-stats.asciidoc index 48ebac280aae3..bd59ee8b258fa 100644 --- a/x-pack/docs/en/rest-api/ml/get-job-stats.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-job-stats.asciidoc @@ -5,7 +5,7 @@ Get Job Statistics ++++ -This API enables you to retrieve usage information for jobs. +Retrieves usage information for jobs. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-job.asciidoc b/x-pack/docs/en/rest-api/ml/get-job.asciidoc index ce3613d6f2cbc..b1329bd9b19c3 100644 --- a/x-pack/docs/en/rest-api/ml/get-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-job.asciidoc @@ -5,7 +5,7 @@ Get Jobs ++++ -This API enables you to retrieve configuration information for jobs. +Retrieves configuration information for jobs. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-overall-buckets.asciidoc b/x-pack/docs/en/rest-api/ml/get-overall-buckets.asciidoc index d0e8c1f214bd4..f2581f4904e37 100644 --- a/x-pack/docs/en/rest-api/ml/get-overall-buckets.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-overall-buckets.asciidoc @@ -5,7 +5,7 @@ Get Overall Buckets ++++ -This API enables you to retrieve overall bucket results that summarize the +Retrieves overall bucket results that summarize the bucket results of multiple jobs. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-record.asciidoc b/x-pack/docs/en/rest-api/ml/get-record.asciidoc index 6cd222027e66b..1870b44159760 100644 --- a/x-pack/docs/en/rest-api/ml/get-record.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-record.asciidoc @@ -5,7 +5,7 @@ Get Records ++++ -This API enables you to retrieve anomaly records for a job. +Retrieves anomaly records for a job. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/get-snapshot.asciidoc b/x-pack/docs/en/rest-api/ml/get-snapshot.asciidoc index 7ac2a3d765fea..6f76096cf29d5 100644 --- a/x-pack/docs/en/rest-api/ml/get-snapshot.asciidoc +++ b/x-pack/docs/en/rest-api/ml/get-snapshot.asciidoc @@ -5,7 +5,7 @@ Get Model Snapshots ++++ -This API enables you to retrieve information about model snapshots. +Retrieves information about model snapshots. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/open-job.asciidoc b/x-pack/docs/en/rest-api/ml/open-job.asciidoc index 37d201ed2264e..59d5568ac775a 100644 --- a/x-pack/docs/en/rest-api/ml/open-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/open-job.asciidoc @@ -5,7 +5,7 @@ Open Jobs ++++ -This API enables you to open one or more jobs. +Opens one or more jobs. A job must be opened in order for it to be ready to receive and analyze data. A job can be opened and closed multiple times throughout its lifecycle. diff --git a/x-pack/docs/en/rest-api/ml/post-calendar-event.asciidoc b/x-pack/docs/en/rest-api/ml/post-calendar-event.asciidoc index ab0c1ebef64ab..41af0841d2e83 100644 --- a/x-pack/docs/en/rest-api/ml/post-calendar-event.asciidoc +++ b/x-pack/docs/en/rest-api/ml/post-calendar-event.asciidoc @@ -5,7 +5,7 @@ Add Events to Calendar ++++ -This API enables you to post scheduled events in a calendar. +Posts scheduled events in a calendar. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/post-data.asciidoc b/x-pack/docs/en/rest-api/ml/post-data.asciidoc index ec20be5dadb12..40354d7f6f760 100644 --- a/x-pack/docs/en/rest-api/ml/post-data.asciidoc +++ b/x-pack/docs/en/rest-api/ml/post-data.asciidoc @@ -5,7 +5,7 @@ Post Data to Jobs ++++ -This API enables you to send data to an anomaly detection job for analysis. +Sends data to an anomaly detection job for analysis. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/preview-datafeed.asciidoc b/x-pack/docs/en/rest-api/ml/preview-datafeed.asciidoc index 5f3bc5054e394..e6b51f8ef069f 100644 --- a/x-pack/docs/en/rest-api/ml/preview-datafeed.asciidoc +++ b/x-pack/docs/en/rest-api/ml/preview-datafeed.asciidoc @@ -5,7 +5,7 @@ Preview {dfeeds-cap} ++++ -This API enables you to preview a {dfeed}. +Previews a {dfeed}. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/put-calendar-job.asciidoc b/x-pack/docs/en/rest-api/ml/put-calendar-job.asciidoc index 5d2c012a919d7..6940957b15926 100644 --- a/x-pack/docs/en/rest-api/ml/put-calendar-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/put-calendar-job.asciidoc @@ -5,7 +5,7 @@ Add Jobs to Calendar ++++ -This API enables you to add a job to a calendar. +Adds a job to a calendar. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/put-calendar.asciidoc b/x-pack/docs/en/rest-api/ml/put-calendar.asciidoc index 23997906cb7f0..a82da5a2c0c0a 100644 --- a/x-pack/docs/en/rest-api/ml/put-calendar.asciidoc +++ b/x-pack/docs/en/rest-api/ml/put-calendar.asciidoc @@ -5,7 +5,7 @@ Create Calendar ++++ -This API enables you to instantiate a calendar. +Instantiates a calendar. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/put-datafeed.asciidoc b/x-pack/docs/en/rest-api/ml/put-datafeed.asciidoc index f1e41cad8b343..6b8ad932a1d42 100644 --- a/x-pack/docs/en/rest-api/ml/put-datafeed.asciidoc +++ b/x-pack/docs/en/rest-api/ml/put-datafeed.asciidoc @@ -5,7 +5,7 @@ Create {dfeeds-cap} ++++ -This API enables you to instantiate a {dfeed}. +Instantiates a {dfeed}. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/put-job.asciidoc b/x-pack/docs/en/rest-api/ml/put-job.asciidoc index 5f8a0197a2602..ea72396f9f56b 100644 --- a/x-pack/docs/en/rest-api/ml/put-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/put-job.asciidoc @@ -5,7 +5,7 @@ Create Jobs ++++ -This API enables you to instantiate a job. +Instantiates a job. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/revert-snapshot.asciidoc b/x-pack/docs/en/rest-api/ml/revert-snapshot.asciidoc index 2e4d8e19604ce..3ba228cc274b1 100644 --- a/x-pack/docs/en/rest-api/ml/revert-snapshot.asciidoc +++ b/x-pack/docs/en/rest-api/ml/revert-snapshot.asciidoc @@ -5,7 +5,7 @@ Revert Model Snapshots ++++ -This API enables you to revert to a specific snapshot. +Reverts to a specific snapshot. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/start-datafeed.asciidoc b/x-pack/docs/en/rest-api/ml/start-datafeed.asciidoc index 865ca4ae99722..fa3ea35a751f7 100644 --- a/x-pack/docs/en/rest-api/ml/start-datafeed.asciidoc +++ b/x-pack/docs/en/rest-api/ml/start-datafeed.asciidoc @@ -5,7 +5,7 @@ Start {dfeeds-cap} ++++ -This API enables you to start one or more {dfeeds}. +Starts one or more {dfeeds}. A {dfeed} must be started in order to retrieve data from {es}. A {dfeed} can be started and stopped multiple times throughout its lifecycle. diff --git a/x-pack/docs/en/rest-api/ml/stop-datafeed.asciidoc b/x-pack/docs/en/rest-api/ml/stop-datafeed.asciidoc index 3511c9362c3fa..27872ff5a2080 100644 --- a/x-pack/docs/en/rest-api/ml/stop-datafeed.asciidoc +++ b/x-pack/docs/en/rest-api/ml/stop-datafeed.asciidoc @@ -5,7 +5,7 @@ Stop {dfeeds-cap} ++++ -This API enables you to stop one or more {dfeeds}. +Stops one or more {dfeeds}. A {dfeed} that is stopped ceases to retrieve data from {es}. A {dfeed} can be started and stopped multiple times throughout its lifecycle. diff --git a/x-pack/docs/en/rest-api/ml/update-datafeed.asciidoc b/x-pack/docs/en/rest-api/ml/update-datafeed.asciidoc index 277a9ce31773f..bc9462347c1c0 100644 --- a/x-pack/docs/en/rest-api/ml/update-datafeed.asciidoc +++ b/x-pack/docs/en/rest-api/ml/update-datafeed.asciidoc @@ -5,7 +5,7 @@ Update {dfeeds-cap} ++++ -This API enables you to update certain properties of a {dfeed}. +Updates certain properties of a {dfeed}. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/update-job.asciidoc b/x-pack/docs/en/rest-api/ml/update-job.asciidoc index 4250108be09f1..f68737a3408c4 100644 --- a/x-pack/docs/en/rest-api/ml/update-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/update-job.asciidoc @@ -5,7 +5,7 @@ Update Jobs ++++ -This API enables you to update certain properties of a job. +Updates certain properties of a job. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/update-snapshot.asciidoc b/x-pack/docs/en/rest-api/ml/update-snapshot.asciidoc index 74a684619c411..8c98a7b732186 100644 --- a/x-pack/docs/en/rest-api/ml/update-snapshot.asciidoc +++ b/x-pack/docs/en/rest-api/ml/update-snapshot.asciidoc @@ -5,7 +5,7 @@ Update Model Snapshots ++++ -This API enables you to update certain properties of a snapshot. +Updates certain properties of a snapshot. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/validate-detector.asciidoc b/x-pack/docs/en/rest-api/ml/validate-detector.asciidoc index 6fc5fea6fbb94..f688ef91cfe53 100644 --- a/x-pack/docs/en/rest-api/ml/validate-detector.asciidoc +++ b/x-pack/docs/en/rest-api/ml/validate-detector.asciidoc @@ -5,7 +5,7 @@ Validate Detectors ++++ -This API validates detector configuration information. +Validates detector configuration information. ==== Request diff --git a/x-pack/docs/en/rest-api/ml/validate-job.asciidoc b/x-pack/docs/en/rest-api/ml/validate-job.asciidoc index b206734bc033f..61d0c70514e8d 100644 --- a/x-pack/docs/en/rest-api/ml/validate-job.asciidoc +++ b/x-pack/docs/en/rest-api/ml/validate-job.asciidoc @@ -5,7 +5,7 @@ Validate Jobs ++++ -This API validates job configuration information. +Validates job configuration information. ==== Request From 2b9b4c041dfafd5e7d1afa47d1f2ef7d067bc938 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Wed, 13 Jun 2018 13:53:08 +0300 Subject: [PATCH 64/71] [DOC] Extend SQL docs Add overview section Add data type section Improve function section --- x-pack/docs/en/sql/functions/index.asciidoc | 50 ++++++++--- x-pack/docs/en/sql/getting-started.asciidoc | 2 +- x-pack/docs/en/sql/index.asciidoc | 18 +++- .../docs/en/sql/language/data-types.asciidoc | 89 ++++++++++++++++++- x-pack/docs/en/sql/overview.asciidoc | 30 +++++++ x-pack/docs/en/sql/standalone.asciidoc | 9 -- 6 files changed, 167 insertions(+), 31 deletions(-) create mode 100644 x-pack/docs/en/sql/overview.asciidoc delete mode 100644 x-pack/docs/en/sql/standalone.asciidoc diff --git a/x-pack/docs/en/sql/functions/index.asciidoc b/x-pack/docs/en/sql/functions/index.asciidoc index 7220c73623056..a4e7028cf39c3 100644 --- a/x-pack/docs/en/sql/functions/index.asciidoc +++ b/x-pack/docs/en/sql/functions/index.asciidoc @@ -1,9 +1,11 @@ [[sql-functions]] == Functions and Operators +{es-sql} provides a number of built-in operators and functions. + === Comparison Operators -Elasticsearch SQL supports the following comparison operators: +{es-sql} supports the following comparison operators: * Equality (`=`) @@ -12,7 +14,7 @@ Elasticsearch SQL supports the following comparison operators: include-tagged::{sql-specs}/filter.sql-spec[whereFieldEquality] -------------------------------------------------- -* Inequality (`<>` or `!=`) +* Inequality (`<>` or `!=` or `<=>`) ["source","sql",subs="attributes,callouts,macros"] -------------------------------------------------- @@ -43,7 +45,7 @@ include-tagged::{sql-specs}/filter.sql-spec[whereIsNotNullAndIsNull] === Logical Operators -Elasticsearch SQL supports the following logical operators: +{es-sql} supports the following logical operators: * `AND` @@ -69,7 +71,7 @@ include-tagged::{sql-specs}/filter.sql-spec[whereFieldEqualityNot] === Math Operators -Elasticsearch SQL supports the following math operators: +{es-sql} supports the following math operators: * Add (`+`) @@ -106,7 +108,7 @@ include-tagged::{sql-specs}/arithmetic.sql-spec[multiply] include-tagged::{sql-specs}/arithmetic.sql-spec[divide] -------------------------------------------------- -* https://en.wikipedia.org/wiki/Modulo_operation[Modulo] (`%`) +* https://en.wikipedia.org/wiki/Modulo_operation[Modulo] or Reminder(`%`) ["source","sql",subs="attributes,callouts,macros"] -------------------------------------------------- @@ -115,25 +117,49 @@ include-tagged::{sql-specs}/arithmetic.sql-spec[mod] === Math Functions -==== Basic -* https://en.wikipedia.org/wiki/Absolute_value[Absolute value] (`ABS`) +All math and trigonometric functions require their input (where applicable) +to be numeric. + +==== Generic + +* `ABS` + +https://en.wikipedia.org/wiki/Absolute_value[Absolute value], returns \[same type as input] ["source","sql",subs="attributes,callouts,macros"] -------------------------------------------------- include-tagged::{sql-specs}/math.sql-spec[abs] -------------------------------------------------- -* https://en.wikipedia.org/wiki/Rounding#Round_half_up[Round] (`ROUND`) +* `CBRT` + +https://en.wikipedia.org/wiki/Cube_root[Cube root], returns `double` // TODO make the example in the tests presentable -NOTE: This rounds "half up" meaning that `ROUND(-1.5)` results in `-1`. +* `CEIL` + +https://en.wikipedia.org/wiki/Floor_and_ceiling_functions[Ceiling], returns `double` -* https://en.wikipedia.org/wiki/Floor_and_ceiling_functions[Ceiling] (`CEIL`) +* `CEILING` + +Same as `CEIL` // TODO make the example in the tests presentable +* `E` + +https://en.wikipedia.org/wiki/E_%28mathematical_constant%29[Euler's number], returns `2.7182818284590452354` + + +* https://en.wikipedia.org/wiki/Rounding#Round_half_up[Round] (`ROUND`) + +// TODO make the example in the tests presentable + +NOTE: This rounds "half up" meaning that `ROUND(-1.5)` results in `-1`. + + * https://en.wikipedia.org/wiki/Floor_and_ceiling_functions[Floor] (`FLOOR`) // TODO make the example in the tests presentable @@ -159,10 +185,6 @@ include-tagged::{sql-specs}/math.sql-spec[log10] include-tagged::{sql-specs}/math.sql-spec[sqrt] -------------------------------------------------- -* https://en.wikipedia.org/wiki/Cube_root[Cube root] (`CBRT`) - -// TODO make the example in the tests presentable - * https://en.wikipedia.org/wiki/Exponential_function[e^x^] (`EXP`) ["source","sql",subs="attributes,callouts,macros"] diff --git a/x-pack/docs/en/sql/getting-started.asciidoc b/x-pack/docs/en/sql/getting-started.asciidoc index 966ac5336cfdd..24f01910551bb 100644 --- a/x-pack/docs/en/sql/getting-started.asciidoc +++ b/x-pack/docs/en/sql/getting-started.asciidoc @@ -1,7 +1,7 @@ [[sql-getting-started]] == Getting Started with SQL -To start using Elasticsearch SQL, create +To start using {es-sql}, create an index with some data to experiment with: [source,js] diff --git a/x-pack/docs/en/sql/index.asciidoc b/x-pack/docs/en/sql/index.asciidoc index 1be44b2d05965..902ea8ada7e22 100644 --- a/x-pack/docs/en/sql/index.asciidoc +++ b/x-pack/docs/en/sql/index.asciidoc @@ -6,6 +6,7 @@ :sql-specs: {sql-tests}/src/main/resources :jdbc-tests: {sql-tests}/src/main/java/org/elasticsearch/xpack/qa/sql/jdbc :security-tests: {sql-tests}/security/src/test/java/org/elasticsearch/xpack/qa/sql/security +:es-sql: Elasticsearch SQL [partintro] -- @@ -13,8 +14,12 @@ experimental[] X-Pack includes a SQL feature to execute SQL against Elasticsearch -indices and return tabular results. There are four main components: +indices and return results in tabular format. +<>:: + Overview of {es-sql} and its features. +<>:: + Start using SQL right away in {es} <>:: Accepts SQL in a JSON document, executes it, and returns the results. @@ -22,12 +27,19 @@ indices and return tabular results. There are four main components: Accepts SQL in a JSON document and translates it into a native Elasticsearch query and returns that. <>:: - Command line application that connects to Elasticsearch to execute + Command-line application that connects to {es} to execute SQL and print tabular results. <>:: - A JDBC driver for Elasticsearch. + A JDBC driver for {es}. +<>:: + List of functions and operators supported. +<>:: + Overview of the {es-sql} language, such as data types, syntax and + reserved keywords. + -- +include::overview.asciidoc[] include::getting-started.asciidoc[] include::endpoints/index.asciidoc[] include::functions/index.asciidoc[] diff --git a/x-pack/docs/en/sql/language/data-types.asciidoc b/x-pack/docs/en/sql/language/data-types.asciidoc index f97299eb0f243..322269bddaf8f 100644 --- a/x-pack/docs/en/sql/language/data-types.asciidoc +++ b/x-pack/docs/en/sql/language/data-types.asciidoc @@ -1,6 +1,87 @@ [[sql-data-types]] -=== Data Type and Mapping +=== Data Types + +Most of {es} <> are available in {es-sql}, as indicated below: + +[cols="^,^,^",options="header"] + +|=== +| {es} type | SQL type | SQL precision + +3+h| Core types + +| <> | `null` | 0 +| <> | `boolean` | 1 +| <> | `tinyint` | 3 +| <> | `smallint` | 5 +| <> | `integer` | 10 +| <> | `long` | 19 +| <> | `double` | 15 +| <> | `real` | 7 +| <> | `float` | 16 +| <> | `float` | 19 +| <> | `varchar` | based on <> +| <> | `varchar` | 2,147,483,647 +| <> | `varbinary` | 2,147,483,647 +| <> | `timestamp` | 24 + +3+h| Complex types + +| <> | `struct` | 0 +| <> | `struct` | 0 + +3+h| Unsupported types + +| _types not mentioned above_ | `unsupported`| 0 + +|=== + +Obviously, not all types in {es} have an equivalent in SQL and vice-versa hence why, {es-sql} +uses the data type _particularities_ of the former over the latter as ultimately {es} is the backing store. + + +[[sql-multi-field]] +[float] +==== SQL and multi-fields + +A core concept in {es} is that of an `analyzed` field, that is a full-text value that is interpreted in order +to be effectively indexed. These fields are of type <> and are not used for sorting or aggregations as their actual value depends on the <> used hence why {es} also offers the <> type for storing the _exact_ +value. + +In most case, and the default actually, is to use both types when for strings which {es} supports through <>, that is the ability to index the same string in multiple ways; for example index it both as `text` for search but also as `keyword` for sorting and aggregations. + +As SQL requires exact values, when encountering a `text` field {es-sql} will search for an exact multi-field that it can use for comparisons, sorting and aggregations. +To do that, it will search for the first `keyword` that it can find that is _not_ normalized and use that as the original field _exact_ value. + +Consider the following `string` mapping: + +[source, js] +---- +{ + "first_name" : { + "type" : "text", + "fields" : { + "raw" : { + "type" : "keyword" + } + } + } +} +---- + +The following SQL query: + +[source, sql] +---- +SELECT first_name FROM index WHERE first_name = 'John' +---- + +is identical to: + +[source, sql] +---- +SELECT first_name FROM index WHERE first_name.raw = 'John' +---- + +as {es-sql} automatically _picks_ up the `raw` multi-field from `raw` for exact matching. -// TODO finish this -List of data types in SQL and how they actually map to Elasticsearch. -Also mention the corner cases - multi-fields, names with dots, etc... diff --git a/x-pack/docs/en/sql/overview.asciidoc b/x-pack/docs/en/sql/overview.asciidoc new file mode 100644 index 0000000000000..34d0dfb538352 --- /dev/null +++ b/x-pack/docs/en/sql/overview.asciidoc @@ -0,0 +1,30 @@ +[[sql-overview]] +== Overview + +{es-sql} aims to provide a powerful yet lightweight SQL interface to {es}. + +[[sql-introduction]] +=== Introduction + +{es-sql} is an X-Pack component that allows SQL-like queries to be executed in real-time against {es}. +Whether using the REST interface, command-line or JDBC, any client can use SQL to search and aggregate data +_natively_ inside {es}. +One can think of {es-sql} as a _translator_, one that understands both SQL and {es} and makes it easy to read and process data in real-time, at scale by leveraging {es} capabilities. + +[[sql-why]] +=== Why {es-sql} ? + +Native integration:: + +{es-sql} is built from the ground up for {es}. Each and every query is efficiently executed against the relevant nodes according to the underlying storage. + +No external parts:: + +No need for additional hardware, processes, runtimes or libraries to query {es}; {es-sql} eliminates extra moving parts by running _inside_ the {es} cluster. + +Lightweight and efficient:: + +{es-sql} does not abstract {es} and its search capabilities - on the contrary, it embrases and exposes to SQL to allow proper full-text search, in real-time, in the same declarative, succint fashion. + + + diff --git a/x-pack/docs/en/sql/standalone.asciidoc b/x-pack/docs/en/sql/standalone.asciidoc deleted file mode 100644 index 6649ce9eb8de9..0000000000000 --- a/x-pack/docs/en/sql/standalone.asciidoc +++ /dev/null @@ -1,9 +0,0 @@ -[[elasticsearch-sql-standalone]] -= Elasticsearch SQL Standalone - -:es-repo-dir: {docdir}/../../../../../elasticsearch/docs - -:edit_url: -include::{es-repo-dir}/reference/index-shared3.asciidoc[] -:edit_url!: -include::index.asciidoc[] From 43c0ee073b2c64d6e952763e50f54569bd5471f3 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Thu, 14 Jun 2018 16:26:17 +1000 Subject: [PATCH 65/71] Fix non-REST doc snippet --- x-pack/docs/en/sql/language/data-types.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/docs/en/sql/language/data-types.asciidoc b/x-pack/docs/en/sql/language/data-types.asciidoc index 322269bddaf8f..a01c2fda5c726 100644 --- a/x-pack/docs/en/sql/language/data-types.asciidoc +++ b/x-pack/docs/en/sql/language/data-types.asciidoc @@ -68,6 +68,7 @@ Consider the following `string` mapping: } } ---- +// NOTCONSOLE The following SQL query: From 2ca0181cd0bdf8752c97c1dbc645cd4970e9b12a Mon Sep 17 00:00:00 2001 From: David Kyle Date: Wed, 13 Jun 2018 15:42:18 +0100 Subject: [PATCH 66/71] [ML] Check licence when datafeeds use cross cluster search (#31247) This change prevents a datafeed using cross cluster search from starting if the remote cluster does not have x-pack installed and a sufficient license. The check is made only when starting a datafeed. --- .../core/ml/datafeed/DatafeedConfigTests.java | 25 ++- .../action/TransportStartDatafeedAction.java | 127 +++++++---- .../ml/datafeed/DatafeedNodeSelector.java | 6 +- .../ml/datafeed/MlRemoteLicenseChecker.java | 192 +++++++++++++++++ .../process/autodetect/AutodetectProcess.java | 2 +- .../datafeed/MlRemoteLicenseCheckerTests.java | 200 ++++++++++++++++++ 6 files changed, 493 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseChecker.java create mode 100644 x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java index 6aa987fc0e932..d59ef16dfdf2c 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/datafeed/DatafeedConfigTests.java @@ -40,7 +40,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.TimeZone; @@ -193,11 +192,11 @@ public void testDefaults() { public void testDefaultQueryDelay() { DatafeedConfig.Builder feedBuilder1 = new DatafeedConfig.Builder("datafeed1", "job1"); - feedBuilder1.setIndices(Arrays.asList("foo")); + feedBuilder1.setIndices(Collections.singletonList("foo")); DatafeedConfig.Builder feedBuilder2 = new DatafeedConfig.Builder("datafeed2", "job1"); - feedBuilder2.setIndices(Arrays.asList("foo")); + feedBuilder2.setIndices(Collections.singletonList("foo")); DatafeedConfig.Builder feedBuilder3 = new DatafeedConfig.Builder("datafeed3", "job2"); - feedBuilder3.setIndices(Arrays.asList("foo")); + feedBuilder3.setIndices(Collections.singletonList("foo")); DatafeedConfig feed1 = feedBuilder1.build(); DatafeedConfig feed2 = feedBuilder2.build(); DatafeedConfig feed3 = feedBuilder3.build(); @@ -208,19 +207,19 @@ public void testDefaultQueryDelay() { assertThat(feed1.getQueryDelay(), not(equalTo(feed3.getQueryDelay()))); } - public void testCheckValid_GivenNullIndices() throws IOException { + public void testCheckValid_GivenNullIndices() { DatafeedConfig.Builder conf = new DatafeedConfig.Builder("datafeed1", "job1"); expectThrows(IllegalArgumentException.class, () -> conf.setIndices(null)); } - public void testCheckValid_GivenEmptyIndices() throws IOException { + public void testCheckValid_GivenEmptyIndices() { DatafeedConfig.Builder conf = new DatafeedConfig.Builder("datafeed1", "job1"); conf.setIndices(Collections.emptyList()); ElasticsearchException e = ESTestCase.expectThrows(ElasticsearchException.class, conf::build); assertEquals(Messages.getMessage(Messages.DATAFEED_CONFIG_INVALID_OPTION_VALUE, "indices", "[]"), e.getMessage()); } - public void testCheckValid_GivenIndicesContainsOnlyNulls() throws IOException { + public void testCheckValid_GivenIndicesContainsOnlyNulls() { List indices = new ArrayList<>(); indices.add(null); indices.add(null); @@ -230,7 +229,7 @@ public void testCheckValid_GivenIndicesContainsOnlyNulls() throws IOException { assertEquals(Messages.getMessage(Messages.DATAFEED_CONFIG_INVALID_OPTION_VALUE, "indices", "[null, null]"), e.getMessage()); } - public void testCheckValid_GivenIndicesContainsOnlyEmptyStrings() throws IOException { + public void testCheckValid_GivenIndicesContainsOnlyEmptyStrings() { List indices = new ArrayList<>(); indices.add(""); indices.add(""); @@ -240,27 +239,27 @@ public void testCheckValid_GivenIndicesContainsOnlyEmptyStrings() throws IOExcep assertEquals(Messages.getMessage(Messages.DATAFEED_CONFIG_INVALID_OPTION_VALUE, "indices", "[, ]"), e.getMessage()); } - public void testCheckValid_GivenNegativeQueryDelay() throws IOException { + public void testCheckValid_GivenNegativeQueryDelay() { DatafeedConfig.Builder conf = new DatafeedConfig.Builder("datafeed1", "job1"); IllegalArgumentException e = ESTestCase.expectThrows(IllegalArgumentException.class, () -> conf.setQueryDelay(TimeValue.timeValueMillis(-10))); assertEquals("query_delay cannot be less than 0. Value = -10", e.getMessage()); } - public void testCheckValid_GivenZeroFrequency() throws IOException { + public void testCheckValid_GivenZeroFrequency() { DatafeedConfig.Builder conf = new DatafeedConfig.Builder("datafeed1", "job1"); IllegalArgumentException e = ESTestCase.expectThrows(IllegalArgumentException.class, () -> conf.setFrequency(TimeValue.ZERO)); assertEquals("frequency cannot be less or equal than 0. Value = 0s", e.getMessage()); } - public void testCheckValid_GivenNegativeFrequency() throws IOException { + public void testCheckValid_GivenNegativeFrequency() { DatafeedConfig.Builder conf = new DatafeedConfig.Builder("datafeed1", "job1"); IllegalArgumentException e = ESTestCase.expectThrows(IllegalArgumentException.class, () -> conf.setFrequency(TimeValue.timeValueMinutes(-1))); assertEquals("frequency cannot be less or equal than 0. Value = -1", e.getMessage()); } - public void testCheckValid_GivenNegativeScrollSize() throws IOException { + public void testCheckValid_GivenNegativeScrollSize() { DatafeedConfig.Builder conf = new DatafeedConfig.Builder("datafeed1", "job1"); ElasticsearchException e = ESTestCase.expectThrows(ElasticsearchException.class, () -> conf.setScrollSize(-1000)); assertEquals(Messages.getMessage(Messages.DATAFEED_CONFIG_INVALID_OPTION_VALUE, "scroll_size", -1000L), e.getMessage()); @@ -414,7 +413,7 @@ public void testDefaultFrequency_GivenNegative() { public void testDefaultFrequency_GivenNoAggregations() { DatafeedConfig.Builder datafeedBuilder = new DatafeedConfig.Builder("feed", "job"); - datafeedBuilder.setIndices(Arrays.asList("my_index")); + datafeedBuilder.setIndices(Collections.singletonList("my_index")); DatafeedConfig datafeed = datafeedBuilder.build(); assertEquals(TimeValue.timeValueMinutes(1), datafeed.defaultFrequency(TimeValue.timeValueSeconds(1))); diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java index bed83ed82c1c9..3d261864ab409 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/action/TransportStartDatafeedAction.java @@ -43,10 +43,12 @@ import org.elasticsearch.persistent.PersistentTasksExecutor; import org.elasticsearch.persistent.PersistentTasksService; import org.elasticsearch.xpack.ml.MachineLearning; +import org.elasticsearch.xpack.ml.datafeed.MlRemoteLicenseChecker; import org.elasticsearch.xpack.ml.datafeed.DatafeedManager; import org.elasticsearch.xpack.ml.datafeed.DatafeedNodeSelector; import org.elasticsearch.xpack.ml.datafeed.extractor.DataExtractorFactory; +import java.util.List; import java.util.Map; import java.util.function.Predicate; @@ -111,23 +113,25 @@ protected void masterOperation(StartDatafeedAction.Request request, ClusterState ActionListener listener) { StartDatafeedAction.DatafeedParams params = request.getParams(); if (licenseState.isMachineLearningAllowed()) { - ActionListener> finalListener = + + ActionListener> waitForTaskListener = new ActionListener>() { - @Override - public void onResponse(PersistentTasksCustomMetaData.PersistentTask persistentTask) { - waitForDatafeedStarted(persistentTask.getId(), params, listener); - } + @Override + public void onResponse(PersistentTasksCustomMetaData.PersistentTask + persistentTask) { + waitForDatafeedStarted(persistentTask.getId(), params, listener); + } - @Override - public void onFailure(Exception e) { - if (e instanceof ResourceAlreadyExistsException) { - logger.debug("datafeed already started", e); - e = new ElasticsearchStatusException("cannot start datafeed [" + params.getDatafeedId() + - "] because it has already been started", RestStatus.CONFLICT); - } - listener.onFailure(e); - } - }; + @Override + public void onFailure(Exception e) { + if (e instanceof ResourceAlreadyExistsException) { + logger.debug("datafeed already started", e); + e = new ElasticsearchStatusException("cannot start datafeed [" + params.getDatafeedId() + + "] because it has already been started", RestStatus.CONFLICT); + } + listener.onFailure(e); + } + }; // Verify data extractor factory can be created, then start persistent task MlMetadata mlMetadata = MlMetadata.getMlMetadata(state); @@ -135,16 +139,39 @@ public void onFailure(Exception e) { validate(params.getDatafeedId(), mlMetadata, tasks); DatafeedConfig datafeed = mlMetadata.getDatafeed(params.getDatafeedId()); Job job = mlMetadata.getJobs().get(datafeed.getJobId()); - DataExtractorFactory.create(client, datafeed, job, ActionListener.wrap( - dataExtractorFactory -> - persistentTasksService.sendStartRequest(MLMetadataField.datafeedTaskId(params.getDatafeedId()), - StartDatafeedAction.TASK_NAME, params, finalListener) - , listener::onFailure)); + + if (MlRemoteLicenseChecker.containsRemoteIndex(datafeed.getIndices())) { + MlRemoteLicenseChecker remoteLicenseChecker = new MlRemoteLicenseChecker(client); + remoteLicenseChecker.checkRemoteClusterLicenses(MlRemoteLicenseChecker.remoteClusterNames(datafeed.getIndices()), + ActionListener.wrap( + response -> { + if (response.isViolated()) { + listener.onFailure(createUnlicensedError(datafeed.getId(), response)); + } else { + createDataExtractor(job, datafeed, params, waitForTaskListener); + } + }, + e -> listener.onFailure(createUnknownLicenseError(datafeed.getId(), + MlRemoteLicenseChecker.remoteIndices(datafeed.getIndices()), e)) + )); + } else { + createDataExtractor(job, datafeed, params, waitForTaskListener); + } } else { listener.onFailure(LicenseUtils.newComplianceException(XPackField.MACHINE_LEARNING)); } } + private void createDataExtractor(Job job, DatafeedConfig datafeed, StartDatafeedAction.DatafeedParams params, + ActionListener> + listener) { + DataExtractorFactory.create(client, datafeed, job, ActionListener.wrap( + dataExtractorFactory -> + persistentTasksService.sendStartRequest(MLMetadataField.datafeedTaskId(params.getDatafeedId()), + StartDatafeedAction.TASK_NAME, params, listener) + , listener::onFailure)); + } + @Override protected ClusterBlockException checkBlock(StartDatafeedAction.Request request, ClusterState state) { // We only delegate here to PersistentTasksService, but if there is a metadata writeblock, @@ -158,28 +185,29 @@ private void waitForDatafeedStarted(String taskId, StartDatafeedAction.DatafeedP DatafeedPredicate predicate = new DatafeedPredicate(); persistentTasksService.waitForPersistentTaskCondition(taskId, predicate, params.getTimeout(), new PersistentTasksService.WaitForPersistentTaskListener() { - @Override - public void onResponse(PersistentTasksCustomMetaData.PersistentTask persistentTask) { - if (predicate.exception != null) { - // We want to return to the caller without leaving an unassigned persistent task, to match - // what would have happened if the error had been detected in the "fast fail" validation - cancelDatafeedStart(persistentTask, predicate.exception, listener); - } else { - listener.onResponse(new StartDatafeedAction.Response(true)); - } - } + @Override + public void onResponse(PersistentTasksCustomMetaData.PersistentTask + persistentTask) { + if (predicate.exception != null) { + // We want to return to the caller without leaving an unassigned persistent task, to match + // what would have happened if the error had been detected in the "fast fail" validation + cancelDatafeedStart(persistentTask, predicate.exception, listener); + } else { + listener.onResponse(new StartDatafeedAction.Response(true)); + } + } - @Override - public void onFailure(Exception e) { - listener.onFailure(e); - } + @Override + public void onFailure(Exception e) { + listener.onFailure(e); + } - @Override - public void onTimeout(TimeValue timeout) { - listener.onFailure(new ElasticsearchException("Starting datafeed [" - + params.getDatafeedId() + "] timed out after [" + timeout + "]")); - } - }); + @Override + public void onTimeout(TimeValue timeout) { + listener.onFailure(new ElasticsearchException("Starting datafeed [" + + params.getDatafeedId() + "] timed out after [" + timeout + "]")); + } + }); } private void cancelDatafeedStart(PersistentTasksCustomMetaData.PersistentTask persistentTask, @@ -203,6 +231,25 @@ public void onFailure(Exception e) { ); } + private ElasticsearchStatusException createUnlicensedError(String datafeedId, + MlRemoteLicenseChecker.LicenseViolation licenseViolation) { + String message = "Cannot start datafeed [" + datafeedId + "] as it is configured to use " + + "indices on a remote cluster [" + licenseViolation.get().getClusterName() + + "] that is not licensed for Machine Learning. " + + MlRemoteLicenseChecker.buildErrorMessage(licenseViolation.get()); + + return new ElasticsearchStatusException(message, RestStatus.BAD_REQUEST); + } + + private ElasticsearchStatusException createUnknownLicenseError(String datafeedId, List remoteIndices, + Exception cause) { + String message = "Cannot start datafeed [" + datafeedId + "] as it is configured to use" + + " indices on a remote cluster " + remoteIndices + + " but the license type could not be verified"; + + return new ElasticsearchStatusException(message, RestStatus.BAD_REQUEST, new Exception(cause.getMessage())); + } + public static class StartDatafeedPersistentTasksExecutor extends PersistentTasksExecutor { private final DatafeedManager datafeedManager; private final IndexNameExpressionResolver resolver; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelector.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelector.java index 37f9715d09464..0eb57ab79be5d 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelector.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/DatafeedNodeSelector.java @@ -91,7 +91,7 @@ private AssignmentFailure verifyIndicesActive(DatafeedConfig datafeed) { List indices = datafeed.getIndices(); for (String index : indices) { - if (isRemoteIndex(index)) { + if (MlRemoteLicenseChecker.isRemoteIndex(index)) { // We cannot verify remote indices continue; } @@ -122,10 +122,6 @@ private AssignmentFailure verifyIndicesActive(DatafeedConfig datafeed) { return null; } - private boolean isRemoteIndex(String index) { - return index.indexOf(':') != -1; - } - private static class AssignmentFailure { private final String reason; private final boolean isCriticalForTaskCreation; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseChecker.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseChecker.java new file mode 100644 index 0000000000000..b55713f6d0ab7 --- /dev/null +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseChecker.java @@ -0,0 +1,192 @@ +/* + * 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.ml.datafeed; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.license.License; +import org.elasticsearch.license.XPackInfoResponse; +import org.elasticsearch.transport.ActionNotFoundTransportException; +import org.elasticsearch.transport.RemoteClusterAware; +import org.elasticsearch.xpack.core.action.XPackInfoAction; +import org.elasticsearch.xpack.core.action.XPackInfoRequest; + +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +/** + * ML datafeeds can use cross cluster search to access data in a remote cluster. + * The remote cluster should be licenced for ML this class performs that check + * using the _xpack (info) endpoint. + */ +public class MlRemoteLicenseChecker { + + private final Client client; + + public static class RemoteClusterLicenseInfo { + private final String clusterName; + private final XPackInfoResponse.LicenseInfo licenseInfo; + + RemoteClusterLicenseInfo(String clusterName, XPackInfoResponse.LicenseInfo licenseInfo) { + this.clusterName = clusterName; + this.licenseInfo = licenseInfo; + } + + public String getClusterName() { + return clusterName; + } + + public XPackInfoResponse.LicenseInfo getLicenseInfo() { + return licenseInfo; + } + } + + public class LicenseViolation { + private final RemoteClusterLicenseInfo licenseInfo; + + private LicenseViolation(@Nullable RemoteClusterLicenseInfo licenseInfo) { + this.licenseInfo = licenseInfo; + } + + public boolean isViolated() { + return licenseInfo != null; + } + + public RemoteClusterLicenseInfo get() { + return licenseInfo; + } + } + + public MlRemoteLicenseChecker(Client client) { + this.client = client; + } + + /** + * Check each cluster is licensed for ML. + * This function evaluates lazily and will terminate when the first cluster + * that is not licensed is found or an error occurs. + * + * @param clusterNames List of remote cluster names + * @param listener Response listener + */ + public void checkRemoteClusterLicenses(List clusterNames, ActionListener listener) { + final Iterator itr = clusterNames.iterator(); + if (itr.hasNext() == false) { + listener.onResponse(new LicenseViolation(null)); + return; + } + + final AtomicReference clusterName = new AtomicReference<>(itr.next()); + + ActionListener infoListener = new ActionListener() { + @Override + public void onResponse(XPackInfoResponse xPackInfoResponse) { + if (licenseSupportsML(xPackInfoResponse.getLicenseInfo()) == false) { + listener.onResponse(new LicenseViolation( + new RemoteClusterLicenseInfo(clusterName.get(), xPackInfoResponse.getLicenseInfo()))); + return; + } + + if (itr.hasNext()) { + clusterName.set(itr.next()); + remoteClusterLicense(clusterName.get(), this); + } else { + listener.onResponse(new LicenseViolation(null)); + } + } + + @Override + public void onFailure(Exception e) { + String message = "Could not determine the X-Pack licence type for cluster [" + clusterName.get() + "]"; + if (e instanceof ActionNotFoundTransportException) { + // This is likely to be because x-pack is not installed in the target cluster + message += ". Is X-Pack installed on the target cluster?"; + } + listener.onFailure(new ElasticsearchException(message, e)); + } + }; + + remoteClusterLicense(clusterName.get(), infoListener); + } + + private void remoteClusterLicense(String clusterName, ActionListener listener) { + Client remoteClusterClient = client.getRemoteClusterClient(clusterName); + ThreadContext threadContext = remoteClusterClient.threadPool().getThreadContext(); + try (ThreadContext.StoredContext ignore = threadContext.stashContext()) { + // we stash any context here since this is an internal execution and should not leak any + // existing context information. + threadContext.markAsSystemContext(); + + XPackInfoRequest request = new XPackInfoRequest(); + request.setCategories(EnumSet.of(XPackInfoRequest.Category.LICENSE)); + remoteClusterClient.execute(XPackInfoAction.INSTANCE, request, listener); + } + } + + static boolean licenseSupportsML(XPackInfoResponse.LicenseInfo licenseInfo) { + License.OperationMode mode = License.OperationMode.resolve(licenseInfo.getMode()); + return licenseInfo.getStatus() == License.Status.ACTIVE && + (mode == License.OperationMode.PLATINUM || mode == License.OperationMode.TRIAL); + } + + public static boolean isRemoteIndex(String index) { + return index.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR) != -1; + } + + public static boolean containsRemoteIndex(List indices) { + return indices.stream().anyMatch(MlRemoteLicenseChecker::isRemoteIndex); + } + + /** + * Get any remote indices used in cross cluster search. + * Remote indices are of the form {@code cluster_name:index_name} + * @return List of remote cluster indices + */ + public static List remoteIndices(List indices) { + return indices.stream().filter(MlRemoteLicenseChecker::isRemoteIndex).collect(Collectors.toList()); + } + + /** + * Extract the list of remote cluster names from the list of indices. + * @param indices List of indices. Remote cluster indices are prefixed + * with {@code cluster-name:} + * @return Every cluster name found in {@code indices} + */ + public static List remoteClusterNames(List indices) { + return indices.stream() + .filter(MlRemoteLicenseChecker::isRemoteIndex) + .map(index -> index.substring(0, index.indexOf(RemoteClusterAware.REMOTE_CLUSTER_INDEX_SEPARATOR))) + .distinct() + .collect(Collectors.toList()); + } + + public static String buildErrorMessage(RemoteClusterLicenseInfo clusterLicenseInfo) { + StringBuilder error = new StringBuilder(); + if (clusterLicenseInfo.licenseInfo.getStatus() != License.Status.ACTIVE) { + error.append("The license on cluster [").append(clusterLicenseInfo.clusterName) + .append("] is not active. "); + } else { + License.OperationMode mode = License.OperationMode.resolve(clusterLicenseInfo.licenseInfo.getMode()); + if (mode != License.OperationMode.PLATINUM && mode != License.OperationMode.TRIAL) { + error.append("The license mode [").append(mode) + .append("] on cluster [") + .append(clusterLicenseInfo.clusterName) + .append("] does not enable Machine Learning. "); + } + } + + error.append(Strings.toString(clusterLicenseInfo.licenseInfo)); + return error.toString(); + } +} diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcess.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcess.java index 049880b1ac224..21be815d561a8 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcess.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcess.java @@ -117,7 +117,7 @@ void writeUpdateDetectorRulesMessage(int detectorIndex, List rule /** * Ask the job to start persisting model state in the background - * @throws IOException + * @throws IOException If writing the request fails */ void persistJob() throws IOException; diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java new file mode 100644 index 0000000000000..47d4d30a7c6e4 --- /dev/null +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java @@ -0,0 +1,200 @@ +/* + * 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.ml.datafeed; + +import org.elasticsearch.action.ActionListener; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.license.License; +import org.elasticsearch.license.XPackInfoResponse; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.xpack.core.action.XPackInfoAction; +import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.is; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class MlRemoteLicenseCheckerTests extends ESTestCase { + + public void testIsRemoteIndex() { + List indices = Arrays.asList("local-index1", "local-index2"); + assertFalse(MlRemoteLicenseChecker.containsRemoteIndex(indices)); + indices = Arrays.asList("local-index1", "remote-cluster:remote-index2"); + assertTrue(MlRemoteLicenseChecker.containsRemoteIndex(indices)); + } + + public void testRemoteIndices() { + List indices = Collections.singletonList("local-index"); + assertThat(MlRemoteLicenseChecker.remoteIndices(indices), is(empty())); + indices = Arrays.asList("local-index", "remote-cluster:index1", "local-index2", "remote-cluster2:index1"); + assertThat(MlRemoteLicenseChecker.remoteIndices(indices), containsInAnyOrder("remote-cluster:index1", "remote-cluster2:index1")); + } + + public void testRemoteClusterNames() { + List indices = Arrays.asList("local-index1", "local-index2"); + assertThat(MlRemoteLicenseChecker.remoteClusterNames(indices), empty()); + indices = Arrays.asList("local-index1", "remote-cluster1:remote-index2"); + assertThat(MlRemoteLicenseChecker.remoteClusterNames(indices), contains("remote-cluster1")); + indices = Arrays.asList("remote-cluster1:index2", "index1", "remote-cluster2:index1"); + assertThat(MlRemoteLicenseChecker.remoteClusterNames(indices), contains("remote-cluster1", "remote-cluster2")); + indices = Arrays.asList("remote-cluster1:index2", "index1", "remote-cluster2:index1", "remote-cluster2:index2"); + assertThat(MlRemoteLicenseChecker.remoteClusterNames(indices), contains("remote-cluster1", "remote-cluster2")); + } + + public void testLicenseSupportsML() { + XPackInfoResponse.LicenseInfo licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "trial", "trial", + License.Status.ACTIVE, randomNonNegativeLong()); + assertTrue(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo)); + + licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "trial", "trial", License.Status.EXPIRED, randomNonNegativeLong()); + assertFalse(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo)); + + licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "GOLD", "GOLD", License.Status.ACTIVE, randomNonNegativeLong()); + assertFalse(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo)); + + licenseInfo = new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", License.Status.ACTIVE, randomNonNegativeLong()); + assertTrue(MlRemoteLicenseChecker.licenseSupportsML(licenseInfo)); + } + + public void testCheckRemoteClusterLicenses_givenValidLicenses() { + final AtomicInteger index = new AtomicInteger(0); + final List responses = new ArrayList<>(); + + Client client = createMockClient(); + doAnswer(invocationMock -> { + @SuppressWarnings("raw_types") + ActionListener listener = (ActionListener) invocationMock.getArguments()[2]; + listener.onResponse(responses.get(index.getAndIncrement())); + return null; + }).when(client).execute(same(XPackInfoAction.INSTANCE), any(), any()); + + + List remoteClusterNames = Arrays.asList("valid1", "valid2", "valid3"); + responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null)); + responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null)); + responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null)); + + MlRemoteLicenseChecker licenseChecker = new MlRemoteLicenseChecker(client); + AtomicReference licCheckResponse = new AtomicReference<>(); + + licenseChecker.checkRemoteClusterLicenses(remoteClusterNames, + new ActionListener() { + @Override + public void onResponse(MlRemoteLicenseChecker.LicenseViolation response) { + licCheckResponse.set(response); + } + + @Override + public void onFailure(Exception e) { + fail(e.getMessage()); + } + }); + + verify(client, times(3)).execute(same(XPackInfoAction.INSTANCE), any(), any()); + assertNotNull(licCheckResponse.get()); + assertFalse(licCheckResponse.get().isViolated()); + assertNull(licCheckResponse.get().get()); + } + + public void testCheckRemoteClusterLicenses_givenInvalidLicense() { + final AtomicInteger index = new AtomicInteger(0); + List remoteClusterNames = Arrays.asList("good", "cluster-with-basic-license", "good2"); + final List responses = new ArrayList<>(); + responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null)); + responses.add(new XPackInfoResponse(null, createBasicLicenseResponse(), null)); + responses.add(new XPackInfoResponse(null, createPlatinumLicenseResponse(), null)); + + Client client = createMockClient(); + doAnswer(invocationMock -> { + @SuppressWarnings("raw_types") + ActionListener listener = (ActionListener) invocationMock.getArguments()[2]; + listener.onResponse(responses.get(index.getAndIncrement())); + return null; + }).when(client).execute(same(XPackInfoAction.INSTANCE), any(), any()); + + MlRemoteLicenseChecker licenseChecker = new MlRemoteLicenseChecker(client); + AtomicReference licCheckResponse = new AtomicReference<>(); + + licenseChecker.checkRemoteClusterLicenses(remoteClusterNames, + new ActionListener() { + @Override + public void onResponse(MlRemoteLicenseChecker.LicenseViolation response) { + licCheckResponse.set(response); + } + + @Override + public void onFailure(Exception e) { + fail(e.getMessage()); + } + }); + + verify(client, times(2)).execute(same(XPackInfoAction.INSTANCE), any(), any()); + assertNotNull(licCheckResponse.get()); + assertTrue(licCheckResponse.get().isViolated()); + assertEquals("cluster-with-basic-license", licCheckResponse.get().get().getClusterName()); + assertEquals("BASIC", licCheckResponse.get().get().getLicenseInfo().getType()); + } + + public void testBuildErrorMessage() { + XPackInfoResponse.LicenseInfo platinumLicence = createPlatinumLicenseResponse(); + MlRemoteLicenseChecker.RemoteClusterLicenseInfo info = + new MlRemoteLicenseChecker.RemoteClusterLicenseInfo("platinum-cluster", platinumLicence); + assertEquals(Strings.toString(platinumLicence), MlRemoteLicenseChecker.buildErrorMessage(info)); + + XPackInfoResponse.LicenseInfo basicLicense = createBasicLicenseResponse(); + info = new MlRemoteLicenseChecker.RemoteClusterLicenseInfo("basic-cluster", basicLicense); + String expected = "The license mode [BASIC] on cluster [basic-cluster] does not enable Machine Learning. " + + Strings.toString(basicLicense); + assertEquals(expected, MlRemoteLicenseChecker.buildErrorMessage(info)); + + XPackInfoResponse.LicenseInfo expiredLicense = createExpiredLicenseResponse(); + info = new MlRemoteLicenseChecker.RemoteClusterLicenseInfo("expired-cluster", expiredLicense); + expected = "The license on cluster [expired-cluster] is not active. " + Strings.toString(expiredLicense); + assertEquals(expected, MlRemoteLicenseChecker.buildErrorMessage(info)); + } + + private Client createMockClient() { + Client client = mock(Client.class); + ThreadPool threadPool = mock(ThreadPool.class); + when(client.threadPool()).thenReturn(threadPool); + when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY)); + when(client.getRemoteClusterClient(anyString())).thenReturn(client); + return client; + } + + private XPackInfoResponse.LicenseInfo createPlatinumLicenseResponse() { + return new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", License.Status.ACTIVE, randomNonNegativeLong()); + } + + private XPackInfoResponse.LicenseInfo createBasicLicenseResponse() { + return new XPackInfoResponse.LicenseInfo("uid", "BASIC", "BASIC", License.Status.ACTIVE, randomNonNegativeLong()); + } + + private XPackInfoResponse.LicenseInfo createExpiredLicenseResponse() { + return new XPackInfoResponse.LicenseInfo("uid", "PLATINUM", "PLATINUM", License.Status.EXPIRED, randomNonNegativeLong()); + } +} From 75fd06c3b38b70cf68dc0b20fd72ee9fd5e88521 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 14 Jun 2018 09:52:46 +0200 Subject: [PATCH 67/71] Reenable Checkstyle's unused import rule (#31270) --- buildSrc/src/main/resources/checkstyle.xml | 10 +++------- .../documentation/MigrationDocumentationIT.java | 4 ---- .../analysis/common/SynonymsAnalysisTests.java | 2 -- .../common/ScriptProcessorFactoryTests.java | 1 - .../RangeFieldQueryStringQueryBuilderTests.java | 3 --- .../query/LegacyHasChildQueryBuilderTests.java | 1 - .../elasticsearch/percolator/QueryAnalyzer.java | 1 - .../documentation/ReindexDocumentationIT.java | 1 - .../transport/netty4/Netty4Transport.java | 1 - .../transport/netty4/NettyTcpChannel.java | 5 ----- .../netty4/Netty4HttpServerPipeliningTests.java | 1 - .../file/FileBasedUnicastHostsProvider.java | 1 - .../discovery/gce/GceDiscoveryPlugin.java | 1 - .../azure/blobstore/AzureBlobContainer.java | 1 - .../elasticsearch/bwc/QueryBuilderBWCIT.java | 3 --- .../action/bulk/TransportShardBulkAction.java | 1 - .../action/get/TransportGetAction.java | 1 - .../action/ingest/PutPipelineAction.java | 1 - .../search/TransportMultiSearchAction.java | 1 - .../org/elasticsearch/cluster/ClusterName.java | 1 - .../metadata/MetaDataMappingService.java | 1 - .../elasticsearch/common/CheckedRunnable.java | 2 -- .../common/geo/builders/ShapeBuilders.java | 1 - .../search/function/FunctionScoreQuery.java | 8 +++----- .../common/settings/IndexScopedSettings.java | 1 - .../elasticsearch/index/analysis/Analysis.java | 1 - .../engine/DeleteFailedEngineException.java | 3 +-- .../engine/IndexFailedEngineException.java | 4 +--- .../index/mapper/GeoShapeFieldMapper.java | 1 - .../index/mapper/MappedFieldType.java | 3 --- .../index/mapper/StringFieldType.java | 1 - .../index/mapper/TextFieldMapper.java | 5 ----- .../index/query/ScriptQueryBuilder.java | 1 - .../index/query/TypeQueryBuilder.java | 1 - .../elasticsearch/index/search/MatchQuery.java | 9 ++++----- .../index/shard/PrimaryReplicaSyncer.java | 1 - .../elasticsearch/index/shard/ShardPath.java | 2 -- .../action/RestFieldCapabilitiesAction.java | 6 ------ .../RestClusterUpdateSettingsAction.java | 1 - .../rest/action/search/RestSearchAction.java | 1 - .../search/DefaultSearchContext.java | 2 -- .../org/elasticsearch/search/SearchModule.java | 1 - .../MultiBucketConsumerService.java | 1 - .../AdjacencyMatrixAggregationBuilder.java | 1 - .../bucket/composite/CompositeAggregator.java | 17 ++++++++--------- .../search/builder/SearchSourceBuilder.java | 1 - .../subphase/highlight/HighlightPhase.java | 1 - .../org/elasticsearch/monitor/jvm/JvmPid.java | 2 -- .../admin/indices/create/CreateIndexIT.java | 6 ------ .../QueueResizingEsThreadPoolExecutorTests.java | 1 - .../fielddata/AbstractFieldDataTestCase.java | 2 -- .../index/mapper/DynamicMappingIT.java | 6 ------ .../index/mapper/NestedObjectMapperTests.java | 3 +-- .../index/mapper/UidFieldTypeTests.java | 7 ------- .../MatchPhrasePrefixQueryBuilderTests.java | 1 - .../query/MultiMatchQueryBuilderTests.java | 1 - .../indices/mapping/SimpleGetMappingsIT.java | 1 - .../RestFieldCapabilitiesActionTests.java | 3 --- .../search/aggregations/EquivalenceIT.java | 2 -- .../bucket/terms/TermsAggregatorTests.java | 4 ---- .../metrics/InternalStatsTests.java | 3 --- .../builder/SearchSourceBuilderTests.java | 1 - .../search/preference/SearchPreferenceIT.java | 1 - .../search/query/QueryStringIT.java | 1 - .../search/query/SearchQueryIT.java | 7 ------- .../search/query/SimpleQueryStringIT.java | 1 - .../search/scroll/SearchScrollIT.java | 1 - .../search/slice/SliceBuilderTests.java | 1 - .../elasticsearch/snapshots/RepositoriesIT.java | 1 - .../ESIndexLevelReplicationTestCase.java | 1 - .../index/shard/IndexShardTestCase.java | 1 - .../elasticsearch/test/ExternalTestCluster.java | 1 - .../elasticsearch/test/TestSearchContext.java | 3 --- .../transport/nio/WriteOperation.java | 1 - .../nio/channel/AbstractNioChannel.java | 1 - .../test/test/InternalTestClusterTests.java | 2 -- .../license/GetBasicStatusRequest.java | 3 --- .../license/GetTrialStatusRequest.java | 3 --- .../datafeed/MlRemoteLicenseCheckerTests.java | 1 - .../esnative/ESNativeRealmMigrateToolTests.java | 1 - .../authc/ldap/support/LdapTestCase.java | 1 - .../authc/saml/SamlAuthenticatorTests.java | 1 - .../transport/ssl/SslIntegrationTests.java | 1 - .../watcher/execution/ExecutionService.java | 1 - .../rest/action/RestWatchServiceAction.java | 2 -- .../xpack/ml/integration/DetectionRulesIT.java | 1 - .../xpack/ml/integration/ScheduledEventsIT.java | 1 - .../upgrades/IndexAuditUpgradeIT.java | 5 ----- .../UpgradeClusterClientYamlTestSuiteIT.java | 7 ------- .../SmokeTestMonitoringWithSecurityIT.java | 2 +- .../elasticsearch/test/TribeWithSecurityIT.java | 6 ------ 91 files changed, 22 insertions(+), 198 deletions(-) diff --git a/buildSrc/src/main/resources/checkstyle.xml b/buildSrc/src/main/resources/checkstyle.xml index 891a85d50a930..033f020fde0fa 100644 --- a/buildSrc/src/main/resources/checkstyle.xml +++ b/buildSrc/src/main/resources/checkstyle.xml @@ -26,13 +26,9 @@ - + + + diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MigrationDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MigrationDocumentationIT.java index 6ac7e364dd812..b56fb3359ffae 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MigrationDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MigrationDocumentationIT.java @@ -30,10 +30,6 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.Response; import org.elasticsearch.client.RestHighLevelClient; -import org.apache.http.HttpEntity; -import org.apache.http.HttpStatus; -import org.apache.http.entity.ContentType; -import org.apache.http.nio.entity.NStringEntity; import org.elasticsearch.cluster.health.ClusterHealthStatus; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java index 3c602c1713b2e..8d89b67d06117 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/SynonymsAnalysisTests.java @@ -22,10 +22,8 @@ import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; -import org.apache.lucene.queryparser.classic.ParseException; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.lucene.all.AllTokenStream; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; diff --git a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorFactoryTests.java b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorFactoryTests.java index eb0fa9a6e2071..79c1cf1e0bc47 100644 --- a/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorFactoryTests.java +++ b/modules/ingest-common/src/test/java/org/elasticsearch/ingest/common/ScriptProcessorFactoryTests.java @@ -20,7 +20,6 @@ package org.elasticsearch.ingest.common; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.common.xcontent.XContentParseException; import org.elasticsearch.script.Script; import org.elasticsearch.script.ScriptException; import org.elasticsearch.script.ScriptService; diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RangeFieldQueryStringQueryBuilderTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RangeFieldQueryStringQueryBuilderTests.java index 090476703dbdd..442822e11ca22 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RangeFieldQueryStringQueryBuilderTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RangeFieldQueryStringQueryBuilderTests.java @@ -21,7 +21,6 @@ import org.apache.lucene.document.DoubleRange; import org.apache.lucene.document.FloatRange; -import org.apache.lucene.document.InetAddressPoint; import org.apache.lucene.document.InetAddressRange; import org.apache.lucene.document.IntRange; import org.apache.lucene.document.LongRange; @@ -29,12 +28,10 @@ import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.PointRangeQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.common.Strings; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.joda.DateMathParser; -import org.elasticsearch.common.joda.Joda; import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryStringQueryBuilder; diff --git a/modules/parent-join/src/test/java/org/elasticsearch/join/query/LegacyHasChildQueryBuilderTests.java b/modules/parent-join/src/test/java/org/elasticsearch/join/query/LegacyHasChildQueryBuilderTests.java index 15e7a7cca2f03..2e6deae470863 100644 --- a/modules/parent-join/src/test/java/org/elasticsearch/join/query/LegacyHasChildQueryBuilderTests.java +++ b/modules/parent-join/src/test/java/org/elasticsearch/join/query/LegacyHasChildQueryBuilderTests.java @@ -33,7 +33,6 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.Version; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; -import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.settings.Settings; diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/QueryAnalyzer.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/QueryAnalyzer.java index 3855ce53403ca..7c987517ad29a 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/QueryAnalyzer.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/QueryAnalyzer.java @@ -55,7 +55,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; diff --git a/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java b/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java index 5707bede69e40..4c1545913024f 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java +++ b/modules/reindex/src/test/java/org/elasticsearch/client/documentation/ReindexDocumentationIT.java @@ -28,7 +28,6 @@ import org.elasticsearch.index.reindex.BulkByScrollTask; import org.elasticsearch.index.reindex.DeleteByQueryAction; import org.elasticsearch.index.reindex.ReindexAction; -import org.elasticsearch.index.reindex.ReindexRequestBuilder; import org.elasticsearch.index.reindex.RethrottleAction; import org.elasticsearch.index.reindex.UpdateByQueryAction; import org.elasticsearch.index.reindex.UpdateByQueryRequestBuilder; diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java index 87a09ad8ee3a0..ef5a73c8c8a15 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/Netty4Transport.java @@ -56,7 +56,6 @@ import org.elasticsearch.common.util.concurrent.EsExecutors; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.threadpool.ThreadPool; -import org.elasticsearch.transport.TcpChannel; import org.elasticsearch.transport.TcpTransport; import org.elasticsearch.transport.TransportRequestOptions; diff --git a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/NettyTcpChannel.java b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/NettyTcpChannel.java index 17c18f15ae15c..aa93b8bfa0579 100644 --- a/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/NettyTcpChannel.java +++ b/modules/transport-netty4/src/main/java/org/elasticsearch/transport/netty4/NettyTcpChannel.java @@ -20,19 +20,14 @@ package org.elasticsearch.transport.netty4; import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPromise; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.util.Supplier; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.transport.TcpChannel; import org.elasticsearch.transport.TransportException; import java.net.InetSocketAddress; -import java.nio.channels.ClosedSelectorException; import java.util.concurrent.CompletableFuture; public class NettyTcpChannel implements TcpChannel { diff --git a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java index 1c7475379bb87..4f96f185a8abf 100644 --- a/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java +++ b/modules/transport-netty4/src/test/java/org/elasticsearch/http/netty4/Netty4HttpServerPipeliningTests.java @@ -32,7 +32,6 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; -import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; diff --git a/plugins/discovery-file/src/main/java/org/elasticsearch/discovery/file/FileBasedUnicastHostsProvider.java b/plugins/discovery-file/src/main/java/org/elasticsearch/discovery/file/FileBasedUnicastHostsProvider.java index ee5f6c08b91ce..5a3b26e76f722 100644 --- a/plugins/discovery-file/src/main/java/org/elasticsearch/discovery/file/FileBasedUnicastHostsProvider.java +++ b/plugins/discovery-file/src/main/java/org/elasticsearch/discovery/file/FileBasedUnicastHostsProvider.java @@ -23,7 +23,6 @@ import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.component.AbstractComponent; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.discovery.zen.UnicastHostsProvider; import org.elasticsearch.env.Environment; diff --git a/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java b/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java index 552925c0f386d..a8a81a4561718 100644 --- a/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java +++ b/plugins/discovery-gce/src/main/java/org/elasticsearch/plugin/discovery/gce/GceDiscoveryPlugin.java @@ -29,7 +29,6 @@ import org.elasticsearch.cloud.gce.GceMetadataService; import org.elasticsearch.cloud.gce.network.GceNameResolver; import org.elasticsearch.cloud.gce.util.Access; -import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkService; import org.elasticsearch.common.settings.Setting; diff --git a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java index 8cb0c8c803df5..36b56a114a729 100644 --- a/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java +++ b/plugins/repository-azure/src/main/java/org/elasticsearch/cloud/azure/blobstore/AzureBlobContainer.java @@ -32,7 +32,6 @@ import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URISyntaxException; -import java.nio.file.FileAlreadyExistsException; import java.nio.file.NoSuchFileException; import java.util.Map; diff --git a/qa/query-builder-bwc/src/test/java/org/elasticsearch/bwc/QueryBuilderBWCIT.java b/qa/query-builder-bwc/src/test/java/org/elasticsearch/bwc/QueryBuilderBWCIT.java index 284a45fa3055e..2d3f55ab94bb4 100644 --- a/qa/query-builder-bwc/src/test/java/org/elasticsearch/bwc/QueryBuilderBWCIT.java +++ b/qa/query-builder-bwc/src/test/java/org/elasticsearch/bwc/QueryBuilderBWCIT.java @@ -19,9 +19,6 @@ package org.elasticsearch.bwc; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils; import org.elasticsearch.Version; import org.elasticsearch.client.Request; diff --git a/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java b/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java index 99d3f825247a9..3e1cca07918e1 100644 --- a/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java +++ b/server/src/main/java/org/elasticsearch/action/bulk/TransportShardBulkAction.java @@ -29,7 +29,6 @@ import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.support.ActionFilters; -import org.elasticsearch.action.support.TransportActions; import org.elasticsearch.action.support.replication.ReplicationOperation; import org.elasticsearch.action.support.replication.ReplicationResponse.ShardInfo; import org.elasticsearch.action.support.replication.TransportReplicationAction; diff --git a/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java b/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java index 884af4a3af998..5b24da2a2edc5 100644 --- a/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java +++ b/server/src/main/java/org/elasticsearch/action/get/TransportGetAction.java @@ -25,7 +25,6 @@ import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; -import org.elasticsearch.cluster.routing.Preference; import org.elasticsearch.cluster.routing.ShardIterator; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.inject.Inject; diff --git a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineAction.java b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineAction.java index 8f4b4170f51b1..cdf0191e85fe7 100644 --- a/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineAction.java +++ b/server/src/main/java/org/elasticsearch/action/ingest/PutPipelineAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.action.ingest; import org.elasticsearch.action.Action; -import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.ElasticsearchClient; public class PutPipelineAction extends Action { diff --git a/server/src/main/java/org/elasticsearch/action/search/TransportMultiSearchAction.java b/server/src/main/java/org/elasticsearch/action/search/TransportMultiSearchAction.java index 343c84c970388..dea5e73b72b9c 100644 --- a/server/src/main/java/org/elasticsearch/action/search/TransportMultiSearchAction.java +++ b/server/src/main/java/org/elasticsearch/action/search/TransportMultiSearchAction.java @@ -34,7 +34,6 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; -import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterName.java b/server/src/main/java/org/elasticsearch/cluster/ClusterName.java index a81d02897ec4d..36676300954e7 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterName.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterName.java @@ -22,7 +22,6 @@ 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.logging.Loggers; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java index 0e2bc3ad1ba87..70b6f7472ca99 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java @@ -21,7 +21,6 @@ import com.carrotsearch.hppc.cursors.ObjectCursor; import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingClusterStateUpdateRequest; diff --git a/server/src/main/java/org/elasticsearch/common/CheckedRunnable.java b/server/src/main/java/org/elasticsearch/common/CheckedRunnable.java index 196eb53a878d5..721d7be14b4aa 100644 --- a/server/src/main/java/org/elasticsearch/common/CheckedRunnable.java +++ b/server/src/main/java/org/elasticsearch/common/CheckedRunnable.java @@ -19,8 +19,6 @@ package org.elasticsearch.common; -import java.lang.Runnable; - /** * A {@link Runnable}-like interface which allows throwing checked exceptions. */ diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilders.java b/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilders.java index d5aab8f988d34..a4e67baeec48e 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilders.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilders.java @@ -22,7 +22,6 @@ import java.util.List; import org.locationtech.jts.geom.Coordinate; -import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; /** * A collection of static methods for creating ShapeBuilders. diff --git a/server/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java b/server/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java index f7735dd8197ac..c2263fc201e18 100644 --- a/server/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java +++ b/server/src/main/java/org/elasticsearch/common/lucene/search/function/FunctionScoreQuery.java @@ -30,8 +30,6 @@ import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.Weight; import org.apache.lucene.util.Bits; -import org.apache.lucene.search.TopDocsCollector; -import org.apache.lucene.search.TopScoreDocCollector; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -376,9 +374,9 @@ public float score() throws IOException { double factor = computeScore(docId, subQueryScore); float finalScore = scoreCombiner.combine(subQueryScore, factor, maxBoost); if (finalScore == Float.NEGATIVE_INFINITY || Float.isNaN(finalScore)) { - /** - * These scores are invalid for score based {@link TopDocsCollector}s. - * See {@link TopScoreDocCollector} for details. + /* + These scores are invalid for score based {@link org.apache.lucene.search.TopDocsCollector}s. + See {@link org.apache.lucene.search.TopScoreDocCollector} for details. */ throw new ElasticsearchException("function score query returned an invalid score: " + finalScore + " for doc: " + docId); } diff --git a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index f58ce3eead937..eded62c36ebc7 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -36,7 +36,6 @@ import org.elasticsearch.index.fielddata.IndexFieldDataService; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.seqno.LocalCheckpointTracker; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.index.store.FsDirectoryService; import org.elasticsearch.index.store.Store; diff --git a/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java b/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java index dad3768189f24..d3325c91c4189 100644 --- a/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java +++ b/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java @@ -56,7 +56,6 @@ import org.apache.lucene.analysis.tr.TurkishAnalyzer; import org.apache.lucene.util.Version; import org.elasticsearch.common.Strings; -import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.lucene.Lucene; diff --git a/server/src/main/java/org/elasticsearch/index/engine/DeleteFailedEngineException.java b/server/src/main/java/org/elasticsearch/index/engine/DeleteFailedEngineException.java index 8cad7823cb45d..6541642200ddd 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/DeleteFailedEngineException.java +++ b/server/src/main/java/org/elasticsearch/index/engine/DeleteFailedEngineException.java @@ -20,7 +20,6 @@ package org.elasticsearch.index.engine; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.index.shard.ShardId; import java.io.IOException; @@ -34,4 +33,4 @@ public class DeleteFailedEngineException extends EngineException { public DeleteFailedEngineException(StreamInput in) throws IOException{ super(in); } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/elasticsearch/index/engine/IndexFailedEngineException.java b/server/src/main/java/org/elasticsearch/index/engine/IndexFailedEngineException.java index cd5e8a474066f..ea412b1aff025 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/IndexFailedEngineException.java +++ b/server/src/main/java/org/elasticsearch/index/engine/IndexFailedEngineException.java @@ -21,10 +21,8 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.index.shard.ShardId; import java.io.IOException; -import java.util.Objects; /** * Deprecated as not used in 6.0, should be removed in 7.0 @@ -58,4 +56,4 @@ public String type() { public String id() { return this.id; } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java index b4a6840050896..d5089c7be0e6f 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java @@ -30,7 +30,6 @@ import org.apache.lucene.spatial.prefix.tree.PackedQuadPrefixTree; import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree; import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.common.Explicit; import org.elasticsearch.common.geo.GeoUtils; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java index e21a8a0f29791..ea199cfa2c702 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java @@ -30,7 +30,6 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermInSetQuery; @@ -39,8 +38,6 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.joda.DateMathParser; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.lucene.all.AllTermQuery; import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.index.analysis.NamedAnalyzer; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java index b19afe34bbe93..a4dde020f81c7 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/StringFieldType.java @@ -24,7 +24,6 @@ import java.util.List; import org.apache.lucene.analysis.TokenStream; -import org.apache.lucene.analysis.shingle.FixedShingleFilter; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; import org.apache.lucene.index.Term; 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 c4743d97dbff7..ffa8651692789 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java @@ -28,22 +28,17 @@ 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.analysis.tokenattributes.TermToBytesRefAttribute; import org.apache.lucene.document.Field; -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.ConstantScoreQuery; -import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.NormsFieldExistsQuery; -import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.elasticsearch.Version; import org.elasticsearch.common.collect.Iterators; -import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.ESLoggerFactory; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; diff --git a/server/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java index c71762a2104d7..cb92b93685e8c 100644 --- a/server/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java @@ -36,7 +36,6 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.script.FilterScript; import org.elasticsearch.script.Script; -import org.elasticsearch.script.SearchScript; import java.io.IOException; import java.util.Objects; diff --git a/server/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java index 365806ab10c30..d65e319aa3283 100644 --- a/server/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/TypeQueryBuilder.java @@ -27,7 +27,6 @@ import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; -import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.mapper.DocumentMapper; 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 6ba69b463b28e..b20edeccd1b3f 100644 --- a/server/src/main/java/org/elasticsearch/index/search/MatchQuery.java +++ b/server/src/main/java/org/elasticsearch/index/search/MatchQuery.java @@ -54,7 +54,6 @@ import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.unit.Fuzziness; -import org.elasticsearch.index.analysis.ShingleTokenFilterFactory; import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.query.QueryShardContext; @@ -65,7 +64,7 @@ import static org.elasticsearch.common.lucene.search.Queries.newLenientFieldQuery; import static org.elasticsearch.common.lucene.search.Queries.newUnmappedFieldQuery; -public class MatchQuery { +public class MatchQuery { private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(MappedFieldType.class)); @@ -418,9 +417,9 @@ protected Query createFieldQuery(Analyzer analyzer, BooleanClause.Occur operator // query based on the analysis chain. try (TokenStream source = analyzer.tokenStream(field, queryText)) { if (source.hasAttribute(DisableGraphAttribute.class)) { - /** - * A {@link TokenFilter} in this {@link TokenStream} disabled the graph analysis to avoid - * paths explosion. See {@link ShingleTokenFilterFactory} for details. + /* + A {@link TokenFilter} in this {@link TokenStream} disabled the graph analysis to avoid + paths explosion. See {@link org.elasticsearch.index.analysis.ShingleTokenFilterFactory} for details. */ setEnableGraphQueries(false); } diff --git a/server/src/main/java/org/elasticsearch/index/shard/PrimaryReplicaSyncer.java b/server/src/main/java/org/elasticsearch/index/shard/PrimaryReplicaSyncer.java index 8e05e7bf08efa..b39ebd51f2bc8 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/PrimaryReplicaSyncer.java +++ b/server/src/main/java/org/elasticsearch/index/shard/PrimaryReplicaSyncer.java @@ -35,7 +35,6 @@ import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.index.seqno.SeqNoStats; import org.elasticsearch.index.seqno.SequenceNumbers; import org.elasticsearch.index.translog.Translog; import org.elasticsearch.tasks.Task; diff --git a/server/src/main/java/org/elasticsearch/index/shard/ShardPath.java b/server/src/main/java/org/elasticsearch/index/shard/ShardPath.java index 99c1e96cfb793..9f26c817b602c 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/ShardPath.java +++ b/server/src/main/java/org/elasticsearch/index/shard/ShardPath.java @@ -33,9 +33,7 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; public final class ShardPath { public static final String INDEX_FOLDER_NAME = "index"; diff --git a/server/src/main/java/org/elasticsearch/rest/action/RestFieldCapabilitiesAction.java b/server/src/main/java/org/elasticsearch/rest/action/RestFieldCapabilitiesAction.java index 4c477334265f6..28e10c6e54325 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/RestFieldCapabilitiesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/RestFieldCapabilitiesAction.java @@ -20,25 +20,19 @@ package org.elasticsearch.rest.action; import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest; -import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.BaseRestHandler; -import org.elasticsearch.rest.BytesRestResponse; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.RestResponse; -import org.elasticsearch.rest.RestStatus; import java.io.IOException; import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestRequest.Method.POST; -import static org.elasticsearch.rest.RestStatus.OK; public class RestFieldCapabilitiesAction extends BaseRestHandler { public RestFieldCapabilitiesAction(Settings settings, RestController controller) { diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java index 2901cdd2d9ba8..4eb5bbe2a8443 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/cluster/RestClusterUpdateSettingsAction.java @@ -22,7 +22,6 @@ import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; import org.elasticsearch.client.Requests; import org.elasticsearch.client.node.NodeClient; -import org.elasticsearch.common.ParseField; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.rest.BaseRestHandler; diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index b018706fff093..6f26ccce9b476 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -19,7 +19,6 @@ package org.elasticsearch.rest.action.search; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.node.NodeClient; diff --git a/server/src/main/java/org/elasticsearch/search/DefaultSearchContext.java b/server/src/main/java/org/elasticsearch/search/DefaultSearchContext.java index 996cfb4f60937..8c3b9769b1b3c 100644 --- a/server/src/main/java/org/elasticsearch/search/DefaultSearchContext.java +++ b/server/src/main/java/org/elasticsearch/search/DefaultSearchContext.java @@ -24,7 +24,6 @@ import org.apache.lucene.search.Collector; import org.apache.lucene.search.FieldDoc; import org.apache.lucene.search.Query; -import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.Counter; import org.elasticsearch.Version; import org.elasticsearch.action.search.SearchTask; @@ -83,7 +82,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; final class DefaultSearchContext extends SearchContext { diff --git a/server/src/main/java/org/elasticsearch/search/SearchModule.java b/server/src/main/java/org/elasticsearch/search/SearchModule.java index 1f448e6217fbb..8188b69d6c046 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchModule.java +++ b/server/src/main/java/org/elasticsearch/search/SearchModule.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.NamedRegistry; import org.elasticsearch.common.geo.GeoShapeType; import org.elasticsearch.common.geo.ShapesAvailability; -import org.elasticsearch.common.geo.builders.ShapeBuilders; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry; import org.elasticsearch.common.io.stream.Writeable; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java b/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java index 2296b5dcee6f6..295a59c919fc4 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/MultiBucketConsumerService.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java index daabd36222393..5612f32233f93 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java @@ -30,7 +30,6 @@ import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.MultiBucketAggregationBuilder; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java index 4484aa494b4e1..c9380efdf894a 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeAggregator.java @@ -25,7 +25,6 @@ import org.apache.lucene.search.CollectionTerminatedException; import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; -import org.apache.lucene.search.MultiCollector; import org.apache.lucene.search.Query; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -147,20 +146,20 @@ protected LeafBucketCollector getLeafCollector(LeafReaderContext ctx, LeafBucket finishLeaf(); boolean fillDocIdSet = deferredCollectors != NO_OP_COLLECTOR; if (sortedDocsProducer != null) { - /** - * The producer will visit documents sorted by the leading source of the composite definition - * and terminates when the leading source value is guaranteed to be greater than the lowest - * composite bucket in the queue. + /* + The producer will visit documents sorted by the leading source of the composite definition + and terminates when the leading source value is guaranteed to be greater than the lowest + composite bucket in the queue. */ DocIdSet docIdSet = sortedDocsProducer.processLeaf(context.query(), queue, ctx, fillDocIdSet); if (fillDocIdSet) { entries.add(new Entry(ctx, docIdSet)); } - /** - * We can bypass search entirely for this segment, all the processing has been done in the previous call. - * Throwing this exception will terminate the execution of the search for this root aggregation, - * see {@link MultiCollector} for more details on how we handle early termination in aggregations. + /* + We can bypass search entirely for this segment, all the processing has been done in the previous call. + Throwing this exception will terminate the execution of the search for this root aggregation, + see {@link org.apache.lucene.search.MultiCollector} for more details on how we handle early termination in aggregations. */ throw new CollectionTerminatedException(); } else { diff --git a/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index cccfb030a0bcd..5240cd2cf854f 100644 --- a/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -20,7 +20,6 @@ package org.elasticsearch.search.builder; import com.fasterxml.jackson.core.JsonParseException; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightPhase.java index 4343a1ebca564..e5ff7abc68b34 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightPhase.java @@ -23,7 +23,6 @@ import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.mapper.DocumentMapper; import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.SourceFieldMapper; diff --git a/server/src/main/java9/org/elasticsearch/monitor/jvm/JvmPid.java b/server/src/main/java9/org/elasticsearch/monitor/jvm/JvmPid.java index 5ce8959601798..c19a14d836422 100644 --- a/server/src/main/java9/org/elasticsearch/monitor/jvm/JvmPid.java +++ b/server/src/main/java9/org/elasticsearch/monitor/jvm/JvmPid.java @@ -19,8 +19,6 @@ package org.elasticsearch.monitor.jvm; -import java.lang.ProcessHandle; - class JvmPid { static long getPid() { diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java b/server/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java index b55c973c4463e..717f488b4fc29 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/create/CreateIndexIT.java @@ -21,7 +21,6 @@ import com.carrotsearch.hppc.cursors.ObjectCursor; import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.UnavailableShardsException; import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; @@ -39,16 +38,11 @@ import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.index.query.RangeQueryBuilder; -import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.ESIntegTestCase.Scope; -import org.elasticsearch.test.InternalSettingsPlugin; import org.elasticsearch.test.InternalTestCluster; -import org.elasticsearch.test.VersionUtils; -import java.util.Arrays; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Set; diff --git a/server/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java b/server/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java index 125cb572ea54d..25e3f9f09da3a 100644 --- a/server/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java +++ b/server/src/test/java/org/elasticsearch/common/util/concurrent/QueueResizingEsThreadPoolExecutorTests.java @@ -24,7 +24,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.junit.annotations.TestLogging; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThan; diff --git a/server/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java b/server/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java index 482f8d90bedfd..0d7b2bec125b0 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/AbstractFieldDataTestCase.java @@ -31,7 +31,6 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.store.RAMDirectory; -import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexService; @@ -43,7 +42,6 @@ import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapper.BuilderContext; -import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.NumberFieldMapper; import org.elasticsearch.index.mapper.ParentFieldMapper; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java b/server/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java index 5ee0740505cb8..e483bf9071ebf 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DynamicMappingIT.java @@ -20,15 +20,11 @@ import org.elasticsearch.Version; import org.elasticsearch.action.DocWriteResponse; -import org.elasticsearch.action.admin.indices.get.GetIndexResponse; import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse; -import org.elasticsearch.action.bulk.BulkItemResponse; import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.indices.TypeMissingException; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; @@ -41,8 +37,6 @@ import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.instanceOf; public class DynamicMappingIT extends ESIntegTestCase { diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java index e495a6f60d315..30125ad0670f6 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java @@ -19,8 +19,6 @@ package org.elasticsearch.index.mapper; -import java.util.HashMap; -import java.util.HashSet; import org.apache.lucene.index.IndexableField; import org.elasticsearch.Version; import org.elasticsearch.common.Strings; @@ -39,6 +37,7 @@ import java.io.UncheckedIOException; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.function.Function; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java index 174f09a2eee90..9fd4360b22fd9 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/UidFieldTypeTests.java @@ -25,20 +25,13 @@ import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.UUIDs; -import org.elasticsearch.common.breaker.CircuitBreaker; -import org.elasticsearch.common.breaker.NoopCircuitBreaker; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.IndexFieldDataCache; -import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.mockito.Mockito; -import java.io.IOException; import java.util.Collection; import java.util.Collections; diff --git a/server/src/test/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilderTests.java index f2f62411e5f30..d928fc118bb23 100644 --- a/server/src/test/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/MatchPhrasePrefixQueryBuilderTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.query; -import com.carrotsearch.randomizedtesting.annotations.Seed; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.MatchNoDocsQuery; 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 6d390ea8aeba8..adf239b993bba 100644 --- a/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/MultiMatchQueryBuilderTests.java @@ -33,7 +33,6 @@ import org.apache.lucene.search.PointRangeQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; -import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.lucene.all.AllTermQuery; diff --git a/server/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java b/server/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java index da6691660d8f7..80816d70fd731 100644 --- a/server/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java +++ b/server/src/test/java/org/elasticsearch/indices/mapping/SimpleGetMappingsIT.java @@ -27,7 +27,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESIntegTestCase; -import org.elasticsearch.test.ESIntegTestCase.ClusterScope; import org.elasticsearch.test.InternalSettingsPlugin; import java.io.IOException; diff --git a/server/src/test/java/org/elasticsearch/rest/action/RestFieldCapabilitiesActionTests.java b/server/src/test/java/org/elasticsearch/rest/action/RestFieldCapabilitiesActionTests.java index b8dd007f56729..625a6b32b5cbc 100644 --- a/server/src/test/java/org/elasticsearch/rest/action/RestFieldCapabilitiesActionTests.java +++ b/server/src/test/java/org/elasticsearch/rest/action/RestFieldCapabilitiesActionTests.java @@ -25,14 +25,11 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.rest.action.RestFieldCapabilitiesAction; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; -import org.elasticsearch.usage.UsageService; import org.junit.Before; import java.io.IOException; -import java.util.Collections; import static org.mockito.Mockito.mock; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java index e82fba589abb8..02b3632d2e8ca 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/EquivalenceIT.java @@ -41,8 +41,6 @@ import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregatorFactory; import org.elasticsearch.search.aggregations.metrics.sum.Sum; import org.elasticsearch.test.ESIntegTestCase; -import org.junit.After; -import org.junit.Before; import java.util.ArrayList; import java.util.Collection; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java index 63026a1255745..ed1eb6fb5c1f0 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java @@ -30,11 +30,9 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexableField; import org.apache.lucene.index.RandomIndexWriter; -import org.apache.lucene.index.Term; import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; -import org.apache.lucene.search.TermQuery; import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.NumericUtils; @@ -42,7 +40,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.MockBigArrays; import org.elasticsearch.common.util.MockPageCacheRecycler; -import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.IpFieldMapper; import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; @@ -67,7 +64,6 @@ import org.elasticsearch.search.aggregations.bucket.global.InternalGlobal; import org.elasticsearch.search.aggregations.bucket.nested.InternalNested; import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregator; import org.elasticsearch.search.aggregations.metrics.tophits.InternalTopHits; import org.elasticsearch.search.aggregations.metrics.tophits.TopHitsAggregationBuilder; import org.elasticsearch.search.aggregations.support.ValueType; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java index ab3e8564f8ed6..a79a3f0064263 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java @@ -18,7 +18,6 @@ */ package org.elasticsearch.search.aggregations.metrics; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -28,9 +27,7 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.InternalAggregationTestCase; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; diff --git a/server/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java b/server/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java index 50580ed2a934e..5c5a82eba8147 100644 --- a/server/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.builder; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.BytesStreamOutput; diff --git a/server/src/test/java/org/elasticsearch/search/preference/SearchPreferenceIT.java b/server/src/test/java/org/elasticsearch/search/preference/SearchPreferenceIT.java index 6478446a1a254..c3bce3f8f03a1 100644 --- a/server/src/test/java/org/elasticsearch/search/preference/SearchPreferenceIT.java +++ b/server/src/test/java/org/elasticsearch/search/preference/SearchPreferenceIT.java @@ -44,7 +44,6 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThanOrEqualTo; diff --git a/server/src/test/java/org/elasticsearch/search/query/QueryStringIT.java b/server/src/test/java/org/elasticsearch/search/query/QueryStringIT.java index 019322f6aaa84..f5c6834bdbfd2 100644 --- a/server/src/test/java/org/elasticsearch/search/query/QueryStringIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/QueryStringIT.java @@ -29,7 +29,6 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.QueryStringQueryBuilder; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; diff --git a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java index e8b443b39c903..c8a339315f542 100644 --- a/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/SearchQueryIT.java @@ -19,10 +19,8 @@ package org.elasticsearch.search.query; -import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.util.English; import org.elasticsearch.Version; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.search.SearchPhaseExecutionException; @@ -36,12 +34,8 @@ import org.elasticsearch.index.query.MatchQueryBuilder; import org.elasticsearch.index.query.MultiMatchQueryBuilder; import org.elasticsearch.index.query.Operator; -import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.RangeQueryBuilder; -import org.elasticsearch.index.query.SpanMultiTermQueryBuilder; -import org.elasticsearch.index.query.SpanNearQueryBuilder; -import org.elasticsearch.index.query.SpanTermQueryBuilder; import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.index.query.WrapperQueryBuilder; import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders; @@ -59,7 +53,6 @@ import org.joda.time.format.ISODateTimeFormat; import java.io.IOException; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Random; diff --git a/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java b/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java index 6f248b0142c97..22d12b5534155 100644 --- a/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java +++ b/server/src/test/java/org/elasticsearch/search/query/SimpleQueryStringIT.java @@ -31,7 +31,6 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.analysis.PreConfiguredTokenFilter; -import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.Operator; import org.elasticsearch.index.query.QueryBuilders; diff --git a/server/src/test/java/org/elasticsearch/search/scroll/SearchScrollIT.java b/server/src/test/java/org/elasticsearch/search/scroll/SearchScrollIT.java index 7dd6e3fe86e4f..c91462c494a3d 100644 --- a/server/src/test/java/org/elasticsearch/search/scroll/SearchScrollIT.java +++ b/server/src/test/java/org/elasticsearch/search/scroll/SearchScrollIT.java @@ -19,7 +19,6 @@ package org.elasticsearch.search.scroll; -import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.search.ClearScrollResponse; import org.elasticsearch.action.search.SearchRequestBuilder; diff --git a/server/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java b/server/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java index 4a061a1e90e73..b93ebc1adde72 100644 --- a/server/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/search/slice/SliceBuilderTests.java @@ -55,7 +55,6 @@ import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.UidFieldMapper; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.index.shard.ShardId; diff --git a/server/src/test/java/org/elasticsearch/snapshots/RepositoriesIT.java b/server/src/test/java/org/elasticsearch/snapshots/RepositoriesIT.java index cf2fedc23926c..0bdff3116d909 100644 --- a/server/src/test/java/org/elasticsearch/snapshots/RepositoriesIT.java +++ b/server/src/test/java/org/elasticsearch/snapshots/RepositoriesIT.java @@ -40,7 +40,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertThrows; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; diff --git a/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java index 284c71b238e2b..70e8303043713 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/replication/ESIndexLevelReplicationTestCase.java @@ -74,7 +74,6 @@ import org.elasticsearch.threadpool.ThreadPool; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; diff --git a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java index 81d6ab6ca5238..00928bf6c84d5 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/shard/IndexShardTestCase.java @@ -70,7 +70,6 @@ import org.elasticsearch.index.translog.Translog; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService; -import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.indices.recovery.PeerRecoveryTargetService; import org.elasticsearch.indices.recovery.RecoveryFailedException; import org.elasticsearch.indices.recovery.RecoverySourceHandler; diff --git a/test/framework/src/main/java/org/elasticsearch/test/ExternalTestCluster.java b/test/framework/src/main/java/org/elasticsearch/test/ExternalTestCluster.java index 90d58786496f7..12375e66b297f 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ExternalTestCluster.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ExternalTestCluster.java @@ -25,7 +25,6 @@ import org.elasticsearch.action.admin.cluster.node.stats.NodeStats; import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.elasticsearch.client.Client; -import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.breaker.CircuitBreaker; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; diff --git a/test/framework/src/main/java/org/elasticsearch/test/TestSearchContext.java b/test/framework/src/main/java/org/elasticsearch/test/TestSearchContext.java index 64f190f402c2a..9d03383561614 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/TestSearchContext.java +++ b/test/framework/src/main/java/org/elasticsearch/test/TestSearchContext.java @@ -24,15 +24,12 @@ import org.apache.lucene.util.Counter; import org.elasticsearch.action.search.SearchTask; import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.cache.bitset.BitsetFilterCache; import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.IndexFieldDataService; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.ObjectMapper; diff --git a/test/framework/src/main/java/org/elasticsearch/transport/nio/WriteOperation.java b/test/framework/src/main/java/org/elasticsearch/transport/nio/WriteOperation.java index 1b2f2cfede4f0..0a2ef15b0815a 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/nio/WriteOperation.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/nio/WriteOperation.java @@ -22,7 +22,6 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefIterator; import org.elasticsearch.action.ActionListener; -import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.transport.nio.channel.NioSocketChannel; diff --git a/test/framework/src/main/java/org/elasticsearch/transport/nio/channel/AbstractNioChannel.java b/test/framework/src/main/java/org/elasticsearch/transport/nio/channel/AbstractNioChannel.java index 7b08d831df83e..046b4d4c53671 100644 --- a/test/framework/src/main/java/org/elasticsearch/transport/nio/channel/AbstractNioChannel.java +++ b/test/framework/src/main/java/org/elasticsearch/transport/nio/channel/AbstractNioChannel.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.net.InetSocketAddress; -import java.net.StandardSocketOptions; import java.nio.channels.ClosedChannelException; import java.nio.channels.NetworkChannel; import java.nio.channels.SelectableChannel; diff --git a/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java b/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java index cca7cf70df657..3426a6bf03a42 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/test/InternalTestClusterTests.java @@ -62,8 +62,6 @@ import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileExists; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFileNotExists; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.not; /** diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetBasicStatusRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetBasicStatusRequest.java index 18429f2d9c09a..53a973bd42e7b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetBasicStatusRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetBasicStatusRequest.java @@ -7,9 +7,6 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.MasterNodeReadRequest; -import org.elasticsearch.common.io.stream.StreamInput; - -import java.io.IOException; public class GetBasicStatusRequest extends MasterNodeReadRequest { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetTrialStatusRequest.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetTrialStatusRequest.java index 288fc353812ba..1ee317de32c37 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetTrialStatusRequest.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/license/GetTrialStatusRequest.java @@ -7,9 +7,6 @@ import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.support.master.MasterNodeReadRequest; -import org.elasticsearch.common.io.stream.StreamInput; - -import java.io.IOException; public class GetTrialStatusRequest extends MasterNodeReadRequest { diff --git a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java index 47d4d30a7c6e4..dfd7c886ebf42 100644 --- a/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java +++ b/x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/datafeed/MlRemoteLicenseCheckerTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.xpack.core.action.XPackInfoAction; -import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig; import java.util.ArrayList; import java.util.Arrays; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateToolTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateToolTests.java index dc6ed1be5a2c1..04558f148dae9 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateToolTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/ESNativeRealmMigrateToolTests.java @@ -18,7 +18,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.env.TestEnvironment; -import org.elasticsearch.test.SecuritySettingsSource; import org.elasticsearch.test.SecuritySettingsSourceField; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java index da017c460661b..1003324b4548e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ldap/support/LdapTestCase.java @@ -14,7 +14,6 @@ import com.unboundid.ldap.sdk.LDAPURL; import com.unboundid.ldap.sdk.SimpleBindRequest; -import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.support.PlainActionFuture; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.SecureString; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java index aed6d2456014c..7f79ae35adac2 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/saml/SamlAuthenticatorTests.java @@ -14,7 +14,6 @@ import org.apache.xml.security.keys.content.X509Data; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.common.CheckedConsumer; -import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.logging.Loggers; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java index bb0ba8e9e4513..1be64d3f4de89 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/transport/ssl/SslIntegrationTests.java @@ -43,7 +43,6 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java index 520ae2f2306de..f85f30133aa66 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/execution/ExecutionService.java @@ -65,7 +65,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java index eb8d98221b5f4..209e86baa6c87 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/rest/action/RestWatchServiceAction.java @@ -15,8 +15,6 @@ import org.elasticsearch.xpack.core.watcher.transport.actions.service.WatcherServiceRequest; import org.elasticsearch.xpack.watcher.rest.WatcherRestHandler; -import java.io.IOException; - import static org.elasticsearch.rest.RestRequest.Method.POST; import static org.elasticsearch.rest.RestRequest.Method.PUT; diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java index 47ced4a96dde8..886f8dcbef3bb 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/DetectionRulesIT.java @@ -24,7 +24,6 @@ import org.elasticsearch.xpack.core.ml.job.config.RuleConditionType; import org.elasticsearch.xpack.core.ml.job.results.AnomalyRecord; import org.junit.After; -import org.junit.Ignore; import java.io.IOException; import java.util.ArrayList; diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java index 8410075ff5e27..d44df6d85a965 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/ScheduledEventsIT.java @@ -20,7 +20,6 @@ import org.elasticsearch.xpack.core.ml.job.results.AnomalyRecord; import org.elasticsearch.xpack.core.ml.job.results.Bucket; import org.junit.After; -import org.junit.Ignore; import java.io.IOException; import java.time.Instant; diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java index d8c508d730cf2..10cc23d1e6935 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/IndexAuditUpgradeIT.java @@ -11,7 +11,6 @@ import org.elasticsearch.Version; import org.elasticsearch.client.Response; import org.elasticsearch.test.rest.yaml.ObjectPath; -import org.hamcrest.Matcher; import org.elasticsearch.common.Booleans; import org.hamcrest.Matchers; import org.junit.Before; @@ -20,13 +19,9 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.isOneOf; - public class IndexAuditUpgradeIT extends AbstractUpgradeTestCase { private Version minVersionInCluster; diff --git a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java index 6e76e6f7ed12e..963c1887cd223 100644 --- a/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java +++ b/x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/UpgradeClusterClientYamlTestSuiteIT.java @@ -15,7 +15,6 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate; -import org.elasticsearch.test.rest.yaml.ClientYamlTestResponse; import org.elasticsearch.test.rest.yaml.ObjectPath; import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase; import org.elasticsearch.xpack.test.rest.XPackRestTestHelper; @@ -24,14 +23,8 @@ import java.nio.charset.StandardCharsets; import java.util.Base64; -import java.util.HashMap; import java.util.Map; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static org.hamcrest.Matchers.is; - @TimeoutSuite(millis = 5 * TimeUnits.MINUTE) // to account for slow as hell VMs public class UpgradeClusterClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase { diff --git a/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java b/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java index f8d1dd5e2b717..6c999ca2a7291 100644 --- a/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java +++ b/x-pack/qa/smoke-test-plugins-ssl/src/test/java/org/elasticsearch/smoketest/SmokeTestMonitoringWithSecurityIT.java @@ -95,7 +95,7 @@ private boolean getMonitoringUsageExportersDefined() throws Exception { public void testHTTPExporterWithSSL() throws Exception { // Ensures that the exporter is actually on assertBusy(() -> assertThat("[_http] exporter is not defined", getMonitoringUsageExportersDefined(), is(true))); - + // Checks that the monitoring index templates have been installed assertBusy(() -> { GetIndexTemplatesResponse response = client().admin().indices().prepareGetTemplates(MONITORING_PATTERN).get(); diff --git a/x-pack/qa/tribe-tests-with-security/src/test/java/org/elasticsearch/test/TribeWithSecurityIT.java b/x-pack/qa/tribe-tests-with-security/src/test/java/org/elasticsearch/test/TribeWithSecurityIT.java index 650570297cdb2..0f23946ae81f5 100644 --- a/x-pack/qa/tribe-tests-with-security/src/test/java/org/elasticsearch/test/TribeWithSecurityIT.java +++ b/x-pack/qa/tribe-tests-with-security/src/test/java/org/elasticsearch/test/TribeWithSecurityIT.java @@ -8,8 +8,6 @@ import com.carrotsearch.hppc.cursors.ObjectCursor; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; -import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; -import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.io.stream.NotSerializableExceptionWrapper; @@ -23,7 +21,6 @@ import org.elasticsearch.xpack.core.security.action.role.PutRoleResponse; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.elasticsearch.xpack.core.security.client.SecurityClient; -import org.elasticsearch.xpack.security.Security; import org.junit.After; import org.junit.AfterClass; @@ -32,19 +29,16 @@ import java.net.InetSocketAddress; import java.net.URL; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout; import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_SECURITY_INDEX; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.core.IsCollectionContaining.hasItem; public class TribeWithSecurityIT extends SecurityIntegTestCase { From d52dbf96802ea798871113627ac2851f2c325608 Mon Sep 17 00:00:00 2001 From: Tom Veasey Date: Wed, 13 Jun 2018 13:12:53 +0100 Subject: [PATCH 68/71] [ML] Update test thresholds to account for changes to memory control (#31289) To avoid temporary failures, this also disables these tests until elastic/ml-cpp#122 is committed. --- .../integration/AutodetectMemoryLimitIT.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java index 4e0aa9c7e0613..e9ba002779e37 100644 --- a/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java +++ b/x-pack/qa/ml-native-tests/src/test/java/org/elasticsearch/xpack/ml/integration/AutodetectMemoryLimitIT.java @@ -37,6 +37,7 @@ public void cleanUpTest() { cleanUp(); } + @AwaitsFix(bugUrl = "https://github.com/elastic/ml-cpp/pulls/122") public void testTooManyPartitions() throws Exception { Detector.Builder detector = new Detector.Builder("count", null); detector.setPartitionFieldName("user"); @@ -77,11 +78,12 @@ public void testTooManyPartitions() throws Exception { // Assert we haven't violated the limit too much GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0); ModelSizeStats modelSizeStats = jobStats.getModelSizeStats(); - assertThat(modelSizeStats.getModelBytes(), lessThan(35000000L)); - assertThat(modelSizeStats.getModelBytes(), greaterThan(30000000L)); + assertThat(modelSizeStats.getModelBytes(), lessThan(31500000L)); + assertThat(modelSizeStats.getModelBytes(), greaterThan(24000000L)); assertThat(modelSizeStats.getMemoryStatus(), equalTo(ModelSizeStats.MemoryStatus.HARD_LIMIT)); } + @AwaitsFix(bugUrl = "https://github.com/elastic/ml-cpp/pulls/122") public void testTooManyByFields() throws Exception { Detector.Builder detector = new Detector.Builder("count", null); detector.setByFieldName("user"); @@ -122,11 +124,12 @@ public void testTooManyByFields() throws Exception { // Assert we haven't violated the limit too much GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0); ModelSizeStats modelSizeStats = jobStats.getModelSizeStats(); - assertThat(modelSizeStats.getModelBytes(), lessThan(36000000L)); - assertThat(modelSizeStats.getModelBytes(), greaterThan(30000000L)); + assertThat(modelSizeStats.getModelBytes(), lessThan(31500000L)); + assertThat(modelSizeStats.getModelBytes(), greaterThan(25000000L)); assertThat(modelSizeStats.getMemoryStatus(), equalTo(ModelSizeStats.MemoryStatus.HARD_LIMIT)); } + @AwaitsFix(bugUrl = "https://github.com/elastic/ml-cpp/pulls/122") public void testTooManyByAndOverFields() throws Exception { Detector.Builder detector = new Detector.Builder("count", null); detector.setByFieldName("department"); @@ -171,11 +174,12 @@ public void testTooManyByAndOverFields() throws Exception { // Assert we haven't violated the limit too much GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0); ModelSizeStats modelSizeStats = jobStats.getModelSizeStats(); - assertThat(modelSizeStats.getModelBytes(), lessThan(36000000L)); + assertThat(modelSizeStats.getModelBytes(), lessThan(31500000L)); assertThat(modelSizeStats.getModelBytes(), greaterThan(24000000L)); assertThat(modelSizeStats.getMemoryStatus(), equalTo(ModelSizeStats.MemoryStatus.HARD_LIMIT)); } + @AwaitsFix(bugUrl = "https://github.com/elastic/ml-cpp/pulls/122") public void testManyDistinctOverFields() throws Exception { Detector.Builder detector = new Detector.Builder("sum", "value"); detector.setOverFieldName("user"); @@ -221,9 +225,9 @@ public void testManyDistinctOverFields() throws Exception { // Assert we haven't violated the limit too much GetJobsStatsAction.Response.JobStats jobStats = getJobStats(job.getId()).get(0); ModelSizeStats modelSizeStats = jobStats.getModelSizeStats(); - assertThat(modelSizeStats.getModelBytes(), lessThan(90000000L)); - assertThat(modelSizeStats.getModelBytes(), greaterThan(75000000L)); - assertThat(modelSizeStats.getMemoryStatus(), equalTo(ModelSizeStats.MemoryStatus.OK)); + assertThat(modelSizeStats.getModelBytes(), lessThan(116000000L)); + assertThat(modelSizeStats.getModelBytes(), greaterThan(90000000L)); + assertThat(modelSizeStats.getMemoryStatus(), equalTo(ModelSizeStats.MemoryStatus.HARD_LIMIT)); } private static Map createRecord(long timestamp, String user, String department) { From f1a545475436c6fb599620240d5435cb78b7b646 Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Thu, 14 Jun 2018 16:21:28 +0200 Subject: [PATCH 69/71] [TEST] Fix RemoteClusterClientTests#testEnsureWeReconnect Closes #29547 --- .../transport/RemoteClusterClientTests.java | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java b/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java index a497e509c1577..8cfec0a07f910 100644 --- a/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java +++ b/server/src/test/java/org/elasticsearch/transport/RemoteClusterClientTests.java @@ -30,6 +30,7 @@ import org.elasticsearch.threadpool.ThreadPool; import java.util.Collections; +import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import static org.elasticsearch.transport.RemoteClusterConnectionTests.startTransport; @@ -69,7 +70,6 @@ public void testConnectAndExecuteRequest() throws Exception { } } - @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/29547") public void testEnsureWeReconnect() throws Exception { Settings remoteSettings = Settings.builder().put(ClusterName.CLUSTER_NAME_SETTING.getKey(), "foo_bar_cluster").build(); try (MockTransportService remoteTransport = startTransport("remote_node", Collections.emptyList(), Version.CURRENT, threadPool, @@ -79,17 +79,35 @@ public void testEnsureWeReconnect() throws Exception { .put(RemoteClusterService.ENABLE_REMOTE_CLUSTERS.getKey(), true) .put("search.remote.test.seeds", remoteNode.getAddress().getAddress() + ":" + remoteNode.getAddress().getPort()).build(); try (MockTransportService service = MockTransportService.createNewService(localSettings, Version.CURRENT, threadPool, null)) { + Semaphore semaphore = new Semaphore(1); service.start(); + service.addConnectionListener(new TransportConnectionListener() { + @Override + public void onNodeDisconnected(DiscoveryNode node) { + if (remoteNode.equals(node)) { + semaphore.release(); + } + } + }); + // this test is not perfect since we might reconnect concurrently but it will fail most of the time if we don't have + // the right calls in place in the RemoteAwareClient service.acceptIncomingRequests(); - service.disconnectFromNode(remoteNode); - RemoteClusterService remoteClusterService = service.getRemoteClusterService(); - assertBusy(() -> assertFalse(remoteClusterService.isRemoteNodeConnected("test", remoteNode))); - Client client = remoteClusterService.getRemoteClusterClient(threadPool, "test"); - ClusterStateResponse clusterStateResponse = client.admin().cluster().prepareState().execute().get(); - assertNotNull(clusterStateResponse); - assertEquals("foo_bar_cluster", clusterStateResponse.getState().getClusterName().value()); + for (int i = 0; i < 10; i++) { + semaphore.acquire(); + try { + service.disconnectFromNode(remoteNode); + semaphore.acquire(); + RemoteClusterService remoteClusterService = service.getRemoteClusterService(); + Client client = remoteClusterService.getRemoteClusterClient(threadPool, "test"); + ClusterStateResponse clusterStateResponse = client.admin().cluster().prepareState().execute().get(); + assertNotNull(clusterStateResponse); + assertEquals("foo_bar_cluster", clusterStateResponse.getState().getClusterName().value()); + assertTrue(remoteClusterService.isRemoteNodeConnected("test", remoteNode)); + } finally { + semaphore.release(); + } + } } } } - } From 0bf50b232852099d67fac193d65653c7185c1b89 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Thu, 14 Jun 2018 14:09:56 +0200 Subject: [PATCH 70/71] [Tests] Mutualize fixtures code in BaseHttpFixture (#31210) Many fixtures have similar code for writing the pid & ports files or for handling HTTP requests. This commit adds an AbstractHttpFixture class in the test framework that can be extended for specific testing purposes. --- modules/repository-url/build.gradle | 6 - .../repositories/url/URLFixture.java | 152 +--- plugins/examples/rest-handler/build.gradle | 16 +- .../example/resthandler/ExampleFixture.java | 53 ++ .../qa/microsoft-azure-storage/build.gradle | 13 +- .../azure/AzureStorageFixture.java | 380 +++++++--- .../azure/AzureStorageTestServer.java | 402 ----------- .../qa/google-cloud-storage/build.gradle | 12 +- .../gcs/GoogleCloudStorageFixture.java | 639 ++++++++++++++--- .../gcs/GoogleCloudStorageTestServer.java | 663 ------------------ .../repository-s3/qa/amazon-s3/build.gradle | 9 +- .../repositories/s3/AmazonS3Fixture.java | 473 ++++++++++--- .../repositories/s3/AmazonS3TestServer.java | 500 ------------- settings.gradle | 1 - test/fixtures/example-fixture/build.gradle | 26 - .../main/java/example/ExampleTestFixture.java | 78 --- .../test/fixture/AbstractHttpFixture.java | 312 +++++++++ 17 files changed, 1632 insertions(+), 2103 deletions(-) create mode 100644 plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixture.java delete mode 100644 plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java delete mode 100644 plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageTestServer.java delete mode 100644 plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3TestServer.java delete mode 100644 test/fixtures/example-fixture/build.gradle delete mode 100644 test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java create mode 100644 test/framework/src/main/java/org/elasticsearch/test/fixture/AbstractHttpFixture.java diff --git a/modules/repository-url/build.gradle b/modules/repository-url/build.gradle index 62aad486ad804..a0750885f7675 100644 --- a/modules/repository-url/build.gradle +++ b/modules/repository-url/build.gradle @@ -23,12 +23,6 @@ esplugin { classname 'org.elasticsearch.plugin.repository.url.URLRepositoryPlugin' } -forbiddenApisTest { - // we are using jdk-internal instead of jdk-non-portable to allow for com.sun.net.httpserver.* usage - bundledSignatures -= 'jdk-non-portable' - bundledSignatures += 'jdk-internal' -} - // This directory is shared between two URL repositories and one FS repository in YAML integration tests File repositoryDir = new File(project.buildDir, "shared-repository") diff --git a/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java b/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java index 353a0d895c2c7..88e94f5b50267 100644 --- a/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java +++ b/modules/repository-url/src/test/java/org/elasticsearch/repositories/url/URLFixture.java @@ -18,151 +18,71 @@ */ package org.elasticsearch.repositories.url; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; +import org.elasticsearch.test.fixture.AbstractHttpFixture; import org.elasticsearch.common.SuppressForbidden; -import org.elasticsearch.mocksocket.MockHttpServer; import org.elasticsearch.rest.RestStatus; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; +import java.util.HashMap; import java.util.Map; -import java.util.Objects; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonMap; /** * This {@link URLFixture} exposes a filesystem directory over HTTP. It is used in repository-url * integration tests to expose a directory created by a regular FS repository. */ -public class URLFixture { +public class URLFixture extends AbstractHttpFixture { + + private final Path repositoryDir; + + /** + * Creates a {@link URLFixture} + */ + private URLFixture(final String workingDir, final String repositoryDir) { + super(workingDir); + this.repositoryDir = dir(repositoryDir); + } public static void main(String[] args) throws Exception { if (args == null || args.length != 2) { throw new IllegalArgumentException("URLFixture "); } - final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); - final HttpServer httpServer = MockHttpServer.createHttp(socketAddress, 0); - - try { - final Path workingDirectory = dir(args[0]); - /// Writes the PID of the current Java process in a `pid` file located in the working directory - writeFile(workingDirectory, "pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); - - final String addressAndPort = addressToString(httpServer.getAddress()); - // Writes the address and port of the http server in a `ports` file located in the working directory - writeFile(workingDirectory, "ports", addressAndPort); - - // Exposes the repository over HTTP - httpServer.createContext("/", new ResponseHandler(dir(args[1]))); - httpServer.start(); - - // Wait to be killed - Thread.sleep(Long.MAX_VALUE); - - } finally { - httpServer.stop(0); - } - } - - @SuppressForbidden(reason = "Paths#get is fine - we don't have environment here") - private static Path dir(final String dir) { - return Paths.get(dir); - } - - private static void writeFile(final Path dir, final String fileName, final String content) throws IOException { - final Path tempPidFile = Files.createTempFile(dir, null, null); - Files.write(tempPidFile, singleton(content)); - Files.move(tempPidFile, dir.resolve(fileName), StandardCopyOption.ATOMIC_MOVE); + final URLFixture fixture = new URLFixture(args[0], args[1]); + fixture.listen(); } - private static String addressToString(final SocketAddress address) { - final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; - if (inetSocketAddress.getAddress() instanceof Inet6Address) { - return "[" + inetSocketAddress.getHostString() + "]:" + inetSocketAddress.getPort(); - } else { - return inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); - } - } - - static class ResponseHandler implements HttpHandler { - - private final Path repositoryDir; - - ResponseHandler(final Path repositoryDir) { - this.repositoryDir = repositoryDir; - } - - @Override - public void handle(HttpExchange exchange) throws IOException { - Response response; - - final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); - if (userAgent != null && userAgent.startsWith("Apache Ant")) { - // This is a request made by the AntFixture, just reply "OK" - response = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); - - } else if ("GET".equalsIgnoreCase(exchange.getRequestMethod())) { - String path = exchange.getRequestURI().toString(); - if (path.length() > 0 && path.charAt(0) == '/') { - path = path.substring(1); - } + @Override + protected AbstractHttpFixture.Response handle(final Request request) throws IOException { + if ("GET".equalsIgnoreCase(request.getMethod())) { + String path = request.getPath(); + if (path.length() > 0 && path.charAt(0) == '/') { + path = path.substring(1); + } - Path normalizedRepositoryDir = repositoryDir.normalize(); - Path normalizedPath = normalizedRepositoryDir.resolve(path).normalize(); + Path normalizedRepositoryDir = repositoryDir.normalize(); + Path normalizedPath = normalizedRepositoryDir.resolve(path).normalize(); - if (normalizedPath.startsWith(normalizedRepositoryDir)) { - if (Files.exists(normalizedPath) && Files.isReadable(normalizedPath) && Files.isRegularFile(normalizedPath)) { - byte[] content = Files.readAllBytes(normalizedPath); - Map headers = singletonMap("Content-Length", String.valueOf(content.length)); - response = new Response(RestStatus.OK, headers, "application/octet-stream", content); - } else { - response = new Response(RestStatus.NOT_FOUND, emptyMap(), "text/plain; charset=utf-8", new byte[0]); - } + if (normalizedPath.startsWith(normalizedRepositoryDir)) { + if (Files.exists(normalizedPath) && Files.isReadable(normalizedPath) && Files.isRegularFile(normalizedPath)) { + byte[] content = Files.readAllBytes(normalizedPath); + final Map headers = new HashMap<>(contentType("application/octet-stream")); + headers.put("Content-Length", String.valueOf(content.length)); + return new Response(RestStatus.OK.getStatus(), headers, content); } else { - response = new Response(RestStatus.FORBIDDEN, emptyMap(), "text/plain; charset=utf-8", new byte[0]); + return new Response(RestStatus.NOT_FOUND.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); } } else { - response = new Response(RestStatus.INTERNAL_SERVER_ERROR, emptyMap(), "text/plain; charset=utf-8", - "Unsupported HTTP method".getBytes(StandardCharsets.UTF_8)); + return new Response(RestStatus.FORBIDDEN.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); } - exchange.sendResponseHeaders(response.status.getStatus(), response.body.length); - if (response.body.length > 0) { - exchange.getResponseBody().write(response.body); - } - exchange.close(); } + return null; } - /** - * Represents a HTTP Response. - */ - static class Response { - - final RestStatus status; - final Map headers; - final String contentType; - final byte[] body; - - Response(final RestStatus status, final Map headers, final String contentType, final byte[] body) { - this.status = Objects.requireNonNull(status); - this.headers = Objects.requireNonNull(headers); - this.contentType = Objects.requireNonNull(contentType); - this.body = Objects.requireNonNull(body); - } + @SuppressForbidden(reason = "Paths#get is fine - we don't have environment here") + private static Path dir(final String dir) { + return Paths.get(dir); } } diff --git a/plugins/examples/rest-handler/build.gradle b/plugins/examples/rest-handler/build.gradle index 2cccb5a896967..2c55c3c79fce7 100644 --- a/plugins/examples/rest-handler/build.gradle +++ b/plugins/examples/rest-handler/build.gradle @@ -28,20 +28,12 @@ esplugin { // No unit tests in this example test.enabled = false -configurations { - exampleFixture -} - -dependencies { - exampleFixture project(':test:fixtures:example-fixture') -} - task exampleFixture(type: org.elasticsearch.gradle.test.AntFixture) { - dependsOn project.configurations.exampleFixture + dependsOn testClasses executable = new File(project.runtimeJavaHome, 'bin/java') - args '-cp', "${ -> project.configurations.exampleFixture.asPath }", - 'example.ExampleTestFixture', - baseDir + args '-cp', "${ -> project.sourceSets.test.runtimeClasspath.asPath }", + 'org.elasticsearch.example.resthandler.ExampleFixture', + baseDir, 'TEST' } integTestCluster { diff --git a/plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixture.java b/plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixture.java new file mode 100644 index 0000000000000..f07514a4481d5 --- /dev/null +++ b/plugins/examples/rest-handler/src/test/java/org/elasticsearch/example/resthandler/ExampleFixture.java @@ -0,0 +1,53 @@ +/* + * 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.example.resthandler; + +import org.elasticsearch.test.fixture.AbstractHttpFixture; + +import java.io.IOException; +import java.util.Objects; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class ExampleFixture extends AbstractHttpFixture { + + private final String message; + + private ExampleFixture(final String workingDir, final String message) { + super(workingDir); + this.message = Objects.requireNonNull(message); + } + + @Override + protected Response handle(final Request request) throws IOException { + if ("GET".equals(request.getMethod()) && "/".equals(request.getPath())) { + return new Response(200, TEXT_PLAIN_CONTENT_TYPE, message.getBytes(UTF_8)); + } + return null; + } + + public static void main(final String[] args) throws Exception { + if (args == null || args.length != 2) { + throw new IllegalArgumentException("ExampleFixture "); + } + + final ExampleFixture fixture = new ExampleFixture(args[0], args[1]); + fixture.listen(); + } +} diff --git a/plugins/repository-azure/qa/microsoft-azure-storage/build.gradle b/plugins/repository-azure/qa/microsoft-azure-storage/build.gradle index c7deb8a9c76a7..d9658d4d2f9e2 100644 --- a/plugins/repository-azure/qa/microsoft-azure-storage/build.gradle +++ b/plugins/repository-azure/qa/microsoft-azure-storage/build.gradle @@ -23,20 +23,10 @@ import org.elasticsearch.gradle.test.AntFixture apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.rest-test' -dependencies { - testCompile project(path: ':plugins:repository-azure', configuration: 'runtime') -} - integTestCluster { plugin ':plugins:repository-azure' } -forbiddenApisTest { - // we are using jdk-internal instead of jdk-non-portable to allow for com.sun.net.httpserver.* usage - bundledSignatures -= 'jdk-non-portable' - bundledSignatures += 'jdk-internal' -} - boolean useFixture = false String azureAccount = System.getenv("azure_storage_account") @@ -54,7 +44,7 @@ if (!azureAccount && !azureKey && !azureContainer && !azureBasePath) { /** A task to start the fixture which emulates an Azure Storage service **/ task azureStorageFixture(type: AntFixture) { - dependsOn compileTestJava + dependsOn testClasses env 'CLASSPATH', "${ -> project.sourceSets.test.runtimeClasspath.asPath }" executable = new File(project.runtimeJavaHome, 'bin/java') args 'org.elasticsearch.repositories.azure.AzureStorageFixture', baseDir, azureContainer @@ -64,6 +54,7 @@ Map expansions = [ 'container': azureContainer, 'base_path': azureBasePath ] + processTestResources { inputs.properties(expansions) MavenFilteringHack.filter(it, expansions) diff --git a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java b/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java index 2f74c00ef92e2..f906b9fa9a913 100644 --- a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java +++ b/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageFixture.java @@ -18,132 +18,332 @@ */ package org.elasticsearch.repositories.azure; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import org.elasticsearch.common.SuppressForbidden; -import org.elasticsearch.common.io.Streams; -import org.elasticsearch.mocksocket.MockHttpServer; -import org.elasticsearch.repositories.azure.AzureStorageTestServer.Response; +import org.elasticsearch.test.fixture.AbstractHttpFixture; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.path.PathTrie; +import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.RestUtils; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; /** - * {@link AzureStorageFixture} is a fixture that emulates an Azure Storage service. + * {@link AzureStorageFixture} emulates an Azure Storage service. *

- * It starts an asynchronous socket server that binds to a random local port. The server parses - * HTTP requests and uses a {@link AzureStorageTestServer} to handle them before returning - * them to the client as HTTP responses. + * The implementation is based on official documentation available at + * https://docs.microsoft.com/en-us/rest/api/storageservices/blob-service-rest-api. */ -public class AzureStorageFixture { +public class AzureStorageFixture extends AbstractHttpFixture { - public static void main(String[] args) throws Exception { + /** + * List of the containers stored on this test server + **/ + private final Map containers = ConcurrentCollections.newConcurrentMap(); + + /** + * Request handlers for the requests made by the Azure client + **/ + private final PathTrie handlers; + + /** + * Creates a {@link AzureStorageFixture} with a custom endpoint + */ + private AzureStorageFixture(final String workingDir, final String container) { + super(workingDir); + this.containers.put(container, new Container(container)); + this.handlers = defaultHandlers(containers); + } + + @Override + protected AbstractHttpFixture.Response handle(final Request request) throws IOException { + final RequestHandler handler = handlers.retrieve(request.getMethod() + " " + request.getPath(), request.getParameters()); + if (handler != null) { + final String authorization = request.getHeader("Authorization"); + if (authorization == null + || (authorization.length() > 0 && authorization.contains("azure_integration_test_account") == false)) { + return newError(request.getId(), RestStatus.FORBIDDEN, "AccessDenied", "Access Denied"); + } + return handler.handle(request); + } + return null; + } + + public static void main(final String[] args) throws Exception { if (args == null || args.length != 2) { throw new IllegalArgumentException("AzureStorageFixture "); } - final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); - final HttpServer httpServer = MockHttpServer.createHttp(socketAddress, 0); + final AzureStorageFixture fixture = new AzureStorageFixture(args[0], args[1]); + fixture.listen(); + } + + /** + * Builds the default request handlers + **/ + private static PathTrie defaultHandlers(final Map containers) { + final PathTrie handlers = new PathTrie<>(RestUtils.REST_DECODER); + + // Get Blob Properties + // + // https://docs.microsoft.com/en-us/rest/api/storageservices/get-blob-properties + objectsPaths("HEAD /{container}").forEach(path -> + handlers.insert(path, (request) -> { + final String containerName = request.getParam("container"); + + final Container container = containers.get(containerName); + if (container == null) { + return newContainerNotFoundError(request.getId()); + } + + final String blobName = objectName(request.getParameters()); + for (Map.Entry object : container.objects.entrySet()) { + if (object.getKey().equals(blobName)) { + Map responseHeaders = new HashMap<>(); + responseHeaders.put("x-ms-blob-content-length", String.valueOf(object.getValue().length)); + responseHeaders.put("x-ms-blob-type", "blockblob"); + return new Response(RestStatus.OK.getStatus(), responseHeaders, EMPTY_BYTE); + } + } + return newBlobNotFoundError(request.getId()); + }) + ); - try { - final Path workingDirectory = workingDir(args[0]); - /// Writes the PID of the current Java process in a `pid` file located in the working directory - writeFile(workingDirectory, "pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); + // PUT Blob + // + // https://docs.microsoft.com/en-us/rest/api/storageservices/put-blob + objectsPaths("PUT /{container}").forEach(path -> + handlers.insert(path, (request) -> { + final String destContainerName = request.getParam("container"); + final String destBlobName = objectName(request.getParameters()); - final String addressAndPort = addressToString(httpServer.getAddress()); - // Writes the address and port of the http server in a `ports` file located in the working directory - writeFile(workingDirectory, "ports", addressAndPort); + final Container destContainer = containers.get(destContainerName); + if (destContainer == null) { + return newContainerNotFoundError(request.getId()); + } - // Emulates Azure - final String storageUrl = "http://" + addressAndPort; - final AzureStorageTestServer testServer = new AzureStorageTestServer(storageUrl); - testServer.createContainer(args[1]); + byte[] existingBytes = destContainer.objects.putIfAbsent(destBlobName, request.getBody()); + if (existingBytes != null) { + return newBlobAlreadyExistsError(request.getId()); + } - httpServer.createContext("/", new ResponseHandler(testServer)); - httpServer.start(); + return new Response(RestStatus.CREATED.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); }) + ); - // Wait to be killed - Thread.sleep(Long.MAX_VALUE); + // GET Object + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html + objectsPaths("GET /{container}").forEach(path -> + handlers.insert(path, (request) -> { + final String containerName = request.getParam("container"); + + final Container container = containers.get(containerName); + if (container == null) { + return newContainerNotFoundError(request.getId()); + } + + final String blobName = objectName(request.getParameters()); + if (container.objects.containsKey(blobName)) { + Map responseHeaders = new HashMap<>(contentType("application/octet-stream")); + responseHeaders.put("x-ms-copy-status", "success"); + responseHeaders.put("x-ms-blob-type", "blockblob"); + return new Response(RestStatus.OK.getStatus(), responseHeaders, container.objects.get(blobName)); + + } + return newBlobNotFoundError(request.getId()); + }) + ); + + // Delete Blob + // + // https://docs.microsoft.com/en-us/rest/api/storageservices/delete-blob + objectsPaths("DELETE /{container}").forEach(path -> + handlers.insert(path, (request) -> { + final String containerName = request.getParam("container"); + + final Container container = containers.get(containerName); + if (container == null) { + return newContainerNotFoundError(request.getId()); + } + + final String blobName = objectName(request.getParameters()); + if (container.objects.remove(blobName) != null) { + return new Response(RestStatus.ACCEPTED.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } + return newBlobNotFoundError(request.getId()); + }) + ); + + // List Blobs + // + // https://docs.microsoft.com/en-us/rest/api/storageservices/list-blobs + handlers.insert("GET /{container}/", (request) -> { + final String containerName = request.getParam("container"); + + final Container container = containers.get(containerName); + if (container == null) { + return newContainerNotFoundError(request.getId()); + } + + final String prefix = request.getParam("prefix"); + return newEnumerationResultsResponse(request.getId(), container, prefix); + }); + + // Get Container Properties + // + // https://docs.microsoft.com/en-us/rest/api/storageservices/get-container-properties + handlers.insert("HEAD /{container}", (request) -> { + String container = request.getParam("container"); + if (Strings.hasText(container) && containers.containsKey(container)) { + return new Response(RestStatus.OK.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } else { + return newContainerNotFoundError(request.getId()); + } + }); - } finally { - httpServer.stop(0); + return handlers; + } + + /** + * Represents a Azure Storage container. + */ + static class Container { + + /** + * Container name + **/ + final String name; + + /** + * Blobs contained in the container + **/ + final Map objects; + + Container(final String name) { + this.name = Objects.requireNonNull(name); + this.objects = ConcurrentCollections.newConcurrentMap(); } } - @SuppressForbidden(reason = "Paths#get is fine - we don't have environment here") - private static Path workingDir(final String dir) { - return Paths.get(dir); + /** + * Decline a path like "http://host:port/{bucket}" into 10 derived paths like: + * - http://host:port/{bucket}/{path0} + * - http://host:port/{bucket}/{path0}/{path1} + * - http://host:port/{bucket}/{path0}/{path1}/{path2} + * - etc + */ + private static List objectsPaths(final String path) { + final List paths = new ArrayList<>(); + String p = path; + for (int i = 0; i < 10; i++) { + p = p + "/{path" + i + "}"; + paths.add(p); + } + return paths; } - private static void writeFile(final Path dir, final String fileName, final String content) throws IOException { - final Path tempPidFile = Files.createTempFile(dir, null, null); - Files.write(tempPidFile, singleton(content)); - Files.move(tempPidFile, dir.resolve(fileName), StandardCopyOption.ATOMIC_MOVE); + /** + * Retrieves the object name from all derived paths named {pathX} where 0 <= X < 10. + *

+ * This is the counterpart of {@link #objectsPaths(String)} + */ + private static String objectName(final Map params) { + final StringBuilder name = new StringBuilder(); + for (int i = 0; i < 10; i++) { + String value = params.getOrDefault("path" + i, null); + if (value != null) { + if (name.length() > 0) { + name.append('/'); + } + name.append(value); + } + } + return name.toString(); } - private static String addressToString(final SocketAddress address) { - final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; - if (inetSocketAddress.getAddress() instanceof Inet6Address) { - return "[" + inetSocketAddress.getHostString() + "]:" + inetSocketAddress.getPort(); + + /** + * Azure EnumerationResults Response + */ + private static Response newEnumerationResultsResponse(final long requestId, final Container container, final String prefix) { + final String id = Long.toString(requestId); + final StringBuilder response = new StringBuilder(); + response.append(""); + response.append(""); + if (prefix != null) { + response.append("").append(prefix).append(""); } else { - return inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); + response.append(""); + } + response.append("").append(container.objects.size()).append(""); + response.append(""); + + int count = 0; + for (Map.Entry object : container.objects.entrySet()) { + String objectName = object.getKey(); + if (prefix == null || objectName.startsWith(prefix)) { + response.append(""); + response.append("").append(objectName).append(""); + response.append(""); + response.append("").append(object.getValue().length).append(""); + response.append("").append(count++).append(""); + response.append("success"); + response.append("BlockBlob"); + response.append(""); + response.append(""); + } } + + response.append(""); + response.append(""); + response.append(""); + + final Map headers = new HashMap<>(contentType("application/xml")); + headers.put("x-ms-request-id", id); + + return new Response(RestStatus.OK.getStatus(), headers, response.toString().getBytes(UTF_8)); } - static class ResponseHandler implements HttpHandler { + private static Response newContainerNotFoundError(final long requestId) { + return newError(requestId, RestStatus.NOT_FOUND, "ContainerNotFound", "The specified container does not exist"); + } - private final AzureStorageTestServer server; + private static Response newBlobNotFoundError(final long requestId) { + return newError(requestId, RestStatus.NOT_FOUND, "BlobNotFound", "The specified blob does not exist"); + } - private ResponseHandler(final AzureStorageTestServer server) { - this.server = server; - } + private static Response newBlobAlreadyExistsError(final long requestId) { + return newError(requestId, RestStatus.CONFLICT, "BlobAlreadyExists", "The specified blob already exists"); + } - @Override - public void handle(HttpExchange exchange) throws IOException { - String method = exchange.getRequestMethod(); - String path = server.getEndpoint() + exchange.getRequestURI().getRawPath(); - String query = exchange.getRequestURI().getRawQuery(); - Map> headers = exchange.getRequestHeaders(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Streams.copy(exchange.getRequestBody(), out); - - Response response = null; - - final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); - if (userAgent != null && userAgent.startsWith("Apache Ant")) { - // This is a request made by the AntFixture, just reply "OK" - response = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); - } else { - // Otherwise simulate a S3 response - response = server.handle(method, path, query, headers, out.toByteArray()); - } + /** + * Azure Error + *

+ * https://docs.microsoft.com/en-us/rest/api/storageservices/status-and-error-codes2 + */ + private static Response newError(final long requestId, + final RestStatus status, + final String code, + final String message) { - Map> responseHeaders = exchange.getResponseHeaders(); - responseHeaders.put("Content-Type", singletonList(response.contentType)); - response.headers.forEach((k, v) -> responseHeaders.put(k, singletonList(v))); - exchange.sendResponseHeaders(response.status.getStatus(), response.body.length); - if (response.body.length > 0) { - exchange.getResponseBody().write(response.body); - } - exchange.close(); - } + final StringBuilder response = new StringBuilder(); + response.append(""); + response.append(""); + response.append("").append(code).append(""); + response.append("").append(message).append(""); + response.append(""); + + final Map headers = new HashMap<>(contentType("application/xml")); + headers.put("x-ms-request-id", String.valueOf(requestId)); + headers.put("x-ms-error-code", code); + + return new Response(status.getStatus(), headers, response.toString().getBytes(UTF_8)); } } diff --git a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java b/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java deleted file mode 100644 index 8183ee5043ec8..0000000000000 --- a/plugins/repository-azure/qa/microsoft-azure-storage/src/test/java/org/elasticsearch/repositories/azure/AzureStorageTestServer.java +++ /dev/null @@ -1,402 +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.repositories.azure; - -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.path.PathTrie; -import org.elasticsearch.common.util.concurrent.ConcurrentCollections; -import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.rest.RestUtils; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; - -/** - * {@link AzureStorageTestServer} emulates an Azure Storage service through a {@link #handle(String, String, String, Map, byte[])} - * method that provides appropriate responses for specific requests like the real Azure platform would do. - * It is based on official documentation available at https://docs.microsoft.com/en-us/rest/api/storageservices/blob-service-rest-api. - */ -public class AzureStorageTestServer { - - private static byte[] EMPTY_BYTE = new byte[0]; - - /** List of the containers stored on this test server **/ - private final Map containers = ConcurrentCollections.newConcurrentMap(); - - /** Request handlers for the requests made by the Azure client **/ - private final PathTrie handlers; - - /** Server endpoint **/ - private final String endpoint; - - /** Increments for the requests ids **/ - private final AtomicLong requests = new AtomicLong(0); - - /** - * Creates a {@link AzureStorageTestServer} with a custom endpoint - */ - AzureStorageTestServer(final String endpoint) { - this.endpoint = Objects.requireNonNull(endpoint, "endpoint must not be null"); - this.handlers = defaultHandlers(endpoint, containers); - } - - /** Creates a container in the test server **/ - void createContainer(final String containerName) { - containers.put(containerName, new Container(containerName)); - } - - public String getEndpoint() { - return endpoint; - } - - /** - * Returns a response for the given request - * - * @param method the HTTP method of the request - * @param path the path of the URL of the request - * @param query the queryString of the URL of request - * @param headers the HTTP headers of the request - * @param body the HTTP request body - * @return a {@link Response} - * @throws IOException if something goes wrong - */ - public Response handle(final String method, - final String path, - final String query, - final Map> headers, - byte[] body) throws IOException { - - final long requestId = requests.incrementAndGet(); - - final Map params = new HashMap<>(); - if (query != null) { - RestUtils.decodeQueryString(query, 0, params); - } - - final RequestHandler handler = handlers.retrieve(method + " " + path, params); - if (handler != null) { - return handler.execute(params, headers, body, requestId); - } else { - return newInternalError(requestId); - } - } - - @FunctionalInterface - interface RequestHandler { - - /** - * Simulates the execution of a Azure Storage request and returns a corresponding response. - * - * @param params the request's query string parameters - * @param headers the request's headers - * @param body the request body provided as a byte array - * @param requestId a unique id for the incoming request - * @return the corresponding response - * - * @throws IOException if something goes wrong - */ - Response execute(Map params, Map> headers, byte[] body, long requestId) throws IOException; - } - - /** Builds the default request handlers **/ - private static PathTrie defaultHandlers(final String endpoint, final Map containers) { - final PathTrie handlers = new PathTrie<>(RestUtils.REST_DECODER); - - // Get Blob Properties - // - // https://docs.microsoft.com/en-us/rest/api/storageservices/get-blob-properties - objectsPaths("HEAD " + endpoint + "/{container}").forEach(path -> - handlers.insert(path, (params, headers, body, requestId) -> { - final String containerName = params.get("container"); - - final Container container =containers.get(containerName); - if (container == null) { - return newContainerNotFoundError(requestId); - } - - final String blobName = objectName(params); - for (Map.Entry object : container.objects.entrySet()) { - if (object.getKey().equals(blobName)) { - Map responseHeaders = new HashMap<>(); - responseHeaders.put("x-ms-blob-content-length", String.valueOf(object.getValue().length)); - responseHeaders.put("x-ms-blob-type", "blockblob"); - return new Response(RestStatus.OK, responseHeaders, "text/plain", EMPTY_BYTE); - } - } - return newBlobNotFoundError(requestId); - }) - ); - - // PUT Blob - // - // https://docs.microsoft.com/en-us/rest/api/storageservices/put-blob - objectsPaths("PUT " + endpoint + "/{container}").forEach(path -> - handlers.insert(path, (params, headers, body, requestId) -> { - final String destContainerName = params.get("container"); - final String destBlobName = objectName(params); - - final Container destContainer =containers.get(destContainerName); - if (destContainer == null) { - return newContainerNotFoundError(requestId); - } - - byte[] existingBytes = destContainer.objects.putIfAbsent(destBlobName, body); - if (existingBytes != null) { - return newBlobAlreadyExistsError(requestId); - } - - return new Response(RestStatus.CREATED, emptyMap(), "text/plain", EMPTY_BYTE); - }) - ); - - // GET Object - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html - objectsPaths("GET " + endpoint + "/{container}").forEach(path -> - handlers.insert(path, (params, headers, body, requestId) -> { - final String containerName = params.get("container"); - - final Container container =containers.get(containerName); - if (container == null) { - return newContainerNotFoundError(requestId); - } - - final String blobName = objectName(params); - if (container.objects.containsKey(blobName)) { - Map responseHeaders = new HashMap<>(); - responseHeaders.put("x-ms-copy-status", "success"); - responseHeaders.put("x-ms-blob-type", "blockblob"); - return new Response(RestStatus.OK, responseHeaders, "application/octet-stream", container.objects.get(blobName)); - - } - return newBlobNotFoundError(requestId); - }) - ); - - // Delete Blob - // - // https://docs.microsoft.com/en-us/rest/api/storageservices/delete-blob - objectsPaths("DELETE " + endpoint + "/{container}").forEach(path -> - handlers.insert(path, (params, headers, body, requestId) -> { - final String containerName = params.get("container"); - - final Container container =containers.get(containerName); - if (container == null) { - return newContainerNotFoundError(requestId); - } - - final String blobName = objectName(params); - if (container.objects.remove(blobName) != null) { - return new Response(RestStatus.ACCEPTED, emptyMap(), "text/plain", EMPTY_BYTE); - } - return newBlobNotFoundError(requestId); - }) - ); - - // List Blobs - // - // https://docs.microsoft.com/en-us/rest/api/storageservices/list-blobs - handlers.insert("GET " + endpoint + "/{container}/", (params, headers, body, requestId) -> { - final String containerName = params.get("container"); - - final Container container =containers.get(containerName); - if (container == null) { - return newContainerNotFoundError(requestId); - } - - final String prefix = params.get("prefix"); - return newEnumerationResultsResponse(requestId, container, prefix); - }); - - // Get Container Properties - // - // https://docs.microsoft.com/en-us/rest/api/storageservices/get-container-properties - handlers.insert("HEAD " + endpoint + "/{container}", (params, headers, body, requestId) -> { - String container = params.get("container"); - if (Strings.hasText(container) && containers.containsKey(container)) { - return new Response(RestStatus.OK, emptyMap(), "text/plain", EMPTY_BYTE); - } else { - return newContainerNotFoundError(requestId); - } - }); - - return handlers; - } - - /** - * Represents a Azure Storage container. - */ - static class Container { - - /** Container name **/ - final String name; - - /** Blobs contained in the container **/ - final Map objects; - - Container(final String name) { - this.name = Objects.requireNonNull(name); - this.objects = ConcurrentCollections.newConcurrentMap(); - } - } - - /** - * Represents a HTTP Response. - */ - static class Response { - - final RestStatus status; - final Map headers; - final String contentType; - final byte[] body; - - Response(final RestStatus status, final Map headers, final String contentType, final byte[] body) { - this.status = Objects.requireNonNull(status); - this.headers = Objects.requireNonNull(headers); - this.contentType = Objects.requireNonNull(contentType); - this.body = Objects.requireNonNull(body); - } - } - - /** - * Decline a path like "http://host:port/{bucket}" into 10 derived paths like: - * - http://host:port/{bucket}/{path0} - * - http://host:port/{bucket}/{path0}/{path1} - * - http://host:port/{bucket}/{path0}/{path1}/{path2} - * - etc - */ - private static List objectsPaths(final String path) { - final List paths = new ArrayList<>(); - String p = path; - for (int i = 0; i < 10; i++) { - p = p + "/{path" + i + "}"; - paths.add(p); - } - return paths; - } - - /** - * Retrieves the object name from all derived paths named {pathX} where 0 <= X < 10. - * - * This is the counterpart of {@link #objectsPaths(String)} - */ - private static String objectName(final Map params) { - final StringBuilder name = new StringBuilder(); - for (int i = 0; i < 10; i++) { - String value = params.getOrDefault("path" + i, null); - if (value != null) { - if (name.length() > 0) { - name.append('/'); - } - name.append(value); - } - } - return name.toString(); - } - - - /** - * Azure EnumerationResults Response - */ - private static Response newEnumerationResultsResponse(final long requestId, final Container container, final String prefix) { - final String id = Long.toString(requestId); - final StringBuilder response = new StringBuilder(); - response.append(""); - response.append(""); - if (prefix != null) { - response.append("").append(prefix).append(""); - } else { - response.append(""); - } - response.append("").append(container.objects.size()).append(""); - response.append(""); - - int count = 0; - for (Map.Entry object : container.objects.entrySet()) { - String objectName = object.getKey(); - if (prefix == null || objectName.startsWith(prefix)) { - response.append(""); - response.append("").append(objectName).append(""); - response.append(""); - response.append("").append(object.getValue().length).append(""); - response.append("").append(count++).append(""); - response.append("success"); - response.append("BlockBlob"); - response.append(""); - response.append(""); - } - } - - response.append(""); - response.append(""); - response.append(""); - - return new Response(RestStatus.OK, singletonMap("x-amz-request-id", id), "application/xml", response.toString().getBytes(UTF_8)); - } - - private static Response newContainerNotFoundError(final long requestId) { - return newError(requestId, RestStatus.NOT_FOUND, "ContainerNotFound", "The specified container does not exist"); - } - - private static Response newBlobNotFoundError(final long requestId) { - return newError(requestId, RestStatus.NOT_FOUND, "BlobNotFound", "The specified blob does not exist"); - } - - private static Response newBlobAlreadyExistsError(final long requestId) { - return newError(requestId, RestStatus.CONFLICT, "BlobAlreadyExists", "The specified blob already exists"); - } - - private static Response newInternalError(final long requestId) { - return newError(requestId, RestStatus.INTERNAL_SERVER_ERROR, "InternalError", "The server encountered an internal error"); - } - - /** - * Azure Error - * - * https://docs.microsoft.com/en-us/rest/api/storageservices/status-and-error-codes2 - */ - private static Response newError(final long requestId, - final RestStatus status, - final String code, - final String message) { - - final StringBuilder response = new StringBuilder(); - response.append(""); - response.append(""); - response.append("").append(code).append(""); - response.append("").append(message).append(""); - response.append(""); - - final Map headers = new HashMap<>(2); - headers.put("x-ms-request-id", String.valueOf(requestId)); - headers.put("x-ms-error-code", code); - - return new Response(status, headers, "application/xml", response.toString().getBytes(UTF_8)); - } -} diff --git a/plugins/repository-gcs/qa/google-cloud-storage/build.gradle b/plugins/repository-gcs/qa/google-cloud-storage/build.gradle index 34ec92a354277..0a610123a6fcc 100644 --- a/plugins/repository-gcs/qa/google-cloud-storage/build.gradle +++ b/plugins/repository-gcs/qa/google-cloud-storage/build.gradle @@ -26,20 +26,10 @@ import java.security.KeyPairGenerator apply plugin: 'elasticsearch.standalone-rest-test' apply plugin: 'elasticsearch.rest-test' -dependencies { - testCompile project(path: ':plugins:repository-gcs', configuration: 'runtime') -} - integTestCluster { plugin ':plugins:repository-gcs' } -forbiddenApisTest { - // we are using jdk-internal instead of jdk-non-portable to allow for com.sun.net.httpserver.* usage - bundledSignatures -= 'jdk-non-portable' - bundledSignatures += 'jdk-internal' -} - boolean useFixture = false String gcsServiceAccount = System.getenv("google_storage_service_account") @@ -61,7 +51,7 @@ if (!gcsServiceAccount && !gcsBucket && !gcsBasePath) { /** A task to start the GoogleCloudStorageFixture which emulates a Google Cloud Storage service **/ task googleCloudStorageFixture(type: AntFixture) { - dependsOn compileTestJava + dependsOn testClasses env 'CLASSPATH', "${ -> project.sourceSets.test.runtimeClasspath.asPath }" executable = new File(project.runtimeJavaHome, 'bin/java') args 'org.elasticsearch.repositories.gcs.GoogleCloudStorageFixture', baseDir, 'bucket_test' diff --git a/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java b/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java index 978f8c4783197..b1a185c9c08c9 100644 --- a/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java +++ b/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageFixture.java @@ -18,130 +18,591 @@ */ package org.elasticsearch.repositories.gcs; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import org.elasticsearch.common.SuppressForbidden; -import org.elasticsearch.core.internal.io.Streams; -import org.elasticsearch.mocksocket.MockHttpServer; -import org.elasticsearch.repositories.gcs.GoogleCloudStorageTestServer.Response; +import org.elasticsearch.test.fixture.AbstractHttpFixture; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.path.PathTrie; +import org.elasticsearch.common.util.concurrent.ConcurrentCollections; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.RestUtils; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; -import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptyMap; -import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; /** - * {@link GoogleCloudStorageFixture} is a fixture that emulates a Google Cloud Storage service. - *

- * It starts an asynchronous socket server that binds to a random local port. The server parses - * HTTP requests and uses a {@link GoogleCloudStorageTestServer} to handle them before returning - * them to the client as HTTP responses. + * {@link GoogleCloudStorageFixture} emulates a Google Cloud Storage service. + * + * The implementation is based on official documentation available at https://cloud.google.com/storage/docs/json_api/v1/. */ -public class GoogleCloudStorageFixture { +public class GoogleCloudStorageFixture extends AbstractHttpFixture { + + /** List of the buckets stored on this test server **/ + private final Map buckets = ConcurrentCollections.newConcurrentMap(); + + /** Request handlers for the requests made by the Google Cloud Storage client **/ + private final PathTrie handlers; + + /** + * Creates a {@link GoogleCloudStorageFixture} + */ + private GoogleCloudStorageFixture(final String workingDir, final String bucket) { + super(workingDir); + this.buckets.put(bucket, new Bucket(bucket)); + this.handlers = defaultHandlers(buckets); + } - @SuppressForbidden(reason = "PathUtils#get is fine - we don't have environment here") - public static void main(String[] args) throws Exception { + @Override + protected Response handle(final Request request) throws IOException { + final RequestHandler handler = handlers.retrieve(request.getMethod() + " " + request.getPath(), request.getParameters()); + if (handler != null) { + return handler.handle(request); + } + return null; + } + + public static void main(final String[] args) throws Exception { if (args == null || args.length != 2) { throw new IllegalArgumentException("GoogleCloudStorageFixture "); } - final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 43635); - final HttpServer httpServer = MockHttpServer.createHttp(socketAddress, 0); + final GoogleCloudStorageFixture fixture = new GoogleCloudStorageFixture(args[0], args[1]); + fixture.listen(); + } - try { - final Path workingDirectory = Paths.get(args[0]); - /// Writes the PID of the current Java process in a `pid` file located in the working directory - writeFile(workingDirectory, "pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); + /** Builds the default request handlers **/ + private static PathTrie defaultHandlers(final Map buckets) { + final PathTrie handlers = new PathTrie<>(RestUtils.REST_DECODER); - final String addressAndPort = addressToString(httpServer.getAddress()); - // Writes the address and port of the http server in a `ports` file located in the working directory - writeFile(workingDirectory, "ports", addressAndPort); + // GET Bucket + // + // https://cloud.google.com/storage/docs/json_api/v1/buckets/get + handlers.insert("GET /storage/v1/b/{bucket}", (request) -> { + final String name = request.getParam("bucket"); + if (Strings.hasText(name) == false) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "bucket name is missing"); + } - // Emulates a Google Cloud Storage server - final String storageUrl = "http://" + addressAndPort; - final GoogleCloudStorageTestServer storageTestServer = new GoogleCloudStorageTestServer(storageUrl); - storageTestServer.createBucket(args[1]); + if (buckets.containsKey(name)) { + return newResponse(RestStatus.OK, emptyMap(), buildBucketResource(name)); + } else { + return newError(RestStatus.NOT_FOUND, "bucket not found"); + } + }); - httpServer.createContext("/", new ResponseHandler(storageTestServer)); - httpServer.start(); + // GET Object + // + // https://cloud.google.com/storage/docs/json_api/v1/objects/get + handlers.insert("GET /storage/v1/b/{bucket}/o/{object}", (request) -> { + final String objectName = request.getParam("object"); + if (Strings.hasText(objectName) == false) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing"); + } - // Wait to be killed - Thread.sleep(Long.MAX_VALUE); + final Bucket bucket = buckets.get(request.getParam("bucket")); + if (bucket == null) { + return newError(RestStatus.NOT_FOUND, "bucket not found"); + } - } finally { - httpServer.stop(0); - } - } + for (final Map.Entry object : bucket.objects.entrySet()) { + if (object.getKey().equals(objectName)) { + return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(bucket.name, objectName, object.getValue())); + } + } + return newError(RestStatus.NOT_FOUND, "object not found"); + }); - private static void writeFile(final Path dir, final String fileName, final String content) throws IOException { - final Path tempPidFile = Files.createTempFile(dir, null, null); - Files.write(tempPidFile, singleton(content)); - Files.move(tempPidFile, dir.resolve(fileName), StandardCopyOption.ATOMIC_MOVE); - } + // Delete Object + // + // https://cloud.google.com/storage/docs/json_api/v1/objects/delete + handlers.insert("DELETE /storage/v1/b/{bucket}/o/{object}", (request) -> { + final String objectName = request.getParam("object"); + if (Strings.hasText(objectName) == false) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing"); + } - private static String addressToString(final SocketAddress address) { - final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; - if (inetSocketAddress.getAddress() instanceof Inet6Address) { - return "[" + inetSocketAddress.getHostString() + "]:" + inetSocketAddress.getPort(); - } else { - return inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); - } - } + final Bucket bucket = buckets.get(request.getParam("bucket")); + if (bucket == null) { + return newError(RestStatus.NOT_FOUND, "bucket not found"); + } - @SuppressForbidden(reason = "Use a http server") - static class ResponseHandler implements HttpHandler { + final byte[] bytes = bucket.objects.remove(objectName); + if (bytes != null) { + return new Response(RestStatus.NO_CONTENT.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } + return newError(RestStatus.NOT_FOUND, "object not found"); + }); - private final GoogleCloudStorageTestServer storageServer; + // Insert Object (initialization) + // + // https://cloud.google.com/storage/docs/json_api/v1/objects/insert + handlers.insert("POST /upload/storage/v1/b/{bucket}/o", (request) -> { + final String ifGenerationMatch = request.getParam("ifGenerationMatch"); + if ("0".equals(ifGenerationMatch) == false) { + return newError(RestStatus.PRECONDITION_FAILED, "object already exist"); + } - private ResponseHandler(final GoogleCloudStorageTestServer storageServer) { - this.storageServer = storageServer; - } + final String uploadType = request.getParam("uploadType"); + if ("resumable".equals(uploadType)) { + final String objectName = request.getParam("name"); + if (Strings.hasText(objectName) == false) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing"); + } + final Bucket bucket = buckets.get(request.getParam("bucket")); + if (bucket == null) { + return newError(RestStatus.NOT_FOUND, "bucket not found"); + } + if (bucket.objects.putIfAbsent(objectName, EMPTY_BYTE) == null) { + final String location = /*endpoint +*/ "/upload/storage/v1/b/" + bucket.name + "/o?uploadType=resumable&upload_id=" + + objectName; + return newResponse(RestStatus.CREATED, singletonMap("Location", location), jsonBuilder()); + } else { + return newError(RestStatus.CONFLICT, "object already exist"); + } + } else if ("multipart".equals(uploadType)) { + /* + * A multipart/related request body looks like this (note the binary dump inside a text blob! nice!): + * --__END_OF_PART__ + * Content-Length: 135 + * Content-Type: application/json; charset=UTF-8 + * content-transfer-encoding: binary + * + * {"bucket":"bucket_test","crc32c":"7XacHQ==","md5Hash":"fVztGkklMlUamsSmJK7W+w==", + * "name":"tests-KEwE3bU4TuyetBgQIghmUw/master.dat-temp"} + * --__END_OF_PART__ + * content-transfer-encoding: binary + * + * KEwE3bU4TuyetBgQIghmUw + * --__END_OF_PART__-- + */ + String boundary = "__END_OF_PART__"; + // Determine the multipart boundary + final String contentType = request.getContentType(); + if ((contentType != null) && contentType.contains("multipart/related; boundary=")) { + boundary = contentType.replace("multipart/related; boundary=", ""); + } - @Override - public void handle(HttpExchange exchange) throws IOException { - String method = exchange.getRequestMethod(); - String path = storageServer.getEndpoint() + exchange.getRequestURI().getRawPath(); - String query = exchange.getRequestURI().getRawQuery(); - Map> headers = exchange.getRequestHeaders(); + InputStream inputStreamBody = new ByteArrayInputStream(request.getBody()); + final String contentEncoding = request.getHeader("Content-Encoding"); + if (contentEncoding != null) { + if ("gzip".equalsIgnoreCase(contentEncoding)) { + inputStreamBody = new GZIPInputStream(inputStreamBody); + } + } + // Read line by line ?both? parts of the multipart. Decoding headers as + // IS_8859_1 is safe. + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreamBody, StandardCharsets.ISO_8859_1))) { + String line; + // read first part delimiter + line = reader.readLine(); + if ((line == null) || (line.equals("--" + boundary) == false)) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, + "Error parsing multipart request. Does not start with the part delimiter."); + } + final Map> firstPartHeaders = new HashMap<>(); + // Reads the first part's headers, if any + while ((line = reader.readLine()) != null) { + if (line.equals("\r\n") || (line.length() == 0)) { + // end of headers + break; + } else { + final String[] header = line.split(":", 2); + firstPartHeaders.put(header[0], singletonList(header[1])); + } + } + final List firstPartContentTypes = firstPartHeaders.getOrDefault("Content-Type", + firstPartHeaders.get("Content-type")); + if ((firstPartContentTypes == null) + || (firstPartContentTypes.stream().noneMatch(x -> x.contains("application/json")))) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, + "Error parsing multipart request. Metadata part expected to have the \"application/json\" content type."); + } + // read metadata part, a single line + line = reader.readLine(); + final byte[] metadata = line.getBytes(StandardCharsets.ISO_8859_1); + if ((firstPartContentTypes != null) && (firstPartContentTypes.stream().anyMatch((x -> x.contains("charset=utf-8"))))) { + // decode as utf-8 + line = new String(metadata, StandardCharsets.UTF_8); + } + final Matcher objectNameMatcher = Pattern.compile("\"name\":\"([^\"]*)\"").matcher(line); + objectNameMatcher.find(); + final String objectName = objectNameMatcher.group(1); + final Matcher bucketNameMatcher = Pattern.compile("\"bucket\":\"([^\"]*)\"").matcher(line); + bucketNameMatcher.find(); + final String bucketName = bucketNameMatcher.group(1); + // read second part delimiter + line = reader.readLine(); + if ((line == null) || (line.equals("--" + boundary) == false)) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, + "Error parsing multipart request. Second part does not start with delimiter. " + + "Is the metadata multi-line?"); + } + final Map> secondPartHeaders = new HashMap<>(); + // Reads the second part's headers, if any + while ((line = reader.readLine()) != null) { + if (line.equals("\r\n") || (line.length() == 0)) { + // end of headers + break; + } else { + final String[] header = line.split(":", 2); + secondPartHeaders.put(header[0], singletonList(header[1])); + } + } + final List secondPartTransferEncoding = secondPartHeaders.getOrDefault("Content-Transfer-Encoding", + secondPartHeaders.get("content-transfer-encoding")); + if ((secondPartTransferEncoding == null) + || (secondPartTransferEncoding.stream().noneMatch(x -> x.contains("binary")))) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, + "Error parsing multipart request. Data part expected to have the \"binary\" content transfer encoding."); + } + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int c; + while ((c = reader.read()) != -1) { + // one char to one byte, because of the ISO_8859_1 encoding + baos.write(c); + } + final byte[] temp = baos.toByteArray(); + final byte[] trailingEnding = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.ISO_8859_1); + // check trailing + for (int i = trailingEnding.length - 1; i >= 0; i--) { + if (trailingEnding[i] != temp[(temp.length - trailingEnding.length) + i]) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "Error parsing multipart request."); + } + } + final Bucket bucket = buckets.get(bucketName); + if (bucket == null) { + return newError(RestStatus.NOT_FOUND, "bucket not found"); + } + final byte[] objectData = Arrays.copyOf(temp, temp.length - trailingEnding.length); + if ((objectName != null) && (bucketName != null) && (objectData != null)) { + bucket.objects.put(objectName, objectData); + return new Response(RestStatus.OK.getStatus(), JSON_CONTENT_TYPE, metadata); + } else { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "error parsing multipart request"); + } + } + } else { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "upload type must be resumable or multipart"); + } + }); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Streams.copy(exchange.getRequestBody(), out); + // Insert Object (upload) + // + // https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload + handlers.insert("PUT /upload/storage/v1/b/{bucket}/o", (request) -> { + final String objectId = request.getParam("upload_id"); + if (Strings.hasText(objectId) == false) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "upload id is missing"); + } - Response storageResponse = null; + final Bucket bucket = buckets.get(request.getParam("bucket")); + if (bucket == null) { + return newError(RestStatus.NOT_FOUND, "bucket not found"); + } - final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); - if (userAgent != null && userAgent.startsWith("Apache Ant")) { - // This is a request made by the AntFixture, just reply "OK" - storageResponse = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); - } else { - // Otherwise simulate a S3 response - storageResponse = storageServer.handle(method, path, query, headers, out.toByteArray()); + if (bucket.objects.containsKey(objectId) == false) { + return newError(RestStatus.NOT_FOUND, "object name not found"); + } + + bucket.objects.put(objectId, request.getBody()); + return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(bucket.name, objectId, request.getBody())); + }); + + // List Objects + // + // https://cloud.google.com/storage/docs/json_api/v1/objects/list + handlers.insert("GET /storage/v1/b/{bucket}/o", (request) -> { + final Bucket bucket = buckets.get(request.getParam("bucket")); + if (bucket == null) { + return newError(RestStatus.NOT_FOUND, "bucket not found"); + } + + final XContentBuilder builder = jsonBuilder(); + builder.startObject(); + builder.field("kind", "storage#objects"); + { + builder.startArray("items"); + + final String prefixParam = request.getParam("prefix"); + for (final Map.Entry object : bucket.objects.entrySet()) { + if ((prefixParam != null) && (object.getKey().startsWith(prefixParam) == false)) { + continue; + } + buildObjectResource(builder, bucket.name, object.getKey(), object.getValue()); + } + builder.endArray(); + } + builder.endObject(); + return newResponse(RestStatus.OK, emptyMap(), builder); + }); + + // Download Object + // + // https://cloud.google.com/storage/docs/request-body + handlers.insert("GET /download/storage/v1/b/{bucket}/o/{object}", (request) -> { + final String object = request.getParam("object"); + if (Strings.hasText(object) == false) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, "object id is missing"); + } + + final Bucket bucket = buckets.get(request.getParam("bucket")); + if (bucket == null) { + return newError(RestStatus.NOT_FOUND, "bucket not found"); } - Map> responseHeaders = exchange.getResponseHeaders(); - responseHeaders.put("Content-Type", singletonList(storageResponse.contentType)); - storageResponse.headers.forEach((k, v) -> responseHeaders.put(k, singletonList(v))); - exchange.sendResponseHeaders(storageResponse.status.getStatus(), storageResponse.body.length); - if (storageResponse.body.length > 0) { - exchange.getResponseBody().write(storageResponse.body); + if (bucket.objects.containsKey(object) == false) { + return newError(RestStatus.NOT_FOUND, "object name not found"); } - exchange.close(); + + return new Response(RestStatus.OK.getStatus(), contentType("application/octet-stream"), bucket.objects.get(object)); + }); + + // Batch + // + // https://cloud.google.com/storage/docs/json_api/v1/how-tos/batch + handlers.insert("POST /batch/storage/v1", (request) -> { + final List batchedResponses = new ArrayList<>(); + + // A batch request body looks like this: + // + // --__END_OF_PART__ + // Content-Length: 71 + // Content-Type: application/http + // content-id: 1 + // content-transfer-encoding: binary + // + // DELETE https://www.googleapis.com/storage/v1/b/ohifkgu/o/foo%2Ftest HTTP/1.1 + // + // + // --__END_OF_PART__ + // Content-Length: 71 + // Content-Type: application/http + // content-id: 2 + // content-transfer-encoding: binary + // + // DELETE https://www.googleapis.com/storage/v1/b/ohifkgu/o/bar%2Ftest HTTP/1.1 + // + // + // --__END_OF_PART__-- + + // Default multipart boundary + String boundary = "__END_OF_PART__"; + + // Determine the multipart boundary + final String contentType = request.getContentType(); + if ((contentType != null) && contentType.contains("multipart/mixed; boundary=")) { + boundary = contentType.replace("multipart/mixed; boundary=", ""); + } + + long batchedRequests = 0L; + + // Read line by line the batched requests + try (BufferedReader reader = new BufferedReader( + new InputStreamReader( + new ByteArrayInputStream(request.getBody()), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + // Start of a batched request + if (line.equals("--" + boundary)) { + final Map batchedHeaders = new HashMap<>(); + + // Reads the headers, if any + while ((line = reader.readLine()) != null) { + if (line.equals("\r\n") || (line.length() == 0)) { + // end of headers + break; + } else { + final String[] header = line.split(":", 2); + batchedHeaders.put(header[0], header[1]); + } + } + + // Reads the method and URL + line = reader.readLine(); + final String batchedMethod = line.substring(0, line.indexOf(' ')); + final URI batchedUri = URI.create(line.substring(batchedMethod.length() + 1, line.lastIndexOf(' '))); + + // Reads the body + line = reader.readLine(); + byte[] batchedBody = new byte[0]; + if ((line != null) || (line.startsWith("--" + boundary) == false)) { + batchedBody = line.getBytes(StandardCharsets.UTF_8); + } + + final Request batchedRequest = new Request(batchedRequests, batchedMethod, batchedUri, batchedHeaders, batchedBody); + batchedRequests = batchedRequests + 1; + + // Executes the batched request + final RequestHandler handler = + handlers.retrieve(batchedRequest.getMethod() + " " + batchedRequest.getPath(), batchedRequest.getParameters()); + if (handler != null) { + try { + batchedResponses.add(handler.handle(batchedRequest)); + } catch (final IOException e) { + batchedResponses.add(newError(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); + } + } + } + } + } + + // Now we can build the response + final String sep = "--"; + final String line = "\r\n"; + + final StringBuilder builder = new StringBuilder(); + for (final Response response : batchedResponses) { + builder.append(sep).append(boundary).append(line); + builder.append("Content-Type: application/http").append(line); + builder.append(line); + builder.append("HTTP/1.1 ") + .append(response.getStatus()) + .append(' ') + .append(RestStatus.fromCode(response.getStatus()).toString()) + .append(line); + builder.append("Content-Length: ").append(response.getBody().length).append(line); + builder.append("Content-Type: ").append(response.getContentType()).append(line); + response.getHeaders().forEach((k, v) -> builder.append(k).append(": ").append(v).append(line)); + builder.append(line); + builder.append(new String(response.getBody(), StandardCharsets.UTF_8)).append(line); + builder.append(line); + } + builder.append(line); + builder.append(sep).append(boundary).append(sep); + + final byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8); + return new Response(RestStatus.OK.getStatus(), contentType("multipart/mixed; boundary=" + boundary), content); + }); + + // Fake refresh of an OAuth2 token + // + handlers.insert("POST /o/oauth2/token", (request) -> + newResponse(RestStatus.OK, emptyMap(), jsonBuilder() + .startObject() + .field("access_token", "unknown") + .field("token_type", "Bearer") + .field("expires_in", 3600) + .endObject()) + ); + + return handlers; + } + + /** + * Represents a Storage bucket as if it was created on Google Cloud Storage. + */ + static class Bucket { + + /** Bucket name **/ + final String name; + + /** Blobs contained in the bucket **/ + final Map objects; + + Bucket(final String name) { + this.name = Objects.requireNonNull(name); + this.objects = ConcurrentCollections.newConcurrentMap(); + } + } + + /** + * Builds a JSON response + */ + private static Response newResponse(final RestStatus status, final Map headers, final XContentBuilder xContentBuilder) { + final Map responseHeaders = new HashMap<>(JSON_CONTENT_TYPE); + responseHeaders.putAll(headers); + + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + BytesReference.bytes(xContentBuilder).writeTo(out); + + return new Response(status.getStatus(), responseHeaders, out.toByteArray()); + } catch (final IOException e) { + return newError(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage()); } } + + /** + * Storage Error JSON representation + */ + private static Response newError(final RestStatus status, final String message) { + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + try (XContentBuilder builder = jsonBuilder()) { + builder.startObject() + .startObject("error") + .field("code", status.getStatus()) + .field("message", message) + .startArray("errors") + .startObject() + .field("domain", "global") + .field("reason", status.toString()) + .field("message", message) + .endObject() + .endArray() + .endObject() + .endObject(); + BytesReference.bytes(builder).writeTo(out); + } + return new Response(status.getStatus(), JSON_CONTENT_TYPE, out.toByteArray()); + } catch (final IOException e) { + final byte[] bytes = (message != null ? message : "something went wrong").getBytes(StandardCharsets.UTF_8); + return new Response(RestStatus.INTERNAL_SERVER_ERROR.getStatus(), TEXT_PLAIN_CONTENT_TYPE, bytes); + } + } + + /** + * Storage Bucket JSON representation as defined in + * https://cloud.google.com/storage/docs/json_api/v1/bucket#resource + */ + private static XContentBuilder buildBucketResource(final String name) throws IOException { + return jsonBuilder().startObject() + .field("kind", "storage#bucket") + .field("name", name) + .field("id", name) + .endObject(); + } + + /** + * Storage Object JSON representation as defined in + * https://cloud.google.com/storage/docs/json_api/v1/objects#resource + */ + private static XContentBuilder buildObjectResource(final String bucket, final String name, final byte[] bytes) throws IOException { + return buildObjectResource(jsonBuilder(), bucket, name, bytes); + } + + /** + * Storage Object JSON representation as defined in + * https://cloud.google.com/storage/docs/json_api/v1/objects#resource + */ + private static XContentBuilder buildObjectResource(final XContentBuilder builder, + final String bucket, + final String name, + final byte[] bytes) throws IOException { + return builder.startObject() + .field("kind", "storage#object") + .field("id", String.join("/", bucket, name)) + .field("name", name) + .field("bucket", bucket) + .field("size", String.valueOf(bytes.length)) + .endObject(); + } } diff --git a/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageTestServer.java b/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageTestServer.java deleted file mode 100644 index fd09b46c73fc1..0000000000000 --- a/plugins/repository-gcs/qa/google-cloud-storage/src/test/java/org/elasticsearch/repositories/gcs/GoogleCloudStorageTestServer.java +++ /dev/null @@ -1,663 +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.repositories.gcs; - -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.path.PathTrie; -import org.elasticsearch.common.util.concurrent.ConcurrentCollections; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.rest.RestUtils; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.zip.GZIPInputStream; - -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonList; -import static java.util.Collections.singletonMap; -import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; - -/** - * {@link GoogleCloudStorageTestServer} emulates a Google Cloud Storage service through - * a {@link #handle(String, String, String, Map, byte[])} method that provides appropriate - * responses for specific requests like the real Google Cloud platform would do. - * It is largely based on official documentation available at https://cloud.google.com/storage/docs/json_api/v1/. - */ -public class GoogleCloudStorageTestServer { - - private static final byte[] EMPTY_BYTE = new byte[0]; - - /** List of the buckets stored on this test server **/ - private final Map buckets = ConcurrentCollections.newConcurrentMap(); - - /** Request handlers for the requests made by the Google Cloud Storage client **/ - private final PathTrie handlers; - - /** Server endpoint **/ - private final String endpoint; - - /** - * Creates a {@link GoogleCloudStorageTestServer} with a custom endpoint - */ - GoogleCloudStorageTestServer(final String endpoint) { - this.endpoint = Objects.requireNonNull(endpoint, "endpoint must not be null"); - this.handlers = defaultHandlers(endpoint, buckets); - } - - /** Creates a bucket in the test server **/ - void createBucket(final String bucketName) { - buckets.put(bucketName, new Bucket(bucketName)); - } - - public String getEndpoint() { - return endpoint; - } - - /** - * Returns a Google Cloud Storage response for the given request - * - * @param method the HTTP method of the request - * @param path the path of the URL of the request - * @param query the queryString of the URL of request - * @param headers the HTTP headers of the request - * @param body the HTTP request body - * @return a {@link Response} - * @throws IOException if something goes wrong - */ - public Response handle(final String method, - final String path, - final String query, - final Map> headers, - byte[] body) throws IOException { - - final Map params = new HashMap<>(); - if (query != null) { - RestUtils.decodeQueryString(query, 0, params); - } - - final RequestHandler handler = handlers.retrieve(method + " " + path, params); - if (handler != null) { - return handler.execute(params, headers, body); - } else { - return newError(RestStatus.INTERNAL_SERVER_ERROR, - "No handler defined for request [method: " + method + ", path: " + path + "]"); - } - } - - @FunctionalInterface - interface RequestHandler { - - /** - * Simulates the execution of a Storage request and returns a corresponding response. - * - * @param params the request's query string parameters - * @param headers the request's headers - * @param body the request body provided as a byte array - * @return the corresponding response - * - * @throws IOException if something goes wrong - */ - Response execute(Map params, Map> headers, byte[] body) throws IOException; - } - - /** Builds the default request handlers **/ - private static PathTrie defaultHandlers(final String endpoint, final Map buckets) { - final PathTrie handlers = new PathTrie<>(RestUtils.REST_DECODER); - - // GET Bucket - // - // https://cloud.google.com/storage/docs/json_api/v1/buckets/get - handlers.insert("GET " + endpoint + "/storage/v1/b/{bucket}", (params, headers, body) -> { - final String name = params.get("bucket"); - if (Strings.hasText(name) == false) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "bucket name is missing"); - } - - if (buckets.containsKey(name)) { - return newResponse(RestStatus.OK, emptyMap(), buildBucketResource(name)); - } else { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - }); - - // GET Object - // - // https://cloud.google.com/storage/docs/json_api/v1/objects/get - handlers.insert("GET " + endpoint + "/storage/v1/b/{bucket}/o/{object}", (params, headers, body) -> { - final String objectName = params.get("object"); - if (Strings.hasText(objectName) == false) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing"); - } - - final Bucket bucket = buckets.get(params.get("bucket")); - if (bucket == null) { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - - for (final Map.Entry object : bucket.objects.entrySet()) { - if (object.getKey().equals(objectName)) { - return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(bucket.name, objectName, object.getValue())); - } - } - return newError(RestStatus.NOT_FOUND, "object not found"); - }); - - // Delete Object - // - // https://cloud.google.com/storage/docs/json_api/v1/objects/delete - handlers.insert("DELETE " + endpoint + "/storage/v1/b/{bucket}/o/{object}", (params, headers, body) -> { - final String objectName = params.get("object"); - if (Strings.hasText(objectName) == false) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing"); - } - - final Bucket bucket = buckets.get(params.get("bucket")); - if (bucket == null) { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - - final byte[] bytes = bucket.objects.remove(objectName); - if (bytes != null) { - return new Response(RestStatus.NO_CONTENT, emptyMap(), XContentType.JSON.mediaType(), EMPTY_BYTE); - } - return newError(RestStatus.NOT_FOUND, "object not found"); - }); - - // Insert Object (initialization) - // - // https://cloud.google.com/storage/docs/json_api/v1/objects/insert - handlers.insert("POST " + endpoint + "/upload/storage/v1/b/{bucket}/o", (params, headers, body) -> { - final String uploadType = params.get("uploadType"); - if ("resumable".equals(uploadType)) { - final String objectName = params.get("name"); - if (Strings.hasText(objectName) == false) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "object name is missing"); - } - final Bucket bucket = buckets.get(params.get("bucket")); - if (bucket == null) { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - if (bucket.objects.putIfAbsent(objectName, EMPTY_BYTE) == null) { - final String location = endpoint + "/upload/storage/v1/b/" + bucket.name + "/o?uploadType=resumable&upload_id=" - + objectName; - return new Response(RestStatus.CREATED, singletonMap("Location", location), XContentType.JSON.mediaType(), EMPTY_BYTE); - } else { - return newError(RestStatus.CONFLICT, "object already exist"); - } - } else if ("multipart".equals(uploadType)) { - /* - * A multipart/related request body looks like this (note the binary dump inside a text blob! nice!): - * --__END_OF_PART__ - * Content-Length: 135 - * Content-Type: application/json; charset=UTF-8 - * content-transfer-encoding: binary - * - * {"bucket":"bucket_test","crc32c":"7XacHQ==","md5Hash":"fVztGkklMlUamsSmJK7W+w==", - * "name":"tests-KEwE3bU4TuyetBgQIghmUw/master.dat-temp"} - * --__END_OF_PART__ - * content-transfer-encoding: binary - * - * KEwE3bU4TuyetBgQIghmUw - * --__END_OF_PART__-- - */ - String boundary = "__END_OF_PART__"; - // Determine the multipart boundary - final List contentTypes = headers.getOrDefault("Content-Type", headers.get("Content-type")); - if (contentTypes != null) { - final String contentType = contentTypes.get(0); - if ((contentType != null) && contentType.contains("multipart/related; boundary=")) { - boundary = contentType.replace("multipart/related; boundary=", ""); - } - } - InputStream inputStreamBody = new ByteArrayInputStream(body); - final List contentEncodings = headers.getOrDefault("Content-Encoding", headers.get("Content-encoding")); - if (contentEncodings != null) { - if (contentEncodings.stream().anyMatch(x -> "gzip".equalsIgnoreCase(x))) { - inputStreamBody = new GZIPInputStream(inputStreamBody); - } - } - // Read line by line ?both? parts of the multipart. Decoding headers as - // IS_8859_1 is safe. - try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreamBody, StandardCharsets.ISO_8859_1))) { - String line; - // read first part delimiter - line = reader.readLine(); - if ((line == null) || (line.equals("--" + boundary) == false)) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, - "Error parsing multipart request. Does not start with the part delimiter."); - } - final Map> firstPartHeaders = new HashMap<>(); - // Reads the first part's headers, if any - while ((line = reader.readLine()) != null) { - if (line.equals("\r\n") || (line.length() == 0)) { - // end of headers - break; - } else { - final String[] header = line.split(":", 2); - firstPartHeaders.put(header[0], singletonList(header[1])); - } - } - final List firstPartContentTypes = firstPartHeaders.getOrDefault("Content-Type", - firstPartHeaders.get("Content-type")); - if ((firstPartContentTypes == null) - || (firstPartContentTypes.stream().noneMatch(x -> x.contains("application/json")))) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, - "Error parsing multipart request. Metadata part expected to have the \"application/json\" content type."); - } - // read metadata part, a single line - line = reader.readLine(); - final byte[] metadata = line.getBytes(StandardCharsets.ISO_8859_1); - if ((firstPartContentTypes != null) && (firstPartContentTypes.stream().anyMatch((x -> x.contains("charset=utf-8"))))) { - // decode as utf-8 - line = new String(metadata, StandardCharsets.UTF_8); - } - final Matcher objectNameMatcher = Pattern.compile("\"name\":\"([^\"]*)\"").matcher(line); - objectNameMatcher.find(); - final String objectName = objectNameMatcher.group(1); - final Matcher bucketNameMatcher = Pattern.compile("\"bucket\":\"([^\"]*)\"").matcher(line); - bucketNameMatcher.find(); - final String bucketName = bucketNameMatcher.group(1); - // read second part delimiter - line = reader.readLine(); - if ((line == null) || (line.equals("--" + boundary) == false)) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, - "Error parsing multipart request. Second part does not start with delimiter. " - + "Is the metadata multi-line?"); - } - final Map> secondPartHeaders = new HashMap<>(); - // Reads the second part's headers, if any - while ((line = reader.readLine()) != null) { - if (line.equals("\r\n") || (line.length() == 0)) { - // end of headers - break; - } else { - final String[] header = line.split(":", 2); - secondPartHeaders.put(header[0], singletonList(header[1])); - } - } - final List secondPartTransferEncoding = secondPartHeaders.getOrDefault("Content-Transfer-Encoding", - secondPartHeaders.get("content-transfer-encoding")); - if ((secondPartTransferEncoding == null) - || (secondPartTransferEncoding.stream().noneMatch(x -> x.contains("binary")))) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, - "Error parsing multipart request. Data part expected to have the \"binary\" content transfer encoding."); - } - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int c; - while ((c = reader.read()) != -1) { - // one char to one byte, because of the ISO_8859_1 encoding - baos.write(c); - } - final byte[] temp = baos.toByteArray(); - final byte[] trailingEnding = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.ISO_8859_1); - // check trailing - for (int i = trailingEnding.length - 1; i >= 0; i--) { - if (trailingEnding[i] != temp[(temp.length - trailingEnding.length) + i]) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "Error parsing multipart request."); - } - } - final Bucket bucket = buckets.get(bucketName); - if (bucket == null) { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - final byte[] objectData = Arrays.copyOf(temp, temp.length - trailingEnding.length); - if ((objectName != null) && (bucketName != null) && (objectData != null)) { - bucket.objects.put(objectName, objectData); - return new Response(RestStatus.OK, emptyMap(), XContentType.JSON.mediaType(), metadata); - } else { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "error parsing multipart request"); - } - } - } else { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "upload type must be resumable or multipart"); - } - }); - - // Insert Object (upload) - // - // https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload - handlers.insert("PUT " + endpoint + "/upload/storage/v1/b/{bucket}/o", (params, headers, body) -> { - final String objectId = params.get("upload_id"); - if (Strings.hasText(objectId) == false) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "upload id is missing"); - } - - final Bucket bucket = buckets.get(params.get("bucket")); - if (bucket == null) { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - - if (bucket.objects.containsKey(objectId) == false) { - return newError(RestStatus.NOT_FOUND, "object name not found"); - } - - bucket.objects.put(objectId, body); - return newResponse(RestStatus.OK, emptyMap(), buildObjectResource(bucket.name, objectId, body)); - }); - - // List Objects - // - // https://cloud.google.com/storage/docs/json_api/v1/objects/list - handlers.insert("GET " + endpoint + "/storage/v1/b/{bucket}/o", (params, headers, body) -> { - final Bucket bucket = buckets.get(params.get("bucket")); - if (bucket == null) { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - - final XContentBuilder builder = jsonBuilder(); - builder.startObject(); - builder.field("kind", "storage#objects"); - { - builder.startArray("items"); - - final String prefixParam = params.get("prefix"); - for (final Map.Entry object : bucket.objects.entrySet()) { - if ((prefixParam != null) && (object.getKey().startsWith(prefixParam) == false)) { - continue; - } - buildObjectResource(builder, bucket.name, object.getKey(), object.getValue()); - } - builder.endArray(); - } - builder.endObject(); - return newResponse(RestStatus.OK, emptyMap(), builder); - }); - - // Download Object - // - // https://cloud.google.com/storage/docs/request-body - handlers.insert("GET " + endpoint + "/download/storage/v1/b/{bucket}/o/{object}", (params, headers, body) -> { - final String object = params.get("object"); - if (Strings.hasText(object) == false) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, "object id is missing"); - } - - final Bucket bucket = buckets.get(params.get("bucket")); - if (bucket == null) { - return newError(RestStatus.NOT_FOUND, "bucket not found"); - } - - if (bucket.objects.containsKey(object) == false) { - return newError(RestStatus.NOT_FOUND, "object name not found"); - } - - return new Response(RestStatus.OK, emptyMap(), "application/octet-stream", bucket.objects.get(object)); - }); - - // Batch - // - // https://cloud.google.com/storage/docs/json_api/v1/how-tos/batch - handlers.insert("POST " + endpoint + "/batch/storage/v1", (params, headers, body) -> { - final List batchedResponses = new ArrayList<>(); - - // A batch request body looks like this: - // - // --__END_OF_PART__ - // Content-Length: 71 - // Content-Type: application/http - // content-id: 1 - // content-transfer-encoding: binary - // - // DELETE https://www.googleapis.com/storage/v1/b/ohifkgu/o/foo%2Ftest HTTP/1.1 - // - // - // --__END_OF_PART__ - // Content-Length: 71 - // Content-Type: application/http - // content-id: 2 - // content-transfer-encoding: binary - // - // DELETE https://www.googleapis.com/storage/v1/b/ohifkgu/o/bar%2Ftest HTTP/1.1 - // - // - // --__END_OF_PART__-- - - // Default multipart boundary - String boundary = "__END_OF_PART__"; - - // Determine the multipart boundary - final List contentTypes = headers.getOrDefault("Content-Type", headers.get("Content-type")); - if (contentTypes != null) { - final String contentType = contentTypes.get(0); - if ((contentType != null) && contentType.contains("multipart/mixed; boundary=")) { - boundary = contentType.replace("multipart/mixed; boundary=", ""); - } - } - - // Read line by line the batched requests - try (BufferedReader reader = new BufferedReader( - new InputStreamReader( - new ByteArrayInputStream(body), StandardCharsets.UTF_8))) { - String line; - while ((line = reader.readLine()) != null) { - // Start of a batched request - if (line.equals("--" + boundary)) { - final Map> batchedHeaders = new HashMap<>(); - - // Reads the headers, if any - while ((line = reader.readLine()) != null) { - if (line.equals("\r\n") || (line.length() == 0)) { - // end of headers - break; - } else { - final String[] header = line.split(":", 2); - batchedHeaders.put(header[0], singletonList(header[1])); - } - } - - // Reads the method and URL - line = reader.readLine(); - final String batchedUrl = line.substring(0, line.lastIndexOf(' ')); - - final Map batchedParams = new HashMap<>(); - final int questionMark = batchedUrl.indexOf('?'); - if (questionMark != -1) { - RestUtils.decodeQueryString(batchedUrl.substring(questionMark + 1), 0, batchedParams); - } - - // Reads the body - line = reader.readLine(); - byte[] batchedBody = new byte[0]; - if ((line != null) || (line.startsWith("--" + boundary) == false)) { - batchedBody = line.getBytes(StandardCharsets.UTF_8); - } - - // Executes the batched request - final RequestHandler handler = handlers.retrieve(batchedUrl, batchedParams); - if (handler != null) { - try { - batchedResponses.add(handler.execute(batchedParams, batchedHeaders, batchedBody)); - } catch (final IOException e) { - batchedResponses.add(newError(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage())); - } - } - } - } - } - - // Now we can build the response - final String sep = "--"; - final String line = "\r\n"; - - final StringBuilder builder = new StringBuilder(); - for (final Response response : batchedResponses) { - builder.append(sep).append(boundary).append(line); - builder.append("Content-Type: application/http").append(line); - builder.append(line); - builder.append("HTTP/1.1 ") - .append(response.status.getStatus()) - .append(' ') - .append(response.status.toString()) - .append(line); - builder.append("Content-Length: ").append(response.body.length).append(line); - builder.append("Content-Type: ").append(response.contentType).append(line); - response.headers.forEach((k, v) -> builder.append(k).append(": ").append(v).append(line)); - builder.append(line); - builder.append(new String(response.body, StandardCharsets.UTF_8)).append(line); - builder.append(line); - } - builder.append(line); - builder.append(sep).append(boundary).append(sep); - - final byte[] content = builder.toString().getBytes(StandardCharsets.UTF_8); - return new Response(RestStatus.OK, emptyMap(), "multipart/mixed; boundary=" + boundary, content); - }); - - // Fake refresh of an OAuth2 token - // - handlers.insert("POST " + endpoint + "/o/oauth2/token", (url, params, req) -> - newResponse(RestStatus.OK, emptyMap(), jsonBuilder() - .startObject() - .field("access_token", "unknown") - .field("token_type", "Bearer") - .field("expires_in", 3600) - .endObject()) - ); - - return handlers; - } - - /** - * Represents a Storage bucket as if it was created on Google Cloud Storage. - */ - static class Bucket { - - /** Bucket name **/ - final String name; - - /** Blobs contained in the bucket **/ - final Map objects; - - Bucket(final String name) { - this.name = Objects.requireNonNull(name); - this.objects = ConcurrentCollections.newConcurrentMap(); - } - } - - /** - * Represents a Storage HTTP Response. - */ - static class Response { - - final RestStatus status; - final Map headers; - final String contentType; - final byte[] body; - - Response(final RestStatus status, final Map headers, final String contentType, final byte[] body) { - this.status = Objects.requireNonNull(status); - this.headers = Objects.requireNonNull(headers); - this.contentType = Objects.requireNonNull(contentType); - this.body = Objects.requireNonNull(body); - } - } - - /** - * Builds a JSON response - */ - private static Response newResponse(final RestStatus status, final Map headers, final XContentBuilder xContentBuilder) { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - BytesReference.bytes(xContentBuilder).writeTo(out); - return new Response(status, headers, XContentType.JSON.mediaType(), out.toByteArray()); - } catch (final IOException e) { - return newError(RestStatus.INTERNAL_SERVER_ERROR, e.getMessage()); - } - } - - /** - * Storage Error JSON representation - */ - private static Response newError(final RestStatus status, final String message) { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - try (XContentBuilder builder = jsonBuilder()) { - builder.startObject() - .startObject("error") - .field("code", status.getStatus()) - .field("message", message) - .startArray("errors") - .startObject() - .field("domain", "global") - .field("reason", status.toString()) - .field("message", message) - .endObject() - .endArray() - .endObject() - .endObject(); - BytesReference.bytes(builder).writeTo(out); - } - return new Response(status, emptyMap(), XContentType.JSON.mediaType(), out.toByteArray()); - } catch (final IOException e) { - final byte[] bytes = (message != null ? message : "something went wrong").getBytes(StandardCharsets.UTF_8); - return new Response(RestStatus.INTERNAL_SERVER_ERROR, emptyMap(), " text/plain", bytes); - } - } - - /** - * Storage Bucket JSON representation as defined in - * https://cloud.google.com/storage/docs/json_api/v1/bucket#resource - */ - private static XContentBuilder buildBucketResource(final String name) throws IOException { - return jsonBuilder().startObject() - .field("kind", "storage#bucket") - .field("name", name) - .field("id", name) - .endObject(); - } - - /** - * Storage Object JSON representation as defined in - * https://cloud.google.com/storage/docs/json_api/v1/objects#resource - */ - private static XContentBuilder buildObjectResource(final String bucket, final String name, final byte[] bytes) throws IOException { - return buildObjectResource(jsonBuilder(), bucket, name, bytes); - } - - /** - * Storage Object JSON representation as defined in - * https://cloud.google.com/storage/docs/json_api/v1/objects#resource - */ - private static XContentBuilder buildObjectResource(final XContentBuilder builder, - final String bucket, - final String name, - final byte[] bytes) throws IOException { - return builder.startObject() - .field("kind", "storage#object") - .field("id", String.join("/", bucket, name)) - .field("name", name) - .field("bucket", bucket) - .field("size", String.valueOf(bytes.length)) - .endObject(); - } -} diff --git a/plugins/repository-s3/qa/amazon-s3/build.gradle b/plugins/repository-s3/qa/amazon-s3/build.gradle index 5e288899021a1..dbbffdebded47 100644 --- a/plugins/repository-s3/qa/amazon-s3/build.gradle +++ b/plugins/repository-s3/qa/amazon-s3/build.gradle @@ -31,12 +31,6 @@ integTestCluster { plugin ':plugins:repository-s3' } -forbiddenApisTest { - // we are using jdk-internal instead of jdk-non-portable to allow for com.sun.net.httpserver.* usage - bundledSignatures -= 'jdk-non-portable' - bundledSignatures += 'jdk-internal' -} - boolean useFixture = false String s3AccessKey = System.getenv("amazon_s3_access_key") @@ -54,7 +48,7 @@ if (!s3AccessKey && !s3SecretKey && !s3Bucket && !s3BasePath) { /** A task to start the AmazonS3Fixture which emulates a S3 service **/ task s3Fixture(type: AntFixture) { - dependsOn compileTestJava + dependsOn testClasses env 'CLASSPATH', "${ -> project.sourceSets.test.runtimeClasspath.asPath }" executable = new File(project.runtimeJavaHome, 'bin/java') args 'org.elasticsearch.repositories.s3.AmazonS3Fixture', baseDir, s3Bucket @@ -64,6 +58,7 @@ Map expansions = [ 'bucket': s3Bucket, 'base_path': s3BasePath ] + processTestResources { inputs.properties(expansions) MavenFilteringHack.filter(it, expansions) diff --git a/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java b/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java index cf123f85d98a9..20e21675acb79 100644 --- a/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java +++ b/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3Fixture.java @@ -18,132 +18,423 @@ */ package org.elasticsearch.repositories.s3; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.test.fixture.AbstractHttpFixture; +import com.amazonaws.util.DateUtils; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.Streams; -import org.elasticsearch.mocksocket.MockHttpServer; -import org.elasticsearch.repositories.s3.AmazonS3TestServer.Response; +import org.elasticsearch.common.path.PathTrie; +import org.elasticsearch.common.util.concurrent.ConcurrentCollections; import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.rest.RestUtils; -import java.io.ByteArrayOutputStream; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; /** - * {@link AmazonS3Fixture} is a fixture that emulates a S3 service. - *

- * It starts an asynchronous socket server that binds to a random local port. The server parses - * HTTP requests and uses a {@link AmazonS3TestServer} to handle them before returning - * them to the client as HTTP responses. + * {@link AmazonS3Fixture} emulates an AWS S3 service + * . + * he implementation is based on official documentation available at https://docs.aws.amazon.com/AmazonS3/latest/API/. */ -public class AmazonS3Fixture { +public class AmazonS3Fixture extends AbstractHttpFixture { - public static void main(String[] args) throws Exception { + /** List of the buckets stored on this test server **/ + private final Map buckets = ConcurrentCollections.newConcurrentMap(); + + /** Request handlers for the requests made by the S3 client **/ + private final PathTrie handlers; + + /** + * Creates a {@link AmazonS3Fixture} + */ + private AmazonS3Fixture(final String workingDir, final String bucket) { + super(workingDir); + this.buckets.put(bucket, new Bucket(bucket)); + this.handlers = defaultHandlers(buckets); + } + + @Override + protected Response handle(final Request request) throws IOException { + final RequestHandler handler = handlers.retrieve(request.getMethod() + " " + request.getPath(), request.getParameters()); + if (handler != null) { + final String authorization = request.getHeader("Authorization"); + if (authorization == null + || (authorization.length() > 0 && authorization.contains("s3_integration_test_access_key") == false)) { + return newError(request.getId(), RestStatus.FORBIDDEN, "AccessDenied", "Access Denied", ""); + } + return handler.handle(request); + } + return null; + } + + public static void main(final String[] args) throws Exception { if (args == null || args.length != 2) { throw new IllegalArgumentException("AmazonS3Fixture "); } - final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); - final HttpServer httpServer = MockHttpServer.createHttp(socketAddress, 0); + final AmazonS3Fixture fixture = new AmazonS3Fixture(args[0], args[1]); + fixture.listen(); + } - try { - final Path workingDirectory = workingDir(args[0]); - /// Writes the PID of the current Java process in a `pid` file located in the working directory - writeFile(workingDirectory, "pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); + /** Builds the default request handlers **/ + private static PathTrie defaultHandlers(final Map buckets) { + final PathTrie handlers = new PathTrie<>(RestUtils.REST_DECODER); - final String addressAndPort = addressToString(httpServer.getAddress()); - // Writes the address and port of the http server in a `ports` file located in the working directory - writeFile(workingDirectory, "ports", addressAndPort); + // HEAD Object + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html + objectsPaths("HEAD /{bucket}").forEach(path -> + handlers.insert(path, (request) -> { + final String bucketName = request.getParam("bucket"); - // Emulates S3 - final String storageUrl = "http://" + addressAndPort; - final AmazonS3TestServer storageTestServer = new AmazonS3TestServer(storageUrl); - storageTestServer.createBucket(args[1]); + final Bucket bucket = buckets.get(bucketName); + if (bucket == null) { + return newBucketNotFoundError(request.getId(), bucketName); + } - httpServer.createContext("/", new ResponseHandler(storageTestServer)); - httpServer.start(); + final String objectName = objectName(request.getParameters()); + for (Map.Entry object : bucket.objects.entrySet()) { + if (object.getKey().equals(objectName)) { + return new Response(RestStatus.OK.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } + } + return newObjectNotFoundError(request.getId(), objectName); + }) + ); - // Wait to be killed - Thread.sleep(Long.MAX_VALUE); + // PUT Object + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html + objectsPaths("PUT /{bucket}").forEach(path -> + handlers.insert(path, (request) -> { + final String destBucketName = request.getParam("bucket"); - } finally { - httpServer.stop(0); - } - } + final Bucket destBucket = buckets.get(destBucketName); + if (destBucket == null) { + return newBucketNotFoundError(request.getId(), destBucketName); + } - @SuppressForbidden(reason = "Paths#get is fine - we don't have environment here") - private static Path workingDir(final String dir) { - return Paths.get(dir); - } + final String destObjectName = objectName(request.getParameters()); + + // This is a chunked upload request. We should have the header "Content-Encoding : aws-chunked,gzip" + // to detect it but it seems that the AWS SDK does not follow the S3 guidelines here. + // + // See https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html + // + String headerDecodedContentLength = request.getHeader("X-amz-decoded-content-length"); + if (headerDecodedContentLength != null) { + int contentLength = Integer.valueOf(headerDecodedContentLength); + + // Chunked requests have a payload like this: + // + // 105;chunk-signature=01d0de6be013115a7f4794db8c4b9414e6ec71262cc33ae562a71f2eaed1efe8 + // ... bytes of data .... + // 0;chunk-signature=f890420b1974c5469aaf2112e9e6f2e0334929fd45909e03c0eff7a84124f6a4 + // + try (BufferedInputStream inputStream = new BufferedInputStream(new ByteArrayInputStream(request.getBody()))) { + int b; + // Moves to the end of the first signature line + while ((b = inputStream.read()) != -1) { + if (b == '\n') { + break; + } + } + + final byte[] bytes = new byte[contentLength]; + inputStream.read(bytes, 0, contentLength); + + destBucket.objects.put(destObjectName, bytes); + return new Response(RestStatus.OK.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } + } + + return newInternalError(request.getId(), "Something is wrong with this PUT request"); + }) + ); + + // DELETE Object + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html + objectsPaths("DELETE /{bucket}").forEach(path -> + handlers.insert(path, (request) -> { + final String bucketName = request.getParam("bucket"); + + final Bucket bucket = buckets.get(bucketName); + if (bucket == null) { + return newBucketNotFoundError(request.getId(), bucketName); + } + + final String objectName = objectName(request.getParameters()); + if (bucket.objects.remove(objectName) != null) { + return new Response(RestStatus.OK.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } + return newObjectNotFoundError(request.getId(), objectName); + }) + ); + + // GET Object + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html + objectsPaths("GET /{bucket}").forEach(path -> + handlers.insert(path, (request) -> { + final String bucketName = request.getParam("bucket"); + + final Bucket bucket = buckets.get(bucketName); + if (bucket == null) { + return newBucketNotFoundError(request.getId(), bucketName); + } + + final String objectName = objectName(request.getParameters()); + if (bucket.objects.containsKey(objectName)) { + return new Response(RestStatus.OK.getStatus(), contentType("application/octet-stream"), bucket.objects.get(objectName)); + + } + return newObjectNotFoundError(request.getId(), objectName); + }) + ); + + // HEAD Bucket + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketHEAD.html + handlers.insert("HEAD /{bucket}", (request) -> { + String bucket = request.getParam("bucket"); + if (Strings.hasText(bucket) && buckets.containsKey(bucket)) { + return new Response(RestStatus.OK.getStatus(), TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } else { + return newBucketNotFoundError(request.getId(), bucket); + } + }); + + // GET Bucket (List Objects) Version 1 + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html + handlers.insert("GET /{bucket}/", (request) -> { + final String bucketName = request.getParam("bucket"); + + final Bucket bucket = buckets.get(bucketName); + if (bucket == null) { + return newBucketNotFoundError(request.getId(), bucketName); + } + + String prefix = request.getParam("prefix"); + if (prefix == null) { + prefix = request.getHeader("Prefix"); + } + return newListBucketResultResponse(request.getId(), bucket, prefix); + }); + + // Delete Multiple Objects + // + // https://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html + handlers.insert("POST /", (request) -> { + final List deletes = new ArrayList<>(); + final List errors = new ArrayList<>(); - private static void writeFile(final Path dir, final String fileName, final String content) throws IOException { - final Path tempPidFile = Files.createTempFile(dir, null, null); - Files.write(tempPidFile, singleton(content)); - Files.move(tempPidFile, dir.resolve(fileName), StandardCopyOption.ATOMIC_MOVE); + if (request.getParam("delete") != null) { + // The request body is something like: + // ...... + String requestBody = Streams.copyToString(new InputStreamReader(new ByteArrayInputStream(request.getBody()), UTF_8)); + if (requestBody.startsWith("")) { + final String startMarker = ""; + final String endMarker = ""; + + int offset = 0; + while (offset != -1) { + offset = requestBody.indexOf(startMarker, offset); + if (offset > 0) { + int closingOffset = requestBody.indexOf(endMarker, offset); + if (closingOffset != -1) { + offset = offset + startMarker.length(); + final String objectName = requestBody.substring(offset, closingOffset); + + boolean found = false; + for (Bucket bucket : buckets.values()) { + if (bucket.objects.remove(objectName) != null) { + found = true; + } + } + + if (found) { + deletes.add(objectName); + } else { + errors.add(objectName); + } + } + } + } + return newDeleteResultResponse(request.getId(), deletes, errors); + } + } + return newInternalError(request.getId(), "Something is wrong with this POST multiple deletes request"); + }); + + return handlers; } - private static String addressToString(final SocketAddress address) { - final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; - if (inetSocketAddress.getAddress() instanceof Inet6Address) { - return "[" + inetSocketAddress.getHostString() + "]:" + inetSocketAddress.getPort(); - } else { - return inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); + /** + * Represents a S3 bucket. + */ + static class Bucket { + + /** Bucket name **/ + final String name; + + /** Blobs contained in the bucket **/ + final Map objects; + + Bucket(final String name) { + this.name = Objects.requireNonNull(name); + this.objects = ConcurrentCollections.newConcurrentMap(); } } - static class ResponseHandler implements HttpHandler { + /** + * Decline a path like "http://host:port/{bucket}" into 10 derived paths like: + * - http://host:port/{bucket}/{path0} + * - http://host:port/{bucket}/{path0}/{path1} + * - http://host:port/{bucket}/{path0}/{path1}/{path2} + * - etc + */ + private static List objectsPaths(final String path) { + final List paths = new ArrayList<>(); + String p = path; + for (int i = 0; i < 10; i++) { + p = p + "/{path" + i + "}"; + paths.add(p); + } + return paths; + } - private final AmazonS3TestServer storageServer; + /** + * Retrieves the object name from all derives paths named {pathX} where 0 <= X < 10. + * + * This is the counterpart of {@link #objectsPaths(String)} + */ + private static String objectName(final Map params) { + final StringBuilder name = new StringBuilder(); + for (int i = 0; i < 10; i++) { + String value = params.getOrDefault("path" + i, null); + if (value != null) { + if (name.length() > 0) { + name.append('/'); + } + name.append(value); + } + } + return name.toString(); + } - private ResponseHandler(final AmazonS3TestServer storageServer) { - this.storageServer = storageServer; + /** + * S3 ListBucketResult Response + */ + private static Response newListBucketResultResponse(final long requestId, final Bucket bucket, final String prefix) { + final String id = Long.toString(requestId); + final StringBuilder response = new StringBuilder(); + response.append(""); + response.append(""); + response.append(""); + if (prefix != null) { + response.append(prefix); } + response.append(""); + response.append(""); + response.append("1000"); + response.append("false"); - @Override - public void handle(HttpExchange exchange) throws IOException { - String method = exchange.getRequestMethod(); - String path = storageServer.getEndpoint() + exchange.getRequestURI().getRawPath(); - String query = exchange.getRequestURI().getRawQuery(); - Map> headers = exchange.getRequestHeaders(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Streams.copy(exchange.getRequestBody(), out); - - Response storageResponse = null; - - final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); - if (userAgent != null && userAgent.startsWith("Apache Ant")) { - // This is a request made by the AntFixture, just reply "OK" - storageResponse = new Response(RestStatus.OK, emptyMap(), "text/plain; charset=utf-8", "OK".getBytes(UTF_8)); - } else { - // Otherwise simulate a S3 response - storageResponse = storageServer.handle(method, path, query, headers, out.toByteArray()); + int count = 0; + for (Map.Entry object : bucket.objects.entrySet()) { + String objectName = object.getKey(); + if (prefix == null || objectName.startsWith(prefix)) { + response.append(""); + response.append("").append(objectName).append(""); + response.append("").append(DateUtils.formatISO8601Date(new Date())).append(""); + response.append(""").append(count++).append("""); + response.append("").append(object.getValue().length).append(""); + response.append(""); } + } + response.append(""); - Map> responseHeaders = exchange.getResponseHeaders(); - responseHeaders.put("Content-Type", singletonList(storageResponse.contentType)); - storageResponse.headers.forEach((k, v) -> responseHeaders.put(k, singletonList(v))); - exchange.sendResponseHeaders(storageResponse.status.getStatus(), storageResponse.body.length); - if (storageResponse.body.length > 0) { - exchange.getResponseBody().write(storageResponse.body); - } - exchange.close(); + final Map headers = new HashMap<>(contentType("application/xml")); + headers.put("x-amz-request-id", id); + + return new Response(RestStatus.OK.getStatus(), headers, response.toString().getBytes(UTF_8)); + } + + /** + * S3 DeleteResult Response + */ + private static Response newDeleteResultResponse(final long requestId, + final List deletedObjects, + final List ignoredObjects) { + final String id = Long.toString(requestId); + + final StringBuilder response = new StringBuilder(); + response.append(""); + response.append(""); + for (String deletedObject : deletedObjects) { + response.append(""); + response.append("").append(deletedObject).append(""); + response.append(""); + } + for (String ignoredObject : ignoredObjects) { + response.append(""); + response.append("").append(ignoredObject).append(""); + response.append("NoSuchKey"); + response.append(""); } + response.append(""); + + final Map headers = new HashMap<>(contentType("application/xml")); + headers.put("x-amz-request-id", id); + + return new Response(RestStatus.OK.getStatus(), headers, response.toString().getBytes(UTF_8)); + } + + private static Response newBucketNotFoundError(final long requestId, final String bucket) { + return newError(requestId, RestStatus.NOT_FOUND, "NoSuchBucket", "The specified bucket does not exist", bucket); + } + + private static Response newObjectNotFoundError(final long requestId, final String object) { + return newError(requestId, RestStatus.NOT_FOUND, "NoSuchKey", "The specified key does not exist", object); + } + + private static Response newInternalError(final long requestId, final String resource) { + return newError(requestId, RestStatus.INTERNAL_SERVER_ERROR, "InternalError", "We encountered an internal error", resource); + } + + /** + * S3 Error + * + * https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html + */ + private static Response newError(final long requestId, + final RestStatus status, + final String code, + final String message, + final String resource) { + final String id = Long.toString(requestId); + final StringBuilder response = new StringBuilder(); + response.append(""); + response.append(""); + response.append("").append(code).append(""); + response.append("").append(message).append(""); + response.append("").append(resource).append(""); + response.append("").append(id).append(""); + response.append(""); + + final Map headers = new HashMap<>(contentType("application/xml")); + headers.put("x-amz-request-id", id); + + return new Response(status.getStatus(), headers, response.toString().getBytes(UTF_8)); } } diff --git a/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3TestServer.java b/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3TestServer.java deleted file mode 100644 index 029b28320d259..0000000000000 --- a/plugins/repository-s3/qa/amazon-s3/src/test/java/org/elasticsearch/repositories/s3/AmazonS3TestServer.java +++ /dev/null @@ -1,500 +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.repositories.s3; - -import com.amazonaws.util.DateUtils; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.io.Streams; -import org.elasticsearch.common.path.PathTrie; -import org.elasticsearch.common.util.concurrent.ConcurrentCollections; -import org.elasticsearch.rest.RestStatus; -import org.elasticsearch.rest.RestUtils; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; -import static java.util.Collections.singletonMap; - -/** - * {@link AmazonS3TestServer} emulates a S3 service through a {@link #handle(String, String, String, Map, byte[])} - * method that provides appropriate responses for specific requests like the real S3 platform would do. - * It is largely based on official documentation available at https://docs.aws.amazon.com/AmazonS3/latest/API/. - */ -public class AmazonS3TestServer { - - private static byte[] EMPTY_BYTE = new byte[0]; - /** List of the buckets stored on this test server **/ - private final Map buckets = ConcurrentCollections.newConcurrentMap(); - - /** Request handlers for the requests made by the S3 client **/ - private final PathTrie handlers; - - /** Server endpoint **/ - private final String endpoint; - - /** Increments for the requests ids **/ - private final AtomicLong requests = new AtomicLong(0); - - /** - * Creates a {@link AmazonS3TestServer} with a custom endpoint - */ - AmazonS3TestServer(final String endpoint) { - this.endpoint = Objects.requireNonNull(endpoint, "endpoint must not be null"); - this.handlers = defaultHandlers(endpoint, buckets); - } - - /** Creates a bucket in the test server **/ - void createBucket(final String bucketName) { - buckets.put(bucketName, new Bucket(bucketName)); - } - - public String getEndpoint() { - return endpoint; - } - - /** - * Returns a response for the given request - * - * @param method the HTTP method of the request - * @param path the path of the URL of the request - * @param query the queryString of the URL of request - * @param headers the HTTP headers of the request - * @param body the HTTP request body - * @return a {@link Response} - * @throws IOException if something goes wrong - */ - public Response handle(final String method, - final String path, - final String query, - final Map> headers, - byte[] body) throws IOException { - - final long requestId = requests.incrementAndGet(); - - final Map params = new HashMap<>(); - if (query != null) { - RestUtils.decodeQueryString(query, 0, params); - } - - final List authorizations = headers.get("Authorization"); - if (authorizations == null - || (authorizations.isEmpty() == false & authorizations.get(0).contains("s3_integration_test_access_key") == false)) { - return newError(requestId, RestStatus.FORBIDDEN, "AccessDenied", "Access Denied", ""); - } - - final RequestHandler handler = handlers.retrieve(method + " " + path, params); - if (handler != null) { - return handler.execute(params, headers, body, requestId); - } else { - return newInternalError(requestId, "No handler defined for request [method: " + method + ", path: " + path + "]"); - } - } - - @FunctionalInterface - interface RequestHandler { - - /** - * Simulates the execution of a S3 request and returns a corresponding response. - * - * @param params the request's query string parameters - * @param headers the request's headers - * @param body the request body provided as a byte array - * @param requestId a unique id for the incoming request - * @return the corresponding response - * - * @throws IOException if something goes wrong - */ - Response execute(Map params, Map> headers, byte[] body, long requestId) throws IOException; - } - - /** Builds the default request handlers **/ - private static PathTrie defaultHandlers(final String endpoint, final Map buckets) { - final PathTrie handlers = new PathTrie<>(RestUtils.REST_DECODER); - - // HEAD Object - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html - objectsPaths("HEAD " + endpoint + "/{bucket}").forEach(path -> - handlers.insert(path, (params, headers, body, id) -> { - final String bucketName = params.get("bucket"); - - final Bucket bucket = buckets.get(bucketName); - if (bucket == null) { - return newBucketNotFoundError(id, bucketName); - } - - final String objectName = objectName(params); - for (Map.Entry object : bucket.objects.entrySet()) { - if (object.getKey().equals(objectName)) { - return new Response(RestStatus.OK, emptyMap(), "text/plain", EMPTY_BYTE); - } - } - return newObjectNotFoundError(id, objectName); - }) - ); - - // PUT Object - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html - objectsPaths("PUT " + endpoint + "/{bucket}").forEach(path -> - handlers.insert(path, (params, headers, body, id) -> { - final String destBucketName = params.get("bucket"); - - final Bucket destBucket = buckets.get(destBucketName); - if (destBucket == null) { - return newBucketNotFoundError(id, destBucketName); - } - - final String destObjectName = objectName(params); - - // This is a chunked upload request. We should have the header "Content-Encoding : aws-chunked,gzip" - // to detect it but it seems that the AWS SDK does not follow the S3 guidelines here. - // - // See https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html - // - List headerDecodedContentLength = headers.getOrDefault("X-amz-decoded-content-length", emptyList()); - if (headerDecodedContentLength.size() == 1) { - int contentLength = Integer.valueOf(headerDecodedContentLength.get(0)); - - // Chunked requests have a payload like this: - // - // 105;chunk-signature=01d0de6be013115a7f4794db8c4b9414e6ec71262cc33ae562a71f2eaed1efe8 - // ... bytes of data .... - // 0;chunk-signature=f890420b1974c5469aaf2112e9e6f2e0334929fd45909e03c0eff7a84124f6a4 - // - try (BufferedInputStream inputStream = new BufferedInputStream(new ByteArrayInputStream(body))) { - int b; - // Moves to the end of the first signature line - while ((b = inputStream.read()) != -1) { - if (b == '\n') { - break; - } - } - - final byte[] bytes = new byte[contentLength]; - inputStream.read(bytes, 0, contentLength); - - destBucket.objects.put(destObjectName, bytes); - return new Response(RestStatus.OK, emptyMap(), "text/plain", EMPTY_BYTE); - } - } - - return newInternalError(id, "Something is wrong with this PUT request"); - }) - ); - - // DELETE Object - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html - objectsPaths("DELETE " + endpoint + "/{bucket}").forEach(path -> - handlers.insert(path, (params, headers, body, id) -> { - final String bucketName = params.get("bucket"); - - final Bucket bucket = buckets.get(bucketName); - if (bucket == null) { - return newBucketNotFoundError(id, bucketName); - } - - final String objectName = objectName(params); - if (bucket.objects.remove(objectName) != null) { - return new Response(RestStatus.OK, emptyMap(), "text/plain", EMPTY_BYTE); - } - return newObjectNotFoundError(id, objectName); - }) - ); - - // GET Object - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html - objectsPaths("GET " + endpoint + "/{bucket}").forEach(path -> - handlers.insert(path, (params, headers, body, id) -> { - final String bucketName = params.get("bucket"); - - final Bucket bucket = buckets.get(bucketName); - if (bucket == null) { - return newBucketNotFoundError(id, bucketName); - } - - final String objectName = objectName(params); - if (bucket.objects.containsKey(objectName)) { - return new Response(RestStatus.OK, emptyMap(), "application/octet-stream", bucket.objects.get(objectName)); - - } - return newObjectNotFoundError(id, objectName); - }) - ); - - // HEAD Bucket - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketHEAD.html - handlers.insert("HEAD " + endpoint + "/{bucket}", (params, headers, body, id) -> { - String bucket = params.get("bucket"); - if (Strings.hasText(bucket) && buckets.containsKey(bucket)) { - return new Response(RestStatus.OK, emptyMap(), "text/plain", EMPTY_BYTE); - } else { - return newBucketNotFoundError(id, bucket); - } - }); - - // GET Bucket (List Objects) Version 1 - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html - handlers.insert("GET " + endpoint + "/{bucket}/", (params, headers, body, id) -> { - final String bucketName = params.get("bucket"); - - final Bucket bucket = buckets.get(bucketName); - if (bucket == null) { - return newBucketNotFoundError(id, bucketName); - } - - String prefix = params.get("prefix"); - if (prefix == null) { - List prefixes = headers.get("Prefix"); - if (prefixes != null && prefixes.size() == 1) { - prefix = prefixes.get(0); - } - } - return newListBucketResultResponse(id, bucket, prefix); - }); - - // Delete Multiple Objects - // - // https://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html - handlers.insert("POST " + endpoint + "/", (params, headers, body, id) -> { - final List deletes = new ArrayList<>(); - final List errors = new ArrayList<>(); - - if (params.containsKey("delete")) { - // The request body is something like: - // ...... - String request = Streams.copyToString(new InputStreamReader(new ByteArrayInputStream(body), StandardCharsets.UTF_8)); - if (request.startsWith("")) { - final String startMarker = ""; - final String endMarker = ""; - - int offset = 0; - while (offset != -1) { - offset = request.indexOf(startMarker, offset); - if (offset > 0) { - int closingOffset = request.indexOf(endMarker, offset); - if (closingOffset != -1) { - offset = offset + startMarker.length(); - final String objectName = request.substring(offset, closingOffset); - - boolean found = false; - for (Bucket bucket : buckets.values()) { - if (bucket.objects.remove(objectName) != null) { - found = true; - } - } - - if (found) { - deletes.add(objectName); - } else { - errors.add(objectName); - } - } - } - } - return newDeleteResultResponse(id, deletes, errors); - } - } - return newInternalError(id, "Something is wrong with this POST multiple deletes request"); - }); - - return handlers; - } - - /** - * Represents a S3 bucket. - */ - static class Bucket { - - /** Bucket name **/ - final String name; - - /** Blobs contained in the bucket **/ - final Map objects; - - Bucket(final String name) { - this.name = Objects.requireNonNull(name); - this.objects = ConcurrentCollections.newConcurrentMap(); - } - } - - /** - * Represents a HTTP Response. - */ - static class Response { - - final RestStatus status; - final Map headers; - final String contentType; - final byte[] body; - - Response(final RestStatus status, final Map headers, final String contentType, final byte[] body) { - this.status = Objects.requireNonNull(status); - this.headers = Objects.requireNonNull(headers); - this.contentType = Objects.requireNonNull(contentType); - this.body = Objects.requireNonNull(body); - } - } - - /** - * Decline a path like "http://host:port/{bucket}" into 10 derived paths like: - * - http://host:port/{bucket}/{path0} - * - http://host:port/{bucket}/{path0}/{path1} - * - http://host:port/{bucket}/{path0}/{path1}/{path2} - * - etc - */ - private static List objectsPaths(final String path) { - final List paths = new ArrayList<>(); - String p = path; - for (int i = 0; i < 10; i++) { - p = p + "/{path" + i + "}"; - paths.add(p); - } - return paths; - } - - /** - * Retrieves the object name from all derives paths named {pathX} where 0 <= X < 10. - * - * This is the counterpart of {@link #objectsPaths(String)} - */ - private static String objectName(final Map params) { - final StringBuilder name = new StringBuilder(); - for (int i = 0; i < 10; i++) { - String value = params.getOrDefault("path" + i, null); - if (value != null) { - if (name.length() > 0) { - name.append('/'); - } - name.append(value); - } - } - return name.toString(); - } - - /** - * S3 ListBucketResult Response - */ - private static Response newListBucketResultResponse(final long requestId, final Bucket bucket, final String prefix) { - final String id = Long.toString(requestId); - final StringBuilder response = new StringBuilder(); - response.append(""); - response.append(""); - response.append(""); - if (prefix != null) { - response.append(prefix); - } - response.append(""); - response.append(""); - response.append("1000"); - response.append("false"); - - int count = 0; - for (Map.Entry object : bucket.objects.entrySet()) { - String objectName = object.getKey(); - if (prefix == null || objectName.startsWith(prefix)) { - response.append(""); - response.append("").append(objectName).append(""); - response.append("").append(DateUtils.formatISO8601Date(new Date())).append(""); - response.append(""").append(count++).append("""); - response.append("").append(object.getValue().length).append(""); - response.append(""); - } - } - response.append(""); - return new Response(RestStatus.OK, singletonMap("x-amz-request-id", id), "application/xml", response.toString().getBytes(UTF_8)); - } - - /** - * S3 DeleteResult Response - */ - private static Response newDeleteResultResponse(final long requestId, - final List deletedObjects, - final List ignoredObjects) { - final String id = Long.toString(requestId); - - final StringBuilder response = new StringBuilder(); - response.append(""); - response.append(""); - for (String deletedObject : deletedObjects) { - response.append(""); - response.append("").append(deletedObject).append(""); - response.append(""); - } - for (String ignoredObject : ignoredObjects) { - response.append(""); - response.append("").append(ignoredObject).append(""); - response.append("NoSuchKey"); - response.append(""); - } - response.append(""); - return new Response(RestStatus.OK, singletonMap("x-amz-request-id", id), "application/xml", response.toString().getBytes(UTF_8)); - } - - private static Response newBucketNotFoundError(final long requestId, final String bucket) { - return newError(requestId, RestStatus.NOT_FOUND, "NoSuchBucket", "The specified bucket does not exist", bucket); - } - - private static Response newObjectNotFoundError(final long requestId, final String object) { - return newError(requestId, RestStatus.NOT_FOUND, "NoSuchKey", "The specified key does not exist", object); - } - - private static Response newInternalError(final long requestId, final String resource) { - return newError(requestId, RestStatus.INTERNAL_SERVER_ERROR, "InternalError", "We encountered an internal error", resource); - } - - /** - * S3 Error - * - * https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html - */ - private static Response newError(final long requestId, - final RestStatus status, - final String code, - final String message, - final String resource) { - final String id = Long.toString(requestId); - final StringBuilder response = new StringBuilder(); - response.append(""); - response.append(""); - response.append("").append(code).append(""); - response.append("").append(message).append(""); - response.append("").append(resource).append(""); - response.append("").append(id).append(""); - response.append(""); - return new Response(status, singletonMap("x-amz-request-id", id), "application/xml", response.toString().getBytes(UTF_8)); - } -} diff --git a/settings.gradle b/settings.gradle index 9ec0ccb9d7b3e..b11c984b4f074 100644 --- a/settings.gradle +++ b/settings.gradle @@ -34,7 +34,6 @@ List projects = [ 'server', 'server:cli', 'test:framework', - 'test:fixtures:example-fixture', 'test:fixtures:hdfs-fixture', 'test:fixtures:krb5kdc-fixture', 'test:fixtures:old-elasticsearch', diff --git a/test/fixtures/example-fixture/build.gradle b/test/fixtures/example-fixture/build.gradle deleted file mode 100644 index ce562e89abb7f..0000000000000 --- a/test/fixtures/example-fixture/build.gradle +++ /dev/null @@ -1,26 +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. - */ - -apply plugin: 'elasticsearch.build' -test.enabled = false -// Not published so no need to assemble -tasks.remove(assemble) -build.dependsOn.remove('assemble') - -dependenciesInfo.enabled = false diff --git a/test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java b/test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java deleted file mode 100644 index 96103d8eaa900..0000000000000 --- a/test/fixtures/example-fixture/src/main/java/example/ExampleTestFixture.java +++ /dev/null @@ -1,78 +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 example; - -import com.sun.net.httpserver.HttpServer; - -import java.lang.management.ManagementFactory; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.Collections; - -/** Crappy example test fixture that responds with TEST and closes the connection */ -public class ExampleTestFixture { - public static void main(String args[]) throws Exception { - if (args.length != 1) { - throw new IllegalArgumentException("ExampleTestFixture "); - } - Path dir = Paths.get(args[0]); - - final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); - final HttpServer httpServer = HttpServer.create(socketAddress, 0); - - // write pid file - Path tmp = Files.createTempFile(dir, null, null); - String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; - Files.write(tmp, Collections.singleton(pid)); - Files.move(tmp, dir.resolve("pid"), StandardCopyOption.ATOMIC_MOVE); - - // write port file - tmp = Files.createTempFile(dir, null, null); - InetSocketAddress bound = httpServer.getAddress(); - if (bound.getAddress() instanceof Inet6Address) { - Files.write(tmp, Collections.singleton("[" + bound.getHostString() + "]:" + bound.getPort())); - } else { - Files.write(tmp, Collections.singleton(bound.getHostString() + ":" + bound.getPort())); - } - Files.move(tmp, dir.resolve("ports"), StandardCopyOption.ATOMIC_MOVE); - - final byte[] response = "TEST\n".getBytes(StandardCharsets.UTF_8); - - // go time - httpServer.createContext("/", exchange -> { - try { - exchange.sendResponseHeaders(200, response.length); - exchange.getResponseBody().write(response); - } finally { - exchange.close(); - } - }); - httpServer.start(); - - // wait forever, until you kill me - Thread.sleep(Long.MAX_VALUE); - } -} diff --git a/test/framework/src/main/java/org/elasticsearch/test/fixture/AbstractHttpFixture.java b/test/framework/src/main/java/org/elasticsearch/test/fixture/AbstractHttpFixture.java new file mode 100644 index 0000000000000..daa70298224d0 --- /dev/null +++ b/test/framework/src/main/java/org/elasticsearch/test/fixture/AbstractHttpFixture.java @@ -0,0 +1,312 @@ +/* + * 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.test.fixture; + +import com.sun.net.httpserver.HttpServer; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; + +/** + * Base class for test fixtures that requires a {@link HttpServer} to work. + */ +public abstract class AbstractHttpFixture { + + protected static final Map TEXT_PLAIN_CONTENT_TYPE = contentType("text/plain; charset=utf-8"); + protected static final Map JSON_CONTENT_TYPE = contentType("application/json; charset=utf-8"); + + protected static final byte[] EMPTY_BYTE = new byte[0]; + + /** Increments for the requests ids **/ + private final AtomicLong requests = new AtomicLong(0); + + /** Current working directory of the fixture **/ + private final Path workingDirectory; + + protected AbstractHttpFixture(final String workingDir) { + this.workingDirectory = Paths.get(Objects.requireNonNull(workingDir)); + } + + /** + * Opens a {@link HttpServer} and start listening on a random port. + */ + public final void listen() throws IOException, InterruptedException { + final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); + final HttpServer httpServer = HttpServer.create(socketAddress, 0); + + try { + /// Writes the PID of the current Java process in a `pid` file located in the working directory + writeFile(workingDirectory, "pid", ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); + + final String addressAndPort = addressToString(httpServer.getAddress()); + // Writes the address and port of the http server in a `ports` file located in the working directory + writeFile(workingDirectory, "ports", addressAndPort); + + httpServer.createContext("/", exchange -> { + try { + Response response; + + // Check if this is a request made by the AntFixture + final String userAgent = exchange.getRequestHeaders().getFirst("User-Agent"); + if (userAgent != null + && userAgent.startsWith("Apache Ant") + && "GET".equals(exchange.getRequestMethod()) + && "/".equals(exchange.getRequestURI().getPath())) { + response = new Response(200, TEXT_PLAIN_CONTENT_TYPE, "OK".getBytes(UTF_8)); + + } else { + try { + final long requestId = requests.getAndIncrement(); + final String method = exchange.getRequestMethod(); + + + final Map headers = new HashMap<>(); + for (Map.Entry> header : exchange.getRequestHeaders().entrySet()) { + headers.put(header.getKey(), exchange.getRequestHeaders().getFirst(header.getKey())); + } + + final ByteArrayOutputStream body = new ByteArrayOutputStream(); + try (InputStream requestBody = exchange.getRequestBody()) { + final byte[] buffer = new byte[1024]; + int i; + while ((i = requestBody.read(buffer, 0, buffer.length)) != -1) { + body.write(buffer, 0, i); + } + body.flush(); + } + + final Request request = new Request(requestId, method, exchange.getRequestURI(), headers, body.toByteArray()); + response = handle(request); + + } catch (Exception e) { + final String error = e.getMessage() != null ? e.getMessage() : "Exception when processing the request"; + response = new Response(500, singletonMap("Content-Type", "text/plain; charset=utf-8"), error.getBytes(UTF_8)); + } + } + + if (response == null) { + response = new Response(400, TEXT_PLAIN_CONTENT_TYPE, EMPTY_BYTE); + } + + response.headers.forEach((k, v) -> exchange.getResponseHeaders().put(k, singletonList(v))); + if (response.body.length > 0) { + exchange.sendResponseHeaders(response.status, response.body.length); + exchange.getResponseBody().write(response.body); + } else { + exchange.sendResponseHeaders(response.status, -1); + } + } finally { + exchange.close(); + } + }); + httpServer.start(); + + // Wait to be killed + Thread.sleep(Long.MAX_VALUE); + + } finally { + httpServer.stop(0); + } + } + + protected abstract Response handle(Request request) throws IOException; + + @FunctionalInterface + public interface RequestHandler { + Response handle(Request request) throws IOException; + } + + /** + * Represents a HTTP Response. + */ + protected static class Response { + + private final int status; + private final Map headers; + private final byte[] body; + + public Response(final int status, final Map headers, final byte[] body) { + this.status = status; + this.headers = Objects.requireNonNull(headers); + this.body = Objects.requireNonNull(body); + } + + public int getStatus() { + return status; + } + + public Map getHeaders() { + return headers; + } + + public byte[] getBody() { + return body; + } + + public String getContentType() { + for (String header : headers.keySet()) { + if (header.equalsIgnoreCase("Content-Type")) { + return headers.get(header); + } + } + return null; + } + + @Override + public String toString() { + return "Response{" + + "status=" + status + + ", headers=" + headers + + ", body=" + new String(body, UTF_8) + + '}'; + } + } + + /** + * Represents a HTTP Request. + */ + protected static class Request { + + private final long id; + private final String method; + private final URI uri; + private final Map parameters; + private final Map headers; + private final byte[] body; + + public Request(final long id, final String method, final URI uri, final Map headers, final byte[] body) { + this.id = id; + this.method = Objects.requireNonNull(method); + this.uri = Objects.requireNonNull(uri); + this.headers = Objects.requireNonNull(headers); + this.body = Objects.requireNonNull(body); + + final Map params = new HashMap<>(); + if (uri.getQuery() != null && uri.getQuery().length() > 0) { + for (String param : uri.getQuery().split("&")) { + int i = param.indexOf("="); + if (i > 0) { + params.put(param.substring(0, i), param.substring(i + 1)); + } else { + params.put(param, ""); + } + } + } + this.parameters = params; + } + + public long getId() { + return id; + } + + public String getMethod() { + return method; + } + + public Map getHeaders() { + return headers; + } + + public String getHeader(final String headerName) { + for (String header : headers.keySet()) { + if (header.equalsIgnoreCase(headerName)) { + return headers.get(header); + } + } + return null; + } + + public byte[] getBody() { + return body; + } + + public String getPath() { + return uri.getRawPath(); + } + + public Map getParameters() { + return parameters; + } + + public String getParam(final String paramName) { + for (String param : parameters.keySet()) { + if (param.equals(paramName)) { + return parameters.get(param); + } + } + return null; + } + + public String getContentType() { + return getHeader("Content-Type"); + } + + @Override + public String toString() { + return "Request{" + + "method='" + method + '\'' + + ", uri=" + uri + + ", parameters=" + parameters + + ", headers=" + headers + + ", body=" + body + + '}'; + } + } + + private static void writeFile(final Path dir, final String fileName, final String content) throws IOException { + final Path tempPidFile = Files.createTempFile(dir, null, null); + Files.write(tempPidFile, singleton(content)); + Files.move(tempPidFile, dir.resolve(fileName), StandardCopyOption.ATOMIC_MOVE); + } + + private static String addressToString(final SocketAddress address) { + final InetSocketAddress inetSocketAddress = (InetSocketAddress) address; + if (inetSocketAddress.getAddress() instanceof Inet6Address) { + return "[" + inetSocketAddress.getHostString() + "]:" + inetSocketAddress.getPort(); + } else { + return inetSocketAddress.getHostString() + ":" + inetSocketAddress.getPort(); + } + } + + protected static Map contentType(final String contentType) { + return singletonMap("Content-Type", contentType); + } +} From 83053321242006c1fd6b652be041e7a0690f5231 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Thu, 14 Jun 2018 18:07:29 +0300 Subject: [PATCH 71/71] SQL: Fix build on Java 10 Due to a runtime classpath clash, featureAware task was failing on JVMs higher than 1.8 (since the ASM version from Painless was used instead which does not recognized Java 9 or 10 bytecode) causing the task to fail. This commit excludes the ASM dependency (since it's not used by SQL itself). --- x-pack/plugin/sql/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugin/sql/build.gradle b/x-pack/plugin/sql/build.gradle index 8b406235985c6..19dd1a08ec6f6 100644 --- a/x-pack/plugin/sql/build.gradle +++ b/x-pack/plugin/sql/build.gradle @@ -20,7 +20,10 @@ integTest.enabled = false dependencies { compileOnly "org.elasticsearch.plugin:x-pack-core:${version}" - compileOnly project(':modules:lang-painless') + compileOnly(project(':modules:lang-painless')) { + // exclude ASM to not affect featureAware task on Java 10+ + exclude group: "org.ow2.asm" + } compile project('sql-proto') compile "org.elasticsearch.plugin:aggs-matrix-stats-client:${version}" compile "org.antlr:antlr4-runtime:4.5.3"