mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-30 03:48:54 +00:00
Allow plugins to install bootstrap checks (#22110)
Plugins also have the need to provide better OOTB experience by configuring defaults unless the plugin is used in _production_ mode. This change exposes the bootstrap check infrastructure as part of the plugin API to allow plugins to specify / install their own bootstrap checks if necessary.
This commit is contained in:
parent
32fcb7220e
commit
b667ff46c4
@ -55,6 +55,7 @@ import java.io.UnsupportedEncodingException;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
@ -215,8 +216,8 @@ final class Bootstrap {
|
|||||||
@Override
|
@Override
|
||||||
protected void validateNodeBeforeAcceptingRequests(
|
protected void validateNodeBeforeAcceptingRequests(
|
||||||
final Settings settings,
|
final Settings settings,
|
||||||
final BoundTransportAddress boundTransportAddress) throws NodeValidationException {
|
final BoundTransportAddress boundTransportAddress, List<BootstrapCheck> checks) throws NodeValidationException {
|
||||||
BootstrapCheck.check(settings, boundTransportAddress);
|
BootstrapChecks.check(settings, boundTransportAddress, checks);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -19,612 +19,27 @@
|
|||||||
|
|
||||||
package org.elasticsearch.bootstrap;
|
package org.elasticsearch.bootstrap;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
|
||||||
import org.apache.logging.log4j.util.Supplier;
|
|
||||||
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.monitor.jvm.JvmInfo;
|
|
||||||
import org.elasticsearch.monitor.process.ProcessProbe;
|
|
||||||
import org.elasticsearch.node.Node;
|
|
||||||
import org.elasticsearch.node.NodeValidationException;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
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.Locale;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We enforce bootstrap checks once a node has the transport protocol bound to a non-loopback interface. In this case we assume the node is
|
* Encapsulates a bootstrap check.
|
||||||
* running in production and all bootstrap checks must pass.
|
|
||||||
*/
|
*/
|
||||||
final class BootstrapCheck {
|
public interface BootstrapCheck {
|
||||||
|
|
||||||
private BootstrapCheck() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the bootstrap checks if the node has the transport protocol bound to a non-loopback interface.
|
* Test if the node fails the check.
|
||||||
*
|
*
|
||||||
* @param settings the current node settings
|
* @return {@code true} if the node failed the check
|
||||||
* @param boundTransportAddress the node network bindings
|
|
||||||
*/
|
*/
|
||||||
static void check(final Settings settings, final BoundTransportAddress boundTransportAddress) throws NodeValidationException {
|
boolean check();
|
||||||
check(
|
|
||||||
enforceLimits(boundTransportAddress),
|
|
||||||
checks(settings),
|
|
||||||
Node.NODE_NAME_SETTING.get(settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings.
|
* The error message for a failed check.
|
||||||
*
|
*
|
||||||
* @param enforceLimits {@code true} if the checks should be enforced or otherwise warned
|
* @return the error message on check failure
|
||||||
* @param checks the checks to execute
|
|
||||||
* @param nodeName the node name to be used as a logging prefix
|
|
||||||
*/
|
*/
|
||||||
static void check(
|
String errorMessage();
|
||||||
final boolean enforceLimits,
|
|
||||||
final List<Check> checks,
|
|
||||||
final String nodeName) throws NodeValidationException {
|
|
||||||
check(enforceLimits, checks, Loggers.getLogger(BootstrapCheck.class, nodeName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings.
|
|
||||||
*
|
|
||||||
* @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 boolean enforceLimits,
|
|
||||||
final List<Check> checks,
|
|
||||||
final Logger logger) throws NodeValidationException {
|
|
||||||
final List<String> errors = new ArrayList<>();
|
|
||||||
final List<String> ignoredErrors = new ArrayList<>();
|
|
||||||
|
|
||||||
if (enforceLimits) {
|
|
||||||
logger.info("bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Check check : checks) {
|
|
||||||
if (check.check()) {
|
|
||||||
if (!enforceLimits && !check.alwaysEnforce()) {
|
|
||||||
ignoredErrors.add(check.errorMessage());
|
|
||||||
} else {
|
|
||||||
errors.add(check.errorMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignoredErrors.isEmpty()) {
|
|
||||||
ignoredErrors.forEach(error -> log(logger, error));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
final List<String> messages = new ArrayList<>(1 + errors.size());
|
|
||||||
messages.add("bootstrap checks failed");
|
|
||||||
messages.addAll(errors);
|
|
||||||
final NodeValidationException ne = new NodeValidationException(String.join("\n", messages));
|
|
||||||
errors.stream().map(IllegalStateException::new).forEach(ne::addSuppressed);
|
|
||||||
throw ne;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void log(final Logger logger, final String error) {
|
|
||||||
logger.warn(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests if the checks should be enforced.
|
|
||||||
*
|
|
||||||
* @param boundTransportAddress the node network bindings
|
|
||||||
* @return {@code true} if the checks should be enforced
|
|
||||||
*/
|
|
||||||
static boolean enforceLimits(BoundTransportAddress boundTransportAddress) {
|
|
||||||
Predicate<TransportAddress> isLoopbackOrLinkLocalAddress = t -> t.address().getAddress().isLinkLocalAddress()
|
|
||||||
|| t.address().getAddress().isLoopbackAddress();
|
|
||||||
return !(Arrays.stream(boundTransportAddress.boundAddresses()).allMatch(isLoopbackOrLinkLocalAddress) &&
|
|
||||||
isLoopbackOrLinkLocalAddress.test(boundTransportAddress.publishAddress()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// the list of checks to execute
|
|
||||||
static List<Check> checks(final Settings settings) {
|
|
||||||
final List<Check> 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)));
|
|
||||||
if (Constants.LINUX) {
|
|
||||||
checks.add(new MaxNumberOfThreadsCheck());
|
|
||||||
}
|
|
||||||
if (Constants.LINUX || Constants.MAC_OS_X) {
|
|
||||||
checks.add(new MaxSizeVirtualMemoryCheck());
|
|
||||||
}
|
|
||||||
if (Constants.LINUX) {
|
|
||||||
checks.add(new MaxMapCountCheck());
|
|
||||||
}
|
|
||||||
checks.add(new ClientJvmCheck());
|
|
||||||
checks.add(new UseSerialGCCheck());
|
|
||||||
checks.add(new SystemCallFilterCheck(BootstrapSettings.SECCOMP_SETTING.get(settings)));
|
|
||||||
checks.add(new OnErrorCheck());
|
|
||||||
checks.add(new OnOutOfMemoryErrorCheck());
|
|
||||||
checks.add(new G1GCCheck());
|
|
||||||
return Collections.unmodifiableList(checks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulates a bootstrap check.
|
|
||||||
*/
|
|
||||||
interface Check {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if the node fails the check.
|
|
||||||
*
|
|
||||||
* @return {@code true} if the node failed the check
|
|
||||||
*/
|
|
||||||
boolean check();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The error message for a failed check.
|
|
||||||
*
|
|
||||||
* @return the error message on check failure
|
|
||||||
*/
|
|
||||||
String errorMessage();
|
|
||||||
|
|
||||||
default boolean alwaysEnforce() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class HeapSizeCheck implements BootstrapCheck.Check {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
final long initialHeapSize = getInitialHeapSize();
|
|
||||||
final long maxHeapSize = getMaxHeapSize();
|
|
||||||
return initialHeapSize != 0 && maxHeapSize != 0 && initialHeapSize != maxHeapSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"initial heap size [%d] not equal to maximum heap size [%d]; " +
|
|
||||||
"this can cause resize pauses and prevents mlockall from locking the entire heap",
|
|
||||||
getInitialHeapSize(),
|
|
||||||
getMaxHeapSize()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getInitialHeapSize() {
|
|
||||||
return JvmInfo.jvmInfo().getConfiguredInitialHeapSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getMaxHeapSize() {
|
|
||||||
return JvmInfo.jvmInfo().getConfiguredMaxHeapSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class OsXFileDescriptorCheck extends FileDescriptorCheck {
|
|
||||||
|
|
||||||
public OsXFileDescriptorCheck() {
|
|
||||||
// see constant OPEN_MAX defined in
|
|
||||||
// /usr/include/sys/syslimits.h on OS X and its use in JVM
|
|
||||||
// initialization in int os:init_2(void) defined in the JVM
|
|
||||||
// code for BSD (contains OS X)
|
|
||||||
super(10240);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class FileDescriptorCheck implements Check {
|
|
||||||
|
|
||||||
private final int limit;
|
|
||||||
|
|
||||||
FileDescriptorCheck() {
|
|
||||||
this(1 << 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FileDescriptorCheck(final int limit) {
|
|
||||||
if (limit <= 0) {
|
|
||||||
throw new IllegalArgumentException("limit must be positive but was [" + limit + "]");
|
|
||||||
}
|
|
||||||
this.limit = limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean check() {
|
|
||||||
final long maxFileDescriptorCount = getMaxFileDescriptorCount();
|
|
||||||
return maxFileDescriptorCount != -1 && maxFileDescriptorCount < limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"max file descriptors [%d] for elasticsearch process is too low, increase to at least [%d]",
|
|
||||||
getMaxFileDescriptorCount(),
|
|
||||||
limit
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getMaxFileDescriptorCount() {
|
|
||||||
return ProcessProbe.getInstance().getMaxFileDescriptorCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MlockallCheck implements Check {
|
|
||||||
|
|
||||||
private final boolean mlockallSet;
|
|
||||||
|
|
||||||
public MlockallCheck(final boolean mlockAllSet) {
|
|
||||||
this.mlockallSet = mlockAllSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return mlockallSet && !isMemoryLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return "memory locking requested for elasticsearch process but memory is not locked";
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
boolean isMemoryLocked() {
|
|
||||||
return Natives.isMemoryLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MaxNumberOfThreadsCheck implements Check {
|
|
||||||
|
|
||||||
// this should be plenty for machines up to 256 cores
|
|
||||||
private final long maxNumberOfThreadsThreshold = 1 << 12;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return getMaxNumberOfThreads() != -1 && getMaxNumberOfThreads() < maxNumberOfThreadsThreshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"max number of threads [%d] for user [%s] is too low, increase to at least [%d]",
|
|
||||||
getMaxNumberOfThreads(),
|
|
||||||
BootstrapInfo.getSystemProperties().get("user.name"),
|
|
||||||
maxNumberOfThreadsThreshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getMaxNumberOfThreads() {
|
|
||||||
return JNANatives.MAX_NUMBER_OF_THREADS;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MaxSizeVirtualMemoryCheck implements Check {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return getMaxSizeVirtualMemory() != Long.MIN_VALUE && getMaxSizeVirtualMemory() != getRlimInfinity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"max size virtual memory [%d] for user [%s] is too low, increase to [unlimited]",
|
|
||||||
getMaxSizeVirtualMemory(),
|
|
||||||
BootstrapInfo.getSystemProperties().get("user.name"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getRlimInfinity() {
|
|
||||||
return JNACLibrary.RLIM_INFINITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getMaxSizeVirtualMemory() {
|
|
||||||
return JNANatives.MAX_SIZE_VIRTUAL_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class MaxMapCountCheck implements Check {
|
|
||||||
|
|
||||||
private final long limit = 1 << 18;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return getMaxMapCount() != -1 && getMaxMapCount() < limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"max virtual memory areas vm.max_map_count [%d] is too low, increase to at least [%d]",
|
|
||||||
getMaxMapCount(),
|
|
||||||
limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getMaxMapCount() {
|
|
||||||
return getMaxMapCount(Loggers.getLogger(BootstrapCheck.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long getMaxMapCount(Logger logger) {
|
|
||||||
final Path path = getProcSysVmMaxMapCountPath();
|
|
||||||
try (final BufferedReader bufferedReader = getBufferedReader(path)) {
|
|
||||||
final String rawProcSysVmMaxMapCount = readProcSysVmMaxMapCount(bufferedReader);
|
|
||||||
if (rawProcSysVmMaxMapCount != null) {
|
|
||||||
try {
|
|
||||||
return parseProcSysVmMaxMapCount(rawProcSysVmMaxMapCount);
|
|
||||||
} catch (final NumberFormatException e) {
|
|
||||||
logger.warn(
|
|
||||||
(Supplier<?>) () -> new ParameterizedMessage(
|
|
||||||
"unable to parse vm.max_map_count [{}]",
|
|
||||||
rawProcSysVmMaxMapCount),
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
logger.warn((Supplier<?>) () -> new ParameterizedMessage("I/O exception while trying to read [{}]", path), e);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressForbidden(reason = "access /proc/sys/vm/max_map_count")
|
|
||||||
private Path getProcSysVmMaxMapCountPath() {
|
|
||||||
return PathUtils.get("/proc/sys/vm/max_map_count");
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
BufferedReader getBufferedReader(final Path path) throws IOException {
|
|
||||||
return Files.newBufferedReader(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
String readProcSysVmMaxMapCount(final BufferedReader bufferedReader) throws IOException {
|
|
||||||
return bufferedReader.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
long parseProcSysVmMaxMapCount(final String procSysVmMaxMapCount) throws NumberFormatException {
|
|
||||||
return Long.parseLong(procSysVmMaxMapCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class ClientJvmCheck implements BootstrapCheck.Check {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return getVmName().toLowerCase(Locale.ROOT).contains("client");
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
String getVmName() {
|
|
||||||
return JvmInfo.jvmInfo().getVmName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"JVM is using the client VM [%s] but should be using a server VM for the best performance",
|
|
||||||
getVmName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the serial collector is in use. This collector is single-threaded and devastating
|
|
||||||
* for performance and should not be used for a server application like Elasticsearch.
|
|
||||||
*/
|
|
||||||
static class UseSerialGCCheck implements BootstrapCheck.Check {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return getUseSerialGC().equals("true");
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
String getUseSerialGC() {
|
|
||||||
return JvmInfo.jvmInfo().useSerialGC();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"JVM is using the serial collector but should not be for the best performance; " +
|
|
||||||
"either it's the default for the VM [%s] or -XX:+UseSerialGC was explicitly specified",
|
|
||||||
JvmInfo.jvmInfo().getVmName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrap check that if system call filters are enabled, then system call filters must have installed successfully.
|
|
||||||
*/
|
|
||||||
static class SystemCallFilterCheck implements BootstrapCheck.Check {
|
|
||||||
|
|
||||||
private final boolean areSystemCallFiltersEnabled;
|
|
||||||
|
|
||||||
SystemCallFilterCheck(final boolean areSystemCallFiltersEnabled) {
|
|
||||||
this.areSystemCallFiltersEnabled = areSystemCallFiltersEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return areSystemCallFiltersEnabled && !isSeccompInstalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
boolean isSeccompInstalled() {
|
|
||||||
return Natives.isSeccompInstalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return "system call filters failed to install; " +
|
|
||||||
"check the logs and fix your configuration or disable system call filters at your own risk";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract static class MightForkCheck implements BootstrapCheck.Check {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
return isSeccompInstalled() && mightFork();
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
boolean isSeccompInstalled() {
|
|
||||||
return Natives.isSeccompInstalled();
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
abstract boolean mightFork();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean alwaysEnforce() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class OnErrorCheck extends MightForkCheck {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean mightFork() {
|
|
||||||
final String onError = onError();
|
|
||||||
return onError != null && !onError.equals("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
String onError() {
|
|
||||||
return JvmInfo.jvmInfo().onError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"OnError [%s] requires forking but is prevented by system call filters ([%s=true]);" +
|
|
||||||
" upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError",
|
|
||||||
onError(),
|
|
||||||
BootstrapSettings.SECCOMP_SETTING.getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static class OnOutOfMemoryErrorCheck extends MightForkCheck {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean mightFork() {
|
|
||||||
final String onOutOfMemoryError = onOutOfMemoryError();
|
|
||||||
return onOutOfMemoryError != null && !onOutOfMemoryError.equals("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
String onOutOfMemoryError() {
|
|
||||||
return JvmInfo.jvmInfo().onOutOfMemoryError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"OnOutOfMemoryError [%s] requires forking but is prevented by system call filters ([%s=true]);" +
|
|
||||||
" upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError",
|
|
||||||
onOutOfMemoryError(),
|
|
||||||
BootstrapSettings.SECCOMP_SETTING.getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bootstrap check for versions of HotSpot that are known to have issues that can lead to index corruption when G1GC is enabled.
|
|
||||||
*/
|
|
||||||
static class G1GCCheck implements BootstrapCheck.Check {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check() {
|
|
||||||
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
|
|
||||||
final Pattern pattern = Pattern.compile("(\\d+)\\.(\\d+)-b\\d+");
|
|
||||||
final Matcher matcher = pattern.matcher(jvmVersion);
|
|
||||||
final boolean matches = matcher.matches();
|
|
||||||
assert matches : jvmVersion;
|
|
||||||
final int major = Integer.parseInt(matcher.group(1));
|
|
||||||
final int update = Integer.parseInt(matcher.group(2));
|
|
||||||
// HotSpot versions for Java 8 have major version 25, the bad versions are all versions prior to update 40
|
|
||||||
return major == 25 && update < 40;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
String jvmVendor() {
|
|
||||||
return Constants.JVM_VENDOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
boolean isG1GCEnabled() {
|
|
||||||
assert "Oracle Corporation".equals(jvmVendor());
|
|
||||||
return JvmInfo.jvmInfo().useG1GC().equals("true");
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
String jvmVersion() {
|
|
||||||
assert "Oracle Corporation".equals(jvmVendor());
|
|
||||||
return Constants.JVM_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
// visible for testing
|
|
||||||
boolean isJava8() {
|
|
||||||
assert "Oracle Corporation".equals(jvmVendor());
|
|
||||||
return JavaVersion.current().equals(JavaVersion.parse("1.8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String errorMessage() {
|
|
||||||
return String.format(
|
|
||||||
Locale.ROOT,
|
|
||||||
"JVM version [%s] can cause data corruption when used with G1GC; upgrade to at least Java 8u40", jvmVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
default boolean alwaysEnforce() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,609 @@
|
|||||||
|
/*
|
||||||
|
* 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.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||||
|
import org.apache.logging.log4j.util.Supplier;
|
||||||
|
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.monitor.jvm.JvmInfo;
|
||||||
|
import org.elasticsearch.monitor.process.ProcessProbe;
|
||||||
|
import org.elasticsearch.node.Node;
|
||||||
|
import org.elasticsearch.node.NodeValidationException;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
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.Locale;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We enforce bootstrap checks once a node has the transport protocol bound to a non-loopback interface. In this case we assume the node is
|
||||||
|
* running in production and all bootstrap checks must pass.
|
||||||
|
*/
|
||||||
|
final class BootstrapChecks {
|
||||||
|
|
||||||
|
private BootstrapChecks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the bootstrap checks if the node has the transport protocol bound to a non-loopback interface.
|
||||||
|
*
|
||||||
|
* @param settings the current node settings
|
||||||
|
* @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);
|
||||||
|
final List<BootstrapCheck> combinedChecks = new ArrayList<>(builtInChecks);
|
||||||
|
combinedChecks.addAll(additionalChecks);
|
||||||
|
check(
|
||||||
|
enforceLimits(boundTransportAddress),
|
||||||
|
Collections.unmodifiableList(combinedChecks),
|
||||||
|
Node.NODE_NAME_SETTING.get(settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings.
|
||||||
|
*
|
||||||
|
* @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 boolean enforceLimits,
|
||||||
|
final List<BootstrapCheck> checks,
|
||||||
|
final String nodeName) throws NodeValidationException {
|
||||||
|
check(enforceLimits, checks, Loggers.getLogger(BootstrapChecks.class, nodeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the provided checks and fails the node if {@code enforceLimits} is {@code true}, otherwise logs warnings.
|
||||||
|
*
|
||||||
|
* @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 boolean enforceLimits,
|
||||||
|
final List<BootstrapCheck> checks,
|
||||||
|
final Logger logger) throws NodeValidationException {
|
||||||
|
final List<String> errors = new ArrayList<>();
|
||||||
|
final List<String> ignoredErrors = new ArrayList<>();
|
||||||
|
|
||||||
|
if (enforceLimits) {
|
||||||
|
logger.info("bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final BootstrapCheck check : checks) {
|
||||||
|
if (check.check()) {
|
||||||
|
if (!enforceLimits && !check.alwaysEnforce()) {
|
||||||
|
ignoredErrors.add(check.errorMessage());
|
||||||
|
} else {
|
||||||
|
errors.add(check.errorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoredErrors.isEmpty()) {
|
||||||
|
ignoredErrors.forEach(error -> log(logger, error));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
final List<String> messages = new ArrayList<>(1 + errors.size());
|
||||||
|
messages.add("bootstrap checks failed");
|
||||||
|
messages.addAll(errors);
|
||||||
|
final NodeValidationException ne = new NodeValidationException(String.join("\n", messages));
|
||||||
|
errors.stream().map(IllegalStateException::new).forEach(ne::addSuppressed);
|
||||||
|
throw ne;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log(final Logger logger, final String error) {
|
||||||
|
logger.warn(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the checks should be enforced.
|
||||||
|
*
|
||||||
|
* @param boundTransportAddress the node network bindings
|
||||||
|
* @return {@code true} if the checks should be enforced
|
||||||
|
*/
|
||||||
|
static boolean enforceLimits(BoundTransportAddress boundTransportAddress) {
|
||||||
|
Predicate<TransportAddress> isLoopbackOrLinkLocalAddress = t -> t.address().getAddress().isLinkLocalAddress()
|
||||||
|
|| t.address().getAddress().isLoopbackAddress();
|
||||||
|
return !(Arrays.stream(boundTransportAddress.boundAddresses()).allMatch(isLoopbackOrLinkLocalAddress) &&
|
||||||
|
isLoopbackOrLinkLocalAddress.test(boundTransportAddress.publishAddress()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// the list of checks to execute
|
||||||
|
static List<BootstrapCheck> checks(final Settings settings) {
|
||||||
|
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)));
|
||||||
|
if (Constants.LINUX) {
|
||||||
|
checks.add(new MaxNumberOfThreadsCheck());
|
||||||
|
}
|
||||||
|
if (Constants.LINUX || Constants.MAC_OS_X) {
|
||||||
|
checks.add(new MaxSizeVirtualMemoryCheck());
|
||||||
|
}
|
||||||
|
if (Constants.LINUX) {
|
||||||
|
checks.add(new MaxMapCountCheck());
|
||||||
|
}
|
||||||
|
checks.add(new ClientJvmCheck());
|
||||||
|
checks.add(new UseSerialGCCheck());
|
||||||
|
checks.add(new SystemCallFilterCheck(BootstrapSettings.SECCOMP_SETTING.get(settings)));
|
||||||
|
checks.add(new OnErrorCheck());
|
||||||
|
checks.add(new OnOutOfMemoryErrorCheck());
|
||||||
|
checks.add(new G1GCCheck());
|
||||||
|
return Collections.unmodifiableList(checks);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class HeapSizeCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
final long initialHeapSize = getInitialHeapSize();
|
||||||
|
final long maxHeapSize = getMaxHeapSize();
|
||||||
|
return initialHeapSize != 0 && maxHeapSize != 0 && initialHeapSize != maxHeapSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"initial heap size [%d] not equal to maximum heap size [%d]; " +
|
||||||
|
"this can cause resize pauses and prevents mlockall from locking the entire heap",
|
||||||
|
getInitialHeapSize(),
|
||||||
|
getMaxHeapSize()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getInitialHeapSize() {
|
||||||
|
return JvmInfo.jvmInfo().getConfiguredInitialHeapSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getMaxHeapSize() {
|
||||||
|
return JvmInfo.jvmInfo().getConfiguredMaxHeapSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class OsXFileDescriptorCheck extends FileDescriptorCheck {
|
||||||
|
|
||||||
|
public OsXFileDescriptorCheck() {
|
||||||
|
// see constant OPEN_MAX defined in
|
||||||
|
// /usr/include/sys/syslimits.h on OS X and its use in JVM
|
||||||
|
// initialization in int os:init_2(void) defined in the JVM
|
||||||
|
// code for BSD (contains OS X)
|
||||||
|
super(10240);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FileDescriptorCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
private final int limit;
|
||||||
|
|
||||||
|
FileDescriptorCheck() {
|
||||||
|
this(1 << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FileDescriptorCheck(final int limit) {
|
||||||
|
if (limit <= 0) {
|
||||||
|
throw new IllegalArgumentException("limit must be positive but was [" + limit + "]");
|
||||||
|
}
|
||||||
|
this.limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean check() {
|
||||||
|
final long maxFileDescriptorCount = getMaxFileDescriptorCount();
|
||||||
|
return maxFileDescriptorCount != -1 && maxFileDescriptorCount < limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"max file descriptors [%d] for elasticsearch process is too low, increase to at least [%d]",
|
||||||
|
getMaxFileDescriptorCount(),
|
||||||
|
limit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getMaxFileDescriptorCount() {
|
||||||
|
return ProcessProbe.getInstance().getMaxFileDescriptorCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MlockallCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
private final boolean mlockallSet;
|
||||||
|
|
||||||
|
public MlockallCheck(final boolean mlockAllSet) {
|
||||||
|
this.mlockallSet = mlockAllSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return mlockallSet && !isMemoryLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return "memory locking requested for elasticsearch process but memory is not locked";
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
boolean isMemoryLocked() {
|
||||||
|
return Natives.isMemoryLocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MaxNumberOfThreadsCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
// this should be plenty for machines up to 256 cores
|
||||||
|
private final long maxNumberOfThreadsThreshold = 1 << 12;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return getMaxNumberOfThreads() != -1 && getMaxNumberOfThreads() < maxNumberOfThreadsThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"max number of threads [%d] for user [%s] is too low, increase to at least [%d]",
|
||||||
|
getMaxNumberOfThreads(),
|
||||||
|
BootstrapInfo.getSystemProperties().get("user.name"),
|
||||||
|
maxNumberOfThreadsThreshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getMaxNumberOfThreads() {
|
||||||
|
return JNANatives.MAX_NUMBER_OF_THREADS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MaxSizeVirtualMemoryCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return getMaxSizeVirtualMemory() != Long.MIN_VALUE && getMaxSizeVirtualMemory() != getRlimInfinity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"max size virtual memory [%d] for user [%s] is too low, increase to [unlimited]",
|
||||||
|
getMaxSizeVirtualMemory(),
|
||||||
|
BootstrapInfo.getSystemProperties().get("user.name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getRlimInfinity() {
|
||||||
|
return JNACLibrary.RLIM_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getMaxSizeVirtualMemory() {
|
||||||
|
return JNANatives.MAX_SIZE_VIRTUAL_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MaxMapCountCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
private final long limit = 1 << 18;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return getMaxMapCount() != -1 && getMaxMapCount() < limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"max virtual memory areas vm.max_map_count [%d] is too low, increase to at least [%d]",
|
||||||
|
getMaxMapCount(),
|
||||||
|
limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getMaxMapCount() {
|
||||||
|
return getMaxMapCount(Loggers.getLogger(BootstrapChecks.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long getMaxMapCount(Logger logger) {
|
||||||
|
final Path path = getProcSysVmMaxMapCountPath();
|
||||||
|
try (final BufferedReader bufferedReader = getBufferedReader(path)) {
|
||||||
|
final String rawProcSysVmMaxMapCount = readProcSysVmMaxMapCount(bufferedReader);
|
||||||
|
if (rawProcSysVmMaxMapCount != null) {
|
||||||
|
try {
|
||||||
|
return parseProcSysVmMaxMapCount(rawProcSysVmMaxMapCount);
|
||||||
|
} catch (final NumberFormatException e) {
|
||||||
|
logger.warn(
|
||||||
|
(Supplier<?>) () -> new ParameterizedMessage(
|
||||||
|
"unable to parse vm.max_map_count [{}]",
|
||||||
|
rawProcSysVmMaxMapCount),
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (final IOException e) {
|
||||||
|
logger.warn((Supplier<?>) () -> new ParameterizedMessage("I/O exception while trying to read [{}]", path), e);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressForbidden(reason = "access /proc/sys/vm/max_map_count")
|
||||||
|
private Path getProcSysVmMaxMapCountPath() {
|
||||||
|
return PathUtils.get("/proc/sys/vm/max_map_count");
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
BufferedReader getBufferedReader(final Path path) throws IOException {
|
||||||
|
return Files.newBufferedReader(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
String readProcSysVmMaxMapCount(final BufferedReader bufferedReader) throws IOException {
|
||||||
|
return bufferedReader.readLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
long parseProcSysVmMaxMapCount(final String procSysVmMaxMapCount) throws NumberFormatException {
|
||||||
|
return Long.parseLong(procSysVmMaxMapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ClientJvmCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return getVmName().toLowerCase(Locale.ROOT).contains("client");
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
String getVmName() {
|
||||||
|
return JvmInfo.jvmInfo().getVmName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"JVM is using the client VM [%s] but should be using a server VM for the best performance",
|
||||||
|
getVmName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the serial collector is in use. This collector is single-threaded and devastating
|
||||||
|
* for performance and should not be used for a server application like Elasticsearch.
|
||||||
|
*/
|
||||||
|
static class UseSerialGCCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return getUseSerialGC().equals("true");
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
String getUseSerialGC() {
|
||||||
|
return JvmInfo.jvmInfo().useSerialGC();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"JVM is using the serial collector but should not be for the best performance; " +
|
||||||
|
"either it's the default for the VM [%s] or -XX:+UseSerialGC was explicitly specified",
|
||||||
|
JvmInfo.jvmInfo().getVmName());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap check that if system call filters are enabled, then system call filters must have installed successfully.
|
||||||
|
*/
|
||||||
|
static class SystemCallFilterCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
private final boolean areSystemCallFiltersEnabled;
|
||||||
|
|
||||||
|
SystemCallFilterCheck(final boolean areSystemCallFiltersEnabled) {
|
||||||
|
this.areSystemCallFiltersEnabled = areSystemCallFiltersEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return areSystemCallFiltersEnabled && !isSeccompInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
boolean isSeccompInstalled() {
|
||||||
|
return Natives.isSeccompInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return "system call filters failed to install; " +
|
||||||
|
"check the logs and fix your configuration or disable system call filters at your own risk";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract static class MightForkCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return isSeccompInstalled() && mightFork();
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
boolean isSeccompInstalled() {
|
||||||
|
return Natives.isSeccompInstalled();
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
abstract boolean mightFork();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean alwaysEnforce() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class OnErrorCheck extends MightForkCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean mightFork() {
|
||||||
|
final String onError = onError();
|
||||||
|
return onError != null && !onError.equals("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
String onError() {
|
||||||
|
return JvmInfo.jvmInfo().onError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"OnError [%s] requires forking but is prevented by system call filters ([%s=true]);" +
|
||||||
|
" upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError",
|
||||||
|
onError(),
|
||||||
|
BootstrapSettings.SECCOMP_SETTING.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class OnOutOfMemoryErrorCheck extends MightForkCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean mightFork() {
|
||||||
|
final String onOutOfMemoryError = onOutOfMemoryError();
|
||||||
|
return onOutOfMemoryError != null && !onOutOfMemoryError.equals("");
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
String onOutOfMemoryError() {
|
||||||
|
return JvmInfo.jvmInfo().onOutOfMemoryError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"OnOutOfMemoryError [%s] requires forking but is prevented by system call filters ([%s=true]);" +
|
||||||
|
" upgrade to at least Java 8u92 and use ExitOnOutOfMemoryError",
|
||||||
|
onOutOfMemoryError(),
|
||||||
|
BootstrapSettings.SECCOMP_SETTING.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap check for versions of HotSpot that are known to have issues that can lead to index corruption when G1GC is enabled.
|
||||||
|
*/
|
||||||
|
static class G1GCCheck implements BootstrapCheck {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
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
|
||||||
|
final Pattern pattern = Pattern.compile("(\\d+)\\.(\\d+)-b\\d+");
|
||||||
|
final Matcher matcher = pattern.matcher(jvmVersion);
|
||||||
|
final boolean matches = matcher.matches();
|
||||||
|
assert matches : jvmVersion;
|
||||||
|
final int major = Integer.parseInt(matcher.group(1));
|
||||||
|
final int update = Integer.parseInt(matcher.group(2));
|
||||||
|
// HotSpot versions for Java 8 have major version 25, the bad versions are all versions prior to update 40
|
||||||
|
return major == 25 && update < 40;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
String jvmVendor() {
|
||||||
|
return Constants.JVM_VENDOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
boolean isG1GCEnabled() {
|
||||||
|
assert "Oracle Corporation".equals(jvmVendor());
|
||||||
|
return JvmInfo.jvmInfo().useG1GC().equals("true");
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
String jvmVersion() {
|
||||||
|
assert "Oracle Corporation".equals(jvmVendor());
|
||||||
|
return Constants.JVM_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// visible for testing
|
||||||
|
boolean isJava8() {
|
||||||
|
assert "Oracle Corporation".equals(jvmVendor());
|
||||||
|
return JavaVersion.current().equals(JavaVersion.parse("1.8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return String.format(
|
||||||
|
Locale.ROOT,
|
||||||
|
"JVM version [%s] can cause data corruption when used with G1GC; upgrade to at least Java 8u40", jvmVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,6 +32,7 @@ import org.elasticsearch.action.search.SearchPhaseController;
|
|||||||
import org.elasticsearch.action.search.SearchTransportService;
|
import org.elasticsearch.action.search.SearchTransportService;
|
||||||
import org.elasticsearch.action.support.TransportAction;
|
import org.elasticsearch.action.support.TransportAction;
|
||||||
import org.elasticsearch.action.update.UpdateHelper;
|
import org.elasticsearch.action.update.UpdateHelper;
|
||||||
|
import org.elasticsearch.bootstrap.BootstrapCheck;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.client.node.NodeClient;
|
import org.elasticsearch.client.node.NodeClient;
|
||||||
import org.elasticsearch.cluster.ClusterInfoService;
|
import org.elasticsearch.cluster.ClusterInfoService;
|
||||||
@ -456,7 +457,6 @@ public class Node implements Closeable {
|
|||||||
.map(injector::getInstance).collect(Collectors.toList()));
|
.map(injector::getInstance).collect(Collectors.toList()));
|
||||||
resourcesToClose.addAll(pluginLifecycleComponents);
|
resourcesToClose.addAll(pluginLifecycleComponents);
|
||||||
this.pluginLifecycleComponents = Collections.unmodifiableList(pluginLifecycleComponents);
|
this.pluginLifecycleComponents = Collections.unmodifiableList(pluginLifecycleComponents);
|
||||||
|
|
||||||
client.initialize(injector.getInstance(new Key<Map<GenericAction, TransportAction>>() {}));
|
client.initialize(injector.getInstance(new Key<Map<GenericAction, TransportAction>>() {}));
|
||||||
|
|
||||||
logger.info("initialized");
|
logger.info("initialized");
|
||||||
@ -568,8 +568,8 @@ public class Node implements Closeable {
|
|||||||
TransportService transportService = injector.getInstance(TransportService.class);
|
TransportService transportService = injector.getInstance(TransportService.class);
|
||||||
transportService.getTaskManager().setTaskResultsService(injector.getInstance(TaskResultsService.class));
|
transportService.getTaskManager().setTaskResultsService(injector.getInstance(TaskResultsService.class));
|
||||||
transportService.start();
|
transportService.start();
|
||||||
|
validateNodeBeforeAcceptingRequests(settings, transportService.boundAddress(), pluginsService.filterPlugins(Plugin.class).stream()
|
||||||
validateNodeBeforeAcceptingRequests(settings, transportService.boundAddress());
|
.flatMap(p -> p.getBootstrapChecks().stream()).collect(Collectors.toList()));
|
||||||
|
|
||||||
DiscoveryNode localNode = DiscoveryNode.createLocal(settings,
|
DiscoveryNode localNode = DiscoveryNode.createLocal(settings,
|
||||||
transportService.boundAddress().publishAddress(), injector.getInstance(NodeEnvironment.class).nodeId());
|
transportService.boundAddress().publishAddress(), injector.getInstance(NodeEnvironment.class).nodeId());
|
||||||
@ -790,7 +790,7 @@ public class Node implements Closeable {
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
protected void validateNodeBeforeAcceptingRequests(
|
protected void validateNodeBeforeAcceptingRequests(
|
||||||
final Settings settings,
|
final Settings settings,
|
||||||
final BoundTransportAddress boundTransportAddress) throws NodeValidationException {
|
final BoundTransportAddress boundTransportAddress, List<BootstrapCheck> bootstrapChecks) throws NodeValidationException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Writes a file to the logs dir containing the ports for the given transport type */
|
/** Writes a file to the logs dir containing the ports for the given transport type */
|
||||||
|
@ -22,12 +22,13 @@ package org.elasticsearch.node;
|
|||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.transport.BoundTransportAddress;
|
import org.elasticsearch.common.transport.BoundTransportAddress;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An exception thrown during node validation. Node validation runs immediately before a node
|
* An exception thrown during node validation. Node validation runs immediately before a node
|
||||||
* begins accepting network requests in
|
* begins accepting network requests in
|
||||||
* {@link Node#validateNodeBeforeAcceptingRequests(Settings, BoundTransportAddress)}. This
|
* {@link Node#validateNodeBeforeAcceptingRequests(Settings, BoundTransportAddress, List)}. This exception is a checked exception that
|
||||||
* exception is a checked exception that is declared as thrown from this method for the purpose
|
* is declared as thrown from this method for the purpose of bubbling up to the user.
|
||||||
* of bubbling up to the user.
|
|
||||||
*/
|
*/
|
||||||
public class NodeValidationException extends Exception {
|
public class NodeValidationException extends Exception {
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionModule;
|
import org.elasticsearch.action.ActionModule;
|
||||||
|
import org.elasticsearch.bootstrap.BootstrapCheck;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.cluster.ClusterModule;
|
import org.elasticsearch.cluster.ClusterModule;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
@ -164,6 +165,14 @@ public abstract class Plugin implements Closeable {
|
|||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of checks that are enforced when a node starts up once a node has the transport protocol bound to a non-loopback
|
||||||
|
* interface. In this case we assume the node is running in production and all bootstrap checks must pass. This allows plugins
|
||||||
|
* to provide a better out of the box experience by pre-configuring otherwise (in production) mandatory settings or to enforce certain
|
||||||
|
* configurations like OS settings or 3rd party resources.
|
||||||
|
*/
|
||||||
|
public List<BootstrapCheck> getBootstrapChecks() { return Collections.emptyList(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the resources opened by this plugin.
|
* Close the resources opened by this plugin.
|
||||||
*
|
*
|
||||||
|
@ -63,18 +63,18 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
BoundTransportAddress boundTransportAddress = mock(BoundTransportAddress.class);
|
BoundTransportAddress boundTransportAddress = mock(BoundTransportAddress.class);
|
||||||
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
|
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
|
||||||
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
|
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
|
||||||
BootstrapCheck.check(Settings.EMPTY, boundTransportAddress);
|
BootstrapChecks.check(Settings.EMPTY, boundTransportAddress, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNoLogMessageInNonProductionMode() throws NodeValidationException {
|
public void testNoLogMessageInNonProductionMode() throws NodeValidationException {
|
||||||
final Logger logger = mock(Logger.class);
|
final Logger logger = mock(Logger.class);
|
||||||
BootstrapCheck.check(false, Collections.emptyList(), logger);
|
BootstrapChecks.check(false, Collections.emptyList(), logger);
|
||||||
verifyNoMoreInteractions(logger);
|
verifyNoMoreInteractions(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testLogMessageInProductionMode() throws NodeValidationException {
|
public void testLogMessageInProductionMode() throws NodeValidationException {
|
||||||
final Logger logger = mock(Logger.class);
|
final Logger logger = mock(Logger.class);
|
||||||
BootstrapCheck.check(true, Collections.emptyList(), logger);
|
BootstrapChecks.check(true, Collections.emptyList(), logger);
|
||||||
verify(logger).info("bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks");
|
verify(logger).info("bound or publishing to a non-loopback or non-link-local address, enforcing bootstrap checks");
|
||||||
verifyNoMoreInteractions(logger);
|
verifyNoMoreInteractions(logger);
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
|
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
|
||||||
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
|
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
|
||||||
|
|
||||||
assertTrue(BootstrapCheck.enforceLimits(boundTransportAddress));
|
assertTrue(BootstrapChecks.enforceLimits(boundTransportAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEnforceLimitsWhenPublishingToNonLocalAddress() {
|
public void testEnforceLimitsWhenPublishingToNonLocalAddress() {
|
||||||
@ -114,12 +114,12 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
|
when(boundTransportAddress.boundAddresses()).thenReturn(transportAddresses.toArray(new TransportAddress[0]));
|
||||||
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
|
when(boundTransportAddress.publishAddress()).thenReturn(publishAddress);
|
||||||
|
|
||||||
assertTrue(BootstrapCheck.enforceLimits(boundTransportAddress));
|
assertTrue(BootstrapChecks.enforceLimits(boundTransportAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testExceptionAggregation() {
|
public void testExceptionAggregation() {
|
||||||
final List<BootstrapCheck.Check> checks = Arrays.asList(
|
final List<BootstrapCheck> checks = Arrays.asList(
|
||||||
new BootstrapCheck.Check() {
|
new BootstrapCheck() {
|
||||||
@Override
|
@Override
|
||||||
public boolean check() {
|
public boolean check() {
|
||||||
return true;
|
return true;
|
||||||
@ -130,7 +130,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
return "first";
|
return "first";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new BootstrapCheck.Check() {
|
new BootstrapCheck() {
|
||||||
@Override
|
@Override
|
||||||
public boolean check() {
|
public boolean check() {
|
||||||
return true;
|
return true;
|
||||||
@ -144,7 +144,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final NodeValidationException e =
|
final NodeValidationException e =
|
||||||
expectThrows(NodeValidationException.class, () -> BootstrapCheck.check(true, checks, "testExceptionAggregation"));
|
expectThrows(NodeValidationException.class, () -> BootstrapChecks.check(true, checks, "testExceptionAggregation"));
|
||||||
assertThat(e, hasToString(allOf(containsString("bootstrap checks failed"), containsString("first"), containsString("second"))));
|
assertThat(e, hasToString(allOf(containsString("bootstrap checks failed"), containsString("first"), containsString("second"))));
|
||||||
final Throwable[] suppressed = e.getSuppressed();
|
final Throwable[] suppressed = e.getSuppressed();
|
||||||
assertThat(suppressed.length, equalTo(2));
|
assertThat(suppressed.length, equalTo(2));
|
||||||
@ -160,7 +160,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
final AtomicLong initialHeapSize = new AtomicLong(initial);
|
final AtomicLong initialHeapSize = new AtomicLong(initial);
|
||||||
final AtomicLong maxHeapSize = new AtomicLong(max);
|
final AtomicLong maxHeapSize = new AtomicLong(max);
|
||||||
|
|
||||||
final BootstrapCheck.HeapSizeCheck check = new BootstrapCheck.HeapSizeCheck() {
|
final BootstrapChecks.HeapSizeCheck check = new BootstrapChecks.HeapSizeCheck() {
|
||||||
@Override
|
@Override
|
||||||
long getInitialHeapSize() {
|
long getInitialHeapSize() {
|
||||||
return initialHeapSize.get();
|
return initialHeapSize.get();
|
||||||
@ -175,7 +175,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
final NodeValidationException e =
|
final NodeValidationException e =
|
||||||
expectThrows(
|
expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(check), "testHeapSizeCheck"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testHeapSizeCheck"));
|
||||||
assertThat(
|
assertThat(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
containsString("initial heap size [" + initialHeapSize.get() + "] " +
|
containsString("initial heap size [" + initialHeapSize.get() + "] " +
|
||||||
@ -183,7 +183,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
initialHeapSize.set(maxHeapSize.get());
|
initialHeapSize.set(maxHeapSize.get());
|
||||||
|
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testHeapSizeCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testHeapSizeCheck");
|
||||||
|
|
||||||
// nothing should happen if the initial heap size or the max
|
// nothing should happen if the initial heap size or the max
|
||||||
// heap size is not available
|
// heap size is not available
|
||||||
@ -192,23 +192,23 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
} else {
|
} else {
|
||||||
maxHeapSize.set(0);
|
maxHeapSize.set(0);
|
||||||
}
|
}
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testHeapSizeCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testHeapSizeCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFileDescriptorLimits() throws NodeValidationException {
|
public void testFileDescriptorLimits() throws NodeValidationException {
|
||||||
final boolean osX = randomBoolean(); // simulates OS X versus non-OS X
|
final boolean osX = randomBoolean(); // simulates OS X versus non-OS X
|
||||||
final int limit = osX ? 10240 : 1 << 16;
|
final int limit = osX ? 10240 : 1 << 16;
|
||||||
final AtomicLong maxFileDescriptorCount = new AtomicLong(randomIntBetween(1, limit - 1));
|
final AtomicLong maxFileDescriptorCount = new AtomicLong(randomIntBetween(1, limit - 1));
|
||||||
final BootstrapCheck.FileDescriptorCheck check;
|
final BootstrapChecks.FileDescriptorCheck check;
|
||||||
if (osX) {
|
if (osX) {
|
||||||
check = new BootstrapCheck.OsXFileDescriptorCheck() {
|
check = new BootstrapChecks.OsXFileDescriptorCheck() {
|
||||||
@Override
|
@Override
|
||||||
long getMaxFileDescriptorCount() {
|
long getMaxFileDescriptorCount() {
|
||||||
return maxFileDescriptorCount.get();
|
return maxFileDescriptorCount.get();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
check = new BootstrapCheck.FileDescriptorCheck() {
|
check = new BootstrapChecks.FileDescriptorCheck() {
|
||||||
@Override
|
@Override
|
||||||
long getMaxFileDescriptorCount() {
|
long getMaxFileDescriptorCount() {
|
||||||
return maxFileDescriptorCount.get();
|
return maxFileDescriptorCount.get();
|
||||||
@ -218,24 +218,24 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e =
|
final NodeValidationException e =
|
||||||
expectThrows(NodeValidationException.class,
|
expectThrows(NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(check), "testFileDescriptorLimits"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimits"));
|
||||||
assertThat(e.getMessage(), containsString("max file descriptors"));
|
assertThat(e.getMessage(), containsString("max file descriptors"));
|
||||||
|
|
||||||
maxFileDescriptorCount.set(randomIntBetween(limit + 1, Integer.MAX_VALUE));
|
maxFileDescriptorCount.set(randomIntBetween(limit + 1, Integer.MAX_VALUE));
|
||||||
|
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testFileDescriptorLimits");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimits");
|
||||||
|
|
||||||
// nothing should happen if current file descriptor count is
|
// nothing should happen if current file descriptor count is
|
||||||
// not available
|
// not available
|
||||||
maxFileDescriptorCount.set(-1);
|
maxFileDescriptorCount.set(-1);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testFileDescriptorLimits");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimits");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFileDescriptorLimitsThrowsOnInvalidLimit() {
|
public void testFileDescriptorLimitsThrowsOnInvalidLimit() {
|
||||||
final IllegalArgumentException e =
|
final IllegalArgumentException e =
|
||||||
expectThrows(
|
expectThrows(
|
||||||
IllegalArgumentException.class,
|
IllegalArgumentException.class,
|
||||||
() -> new BootstrapCheck.FileDescriptorCheck(-randomIntBetween(0, Integer.MAX_VALUE)));
|
() -> new BootstrapChecks.FileDescriptorCheck(-randomIntBetween(0, Integer.MAX_VALUE)));
|
||||||
assertThat(e.getMessage(), containsString("limit must be positive but was"));
|
assertThat(e.getMessage(), containsString("limit must be positive but was"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +261,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
testCases.add(new MlockallCheckTestCase(false, false, false));
|
testCases.add(new MlockallCheckTestCase(false, false, false));
|
||||||
|
|
||||||
for (final MlockallCheckTestCase testCase : testCases) {
|
for (final MlockallCheckTestCase testCase : testCases) {
|
||||||
final BootstrapCheck.MlockallCheck check = new BootstrapCheck.MlockallCheck(testCase.mlockallSet) {
|
final BootstrapChecks.MlockallCheck check = new BootstrapChecks.MlockallCheck(testCase.mlockallSet) {
|
||||||
@Override
|
@Override
|
||||||
boolean isMemoryLocked() {
|
boolean isMemoryLocked() {
|
||||||
return testCase.isMemoryLocked;
|
return testCase.isMemoryLocked;
|
||||||
@ -271,7 +271,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
if (testCase.shouldFail) {
|
if (testCase.shouldFail) {
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(
|
() -> BootstrapChecks.check(
|
||||||
true,
|
true,
|
||||||
Collections.singletonList(check),
|
Collections.singletonList(check),
|
||||||
"testFileDescriptorLimitsThrowsOnInvalidLimit"));
|
"testFileDescriptorLimitsThrowsOnInvalidLimit"));
|
||||||
@ -280,7 +280,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
containsString("memory locking requested for elasticsearch process but memory is not locked"));
|
containsString("memory locking requested for elasticsearch process but memory is not locked"));
|
||||||
} else {
|
} else {
|
||||||
// nothing should happen
|
// nothing should happen
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testFileDescriptorLimitsThrowsOnInvalidLimit");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testFileDescriptorLimitsThrowsOnInvalidLimit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,7 +288,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
public void testMaxNumberOfThreadsCheck() throws NodeValidationException {
|
public void testMaxNumberOfThreadsCheck() throws NodeValidationException {
|
||||||
final int limit = 1 << 11;
|
final int limit = 1 << 11;
|
||||||
final AtomicLong maxNumberOfThreads = new AtomicLong(randomIntBetween(1, limit - 1));
|
final AtomicLong maxNumberOfThreads = new AtomicLong(randomIntBetween(1, limit - 1));
|
||||||
final BootstrapCheck.MaxNumberOfThreadsCheck check = new BootstrapCheck.MaxNumberOfThreadsCheck() {
|
final BootstrapChecks.MaxNumberOfThreadsCheck check = new BootstrapChecks.MaxNumberOfThreadsCheck() {
|
||||||
@Override
|
@Override
|
||||||
long getMaxNumberOfThreads() {
|
long getMaxNumberOfThreads() {
|
||||||
return maxNumberOfThreads.get();
|
return maxNumberOfThreads.get();
|
||||||
@ -297,23 +297,23 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck"));
|
||||||
assertThat(e.getMessage(), containsString("max number of threads"));
|
assertThat(e.getMessage(), containsString("max number of threads"));
|
||||||
|
|
||||||
maxNumberOfThreads.set(randomIntBetween(limit + 1, Integer.MAX_VALUE));
|
maxNumberOfThreads.set(randomIntBetween(limit + 1, Integer.MAX_VALUE));
|
||||||
|
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck");
|
||||||
|
|
||||||
// nothing should happen if current max number of threads is
|
// nothing should happen if current max number of threads is
|
||||||
// not available
|
// not available
|
||||||
maxNumberOfThreads.set(-1);
|
maxNumberOfThreads.set(-1);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxNumberOfThreadsCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMaxSizeVirtualMemory() throws NodeValidationException {
|
public void testMaxSizeVirtualMemory() throws NodeValidationException {
|
||||||
final long rlimInfinity = Constants.MAC_OS_X ? 9223372036854775807L : -1L;
|
final long rlimInfinity = Constants.MAC_OS_X ? 9223372036854775807L : -1L;
|
||||||
final AtomicLong maxSizeVirtualMemory = new AtomicLong(randomIntBetween(0, Integer.MAX_VALUE));
|
final AtomicLong maxSizeVirtualMemory = new AtomicLong(randomIntBetween(0, Integer.MAX_VALUE));
|
||||||
final BootstrapCheck.MaxSizeVirtualMemoryCheck check = new BootstrapCheck.MaxSizeVirtualMemoryCheck() {
|
final BootstrapChecks.MaxSizeVirtualMemoryCheck check = new BootstrapChecks.MaxSizeVirtualMemoryCheck() {
|
||||||
@Override
|
@Override
|
||||||
long getMaxSizeVirtualMemory() {
|
long getMaxSizeVirtualMemory() {
|
||||||
return maxSizeVirtualMemory.get();
|
return maxSizeVirtualMemory.get();
|
||||||
@ -328,23 +328,23 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory"));
|
||||||
assertThat(e.getMessage(), containsString("max size virtual memory"));
|
assertThat(e.getMessage(), containsString("max size virtual memory"));
|
||||||
|
|
||||||
maxSizeVirtualMemory.set(rlimInfinity);
|
maxSizeVirtualMemory.set(rlimInfinity);
|
||||||
|
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory");
|
||||||
|
|
||||||
// nothing should happen if max size virtual memory is not
|
// nothing should happen if max size virtual memory is not
|
||||||
// available
|
// available
|
||||||
maxSizeVirtualMemory.set(Long.MIN_VALUE);
|
maxSizeVirtualMemory.set(Long.MIN_VALUE);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxSizeVirtualMemory");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMaxMapCountCheck() throws NodeValidationException {
|
public void testMaxMapCountCheck() throws NodeValidationException {
|
||||||
final int limit = 1 << 18;
|
final int limit = 1 << 18;
|
||||||
final AtomicLong maxMapCount = new AtomicLong(randomIntBetween(1, limit - 1));
|
final AtomicLong maxMapCount = new AtomicLong(randomIntBetween(1, limit - 1));
|
||||||
final BootstrapCheck.MaxMapCountCheck check = new BootstrapCheck.MaxMapCountCheck() {
|
final BootstrapChecks.MaxMapCountCheck check = new BootstrapChecks.MaxMapCountCheck() {
|
||||||
@Override
|
@Override
|
||||||
long getMaxMapCount() {
|
long getMaxMapCount() {
|
||||||
return maxMapCount.get();
|
return maxMapCount.get();
|
||||||
@ -353,22 +353,22 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(check), "testMaxMapCountCheck"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testMaxMapCountCheck"));
|
||||||
assertThat(e.getMessage(), containsString("max virtual memory areas vm.max_map_count"));
|
assertThat(e.getMessage(), containsString("max virtual memory areas vm.max_map_count"));
|
||||||
|
|
||||||
maxMapCount.set(randomIntBetween(limit + 1, Integer.MAX_VALUE));
|
maxMapCount.set(randomIntBetween(limit + 1, Integer.MAX_VALUE));
|
||||||
|
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testMaxMapCountCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxMapCountCheck");
|
||||||
|
|
||||||
// nothing should happen if current vm.max_map_count is not
|
// nothing should happen if current vm.max_map_count is not
|
||||||
// available
|
// available
|
||||||
maxMapCount.set(-1);
|
maxMapCount.set(-1);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testMaxMapCountCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testMaxMapCountCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClientJvmCheck() throws NodeValidationException {
|
public void testClientJvmCheck() throws NodeValidationException {
|
||||||
final AtomicReference<String> vmName = new AtomicReference<>("Java HotSpot(TM) 32-Bit Client VM");
|
final AtomicReference<String> vmName = new AtomicReference<>("Java HotSpot(TM) 32-Bit Client VM");
|
||||||
final BootstrapCheck.Check check = new BootstrapCheck.ClientJvmCheck() {
|
final BootstrapCheck check = new BootstrapChecks.ClientJvmCheck() {
|
||||||
@Override
|
@Override
|
||||||
String getVmName() {
|
String getVmName() {
|
||||||
return vmName.get();
|
return vmName.get();
|
||||||
@ -377,19 +377,19 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(check), "testClientJvmCheck"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testClientJvmCheck"));
|
||||||
assertThat(
|
assertThat(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
containsString("JVM is using the client VM [Java HotSpot(TM) 32-Bit Client VM] " +
|
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"));
|
"but should be using a server VM for the best performance"));
|
||||||
|
|
||||||
vmName.set("Java HotSpot(TM) 32-Bit Server VM");
|
vmName.set("Java HotSpot(TM) 32-Bit Server VM");
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testClientJvmCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testClientJvmCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testUseSerialGCCheck() throws NodeValidationException {
|
public void testUseSerialGCCheck() throws NodeValidationException {
|
||||||
final AtomicReference<String> useSerialGC = new AtomicReference<>("true");
|
final AtomicReference<String> useSerialGC = new AtomicReference<>("true");
|
||||||
final BootstrapCheck.Check check = new BootstrapCheck.UseSerialGCCheck() {
|
final BootstrapCheck check = new BootstrapChecks.UseSerialGCCheck() {
|
||||||
@Override
|
@Override
|
||||||
String getUseSerialGC() {
|
String getUseSerialGC() {
|
||||||
return useSerialGC.get();
|
return useSerialGC.get();
|
||||||
@ -398,19 +398,19 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(check), "testUseSerialGCCheck"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(check), "testUseSerialGCCheck"));
|
||||||
assertThat(
|
assertThat(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
containsString("JVM is using the serial collector but should not be for the best performance; " + "" +
|
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"));
|
"either it's the default for the VM [" + JvmInfo.jvmInfo().getVmName() +"] or -XX:+UseSerialGC was explicitly specified"));
|
||||||
|
|
||||||
useSerialGC.set("false");
|
useSerialGC.set("false");
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), "testUseSerialGCCheck");
|
BootstrapChecks.check(true, Collections.singletonList(check), "testUseSerialGCCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSystemCallFilterCheck() throws NodeValidationException {
|
public void testSystemCallFilterCheck() throws NodeValidationException {
|
||||||
final AtomicBoolean isSecompInstalled = new AtomicBoolean();
|
final AtomicBoolean isSecompInstalled = new AtomicBoolean();
|
||||||
final BootstrapCheck.SystemCallFilterCheck systemCallFilterEnabledCheck = new BootstrapCheck.SystemCallFilterCheck(true) {
|
final BootstrapChecks.SystemCallFilterCheck systemCallFilterEnabledCheck = new BootstrapChecks.SystemCallFilterCheck(true) {
|
||||||
@Override
|
@Override
|
||||||
boolean isSeccompInstalled() {
|
boolean isSeccompInstalled() {
|
||||||
return isSecompInstalled.get();
|
return isSecompInstalled.get();
|
||||||
@ -419,31 +419,31 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck"));
|
||||||
assertThat(
|
assertThat(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
containsString("system call filters failed to install; " +
|
containsString("system call filters failed to install; " +
|
||||||
"check the logs and fix your configuration or disable system call filters at your own risk"));
|
"check the logs and fix your configuration or disable system call filters at your own risk"));
|
||||||
|
|
||||||
isSecompInstalled.set(true);
|
isSecompInstalled.set(true);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck");
|
BootstrapChecks.check(true, Collections.singletonList(systemCallFilterEnabledCheck), "testSystemCallFilterCheck");
|
||||||
|
|
||||||
final BootstrapCheck.SystemCallFilterCheck systemCallFilterNotEnabledCheck = new BootstrapCheck.SystemCallFilterCheck(false) {
|
final BootstrapChecks.SystemCallFilterCheck systemCallFilterNotEnabledCheck = new BootstrapChecks.SystemCallFilterCheck(false) {
|
||||||
@Override
|
@Override
|
||||||
boolean isSeccompInstalled() {
|
boolean isSeccompInstalled() {
|
||||||
return isSecompInstalled.get();
|
return isSecompInstalled.get();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
isSecompInstalled.set(false);
|
isSecompInstalled.set(false);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck");
|
BootstrapChecks.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck");
|
||||||
isSecompInstalled.set(true);
|
isSecompInstalled.set(true);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck");
|
BootstrapChecks.check(true, Collections.singletonList(systemCallFilterNotEnabledCheck), "testSystemCallFilterCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMightForkCheck() throws NodeValidationException {
|
public void testMightForkCheck() throws NodeValidationException {
|
||||||
final AtomicBoolean isSeccompInstalled = new AtomicBoolean();
|
final AtomicBoolean isSeccompInstalled = new AtomicBoolean();
|
||||||
final AtomicBoolean mightFork = new AtomicBoolean();
|
final AtomicBoolean mightFork = new AtomicBoolean();
|
||||||
final BootstrapCheck.MightForkCheck check = new BootstrapCheck.MightForkCheck() {
|
final BootstrapChecks.MightForkCheck check = new BootstrapChecks.MightForkCheck() {
|
||||||
@Override
|
@Override
|
||||||
boolean isSeccompInstalled() {
|
boolean isSeccompInstalled() {
|
||||||
return isSeccompInstalled.get();
|
return isSeccompInstalled.get();
|
||||||
@ -471,7 +471,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
public void testOnErrorCheck() throws NodeValidationException {
|
public void testOnErrorCheck() throws NodeValidationException {
|
||||||
final AtomicBoolean isSeccompInstalled = new AtomicBoolean();
|
final AtomicBoolean isSeccompInstalled = new AtomicBoolean();
|
||||||
final AtomicReference<String> onError = new AtomicReference<>();
|
final AtomicReference<String> onError = new AtomicReference<>();
|
||||||
final BootstrapCheck.MightForkCheck check = new BootstrapCheck.OnErrorCheck() {
|
final BootstrapChecks.MightForkCheck check = new BootstrapChecks.OnErrorCheck() {
|
||||||
@Override
|
@Override
|
||||||
boolean isSeccompInstalled() {
|
boolean isSeccompInstalled() {
|
||||||
return isSeccompInstalled.get();
|
return isSeccompInstalled.get();
|
||||||
@ -499,7 +499,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
public void testOnOutOfMemoryErrorCheck() throws NodeValidationException {
|
public void testOnOutOfMemoryErrorCheck() throws NodeValidationException {
|
||||||
final AtomicBoolean isSeccompInstalled = new AtomicBoolean();
|
final AtomicBoolean isSeccompInstalled = new AtomicBoolean();
|
||||||
final AtomicReference<String> onOutOfMemoryError = new AtomicReference<>();
|
final AtomicReference<String> onOutOfMemoryError = new AtomicReference<>();
|
||||||
final BootstrapCheck.MightForkCheck check = new BootstrapCheck.OnOutOfMemoryErrorCheck() {
|
final BootstrapChecks.MightForkCheck check = new BootstrapChecks.OnOutOfMemoryErrorCheck() {
|
||||||
@Override
|
@Override
|
||||||
boolean isSeccompInstalled() {
|
boolean isSeccompInstalled() {
|
||||||
return isSeccompInstalled.get();
|
return isSeccompInstalled.get();
|
||||||
@ -526,7 +526,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void runMightForkTest(
|
private void runMightForkTest(
|
||||||
final BootstrapCheck.MightForkCheck check,
|
final BootstrapChecks.MightForkCheck check,
|
||||||
final AtomicBoolean isSeccompInstalled,
|
final AtomicBoolean isSeccompInstalled,
|
||||||
final Runnable disableMightFork,
|
final Runnable disableMightFork,
|
||||||
final Runnable enableMightFork,
|
final Runnable enableMightFork,
|
||||||
@ -541,13 +541,13 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
} else {
|
} else {
|
||||||
enableMightFork.run();
|
enableMightFork.run();
|
||||||
}
|
}
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), methodName);
|
BootstrapChecks.check(true, Collections.singletonList(check), methodName);
|
||||||
|
|
||||||
// if seccomp is enabled, but we will not fork, nothing should
|
// if seccomp is enabled, but we will not fork, nothing should
|
||||||
// happen
|
// happen
|
||||||
isSeccompInstalled.set(true);
|
isSeccompInstalled.set(true);
|
||||||
disableMightFork.run();
|
disableMightFork.run();
|
||||||
BootstrapCheck.check(true, Collections.singletonList(check), methodName);
|
BootstrapChecks.check(true, Collections.singletonList(check), methodName);
|
||||||
|
|
||||||
// if seccomp is enabled, and we might fork, the check should
|
// if seccomp is enabled, and we might fork, the check should
|
||||||
// be enforced, regardless of bootstrap checks being enabled or
|
// be enforced, regardless of bootstrap checks being enabled or
|
||||||
@ -557,7 +557,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException e = expectThrows(
|
final NodeValidationException e = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(randomBoolean(), Collections.singletonList(check), methodName));
|
() -> BootstrapChecks.check(randomBoolean(), Collections.singletonList(check), methodName));
|
||||||
consumer.accept(e);
|
consumer.accept(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,7 +566,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
final AtomicBoolean isJava8 = new AtomicBoolean(true);
|
final AtomicBoolean isJava8 = new AtomicBoolean(true);
|
||||||
final AtomicReference<String> jvmVersion =
|
final AtomicReference<String> jvmVersion =
|
||||||
new AtomicReference<>(String.format(Locale.ROOT, "25.%d-b%d", randomIntBetween(0, 39), randomIntBetween(1, 128)));
|
new AtomicReference<>(String.format(Locale.ROOT, "25.%d-b%d", randomIntBetween(0, 39), randomIntBetween(1, 128)));
|
||||||
final BootstrapCheck.G1GCCheck oracleCheck = new BootstrapCheck.G1GCCheck() {
|
final BootstrapChecks.G1GCCheck oracleCheck = new BootstrapChecks.G1GCCheck() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String jvmVendor() {
|
String jvmVendor() {
|
||||||
@ -593,7 +593,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
final NodeValidationException e =
|
final NodeValidationException e =
|
||||||
expectThrows(
|
expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(true, Collections.singletonList(oracleCheck), "testG1GCCheck"));
|
() -> BootstrapChecks.check(true, Collections.singletonList(oracleCheck), "testG1GCCheck"));
|
||||||
assertThat(
|
assertThat(
|
||||||
e.getMessage(),
|
e.getMessage(),
|
||||||
containsString(
|
containsString(
|
||||||
@ -601,14 +601,14 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
// if G1GC is disabled, nothing should happen
|
// if G1GC is disabled, nothing should happen
|
||||||
isG1GCEnabled.set(false);
|
isG1GCEnabled.set(false);
|
||||||
BootstrapCheck.check(true, Collections.singletonList(oracleCheck), "testG1GCCheck");
|
BootstrapChecks.check(true, Collections.singletonList(oracleCheck), "testG1GCCheck");
|
||||||
|
|
||||||
// if on or after update 40, nothing should happen independent of whether or not G1GC is enabled
|
// if on or after update 40, nothing should happen independent of whether or not G1GC is enabled
|
||||||
isG1GCEnabled.set(randomBoolean());
|
isG1GCEnabled.set(randomBoolean());
|
||||||
jvmVersion.set(String.format(Locale.ROOT, "25.%d-b%d", randomIntBetween(40, 112), randomIntBetween(1, 128)));
|
jvmVersion.set(String.format(Locale.ROOT, "25.%d-b%d", randomIntBetween(40, 112), randomIntBetween(1, 128)));
|
||||||
BootstrapCheck.check(true, Collections.singletonList(oracleCheck), "testG1GCCheck");
|
BootstrapChecks.check(true, Collections.singletonList(oracleCheck), "testG1GCCheck");
|
||||||
|
|
||||||
final BootstrapCheck.G1GCCheck nonOracleCheck = new BootstrapCheck.G1GCCheck() {
|
final BootstrapChecks.G1GCCheck nonOracleCheck = new BootstrapChecks.G1GCCheck() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
String jvmVendor() {
|
String jvmVendor() {
|
||||||
@ -618,9 +618,9 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// if not on an Oracle JVM, nothing should happen
|
// if not on an Oracle JVM, nothing should happen
|
||||||
BootstrapCheck.check(true, Collections.singletonList(nonOracleCheck), "testG1GCCheck");
|
BootstrapChecks.check(true, Collections.singletonList(nonOracleCheck), "testG1GCCheck");
|
||||||
|
|
||||||
final BootstrapCheck.G1GCCheck nonJava8Check = new BootstrapCheck.G1GCCheck() {
|
final BootstrapChecks.G1GCCheck nonJava8Check = new BootstrapChecks.G1GCCheck() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isJava8() {
|
boolean isJava8() {
|
||||||
@ -630,11 +630,11 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// if not Java 8, nothing should happen
|
// if not Java 8, nothing should happen
|
||||||
BootstrapCheck.check(true, Collections.singletonList(nonJava8Check), "testG1GCCheck");
|
BootstrapChecks.check(true, Collections.singletonList(nonJava8Check), "testG1GCCheck");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAlwaysEnforcedChecks() {
|
public void testAlwaysEnforcedChecks() {
|
||||||
final BootstrapCheck.Check check = new BootstrapCheck.Check() {
|
final BootstrapCheck check = new BootstrapCheck() {
|
||||||
@Override
|
@Override
|
||||||
public boolean check() {
|
public boolean check() {
|
||||||
return true;
|
return true;
|
||||||
@ -653,7 +653,7 @@ public class BootstrapCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
final NodeValidationException alwaysEnforced = expectThrows(
|
final NodeValidationException alwaysEnforced = expectThrows(
|
||||||
NodeValidationException.class,
|
NodeValidationException.class,
|
||||||
() -> BootstrapCheck.check(randomBoolean(), Collections.singletonList(check), "testAlwaysEnforcedChecks"));
|
() -> BootstrapChecks.check(randomBoolean(), Collections.singletonList(check), "testAlwaysEnforcedChecks"));
|
||||||
assertThat(alwaysEnforced, hasToString(containsString("error")));
|
assertThat(alwaysEnforced, hasToString(containsString("error")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class MaxMapCountCheckTests extends ESTestCase {
|
|||||||
|
|
||||||
public void testGetMaxMapCountOnLinux() {
|
public void testGetMaxMapCountOnLinux() {
|
||||||
if (Constants.LINUX) {
|
if (Constants.LINUX) {
|
||||||
final BootstrapCheck.MaxMapCountCheck check = new BootstrapCheck.MaxMapCountCheck();
|
final BootstrapChecks.MaxMapCountCheck check = new BootstrapChecks.MaxMapCountCheck();
|
||||||
assertThat(check.getMaxMapCount(), greaterThan(0L));
|
assertThat(check.getMaxMapCount(), greaterThan(0L));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ public class MaxMapCountCheckTests extends ESTestCase {
|
|||||||
final BufferedReader reader = mock(BufferedReader.class);
|
final BufferedReader reader = mock(BufferedReader.class);
|
||||||
when(reader.readLine()).thenReturn(Long.toString(procSysVmMaxMapCount));
|
when(reader.readLine()).thenReturn(Long.toString(procSysVmMaxMapCount));
|
||||||
final Path procSysVmMaxMapCountPath = PathUtils.get("/proc/sys/vm/max_map_count");
|
final Path procSysVmMaxMapCountPath = PathUtils.get("/proc/sys/vm/max_map_count");
|
||||||
BootstrapCheck.MaxMapCountCheck check = new BootstrapCheck.MaxMapCountCheck() {
|
BootstrapChecks.MaxMapCountCheck check = new BootstrapChecks.MaxMapCountCheck() {
|
||||||
@Override
|
@Override
|
||||||
BufferedReader getBufferedReader(Path path) throws IOException {
|
BufferedReader getBufferedReader(Path path) throws IOException {
|
||||||
assertEquals(path, procSysVmMaxMapCountPath);
|
assertEquals(path, procSysVmMaxMapCountPath);
|
||||||
@ -164,13 +164,13 @@ public class MaxMapCountCheckTests extends ESTestCase {
|
|||||||
final String rawProcSysVmMaxMapCount = Long.toString(randomIntBetween(1, Integer.MAX_VALUE));
|
final String rawProcSysVmMaxMapCount = Long.toString(randomIntBetween(1, Integer.MAX_VALUE));
|
||||||
final BufferedReader reader = mock(BufferedReader.class);
|
final BufferedReader reader = mock(BufferedReader.class);
|
||||||
when(reader.readLine()).thenReturn(rawProcSysVmMaxMapCount);
|
when(reader.readLine()).thenReturn(rawProcSysVmMaxMapCount);
|
||||||
final BootstrapCheck.MaxMapCountCheck check = new BootstrapCheck.MaxMapCountCheck();
|
final BootstrapChecks.MaxMapCountCheck check = new BootstrapChecks.MaxMapCountCheck();
|
||||||
assertThat(check.readProcSysVmMaxMapCount(reader), equalTo(rawProcSysVmMaxMapCount));
|
assertThat(check.readProcSysVmMaxMapCount(reader), equalTo(rawProcSysVmMaxMapCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMaxMapCountCheckParse() {
|
public void testMaxMapCountCheckParse() {
|
||||||
final long procSysVmMaxMapCount = randomIntBetween(1, Integer.MAX_VALUE);
|
final long procSysVmMaxMapCount = randomIntBetween(1, Integer.MAX_VALUE);
|
||||||
final BootstrapCheck.MaxMapCountCheck check = new BootstrapCheck.MaxMapCountCheck();
|
final BootstrapChecks.MaxMapCountCheck check = new BootstrapChecks.MaxMapCountCheck();
|
||||||
assertThat(check.parseProcSysVmMaxMapCount(Long.toString(procSysVmMaxMapCount)), equalTo(procSysVmMaxMapCount));
|
assertThat(check.parseProcSysVmMaxMapCount(Long.toString(procSysVmMaxMapCount)), equalTo(procSysVmMaxMapCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,17 +20,23 @@ package org.elasticsearch.node;
|
|||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.bootstrap.BootstrapCheck;
|
||||||
import org.elasticsearch.cluster.ClusterName;
|
import org.elasticsearch.cluster.ClusterName;
|
||||||
import org.elasticsearch.common.network.NetworkModule;
|
import org.elasticsearch.common.network.NetworkModule;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.transport.BoundTransportAddress;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.InternalTestCluster;
|
import org.elasticsearch.test.InternalTestCluster;
|
||||||
import org.elasticsearch.transport.MockTcpTransportPlugin;
|
import org.elasticsearch.transport.MockTcpTransportPlugin;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@ -62,6 +68,52 @@ public class NodeTests extends ESTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class CheckPlugin extends Plugin {
|
||||||
|
public static final BootstrapCheck CHECK = new BootstrapCheck() {
|
||||||
|
@Override
|
||||||
|
public boolean check() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String errorMessage() {
|
||||||
|
return "boom";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@Override
|
||||||
|
public List<BootstrapCheck> getBootstrapChecks() {
|
||||||
|
return Collections.singletonList(CHECK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLoadPluginBootstrapChecks() throws IOException {
|
||||||
|
final Path tempDir = createTempDir();
|
||||||
|
final String name = randomBoolean() ? randomAsciiOfLength(10) : null;
|
||||||
|
Settings.Builder settings = Settings.builder()
|
||||||
|
.put(ClusterName.CLUSTER_NAME_SETTING.getKey(), InternalTestCluster.clusterName("single-node-cluster", randomLong()))
|
||||||
|
.put(Environment.PATH_HOME_SETTING.getKey(), tempDir)
|
||||||
|
.put(NetworkModule.HTTP_ENABLED.getKey(), false)
|
||||||
|
.put("transport.type", "mock-socket-network")
|
||||||
|
.put(Node.NODE_DATA_SETTING.getKey(), true);
|
||||||
|
if (name != null) {
|
||||||
|
settings.put(Node.NODE_NAME_SETTING.getKey(), name);
|
||||||
|
}
|
||||||
|
AtomicBoolean executed = new AtomicBoolean(false);
|
||||||
|
try (Node node = new MockNode(settings.build(), Arrays.asList(MockTcpTransportPlugin.class, CheckPlugin.class)) {
|
||||||
|
@Override
|
||||||
|
protected void validateNodeBeforeAcceptingRequests(Settings settings, BoundTransportAddress boundTransportAddress,
|
||||||
|
List<BootstrapCheck> bootstrapChecks) throws NodeValidationException {
|
||||||
|
assertEquals(1, bootstrapChecks.size());
|
||||||
|
assertSame(CheckPlugin.CHECK, bootstrapChecks.get(0));
|
||||||
|
executed.set(true);
|
||||||
|
throw new NodeValidationException("boom");
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
expectThrows(NodeValidationException.class, () -> node.start());
|
||||||
|
assertTrue(executed.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testWarnIfPreRelease() {
|
public void testWarnIfPreRelease() {
|
||||||
final Logger logger = mock(Logger.class);
|
final Logger logger = mock(Logger.class);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user