org.elasticsearch.cluster.desirednodes.DesiredNodesSettingsValidator Maven / Gradle / Ivy
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.cluster.desirednodes;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.DesiredNode;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.core.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import static java.lang.String.format;
import static org.elasticsearch.common.util.concurrent.EsExecutors.NODE_PROCESSORS_SETTING;
public class DesiredNodesSettingsValidator {
private record DesiredNodeValidationError(int position, @Nullable String externalId, RuntimeException exception) {}
private final ClusterSettings clusterSettings;
public DesiredNodesSettingsValidator(ClusterSettings clusterSettings) {
this.clusterSettings = clusterSettings;
}
public void validate(List nodes) {
final List validationErrors = new ArrayList<>();
for (int i = 0; i < nodes.size(); i++) {
final DesiredNode node = nodes.get(i);
try {
validate(node);
} catch (IllegalArgumentException e) {
validationErrors.add(new DesiredNodeValidationError(i, node.externalId(), e));
}
}
if (validationErrors.isEmpty() == false) {
final String nodeIndicesWithFailures = validationErrors.stream()
.map(DesiredNodeValidationError::position)
.map(i -> Integer.toString(i))
.collect(Collectors.joining(","));
final String nodeIdsWithFailures = validationErrors.stream()
.map(DesiredNodeValidationError::externalId)
.collect(Collectors.joining(","));
IllegalArgumentException invalidSettingsException = new IllegalArgumentException(
format(
Locale.ROOT,
"Nodes with ids [%s] in positions [%s] contain invalid settings",
nodeIdsWithFailures,
nodeIndicesWithFailures
)
);
for (DesiredNodeValidationError exceptionTuple : validationErrors) {
invalidSettingsException.addSuppressed(exceptionTuple.exception);
}
throw invalidSettingsException;
}
}
private void validate(DesiredNode node) {
if (node.version().before(Version.CURRENT)) {
throw new IllegalArgumentException(
format(Locale.ROOT, "Illegal node version [%s]. Only [%s] or newer versions are supported", node.version(), Version.CURRENT)
);
}
// Validating settings for future versions can be unsafe:
// - If the legal range is upgraded in the newer version
// - If a new setting is used as the default value for a previous setting
// To avoid considering these as invalid settings,
// We just don't validate settings for versions in newer versions.
if (node.version().after(Version.CURRENT)) {
return;
}
Settings settings = node.settings();
// node.processors rely on the environment to define its ranges, in this case
// we create a new setting just to run the validations using the desired node
// number of available processors
if (settings.hasValue(NODE_PROCESSORS_SETTING.getKey())) {
int minProcessors = node.roundedDownMinProcessors();
Integer roundedUpMaxProcessors = node.roundedUpMaxProcessors();
int maxProcessors = roundedUpMaxProcessors == null ? minProcessors : roundedUpMaxProcessors;
Setting.intSetting(NODE_PROCESSORS_SETTING.getKey(), minProcessors, 1, maxProcessors, Setting.Property.NodeScope).get(settings);
final Settings.Builder updatedSettings = Settings.builder().put(settings);
updatedSettings.remove(NODE_PROCESSORS_SETTING.getKey());
settings = updatedSettings.build();
}
clusterSettings.validate(settings, true);
}
}