mirror of
https://github.com/Graylog2/graylog2-server.git
synced 2026-03-13 09:32:21 +08:00
Refactored all usages of directories used by datanode and managed opensearch (#16905)
* Refactored all usages of directories used by datanode and managed opensearch * add datanode directories preflight check * code cleanup * fixed OpensearchSecurityConfiguration * Fixed DatanodeDirectories logging * code cleanup
This commit is contained in:
@@ -30,7 +30,7 @@ import com.github.joschi.jadconfig.validators.URIAbsoluteValidator;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import org.graylog.datanode.configuration.BaseConfiguration;
|
||||
import org.graylog.datanode.configuration.OpensearchDistributionProvider;
|
||||
import org.graylog.datanode.configuration.DatanodeDirectories;
|
||||
import org.graylog2.plugin.Tools;
|
||||
import org.graylog2.shared.SuppressForbidden;
|
||||
import org.joda.time.DateTimeZone;
|
||||
@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
@@ -83,16 +82,16 @@ public class Configuration extends BaseConfiguration {
|
||||
@Parameter(value = "opensearch_location")
|
||||
private String opensearchDistributionRoot = "dist";
|
||||
|
||||
@Parameter(value = "opensearch_data_location")
|
||||
@Parameter(value = "opensearch_data_location", required = true)
|
||||
private String opensearchDataLocation = "datanode/data";
|
||||
|
||||
@Parameter(value = "opensearch_logs_location")
|
||||
@Parameter(value = "opensearch_logs_location", required = true)
|
||||
private String opensearchLogsLocation = "datanode/logs";
|
||||
|
||||
@Parameter(value = "opensearch_config_location")
|
||||
@Parameter(value = "opensearch_config_location", required = true)
|
||||
private String opensearchConfigLocation = "datanode/config";
|
||||
|
||||
@Parameter(value = "config_location")
|
||||
@Parameter(value = "config_location", required = true)
|
||||
private String configLocation = "config";
|
||||
|
||||
@Parameter(value = "process_logs_buffer_size")
|
||||
@@ -235,18 +234,33 @@ public class Configuration extends BaseConfiguration {
|
||||
return opensearchDistributionRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
|
||||
*/
|
||||
public String getOpensearchConfigLocation() {
|
||||
return opensearchConfigLocation;
|
||||
}
|
||||
|
||||
public String getConfigLocation() {
|
||||
|
||||
/**
|
||||
* This is a pointer to a directory holding configuration files (and certificates) for the datanode itself.
|
||||
* We treat it as read only for the datanode and should never persist anything in it.
|
||||
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
|
||||
*/
|
||||
public String getDatanodeConfigurationLocation() {
|
||||
return configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
|
||||
*/
|
||||
public String getOpensearchDataLocation() {
|
||||
return opensearchDataLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link DatanodeDirectories} to obtain a reference to this directory.
|
||||
*/
|
||||
public String getOpensearchLogsLocation() {
|
||||
return opensearchLogsLocation;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.graylog.datanode.configuration.OpensearchArchitecture;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public record OpensearchDistribution(Path directory, String version, @Nullable String platform,
|
||||
@Nullable OpensearchArchitecture architecture) {
|
||||
@@ -28,4 +29,7 @@ public record OpensearchDistribution(Path directory, String version, @Nullable S
|
||||
this(path, version, null, null);
|
||||
}
|
||||
|
||||
public Path getOpensearchExecutable() {
|
||||
return directory.resolve(Paths.get("bin", "opensearch"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,17 @@
|
||||
package org.graylog.datanode.bindings;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import org.graylog.datanode.OpensearchDistribution;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog.datanode.configuration.DatanodeConfigurationProvider;
|
||||
import org.graylog2.plugin.system.FilePersistedNodeIdProvider;
|
||||
import org.graylog2.plugin.system.NodeId;
|
||||
import org.graylog.datanode.configuration.OpensearchDistributionProvider;
|
||||
import org.graylog.datanode.OpensearchDistribution;
|
||||
|
||||
public class DatanodeConfigurationBindings extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(NodeId.class).toProvider(FilePersistedNodeIdProvider.class).asEagerSingleton();
|
||||
bind(DatanodeConfiguration.class).toProvider(DatanodeConfigurationProvider.class);
|
||||
bind(OpensearchDistribution.class).toProvider(OpensearchDistributionProvider.class);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.graylog.datanode.bindings;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.multibindings.MapBinder;
|
||||
import org.graylog.datanode.bootstrap.preflight.DatanodeDirectoriesCheck;
|
||||
import org.graylog.datanode.bootstrap.preflight.OpensearchBinPreflightCheck;
|
||||
import org.graylog.datanode.bootstrap.preflight.OpensearchConfigSync;
|
||||
import org.graylog2.bootstrap.preflight.PreflightCheck;
|
||||
@@ -29,6 +30,7 @@ public class PreflightChecksBindings extends AbstractModule {
|
||||
protected void configure() {
|
||||
addPreflightCheck(OpensearchConfigSync.class);
|
||||
addPreflightCheck(OpensearchBinPreflightCheck.class);
|
||||
addPreflightCheck(DatanodeDirectoriesCheck.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,6 @@ public class ServerBindings extends Graylog2Module {
|
||||
private void bindProviders() {
|
||||
bind(ClusterEventBus.class).toProvider(ClusterEventBusProvider.class).asEagerSingleton();
|
||||
bind(EventBus.class).toProvider(EventBusProvider.class).asEagerSingleton();
|
||||
bind(NodeId.class).toProvider(FilePersistedNodeIdProvider.class).asEagerSingleton();
|
||||
bind(InputConfigurationBeanDeserializerModifier.class).toInstance(InputConfigurationBeanDeserializerModifier.withoutConfig());
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.graylog.datanode.bootstrap.preflight;
|
||||
|
||||
import org.bouncycastle.operator.OperatorException;
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog.security.certutil.CertConstants;
|
||||
import org.graylog.security.certutil.cert.CertificateChain;
|
||||
import org.graylog.security.certutil.cert.storage.CertChainMongoStorage;
|
||||
@@ -43,7 +43,6 @@ import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -72,7 +71,7 @@ public class DataNodeConfigurationPeriodical extends Periodical {
|
||||
final CertificateAndPrivateKeyMerger certificateAndPrivateKeyMerger,
|
||||
final SmartKeystoreStorage keystoreStorage,
|
||||
final @Named("password_secret") String passwordSecret,
|
||||
final Configuration configuration) {
|
||||
final DatanodeConfiguration datanodeConfiguration) {
|
||||
this.dataNodeProvisioningService = dataNodeProvisioningService;
|
||||
this.nodeService = nodeService;
|
||||
this.nodeId = nodeId;
|
||||
@@ -82,7 +81,7 @@ public class DataNodeConfigurationPeriodical extends Periodical {
|
||||
this.certificateAndPrivateKeyMerger = certificateAndPrivateKeyMerger;
|
||||
this.keystoreStorage = keystoreStorage;
|
||||
// TODO: merge with real storage
|
||||
this.privateKeyEncryptedStorage = new PrivateKeyEncryptedFileStorage(Path.of(configuration.getOpensearchConfigLocation()).resolve("privateKey.cert"));
|
||||
this.privateKeyEncryptedStorage = new PrivateKeyEncryptedFileStorage(datanodeConfiguration.datanodeDirectories().getConfigurationTargetDir().resolve("privateKey.cert"));
|
||||
this.passwordSecret = passwordSecret.toCharArray();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Graylog, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
package org.graylog.datanode.bootstrap.preflight;
|
||||
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog.datanode.configuration.DatanodeDirectories;
|
||||
import org.graylog2.bootstrap.preflight.PreflightCheck;
|
||||
import org.graylog2.bootstrap.preflight.PreflightCheckException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* This check will verify that all pointers to all directories that the datanode and its managed opensearch process
|
||||
* are available and configured with correct permissions.
|
||||
*
|
||||
* Checking this early will prevent some later hard to understand exceptions, for example errors when starting the opensearch
|
||||
* process with non-writeable configuration directory.
|
||||
*/
|
||||
public class DatanodeDirectoriesCheck implements PreflightCheck {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DatanodeDirectoriesCheck.class);
|
||||
|
||||
private final DatanodeConfiguration datanodeConfiguration;
|
||||
|
||||
@Inject
|
||||
public DatanodeDirectoriesCheck(DatanodeConfiguration datanodeConfiguration) {
|
||||
this.datanodeConfiguration = datanodeConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCheck() throws PreflightCheckException {
|
||||
try {
|
||||
checkDirectories(datanodeConfiguration.datanodeDirectories());
|
||||
} catch (DatanodeDirectoryException e) {
|
||||
throw new PreflightCheckException("Failed to check required datanode directories", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDirectories(DatanodeDirectories directories) throws DatanodeDirectoryException {
|
||||
checkReadWriteDir(directories.getDataTargetDir(), "opensearch_data_location");
|
||||
checkReadWriteDir(directories.getLogsTargetDir(), "opensearch_logs_location");
|
||||
checkReadWriteDir(directories.getOpensearchProcessConfigurationDir(), "opensearch_config_location");
|
||||
checkReadDir(directories.getConfigurationSourceDir(), "config_location");
|
||||
}
|
||||
|
||||
static void checkReadDir(Path dir, String configPropertyName) throws DatanodeDirectoryException {
|
||||
checkOrCreate(dir, configPropertyName);
|
||||
checkReadable(dir, configPropertyName);
|
||||
}
|
||||
|
||||
static void checkReadWriteDir(Path dir, String configPropertyName) throws DatanodeDirectoryException {
|
||||
checkOrCreate(dir, configPropertyName);
|
||||
checkReadable(dir, configPropertyName);
|
||||
checkWriteable(dir, configPropertyName);
|
||||
}
|
||||
|
||||
private static void checkOrCreate(Path dir, String configPropertyName) throws DatanodeDirectoryException {
|
||||
if (!Files.exists(dir)) {
|
||||
createDirectory(dir, configPropertyName);
|
||||
} else if (!Files.isDirectory(dir)) {
|
||||
throw new DatanodeDirectoryException("Datanode expects " + dir + " to be a directory, please make sure the configuration property " + configPropertyName + " points to a directory.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void createDirectory(Path dir, String configPropertyName) throws DatanodeDirectoryException {
|
||||
try {
|
||||
LOG.info("Datanode directory " + dir + " doesn't existing, creating now.");
|
||||
Files.createDirectories(dir);
|
||||
} catch (IOException e) {
|
||||
throw new DatanodeDirectoryException("Failed to create directory " + dir + ". Please make sure that user running datanode has permissions to create directory there or adapt configuration property " + configPropertyName, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkReadable(Path dir, String configPropertyName) throws DatanodeDirectoryException {
|
||||
if (!Files.isReadable(dir)) {
|
||||
throw new DatanodeDirectoryException("Datanode needs READ permissions to the " + dir + " directory. Please configure READ permissions for the user that runs the datanode or change the " + configPropertyName + " configuration");
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkWriteable(Path dir, String configPropertyName) throws DatanodeDirectoryException {
|
||||
if (!Files.isWritable(dir)) {
|
||||
throw new DatanodeDirectoryException("Datanode needs WRITE permissions to the " + dir + " directory. Please configure WRITE permissions for the user that runs the datanode or change the " + configPropertyName + " configuration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Graylog, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
package org.graylog.datanode.bootstrap.preflight;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DatanodeDirectoryException extends IOException {
|
||||
public DatanodeDirectoryException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DatanodeDirectoryException(String message, IOException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package org.graylog.datanode.bootstrap.preflight;
|
||||
|
||||
import org.graylog.datanode.OpensearchDistribution;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog2.bootstrap.preflight.PreflightCheck;
|
||||
import org.graylog2.bootstrap.preflight.PreflightCheckException;
|
||||
@@ -26,34 +27,35 @@ import javax.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class OpensearchBinPreflightCheck implements PreflightCheck {
|
||||
|
||||
private final Supplier<Path> opensearchDirSupplier;
|
||||
private final Supplier<OpensearchDistribution> opensearchDistributionSupplier;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(OpensearchBinPreflightCheck.class);
|
||||
|
||||
@Inject
|
||||
public OpensearchBinPreflightCheck(DatanodeConfiguration datanodeConfiguration) {
|
||||
this(() -> datanodeConfiguration.opensearchDistributionProvider().get().directory());
|
||||
this(datanodeConfiguration.opensearchDistributionProvider()::get);
|
||||
}
|
||||
|
||||
public OpensearchBinPreflightCheck(Supplier<Path> opensearchBaseDirectory) {
|
||||
this.opensearchDirSupplier = opensearchBaseDirectory;
|
||||
OpensearchBinPreflightCheck(Supplier<OpensearchDistribution> distribution) {
|
||||
this.opensearchDistributionSupplier = distribution;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCheck() throws PreflightCheckException {
|
||||
final Path opensearchDir = opensearchDirSupplier.get();
|
||||
final OpensearchDistribution distribution = opensearchDistributionSupplier.get();
|
||||
final Path opensearchDir = distribution.directory();
|
||||
|
||||
if (!Files.isDirectory(opensearchDir)) {
|
||||
throw new PreflightCheckException("Opensearch base directory " + opensearchDir + " doesn't exist!");
|
||||
}
|
||||
|
||||
final Path binPath = opensearchDir.resolve(Paths.get("bin", "opensearch"));
|
||||
final Path binPath = distribution.getOpensearchExecutable();
|
||||
|
||||
if (!Files.exists(binPath)) {
|
||||
throw new PreflightCheckException("Opensearch binary " + binPath + " doesn't exist!");
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
*/
|
||||
package org.graylog.datanode.bootstrap.preflight;
|
||||
|
||||
import org.apache.logging.log4j.core.appender.rolling.action.DeletingVisitor;
|
||||
import org.apache.logging.log4j.core.appender.rolling.action.PathCondition;
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog2.bootstrap.preflight.PreflightCheck;
|
||||
import org.graylog2.bootstrap.preflight.PreflightCheckException;
|
||||
import org.slf4j.Logger;
|
||||
@@ -30,34 +28,37 @@ import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Collections;
|
||||
|
||||
public class OpensearchConfigSync implements PreflightCheck {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(OpensearchConfigSync.class);
|
||||
|
||||
private final Configuration configuration;
|
||||
private final DatanodeConfiguration configuration;
|
||||
|
||||
@Inject
|
||||
public OpensearchConfigSync(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
public OpensearchConfigSync(DatanodeConfiguration datanodeConfiguration) {
|
||||
this.configuration = datanodeConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCheck() throws PreflightCheckException {
|
||||
final Path localOpensearchConfigDir = Path.of(configuration.getOpensearchConfigLocation()).resolve("opensearch");
|
||||
LOG.info("Directory used for Opensearch configuration is {}", localOpensearchConfigDir.toAbsolutePath());
|
||||
final Path opensearchProcessConfigurationDir = configuration.datanodeDirectories().getOpensearchProcessConfigurationDir();
|
||||
LOG.info("Directory used for Opensearch process configuration is {}", opensearchProcessConfigurationDir.toAbsolutePath());
|
||||
|
||||
try {
|
||||
Files.createDirectories(localOpensearchConfigDir);
|
||||
synchronizeConfig(Path.of("opensearch", "config"), localOpensearchConfigDir);
|
||||
Files.createDirectories(opensearchProcessConfigurationDir);
|
||||
|
||||
// this is a directory in main/resources that holds all the initial configuration files needed by the opensearch
|
||||
// we manage this directory in git. Generally we assume that this is a read-only location and we need to copy
|
||||
// its content to a read-write location for the managed opensearch process.
|
||||
// This copy happens during each opensearch process start and will override any files that already exist
|
||||
// from previous runs.
|
||||
final Path sourceOfInitialConfiguration = Path.of("opensearch", "config");
|
||||
synchronizeConfig(sourceOfInitialConfiguration, opensearchProcessConfigurationDir);
|
||||
} catch (IOException | URISyntaxException e) {
|
||||
throw new RuntimeException("Failed to prepare opensearch config directory", e);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.graylog2.security.IndexerJwtAuthTokenProvider;
|
||||
*/
|
||||
public record DatanodeConfiguration(
|
||||
OpensearchDistributionProvider opensearchDistributionProvider,
|
||||
DatanodeDirectories datanodeDirectories,
|
||||
String nodeName,
|
||||
int processLogsBufferSize,
|
||||
IndexerJwtAuthTokenProvider indexerJwtAuthTokenProvider
|
||||
|
||||
@@ -17,25 +17,13 @@
|
||||
package org.graylog.datanode.configuration;
|
||||
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog.datanode.OpensearchDistribution;
|
||||
import org.graylog.datanode.process.OpensearchConfiguration;
|
||||
import org.graylog2.plugin.Tools;
|
||||
import org.graylog2.plugin.system.NodeId;
|
||||
import org.graylog2.security.IndexerJwtAuthTokenProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Singleton
|
||||
public class DatanodeConfigurationProvider implements Provider<DatanodeConfiguration> {
|
||||
@@ -43,9 +31,14 @@ public class DatanodeConfigurationProvider implements Provider<DatanodeConfigura
|
||||
private final DatanodeConfiguration datanodeConfiguration;
|
||||
|
||||
@Inject
|
||||
public DatanodeConfigurationProvider(final Configuration localConfiguration, IndexerJwtAuthTokenProvider jwtTokenProvider, OpensearchDistributionProvider opensearchDistribution) throws IOException {
|
||||
public DatanodeConfigurationProvider(
|
||||
final Configuration localConfiguration,
|
||||
IndexerJwtAuthTokenProvider jwtTokenProvider,
|
||||
OpensearchDistributionProvider opensearchDistributionProvider,
|
||||
NodeId nodeId) {
|
||||
datanodeConfiguration = new DatanodeConfiguration(
|
||||
opensearchDistribution,
|
||||
opensearchDistributionProvider,
|
||||
DatanodeDirectories.fromConfiguration(localConfiguration, nodeId),
|
||||
getNodesFromConfig(localConfiguration.getDatanodeNodeName()),
|
||||
localConfiguration.getProcessLogsBufferSize(),
|
||||
jwtTokenProvider
|
||||
@@ -53,7 +46,7 @@ public class DatanodeConfigurationProvider implements Provider<DatanodeConfigura
|
||||
}
|
||||
|
||||
public static String getNodesFromConfig(final String configProperty) {
|
||||
if(configProperty != null) return configProperty;
|
||||
if (configProperty != null) return configProperty;
|
||||
return Tools.getLocalCanonicalHostname();
|
||||
}
|
||||
|
||||
@@ -61,5 +54,4 @@ public class DatanodeConfigurationProvider implements Provider<DatanodeConfigura
|
||||
public DatanodeConfiguration get() {
|
||||
return datanodeConfiguration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Graylog, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*/
|
||||
package org.graylog.datanode.configuration;
|
||||
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog2.plugin.system.NodeId;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* This is a collection of pointers to directories used to store data, logs and configuration of the managed opensearch.
|
||||
* Each data type is additionally stored in a subdirectory named after the nodeId, to avoid unexpected collisions when
|
||||
* running more datanode instances in the same machine.
|
||||
*/
|
||||
public class DatanodeDirectories {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DatanodeDirectories.class);
|
||||
|
||||
private final String nodeId;
|
||||
private final String dataTargetDir;
|
||||
private final String logsTargetDir;
|
||||
private final String configurationSourceDir;
|
||||
private final String configurationTargetDir;
|
||||
|
||||
public DatanodeDirectories(String nodeName, String dataTargetDir, String logsTargetDir, String configurationSourceDir, String configurationTargetDir) {
|
||||
this.nodeId = nodeName;
|
||||
this.dataTargetDir = dataTargetDir;
|
||||
this.logsTargetDir = logsTargetDir;
|
||||
this.configurationSourceDir = configurationSourceDir;
|
||||
this.configurationTargetDir = configurationTargetDir;
|
||||
}
|
||||
|
||||
public static DatanodeDirectories fromConfiguration(Configuration configuration, NodeId nodeId) {
|
||||
final DatanodeDirectories directories = new DatanodeDirectories(
|
||||
nodeId.getNodeId(),
|
||||
configuration.getOpensearchDataLocation(),
|
||||
configuration.getOpensearchLogsLocation(),
|
||||
configuration.getDatanodeConfigurationLocation(),
|
||||
configuration.getOpensearchConfigLocation()
|
||||
);
|
||||
|
||||
LOG.info("Opensearch of the node {} uses following directories as its storage: {}", nodeId.getNodeId(), directories);
|
||||
return directories;
|
||||
}
|
||||
|
||||
/**
|
||||
* This directory is used by the managed opensearch to store its data in it.
|
||||
* Read-write permissions required.
|
||||
*/
|
||||
public Path getDataTargetDir() {
|
||||
return resolvePath(dataTargetDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* This directory is used by the managed opensearch to store its logs in it.
|
||||
* Read-write permissions required.
|
||||
*/
|
||||
public Path getLogsTargetDir() {
|
||||
return resolvePath(logsTargetDir);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This directory is provided by system admin to the datanode. We read our configuration from this location,
|
||||
* we read certificates from here. We'll never write anything to it.
|
||||
* Read-only permissions required.
|
||||
*/
|
||||
public Path getConfigurationSourceDir() {
|
||||
return Path.of(configurationSourceDir).toAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* This directory is used by us to store all runtime-generated configuration of datanode. This
|
||||
* could be truststores, private keys, certificates and other generated config files.
|
||||
*
|
||||
* We also synchronize and generate opensearch configuration into a subdir of this dir, see {@link #getOpensearchProcessConfigurationDir()}
|
||||
* Read-write permissions required.
|
||||
*/
|
||||
public Path getConfigurationTargetDir() {
|
||||
return resolvePath(configurationTargetDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a subdirectory of {@link #getConfigurationTargetDir()}. It's used by us to synchronize and generate opensearch
|
||||
* configuration. Opensearch is then instructed to accept this dir as its base configuration dir (OPENSEARCH_PATH_CONF env property).
|
||||
* @see org.graylog.datanode.bootstrap.preflight.OpensearchConfigSync
|
||||
*/
|
||||
public Path getOpensearchProcessConfigurationDir() {
|
||||
return resolvePath(configurationTargetDir).resolve("opensearch");
|
||||
}
|
||||
|
||||
private Path resolvePath(String path) {
|
||||
return Path.of(path).resolve(nodeId).toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DatanodeDirectories{" +
|
||||
"dataTargetDir='" + getDataTargetDir() + '\'' +
|
||||
", logsTargetDir='" + getLogsTargetDir() + '\'' +
|
||||
", configurationSourceDir='" + getConfigurationSourceDir() + '\'' +
|
||||
", configurationTargetDir='" + getConfigurationTargetDir() + '\'' +
|
||||
", opensearchProcessConfigurationDir='" + getOpensearchProcessConfigurationDir() + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import org.graylog.datanode.configuration.variants.SecurityConfigurationVariant;
|
||||
import org.graylog.datanode.configuration.variants.UploadedCertFilesSecureConfiguration;
|
||||
import org.graylog.datanode.process.OpensearchConfiguration;
|
||||
import org.graylog.security.certutil.ca.exceptions.KeyStoreStorageException;
|
||||
import org.graylog2.bootstrap.preflight.PreflightConfig;
|
||||
import org.graylog2.bootstrap.preflight.PreflightConfigResult;
|
||||
import org.graylog2.bootstrap.preflight.PreflightConfigService;
|
||||
import org.graylog2.cluster.Node;
|
||||
@@ -40,7 +39,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@Singleton
|
||||
@@ -112,13 +110,13 @@ public class OpensearchConfigurationProvider implements Provider<OpensearchConfi
|
||||
if (chosenSecurityConfigurationVariant.isPresent()) {
|
||||
securityConfiguration = chosenSecurityConfigurationVariant.get()
|
||||
.build()
|
||||
.configure(localConfiguration, signingKey);
|
||||
.configure(datanodeConfiguration, signingKey);
|
||||
opensearchProperties.putAll(securityConfiguration.getProperties());
|
||||
}
|
||||
|
||||
return new OpensearchConfiguration(
|
||||
datanodeConfiguration.opensearchDistributionProvider().get().directory(),
|
||||
opensearchConfigLocation,
|
||||
datanodeConfiguration.opensearchDistributionProvider().get(),
|
||||
datanodeConfiguration.datanodeDirectories(),
|
||||
localConfiguration.getBindAddress(),
|
||||
localConfiguration.getHostname(),
|
||||
localConfiguration.getOpensearchHttpPort(),
|
||||
@@ -137,7 +135,6 @@ public class OpensearchConfigurationProvider implements Provider<OpensearchConfi
|
||||
|
||||
private ImmutableMap<String, String> commonOpensearchConfig(final Configuration localConfiguration) {
|
||||
final ImmutableMap.Builder<String, String> config = ImmutableMap.builder();
|
||||
Objects.requireNonNull(localConfiguration.getConfigLocation(), "config_location setting is required!");
|
||||
localConfiguration.getOpensearchNetworkHostHost().ifPresent(
|
||||
networkHost -> config.put("network.host", networkHost));
|
||||
config.put("path.data", Path.of(localConfiguration.getOpensearchDataLocation()).resolve(nodeName).toAbsolutePath().toString());
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.graylog.datanode.configuration.variants;
|
||||
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog.datanode.configuration.certificates.KeystoreReEncryption;
|
||||
import org.graylog.security.certutil.ca.exceptions.KeyStoreStorageException;
|
||||
import org.graylog.security.certutil.keystore.storage.location.KeystoreFileLocation;
|
||||
@@ -44,12 +45,13 @@ public final class MongoCertSecureConfiguration extends SecureConfiguration {
|
||||
|
||||
@Inject
|
||||
public MongoCertSecureConfiguration(final Configuration localConfiguration,
|
||||
final DatanodeConfiguration datanodeConfiguration,
|
||||
final KeystoreReEncryption keystoreReEncryption,
|
||||
final NodeId nodeId,
|
||||
final @Named("password_secret") String passwordSecret,
|
||||
final CertificatesService certificatesService
|
||||
) {
|
||||
super(localConfiguration);
|
||||
super(datanodeConfiguration);
|
||||
this.keystoreReEncryption = keystoreReEncryption;
|
||||
this.certificatesService = certificatesService;
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog.datanode.configuration.TruststoreCreator;
|
||||
import org.graylog.security.certutil.CertConstants;
|
||||
import org.slf4j.Logger;
|
||||
@@ -73,13 +74,13 @@ public class OpensearchSecurityConfiguration {
|
||||
* initial set of opensearch users, it will create and persist a truststore that will be set as a system-wide
|
||||
* truststore.
|
||||
*/
|
||||
public OpensearchSecurityConfiguration configure(Configuration localConfiguration, byte[] signingKey) throws GeneralSecurityException, IOException {
|
||||
public OpensearchSecurityConfiguration configure(DatanodeConfiguration datanodeConfiguration, byte[] signingKey) throws GeneralSecurityException, IOException {
|
||||
if (securityEnabled()) {
|
||||
|
||||
logCertificateInformation("transport certificate", transportCertificate);
|
||||
logCertificateInformation("HTTP certificate", httpCertificate);
|
||||
|
||||
final Path opensearchConfigDir = Path.of(localConfiguration.getOpensearchConfigLocation()).resolve("opensearch");
|
||||
final Path opensearchConfigDir = datanodeConfiguration.datanodeDirectories().getOpensearchProcessConfigurationDir();
|
||||
|
||||
final Path trustStorePath = opensearchConfigDir.resolve(TRUSTSTORE_FILENAME);
|
||||
final String truststorePassword = RandomStringUtils.randomAlphabetic(256);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
package org.graylog.datanode.configuration.variants;
|
||||
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
@@ -25,8 +25,8 @@ public sealed abstract class SecureConfiguration implements SecurityConfiguratio
|
||||
final Path datanodeConfigDir;
|
||||
final Path opensearchConfigDir;
|
||||
|
||||
public SecureConfiguration(final Configuration localConfiguration) {
|
||||
this.opensearchConfigDir = Path.of(localConfiguration.getOpensearchConfigLocation()).resolve("opensearch");
|
||||
this.datanodeConfigDir = Path.of(localConfiguration.getConfigLocation());
|
||||
public SecureConfiguration(final DatanodeConfiguration localConfiguration) {
|
||||
this.datanodeConfigDir = localConfiguration.datanodeDirectories().getConfigurationSourceDir();
|
||||
this.opensearchConfigDir = localConfiguration.datanodeDirectories().getOpensearchProcessConfigurationDir();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.graylog.datanode.configuration.variants;
|
||||
|
||||
import org.graylog.datanode.Configuration;
|
||||
import org.graylog.datanode.configuration.DatanodeConfiguration;
|
||||
import org.graylog.datanode.configuration.OpensearchConfigurationException;
|
||||
import org.graylog.datanode.configuration.certificates.KeystoreReEncryption;
|
||||
import org.graylog.datanode.configuration.verification.ConfigProperty;
|
||||
@@ -49,9 +50,10 @@ public final class UploadedCertFilesSecureConfiguration extends SecureConfigurat
|
||||
|
||||
@Inject
|
||||
public UploadedCertFilesSecureConfiguration(final Configuration localConfiguration,
|
||||
final DatanodeConfiguration datanodeConfiguration,
|
||||
final KeystoreReEncryption keystoreReEncryption,
|
||||
final ConfigSectionCompletenessVerifier configSectionCompletenessVerifier) {
|
||||
super(localConfiguration);
|
||||
super(datanodeConfiguration);
|
||||
this.keystoreReEncryption = keystoreReEncryption;
|
||||
this.configSectionCompletenessVerifier = configSectionCompletenessVerifier;
|
||||
this.uploadedTransportKeystorePath = datanodeConfigDir
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -49,7 +48,7 @@ public class OpensearchCommandLineProcess implements Closeable {
|
||||
*/
|
||||
private void fixJdkOnMac(final OpensearchConfiguration config) {
|
||||
final var isMacOS = OS.isFamilyMac();
|
||||
final var jdk = config.opensearchDir().resolve("jdk.app");
|
||||
final var jdk = config.opensearchDistribution().directory().resolve("jdk.app");
|
||||
final var jdkNotLinked = !Files.exists(jdk);
|
||||
if (isMacOS && jdkNotLinked) {
|
||||
// Link System jdk into startup folder, get path:
|
||||
@@ -77,7 +76,7 @@ public class OpensearchCommandLineProcess implements Closeable {
|
||||
|
||||
public OpensearchCommandLineProcess(OpensearchConfiguration config, ProcessListener listener) {
|
||||
fixJdkOnMac(config);
|
||||
final Path executable = config.opensearchDir().resolve(Paths.get("bin", "opensearch"));
|
||||
final Path executable = config.opensearchDistribution().getOpensearchExecutable();;
|
||||
final List<String> arguments = getOpensearchConfigurationArguments(config).entrySet().stream()
|
||||
.map(it -> String.format(Locale.ROOT, "-E%s=%s", it.getKey(), it.getValue())).toList();
|
||||
resultHandler = new CommandLineProcessListener(listener);
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -86,7 +85,7 @@ class OpensearchProcessImpl implements OpensearchProcess, ProcessListener {
|
||||
this.trustManager = trustManager;
|
||||
this.nodeService = nodeService;
|
||||
this.configuration = configuration;
|
||||
this.hostsfile = Path.of(configuration.getOpensearchConfigLocation()).resolve("opensearch").resolve("unicast_hosts.txt");
|
||||
this.hostsfile = datanodeConfiguration.datanodeDirectories().getOpensearchProcessConfigurationDir().resolve("unicast_hosts.txt");
|
||||
}
|
||||
|
||||
private RestHighLevelClient createRestClient(OpensearchConfiguration configuration) {
|
||||
|
||||
@@ -17,18 +17,19 @@
|
||||
package org.graylog.datanode.process;
|
||||
|
||||
import org.apache.commons.exec.OS;
|
||||
import org.graylog.datanode.OpensearchDistribution;
|
||||
import org.graylog.datanode.configuration.DatanodeDirectories;
|
||||
import org.graylog.datanode.configuration.variants.OpensearchSecurityConfiguration;
|
||||
import org.graylog.datanode.management.Environment;
|
||||
import org.graylog.shaded.opensearch2.org.apache.http.HttpHost;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public record OpensearchConfiguration(
|
||||
Path opensearchDir,
|
||||
Path opensearchConfigDir,
|
||||
OpensearchDistribution opensearchDistribution,
|
||||
DatanodeDirectories datanodeDirectories,
|
||||
String bindAddress,
|
||||
String hostname,
|
||||
int httpPort,
|
||||
@@ -80,7 +81,7 @@ public record OpensearchConfiguration(
|
||||
|
||||
public Environment getEnv() {
|
||||
final Environment env = new Environment(System.getenv());
|
||||
env.put("OPENSEARCH_PATH_CONF", opensearchConfigDir.resolve("opensearch").toAbsolutePath().toString());
|
||||
env.put("OPENSEARCH_PATH_CONF", datanodeDirectories.getOpensearchProcessConfigurationDir().toString());
|
||||
return env;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user