diff --git a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 840a62e5e91..30b9fb7e28d 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -212,9 +212,9 @@ final class Bootstrap { node = new Node(environment) { @Override protected void validateNodeBeforeAcceptingRequests( - final Settings settings, + final BootstrapContext context, final BoundTransportAddress boundTransportAddress, List checks) throws NodeValidationException { - BootstrapChecks.check(settings, boundTransportAddress, checks); + BootstrapChecks.check(context, boundTransportAddress, checks); } }; } diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java index ffe52dfe5b9..a2620b2560c 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapCheck.java @@ -27,9 +27,10 @@ public interface BootstrapCheck { /** * Test if the node fails the check. * + * @param context the bootstrap context for more sophisticated checks * @return {@code true} if the node failed the check */ - boolean check(); + boolean check(BootstrapContext context); /** * The error message for a failed check. @@ -41,5 +42,4 @@ public interface BootstrapCheck { default boolean alwaysEnforce() { return false; } - } diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java index 4adec75ae67..b13f36229f9 100644 --- a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapChecks.java @@ -26,7 +26,6 @@ import org.apache.lucene.util.Constants; import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.logging.Loggers; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.discovery.DiscoveryModule; @@ -65,18 +64,18 @@ final class BootstrapChecks { * {@code es.enforce.bootstrap.checks} is set to {@code true} then the bootstrap checks will be enforced regardless of whether or not * the transport protocol is bound to a non-loopback interface. * - * @param settings the current node settings + * @param context the current node bootstrap context * @param boundTransportAddress the node network bindings */ - static void check(final Settings settings, final BoundTransportAddress boundTransportAddress, List additionalChecks) - throws NodeValidationException { - final List builtInChecks = checks(settings); + static void check(final BootstrapContext context, final BoundTransportAddress boundTransportAddress, + List additionalChecks) throws NodeValidationException { + final List builtInChecks = checks(); final List combinedChecks = new ArrayList<>(builtInChecks); combinedChecks.addAll(additionalChecks); - check( - enforceLimits(boundTransportAddress, DiscoveryModule.DISCOVERY_TYPE_SETTING.get(settings)), + check( context, + enforceLimits(boundTransportAddress, DiscoveryModule.DISCOVERY_TYPE_SETTING.get(context.settings)), Collections.unmodifiableList(combinedChecks), - Node.NODE_NAME_SETTING.get(settings)); + Node.NODE_NAME_SETTING.get(context.settings)); } /** @@ -84,15 +83,17 @@ final class BootstrapChecks { * property {@code es.enforce.bootstrap.checks} is set to {@code true} then the bootstrap checks will be enforced regardless of whether * or not the transport protocol is bound to a non-loopback interface. * + * @param context the current node boostrap context * @param enforceLimits {@code true} if the checks should be enforced or otherwise warned * @param checks the checks to execute * @param nodeName the node name to be used as a logging prefix */ static void check( + final BootstrapContext context, final boolean enforceLimits, final List checks, final String nodeName) throws NodeValidationException { - check(enforceLimits, checks, Loggers.getLogger(BootstrapChecks.class, nodeName)); + check(context, enforceLimits, checks, Loggers.getLogger(BootstrapChecks.class, nodeName)); } /** @@ -100,11 +101,13 @@ final class BootstrapChecks { * property {@code es.enforce.bootstrap.checks }is set to {@code true} then the bootstrap checks will be enforced regardless of whether * or not the transport protocol is bound to a non-loopback interface. * + * @param context the current node boostrap context * @param enforceLimits {@code true} if the checks should be enforced or otherwise warned * @param checks the checks to execute * @param logger the logger to */ static void check( + final BootstrapContext context, final boolean enforceLimits, final List checks, final Logger logger) throws NodeValidationException { @@ -134,7 +137,7 @@ final class BootstrapChecks { } for (final BootstrapCheck check : checks) { - if (check.check()) { + if (check.check(context)) { if (!(enforceLimits || enforceBootstrapChecks) && !check.alwaysEnforce()) { ignoredErrors.add(check.errorMessage()); } else { @@ -180,13 +183,13 @@ final class BootstrapChecks { } // the list of checks to execute - static List checks(final Settings settings) { + static List checks() { final List checks = new ArrayList<>(); checks.add(new HeapSizeCheck()); final FileDescriptorCheck fileDescriptorCheck = Constants.MAC_OS_X ? new OsXFileDescriptorCheck() : new FileDescriptorCheck(); checks.add(fileDescriptorCheck); - checks.add(new MlockallCheck(BootstrapSettings.MEMORY_LOCK_SETTING.get(settings))); + checks.add(new MlockallCheck()); if (Constants.LINUX) { checks.add(new MaxNumberOfThreadsCheck()); } @@ -201,7 +204,7 @@ final class BootstrapChecks { } checks.add(new ClientJvmCheck()); checks.add(new UseSerialGCCheck()); - checks.add(new SystemCallFilterCheck(BootstrapSettings.SYSTEM_CALL_FILTER_SETTING.get(settings))); + checks.add(new SystemCallFilterCheck()); checks.add(new OnErrorCheck()); checks.add(new OnOutOfMemoryErrorCheck()); checks.add(new EarlyAccessCheck()); @@ -212,7 +215,7 @@ final class BootstrapChecks { static class HeapSizeCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { final long initialHeapSize = getInitialHeapSize(); final long maxHeapSize = getMaxHeapSize(); return initialHeapSize != 0 && maxHeapSize != 0 && initialHeapSize != maxHeapSize; @@ -268,7 +271,7 @@ final class BootstrapChecks { this.limit = limit; } - public final boolean check() { + public final boolean check(BootstrapContext context) { final long maxFileDescriptorCount = getMaxFileDescriptorCount(); return maxFileDescriptorCount != -1 && maxFileDescriptorCount < limit; } @@ -292,15 +295,9 @@ final class BootstrapChecks { static class MlockallCheck implements BootstrapCheck { - private final boolean mlockallSet; - - MlockallCheck(final boolean mlockAllSet) { - this.mlockallSet = mlockAllSet; - } - @Override - public boolean check() { - return mlockallSet && !isMemoryLocked(); + public boolean check(BootstrapContext context) { + return BootstrapSettings.MEMORY_LOCK_SETTING.get(context.settings) && !isMemoryLocked(); } @Override @@ -321,7 +318,7 @@ final class BootstrapChecks { private static final long MAX_NUMBER_OF_THREADS_THRESHOLD = 1 << 12; @Override - public boolean check() { + public boolean check(BootstrapContext context) { return getMaxNumberOfThreads() != -1 && getMaxNumberOfThreads() < MAX_NUMBER_OF_THREADS_THRESHOLD; } @@ -345,7 +342,7 @@ final class BootstrapChecks { static class MaxSizeVirtualMemoryCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return getMaxSizeVirtualMemory() != Long.MIN_VALUE && getMaxSizeVirtualMemory() != getRlimInfinity(); } @@ -376,7 +373,7 @@ final class BootstrapChecks { static class MaxFileSizeCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { final long maxFileSize = getMaxFileSize(); return maxFileSize != Long.MIN_VALUE && maxFileSize != getRlimInfinity(); } @@ -405,7 +402,7 @@ final class BootstrapChecks { private static final long LIMIT = 1 << 18; @Override - public boolean check() { + public boolean check(BootstrapContext context) { return getMaxMapCount() != -1 && getMaxMapCount() < LIMIT; } @@ -470,7 +467,7 @@ final class BootstrapChecks { static class ClientJvmCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return getVmName().toLowerCase(Locale.ROOT).contains("client"); } @@ -496,7 +493,7 @@ final class BootstrapChecks { static class UseSerialGCCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return getUseSerialGC().equals("true"); } @@ -521,15 +518,9 @@ final class BootstrapChecks { */ static class SystemCallFilterCheck implements BootstrapCheck { - private final boolean areSystemCallFiltersEnabled; - - SystemCallFilterCheck(final boolean areSystemCallFiltersEnabled) { - this.areSystemCallFiltersEnabled = areSystemCallFiltersEnabled; - } - @Override - public boolean check() { - return areSystemCallFiltersEnabled && !isSystemCallFilterInstalled(); + public boolean check(BootstrapContext context) { + return BootstrapSettings.SYSTEM_CALL_FILTER_SETTING.get(context.settings) && !isSystemCallFilterInstalled(); } // visible for testing @@ -548,7 +539,7 @@ final class BootstrapChecks { abstract static class MightForkCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return isSystemCallFilterInstalled() && mightFork(); } @@ -623,7 +614,7 @@ final class BootstrapChecks { static class EarlyAccessCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return "Oracle Corporation".equals(jvmVendor()) && javaVersion().endsWith("-ea"); } @@ -651,7 +642,7 @@ final class BootstrapChecks { static class G1GCCheck implements BootstrapCheck { @Override - public boolean check() { + public boolean check(BootstrapContext context) { if ("Oracle Corporation".equals(jvmVendor()) && isJava8() && isG1GCEnabled()) { final String jvmVersion = jvmVersion(); // HotSpot versions on Java 8 match this regular expression; note that this changes with Java 9 after JEP-223 diff --git a/core/src/main/java/org/elasticsearch/bootstrap/BootstrapContext.java b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapContext.java new file mode 100644 index 00000000000..f23d0db6d80 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/bootstrap/BootstrapContext.java @@ -0,0 +1,41 @@ +/* + * 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.bootstrap; + +import org.elasticsearch.cluster.metadata.MetaData; +import org.elasticsearch.common.settings.Settings; + +/** + * Context that is passed to every bootstrap check to make decisions on. + */ +public class BootstrapContext { + /** + * The nodes settings + */ + public final Settings settings; + /** + * The nodes local state metadata loaded on startup + */ + public final MetaData metaData; + + public BootstrapContext(Settings settings, MetaData metaData) { + this.settings = settings; + this.metaData = metaData; + } +} diff --git a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java index 99a51adf961..9d57392030c 100644 --- a/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java +++ b/core/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java @@ -41,6 +41,7 @@ import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.index.Index; import org.elasticsearch.plugins.MetaDataUpgrader; +import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; @@ -114,7 +115,7 @@ public class GatewayMetaState extends AbstractComponent implements ClusterStateA } } - public MetaData loadMetaState() throws Exception { + public MetaData loadMetaState() throws IOException { return metaStateService.loadFullState(); } diff --git a/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java b/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java index b900305ab55..5c820343cc8 100644 --- a/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java +++ b/core/src/main/java/org/elasticsearch/gateway/MetaStateService.java @@ -53,7 +53,7 @@ public class MetaStateService extends AbstractComponent { * Loads the full state, which includes both the global state and all the indices * meta state. */ - MetaData loadFullState() throws Exception { + MetaData loadFullState() throws IOException { MetaData globalMetaData = loadGlobalState(); MetaData.Builder metaDataBuilder; if (globalMetaData != null) { diff --git a/core/src/main/java/org/elasticsearch/node/Node.java b/core/src/main/java/org/elasticsearch/node/Node.java index 10d8ddcf210..cee85c96199 100644 --- a/core/src/main/java/org/elasticsearch/node/Node.java +++ b/core/src/main/java/org/elasticsearch/node/Node.java @@ -35,6 +35,7 @@ import org.elasticsearch.action.search.SearchTransportService; import org.elasticsearch.action.support.TransportAction; import org.elasticsearch.action.update.UpdateHelper; import org.elasticsearch.bootstrap.BootstrapCheck; +import org.elasticsearch.bootstrap.BootstrapContext; import org.elasticsearch.client.Client; import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.cluster.ClusterInfo; @@ -86,6 +87,7 @@ import org.elasticsearch.discovery.DiscoverySettings; import org.elasticsearch.env.Environment; import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.gateway.GatewayAllocator; +import org.elasticsearch.gateway.GatewayMetaState; import org.elasticsearch.gateway.GatewayModule; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.gateway.MetaStateService; @@ -139,6 +141,7 @@ import org.elasticsearch.watcher.ResourceWatcherService; import java.io.BufferedWriter; import java.io.Closeable; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -604,7 +607,23 @@ public class Node implements Closeable { assert localNodeFactory.getNode() != null; assert transportService.getLocalNode().equals(localNodeFactory.getNode()) : "transportService has a different local node than the factory provided"; - validateNodeBeforeAcceptingRequests(settings, transportService.boundAddress(), pluginsService.filterPlugins(Plugin.class).stream() + final MetaData onDiskMetadata; + try { + // we load the global state here (the persistent part of the cluster state stored on disk) to + // pass it to the bootstrap checks to allow plugins to enforce certain preconditions based on the recovered state. + if (DiscoveryNode.isMasterNode(settings) || DiscoveryNode.isDataNode(settings)) { + onDiskMetadata = injector.getInstance(GatewayMetaState.class).loadMetaState(); + } else { + onDiskMetadata = MetaData.EMPTY_META_DATA; + } + assert onDiskMetadata != null : "metadata is null but shouldn't"; // this is never null + } catch (IOException e) { + throw new UncheckedIOException(e); + } + validateNodeBeforeAcceptingRequests(new BootstrapContext(settings, onDiskMetadata), transportService.boundAddress(), pluginsService + .filterPlugins(Plugin + .class) + .stream() .flatMap(p -> p.getBootstrapChecks().stream()).collect(Collectors.toList())); clusterService.addStateApplier(transportService.getTaskManager()); @@ -811,13 +830,13 @@ public class Node implements Closeable { * and before the network service starts accepting incoming network * requests. * - * @param settings the fully-resolved settings + * @param context the bootstrap context for this node * @param boundTransportAddress the network addresses the node is * bound and publishing to */ @SuppressWarnings("unused") protected void validateNodeBeforeAcceptingRequests( - final Settings settings, + final BootstrapContext context, final BoundTransportAddress boundTransportAddress, List bootstrapChecks) throws NodeValidationException { } diff --git a/core/src/main/java/org/elasticsearch/node/NodeValidationException.java b/core/src/main/java/org/elasticsearch/node/NodeValidationException.java index 01840b2556b..58e2c4ef951 100644 --- a/core/src/main/java/org/elasticsearch/node/NodeValidationException.java +++ b/core/src/main/java/org/elasticsearch/node/NodeValidationException.java @@ -27,8 +27,8 @@ import java.util.List; /** * An exception thrown during node validation. Node validation runs immediately before a node * begins accepting network requests in - * {@link Node#validateNodeBeforeAcceptingRequests(Settings, BoundTransportAddress, List)}. This exception is a checked exception that - * is declared as thrown from this method for the purpose of bubbling up to the user. + * {@link Node#validateNodeBeforeAcceptingRequests(org.elasticsearch.bootstrap.BootstrapContext, BoundTransportAddress, List)}. + * This exception is a checked exception that is declared as thrown from this method for the purpose of bubbling up to the user. */ public class NodeValidationException extends Exception { diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java index 77276b8787f..a02590c30e0 100644 --- a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java +++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapChecksTests.java @@ -21,6 +21,7 @@ package org.elasticsearch.bootstrap; import org.apache.logging.log4j.Logger; import org.apache.lucene.util.Constants; +import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.common.transport.TransportAddress; @@ -51,6 +52,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; public class BootstrapChecksTests extends ESTestCase { + private static final BootstrapContext defaultContext = new BootstrapContext(Settings.EMPTY, MetaData.EMPTY_META_DATA); public void testNonProductionMode() throws NodeValidationException { // nothing should happen since we are in non-production mode @@ -64,18 +66,18 @@ public class BootstrapChecksTests extends ESTestCase { BoundTransportAddress boundTransportAddress = mock(BoundTransportAddress.class); when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0])); when(boundTransportAddress.publishAddress()).thenReturn(publishAddress); - BootstrapChecks.check(Settings.EMPTY, boundTransportAddress, Collections.emptyList()); + BootstrapChecks.check(defaultContext, boundTransportAddress, Collections.emptyList()); } public void testNoLogMessageInNonProductionMode() throws NodeValidationException { final Logger logger = mock(Logger.class); - BootstrapChecks.check(false, Collections.emptyList(), logger); + BootstrapChecks.check(defaultContext, false, Collections.emptyList(), logger); verifyNoMoreInteractions(logger); } public void testLogMessageInProductionMode() throws NodeValidationException { final Logger logger = mock(Logger.class); - BootstrapChecks.check(true, Collections.emptyList(), logger); + BootstrapChecks.check(defaultContext, true, Collections.emptyList(), logger); verify(logger).info("bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks"); verifyNoMoreInteractions(logger); } @@ -126,7 +128,7 @@ public class BootstrapChecksTests extends ESTestCase { final List checks = Arrays.asList( new BootstrapCheck() { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return true; } @@ -137,7 +139,7 @@ public class BootstrapChecksTests extends ESTestCase { }, new BootstrapCheck() { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return true; } @@ -149,7 +151,8 @@ public class BootstrapChecksTests extends ESTestCase { ); final NodeValidationException e = - expectThrows(NodeValidationException.class, () -> BootstrapChecks.check(true, checks, "testExceptionAggregation")); + expectThrows(NodeValidationException.class, + () -> BootstrapChecks.check(defaultContext, true, checks, "testExceptionAggregation")); assertThat(e, hasToString(allOf(containsString("bootstrap checks failed"), containsString("first"), containsString("second")))); final Throwable[] suppressed = e.getSuppressed(); assertThat(suppressed.length, equalTo(2)); @@ -180,7 +183,7 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testHeapSizeCheck")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testHeapSizeCheck")); assertThat( e.getMessage(), containsString("initial heap size [" + initialHeapSize.get() + "] " + @@ -188,7 +191,7 @@ public class BootstrapChecksTests extends ESTestCase { initialHeapSize.set(maxHeapSize.get()); - BootstrapChecks.check(true, Collections.singletonList(check), "testHeapSizeCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testHeapSizeCheck"); // nothing should happen if the initial heap size or the max // heap size is not available @@ -197,7 +200,7 @@ public class BootstrapChecksTests extends ESTestCase { } else { maxHeapSize.set(0); } - BootstrapChecks.check(true, Collections.singletonList(check), "testHeapSizeCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testHeapSizeCheck"); } public void testFileDescriptorLimits() throws NodeValidationException { @@ -223,17 +226,17 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows(NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimits")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testFileDescriptorLimits")); assertThat(e.getMessage(), containsString("max file descriptors")); maxFileDescriptorCount.set(randomIntBetween(limit + 1, Integer.MAX_VALUE)); - BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimits"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testFileDescriptorLimits"); // nothing should happen if current file descriptor count is // not available maxFileDescriptorCount.set(-1); - BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimits"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testFileDescriptorLimits"); } public void testFileDescriptorLimitsThrowsOnInvalidLimit() { @@ -266,17 +269,19 @@ public class BootstrapChecksTests extends ESTestCase { testCases.add(new MlockallCheckTestCase(false, false, false)); for (final MlockallCheckTestCase testCase : testCases) { - final BootstrapChecks.MlockallCheck check = new BootstrapChecks.MlockallCheck(testCase.mlockallSet) { + final BootstrapChecks.MlockallCheck check = new BootstrapChecks.MlockallCheck() { @Override boolean isMemoryLocked() { return testCase.isMemoryLocked; } }; - + BootstrapContext bootstrapContext = new BootstrapContext( + Settings.builder().put("bootstrap.memory_lock", testCase.mlockallSet).build(), null); if (testCase.shouldFail) { final NodeValidationException e = expectThrows( NodeValidationException.class, () -> BootstrapChecks.check( + bootstrapContext, true, Collections.singletonList(check), "testFileDescriptorLimitsThrowsOnInvalidLimit")); @@ -285,7 +290,8 @@ public class BootstrapChecksTests extends ESTestCase { containsString("memory locking requested for elasticsearch process but memory is not locked")); } else { // nothing should happen - BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimitsThrowsOnInvalidLimit"); + BootstrapChecks.check(bootstrapContext, true, Collections.singletonList(check), + "testFileDescriptorLimitsThrowsOnInvalidLimit"); } } } @@ -302,17 +308,17 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck")); assertThat(e.getMessage(), containsString("max number of threads")); maxNumberOfThreads.set(randomIntBetween(limit + 1, Integer.MAX_VALUE)); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck"); // nothing should happen if current max number of threads is // not available maxNumberOfThreads.set(-1); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck"); } public void testMaxSizeVirtualMemory() throws NodeValidationException { @@ -332,16 +338,16 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxSizeVirtualMemory")); assertThat(e.getMessage(), containsString("max size virtual memory")); maxSizeVirtualMemory.set(rlimInfinity); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxSizeVirtualMemory"); // nothing should happen if max size virtual memory is not available maxSizeVirtualMemory.set(Long.MIN_VALUE); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxSizeVirtualMemory"); } public void testMaxFileSizeCheck() throws NodeValidationException { @@ -361,16 +367,16 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxFileSize")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxFileSize")); assertThat(e.getMessage(), containsString("max file size")); maxFileSize.set(rlimInfinity); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxFileSize"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxFileSize"); // nothing should happen if max file size is not available maxFileSize.set(Long.MIN_VALUE); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxFileSize"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxFileSize"); } public void testMaxMapCountCheck() throws NodeValidationException { @@ -385,17 +391,17 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxMapCountCheck")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxMapCountCheck")); assertThat(e.getMessage(), containsString("max virtual memory areas vm.max_map_count")); maxMapCount.set(randomIntBetween(limit + 1, Integer.MAX_VALUE)); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxMapCountCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxMapCountCheck"); // nothing should happen if current vm.max_map_count is not // available maxMapCount.set(-1); - BootstrapChecks.check(true, Collections.singletonList(check), "testMaxMapCountCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testMaxMapCountCheck"); } public void testClientJvmCheck() throws NodeValidationException { @@ -409,14 +415,14 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testClientJvmCheck")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testClientJvmCheck")); assertThat( e.getMessage(), containsString("JVM is using the client VM [Java HotSpot(TM) 32-Bit Client VM] " + "but should be using a server VM for the best performance")); vmName.set("Java HotSpot(TM) 32-Bit Server VM"); - BootstrapChecks.check(true, Collections.singletonList(check), "testClientJvmCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testClientJvmCheck"); } public void testUseSerialGCCheck() throws NodeValidationException { @@ -430,19 +436,22 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(check), "testUseSerialGCCheck")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testUseSerialGCCheck")); assertThat( e.getMessage(), containsString("JVM is using the serial collector but should not be for the best performance; " + "" + "either it's the default for the VM [" + JvmInfo.jvmInfo().getVmName() +"] or -XX:+UseSerialGC was explicitly specified")); useSerialGC.set("false"); - BootstrapChecks.check(true, Collections.singletonList(check), "testUseSerialGCCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), "testUseSerialGCCheck"); } public void testSystemCallFilterCheck() throws NodeValidationException { final AtomicBoolean isSystemCallFilterInstalled = new AtomicBoolean(); - final BootstrapChecks.SystemCallFilterCheck systemCallFilterEnabledCheck = new BootstrapChecks.SystemCallFilterCheck(true) { + BootstrapContext context = randomBoolean() ? new BootstrapContext(Settings.builder().put("bootstrap.system_call_filter", true) + .build(), null) : defaultContext; + + final BootstrapChecks.SystemCallFilterCheck systemCallFilterEnabledCheck = new BootstrapChecks.SystemCallFilterCheck() { @Override boolean isSystemCallFilterInstalled() { return isSystemCallFilterInstalled.get(); @@ -451,25 +460,26 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck")); + () -> BootstrapChecks.check(context, true, Collections.singletonList(systemCallFilterEnabledCheck), + "testSystemCallFilterCheck")); assertThat( e.getMessage(), containsString("system call filters failed to install; " + "check the logs and fix your configuration or disable system call filters at your own risk")); isSystemCallFilterInstalled.set(true); - BootstrapChecks.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck"); - - final BootstrapChecks.SystemCallFilterCheck systemCallFilterNotEnabledCheck = new BootstrapChecks.SystemCallFilterCheck(false) { + BootstrapChecks.check(context, true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck"); + BootstrapContext context_1 = new BootstrapContext(Settings.builder().put("bootstrap.system_call_filter", false).build(), null); + final BootstrapChecks.SystemCallFilterCheck systemCallFilterNotEnabledCheck = new BootstrapChecks.SystemCallFilterCheck() { @Override boolean isSystemCallFilterInstalled() { return isSystemCallFilterInstalled.get(); } }; isSystemCallFilterInstalled.set(false); - BootstrapChecks.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck"); + BootstrapChecks.check(context_1, true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck"); isSystemCallFilterInstalled.set(true); - BootstrapChecks.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck"); + BootstrapChecks.check(context_1, true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck"); } public void testMightForkCheck() throws NodeValidationException { @@ -573,13 +583,13 @@ public class BootstrapChecksTests extends ESTestCase { } else { enableMightFork.run(); } - BootstrapChecks.check(true, Collections.singletonList(check), methodName); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), methodName); // if system call filter is enabled, but we will not fork, nothing should // happen isSystemCallFilterInstalled.set(true); disableMightFork.run(); - BootstrapChecks.check(true, Collections.singletonList(check), methodName); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(check), methodName); // if system call filter is enabled, and we might fork, the check should be enforced, regardless of bootstrap checks being enabled // or not @@ -588,7 +598,7 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(randomBoolean(), Collections.singletonList(check), methodName)); + () -> BootstrapChecks.check(defaultContext, randomBoolean(), Collections.singletonList(check), methodName)); consumer.accept(e); } @@ -613,7 +623,7 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, () -> { - BootstrapChecks.check(true, checks, "testEarlyAccessCheck"); + BootstrapChecks.check(defaultContext, true, checks, "testEarlyAccessCheck"); }); assertThat( e.getMessage(), @@ -624,7 +634,7 @@ public class BootstrapChecksTests extends ESTestCase { // if not on an early-access build, nothing should happen javaVersion.set(randomFrom("1.8.0_152", "9")); - BootstrapChecks.check(true, checks, "testEarlyAccessCheck"); + BootstrapChecks.check(defaultContext, true, checks, "testEarlyAccessCheck"); } @@ -660,7 +670,7 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(true, Collections.singletonList(g1GCCheck), "testG1GCCheck")); + () -> BootstrapChecks.check(defaultContext, true, Collections.singletonList(g1GCCheck), "testG1GCCheck")); assertThat( e.getMessage(), containsString( @@ -668,12 +678,12 @@ public class BootstrapChecksTests extends ESTestCase { // if G1GC is disabled, nothing should happen isG1GCEnabled.set(false); - BootstrapChecks.check(true, Collections.singletonList(g1GCCheck), "testG1GCCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(g1GCCheck), "testG1GCCheck"); // if on or after update 40, nothing should happen independent of whether or not G1GC is enabled isG1GCEnabled.set(randomBoolean()); jvmVersion.set(String.format(Locale.ROOT, "25.%d-b%d", randomIntBetween(40, 112), randomIntBetween(1, 128))); - BootstrapChecks.check(true, Collections.singletonList(g1GCCheck), "testG1GCCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(g1GCCheck), "testG1GCCheck"); final BootstrapChecks.G1GCCheck nonOracleCheck = new BootstrapChecks.G1GCCheck() { @@ -685,7 +695,7 @@ public class BootstrapChecksTests extends ESTestCase { }; // if not on an Oracle JVM, nothing should happen - BootstrapChecks.check(true, Collections.singletonList(nonOracleCheck), "testG1GCCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(nonOracleCheck), "testG1GCCheck"); final BootstrapChecks.G1GCCheck nonJava8Check = new BootstrapChecks.G1GCCheck() { @@ -697,13 +707,13 @@ public class BootstrapChecksTests extends ESTestCase { }; // if not Java 8, nothing should happen - BootstrapChecks.check(true, Collections.singletonList(nonJava8Check), "testG1GCCheck"); + BootstrapChecks.check(defaultContext, true, Collections.singletonList(nonJava8Check), "testG1GCCheck"); } public void testAlwaysEnforcedChecks() { final BootstrapCheck check = new BootstrapCheck() { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return true; } @@ -720,7 +730,7 @@ public class BootstrapChecksTests extends ESTestCase { final NodeValidationException alwaysEnforced = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(randomBoolean(), Collections.singletonList(check), "testAlwaysEnforcedChecks")); + () -> BootstrapChecks.check(defaultContext, randomBoolean(), Collections.singletonList(check), "testAlwaysEnforcedChecks")); assertThat(alwaysEnforced, hasToString(containsString("error"))); } diff --git a/core/src/test/java/org/elasticsearch/node/NodeTests.java b/core/src/test/java/org/elasticsearch/node/NodeTests.java index ec806799e71..edf5dfac76d 100644 --- a/core/src/test/java/org/elasticsearch/node/NodeTests.java +++ b/core/src/test/java/org/elasticsearch/node/NodeTests.java @@ -22,6 +22,7 @@ import org.apache.logging.log4j.Logger; import org.apache.lucene.util.LuceneTestCase; import org.elasticsearch.Version; import org.elasticsearch.bootstrap.BootstrapCheck; +import org.elasticsearch.bootstrap.BootstrapContext; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; @@ -66,7 +67,7 @@ public class NodeTests extends ESTestCase { public static class CheckPlugin extends Plugin { public static final BootstrapCheck CHECK = new BootstrapCheck() { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return false; } @@ -90,7 +91,7 @@ public class NodeTests extends ESTestCase { AtomicBoolean executed = new AtomicBoolean(false); try (Node node = new MockNode(settings.build(), Arrays.asList(getTestTransportPlugin(), CheckPlugin.class)) { @Override - protected void validateNodeBeforeAcceptingRequests(Settings settings, BoundTransportAddress boundTransportAddress, + protected void validateNodeBeforeAcceptingRequests(BootstrapContext context, BoundTransportAddress boundTransportAddress, List bootstrapChecks) throws NodeValidationException { assertEquals(1, bootstrapChecks.size()); assertSame(CheckPlugin.CHECK, bootstrapChecks.get(0)); diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java index 8e346bf7d9c..8b0fbd6862f 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/bootstrap/EvilBootstrapChecksTests.java @@ -21,6 +21,7 @@ package org.elasticsearch.bootstrap; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.node.NodeValidationException; import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matcher; @@ -61,7 +62,7 @@ public class EvilBootstrapChecksTests extends ESTestCase { final List checks = Collections.singletonList( new BootstrapCheck() { @Override - public boolean check() { + public boolean check(BootstrapContext context) { return true; } @@ -75,7 +76,7 @@ public class EvilBootstrapChecksTests extends ESTestCase { final NodeValidationException e = expectThrows( NodeValidationException.class, - () -> BootstrapChecks.check(false, checks, logger)); + () -> BootstrapChecks.check(new BootstrapContext(Settings.EMPTY, null), false, checks, logger)); final Matcher allOf = allOf(containsString("bootstrap checks failed"), containsString("error")); assertThat(e, hasToString(allOf)); @@ -87,7 +88,7 @@ public class EvilBootstrapChecksTests extends ESTestCase { setEsEnforceBootstrapChecks(null); final Logger logger = mock(Logger.class); // nothing should happen - BootstrapChecks.check(false, emptyList(), logger); + BootstrapChecks.check(new BootstrapContext(Settings.EMPTY, null), false, emptyList(), logger); verifyNoMoreInteractions(logger); } @@ -97,7 +98,7 @@ public class EvilBootstrapChecksTests extends ESTestCase { final boolean enforceLimits = randomBoolean(); final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, - () -> BootstrapChecks.check(enforceLimits, emptyList(), "testInvalidValue")); + () -> BootstrapChecks.check(new BootstrapContext(Settings.EMPTY, null), enforceLimits, emptyList(), "testInvalidValue")); final Matcher matcher = containsString( "[es.enforce.bootstrap.checks] must be [true] but was [" + value + "]"); assertThat(e, hasToString(matcher));