Add BootstrapContext to expose settings and recovered state to bootstrap checks (#26628)

This exposes the node settings and the persistent part of the cluster state to the
bootstrap checks to allow plugins to enforce certain preconditions based on the
recovered state.
This commit is contained in:
Simon Willnauer 2017-09-13 22:14:17 +02:00 committed by GitHub
parent 2eaf7534f3
commit b4de2a6f28
11 changed files with 171 additions and 107 deletions

View File

@ -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<BootstrapCheck> checks) throws NodeValidationException {
BootstrapChecks.check(settings, boundTransportAddress, checks);
BootstrapChecks.check(context, boundTransportAddress, checks);
}
};
}

View File

@ -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;
}
}

View File

@ -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<BootstrapCheck> additionalChecks)
throws NodeValidationException {
final List<BootstrapCheck> builtInChecks = checks(settings);
static void check(final BootstrapContext context, final BoundTransportAddress boundTransportAddress,
List<BootstrapCheck> additionalChecks) throws NodeValidationException {
final List<BootstrapCheck> builtInChecks = checks();
final List<BootstrapCheck> 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<BootstrapCheck> 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<BootstrapCheck> 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<BootstrapCheck> checks(final Settings settings) {
static List<BootstrapCheck> checks() {
final List<BootstrapCheck> 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

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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) {

View File

@ -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<BootstrapCheck> bootstrapChecks) throws NodeValidationException {
}

View File

@ -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 {

View File

@ -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<BootstrapCheck> 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")));
}

View File

@ -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<BootstrapCheck> bootstrapChecks) throws NodeValidationException {
assertEquals(1, bootstrapChecks.size());
assertSame(CheckPlugin.CHECK, bootstrapChecks.get(0));

View File

@ -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<BootstrapCheck> 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<String> 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<String> matcher = containsString(
"[es.enforce.bootstrap.checks] must be [true] but was [" + value + "]");
assertThat(e, hasToString(matcher));