org.elasticsearch.node.InternalSettingsPreparer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* 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.node;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsException;
import org.elasticsearch.core.PathUtils;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.env.Environment;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
public class InternalSettingsPreparer {
/**
* Prepares the settings by gathering all elasticsearch system properties, optionally loading the configuration settings.
*
* @param input the custom settings to use; these are not overwritten by settings in the configuration file
* @param properties map of properties key/value pairs (usually from the command-line)
* @param configPath path to config directory; (use null to indicate the default)
* @param defaultNodeName supplier for the default node.name if the setting isn't defined
* @return the {@link Environment}
*/
public static Environment prepareEnvironment(
Settings input,
Map properties,
Path configPath,
Supplier defaultNodeName
) {
Path configDir = findConfigDir(configPath, input, properties);
Path configFile = configDir.resolve("elasticsearch.yml");
Settings.Builder output = Settings.builder(); // start with a fresh output
loadConfigWithSubstitutions(output, configFile, System::getenv);
loadOverrides(output, properties);
output.put(input);
replaceForcedSettings(output);
output.replacePropertyPlaceholders();
ensureSpecialSettingsExist(output, defaultNodeName);
return new Environment(output.build(), configDir);
}
static Path findConfigDir(Path configPath, Settings input, Map properties) {
if (configPath != null) {
return configPath;
}
String esHome = properties.get(Environment.PATH_HOME_SETTING.getKey());
if (esHome == null) {
// TODO: this fallback is only needed for tests, in production input is always Settings.EMPTY
esHome = Environment.PATH_HOME_SETTING.get(input);
if (esHome == null) {
throw new IllegalStateException(Environment.PATH_HOME_SETTING.getKey() + " is not configured");
}
}
return resolveConfigDir(esHome);
}
@SuppressForbidden(reason = "reading initial config")
private static Path resolveConfigDir(String esHome) {
return PathUtils.get(esHome).resolve("config");
}
static void loadConfigWithSubstitutions(Settings.Builder output, Path configFile, Function substitutions) {
if (Files.exists(configFile) == false) {
return;
}
try {
long existingSize = Files.size(configFile);
StringBuilder builder = new StringBuilder((int) existingSize);
try (BufferedReader reader = Files.newBufferedReader(configFile, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
int dollarNdx;
int nextNdx = 0;
while ((dollarNdx = line.indexOf("${", nextNdx)) != -1) {
int closeNdx = line.indexOf('}', dollarNdx + 2);
if (closeNdx == -1) {
// No close substitution was found. Break to leniently copy the rest of the line as is.
break;
}
// copy up to the dollar
if (dollarNdx > nextNdx) {
builder.append(line, nextNdx, dollarNdx);
}
nextNdx = closeNdx + 1;
String substKey = line.substring(dollarNdx + 2, closeNdx);
String substValue = substitutions.apply(substKey);
if (substValue != null) {
builder.append(substValue);
} else {
// the substitution name doesn't exist, defer to setting based substitution after yaml parsing
builder.append(line, dollarNdx, nextNdx);
}
}
if (nextNdx < line.length()) {
builder.append(line, nextNdx, line.length());
}
builder.append(System.lineSeparator());
}
}
var is = new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8));
output.loadFromStream(configFile.getFileName().toString(), is, false);
} catch (IOException e) {
throw new SettingsException("Failed to load settings from " + configFile.toString(), e);
}
}
static void loadOverrides(Settings.Builder output, Map overrides) {
StringBuilder builder = new StringBuilder();
for (var entry : overrides.entrySet()) {
builder.append(entry.getKey());
builder.append(": ");
builder.append(entry.getValue());
builder.append(System.lineSeparator());
}
var is = new ByteArrayInputStream(builder.toString().getBytes(StandardCharsets.UTF_8));
// fake the resource name so it loads yaml
try {
output.loadFromStream("overrides.yml", is, false);
} catch (IOException e) {
throw new SettingsException("Malformed setting override value", e);
}
}
private static void replaceForcedSettings(Settings.Builder output) {
List forcedSettings = new ArrayList<>();
for (String setting : output.keys()) {
if (setting.startsWith("force.")) {
forcedSettings.add(setting);
}
}
for (String forcedSetting : forcedSettings) {
String value = output.remove(forcedSetting);
output.put(forcedSetting.substring("force.".length()), value);
}
}
private static void ensureSpecialSettingsExist(Settings.Builder output, Supplier defaultNodeName) {
// put the cluster and node name if they aren't set
if (output.get(ClusterName.CLUSTER_NAME_SETTING.getKey()) == null) {
output.put(ClusterName.CLUSTER_NAME_SETTING.getKey(), ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY).value());
}
if (output.get(Node.NODE_NAME_SETTING.getKey()) == null) {
output.put(Node.NODE_NAME_SETTING.getKey(), defaultNodeName.get());
}
}
}