From 917ee1e8949a0a477ccfc441e36d91149234323f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 30 Mar 2022 21:00:10 -0700 Subject: [PATCH 001/166] stash --- distribution/build.gradle | 6 +- distribution/src/bin/elasticsearch-cli-new | 74 +++++++++++++++++++ .../src/bin/elasticsearch-cli-new.bat | 0 distribution/tools/launcher/build.gradle | 24 ++++++ .../org/elasticsearch/launcher/Launcher.java | 58 +++++++++++++++ .../elasticsearch/launcher/ToolProvider.java | 17 +++++ .../org/elasticsearch/launcher/Launcher.java | 24 ++++++ settings.gradle | 1 + 8 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 distribution/src/bin/elasticsearch-cli-new create mode 100644 distribution/src/bin/elasticsearch-cli-new.bat create mode 100644 distribution/tools/launcher/build.gradle create mode 100644 distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java create mode 100644 distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/ToolProvider.java create mode 100644 distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java diff --git a/distribution/build.gradle b/distribution/build.gradle index 0c33ea03b7c4f..b87b5ebef480b 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -230,7 +230,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { * Properties to expand when copying packaging files * *****************************************************************************/ configurations { - ['libs', 'libsLaunchers', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { + ['libs', 'libsLauncher', 'libsLaunchers', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { create(it) { canBeConsumed = false canBeResolved = true @@ -250,6 +250,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { dependencies { libs project(':server') + libsLauncher project(':distribution:tools:launcher') libsLaunchers project(':distribution:tools:java-version-checker') libsLaunchers project(':distribution:tools:launchers') libsAnsiConsole project(':distribution:tools:ansi-console') @@ -268,6 +269,9 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { copySpec { // Delay by using closures, since they have not yet been configured, so no jar task exists yet. from(configurations.libs) + into('launcher') { + from(configurations.libsLauncher) + } into('launchers') { from(configurations.libsLaunchers) } diff --git a/distribution/src/bin/elasticsearch-cli-new b/distribution/src/bin/elasticsearch-cli-new new file mode 100644 index 0000000000000..e683c42b29fa4 --- /dev/null +++ b/distribution/src/bin/elasticsearch-cli-new @@ -0,0 +1,74 @@ +#!/bin/bash + +set -e -o pipefail + +CDPATH="" + +SCRIPT="$0" + +# SCRIPT might be an arbitrarily deep series of symbolic links; loop until we +# have the concrete path +while [ -h "$SCRIPT" ] ; do + ls=`ls -ld "$SCRIPT"` + # Drop everything prior to -> + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=`dirname "$SCRIPT"`/"$link" + fi +done + +# determine Elasticsearch home; to do this, we strip from the path until we find +# bin, and then strip bin (there is an assumption here that there is no nested +# directory under bin also named bin) +ES_HOME=`dirname "$SCRIPT"` + +# now make ES_HOME absolute +ES_HOME=`cd "$ES_HOME"; pwd` + +while [ "`basename "$ES_HOME"`" != "bin" ]; do + ES_HOME=`dirname "$ES_HOME"` +done +ES_HOME=`dirname "$ES_HOME"` +cd $ES_HOME + +# now set the path to java +if [ ! -z "$ES_JAVA_HOME" ]; then + JAVA="$ES_JAVA_HOME/bin/java" + JAVA_TYPE="ES_JAVA_HOME" +else + # use the bundled JDK (default) + if [ "$(uname -s)" = "Darwin" ]; then + # macOS has a different structure + JAVA="$ES_HOME/jdk.app/Contents/Home/bin/java" + else + JAVA="$ES_HOME/jdk/bin/java" + fi + JAVA_TYPE="bundled JDK" +fi + +if [ ! -x "$JAVA" ]; then + echo "could not find java in $JAVA_TYPE at $JAVA" >&2 + exit 1 +fi + +# do not let JAVA_TOOL_OPTIONS slip in (as the JVM does by default) +if [ ! -z "$JAVA_TOOL_OPTIONS" ]; then + echo "warning: ignoring JAVA_TOOL_OPTIONS=$JAVA_TOOL_OPTIONS" + unset JAVA_TOOL_OPTIONS +fi + +# warn that we are not observing the value of JAVA_HOME +if [ ! -z "$JAVA_HOME" ]; then + echo "warning: ignoring JAVA_HOME=$JAVA_HOME; using $JAVA_TYPE" >&2 +fi + +LAUNCHER_CLASSPATH=$ES_HOME/lib/launcher/* + +exec \ + "$JAVA" \ + -Des.tool="$ES_TOOLNAME" \ + -cp "$LAUNCHER_CLASSPATH" \ + org.elasticsearch.launcher.Launcher \ + "${ARG_LIST[@]}" \ diff --git a/distribution/src/bin/elasticsearch-cli-new.bat b/distribution/src/bin/elasticsearch-cli-new.bat new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/distribution/tools/launcher/build.gradle b/distribution/tools/launcher/build.gradle new file mode 100644 index 0000000000000..a85e24ac2238a --- /dev/null +++ b/distribution/tools/launcher/build.gradle @@ -0,0 +1,24 @@ + +apply plugin: 'elasticsearch.java' + +sourceSets { + unsupportedJdkVersionEntrypoint +} + +tasks.named('compileUnsupportedJdkVersionEntrypointJava').configure { + targetCompatibility = JavaVersion.VERSION_1_7 +} + +tasks.named("jar") { + manifest { + attributes("Multi-Release": "true") + } + + from(sourceSets.unsupportedJdkVersionEntrypoint.output) + eachFile { details -> + if (details.path.equals("org/elasticsearch/launcher/Launcher.class") && + sourceSets.main.output.asFileTree.contains(details.file)) { + details.relativePath = details.relativePath.prepend("META-INF/versions/17") + } + } +} diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java new file mode 100644 index 0000000000000..d197ecf49004f --- /dev/null +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -0,0 +1,58 @@ +/* + * 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.launcher; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ServiceLoader; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.spi.ToolProvider; +import java.util.stream.Stream; + +public class Launcher { + + private static Function MAP_TO_URL = p -> { + try { + return p.toUri().toURL(); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + }; + private static Predicate JAR_PREDICATE = p -> p.getFileName().toString().endsWith(".jar"); + + // TODO: don't throw, catch this and give a nice error message + public static void main(String[] args) throws IOException { + String toolname = System.getProperty("es.tool"); + Path homeDir = Paths.get("").toAbsolutePath(); + + System.out.println("Running ES cli"); + System.out.println("ES_HOME=" + homeDir); + System.out.println("tool: " + toolname); + + Path libDir = homeDir.resolve("lib"); + ClassLoader serverLoader = loadJars(libDir, null); + ClassLoader cliLoader = loadJars(libDir.resolve("tools").resolve(toolname), serverLoader); + + } + + private static ClassLoader loadJars(Path dir, ClassLoader parent) throws IOException { + final URL[] urls; + try (Stream jarFiles = Files.list(dir)) { + urls = jarFiles.filter(JAR_PREDICATE).map(MAP_TO_URL).toArray(URL[]::new); + } + return URLClassLoader.newInstance(urls, parent); + } +} diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/ToolProvider.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/ToolProvider.java new file mode 100644 index 0000000000000..7a80c752f523d --- /dev/null +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/ToolProvider.java @@ -0,0 +1,17 @@ +/* + * 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.launcher; + +public interface ToolProvider { + + // TODO: merge launcher and cli lib, move to subdir in distro, make compileOnly dep in server + + // TODO: this is temporary + void main(String[] args, Terminal terminal) throws Exception; +} diff --git a/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java new file mode 100644 index 0000000000000..2814b6be093ec --- /dev/null +++ b/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java @@ -0,0 +1,24 @@ +/* + * 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.launcher; + +import java.util.Locale; + +public class Launcher { + public static void main(String[] args) { + final String message = String.format( + Locale.ROOT, + "The minimum required Java version is 17; your Java version %s from [%s] does not meet that requirement.", + System.getProperty("java.specification.version"), + System.getProperty("java.home") + ); + System.err.println(message); + System.exit(1); + } +} diff --git a/settings.gradle b/settings.gradle index f28f7ec51eb53..e66e217085a0a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -53,6 +53,7 @@ List projects = [ 'distribution:bwc:minor', 'distribution:bwc:staged', 'distribution:tools:java-version-checker', + 'distribution:tools:launcher', 'distribution:tools:launchers', 'distribution:tools:plugin-cli', 'distribution:tools:keystore-cli', From 5ea8dbcef230f8871ff9ed4355b25f61ae31e85e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 1 Apr 2022 16:26:14 -0700 Subject: [PATCH 002/166] cleanup --- .../src/main/java/org/elasticsearch/launcher/Launcher.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index d197ecf49004f..0b4581aa3d51c 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -9,22 +9,19 @@ package org.elasticsearch.launcher; import java.io.IOException; -import java.io.UncheckedIOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ServiceLoader; import java.util.function.Function; import java.util.function.Predicate; -import java.util.spi.ToolProvider; import java.util.stream.Stream; public class Launcher { - private static Function MAP_TO_URL = p -> { + private static Function MAP_PATH_TO_URL = p -> { try { return p.toUri().toURL(); } catch (MalformedURLException e) { @@ -51,7 +48,7 @@ public static void main(String[] args) throws IOException { private static ClassLoader loadJars(Path dir, ClassLoader parent) throws IOException { final URL[] urls; try (Stream jarFiles = Files.list(dir)) { - urls = jarFiles.filter(JAR_PREDICATE).map(MAP_TO_URL).toArray(URL[]::new); + urls = jarFiles.filter(JAR_PREDICATE).map(MAP_PATH_TO_URL).toArray(URL[]::new); } return URLClassLoader.newInstance(urls, parent); } From 2bf16e13d23eb68676b66420d47a814ab8dec93c Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 1 Apr 2022 18:04:05 -0700 Subject: [PATCH 003/166] plugin cli sort of works, invoked through cli tool manually --- distribution/build.gradle | 11 +++++--- distribution/src/bin/elasticsearch-cli-new | 5 ++-- .../elasticsearch/plugins/cli/PluginCli.java | 5 ++-- .../org.elasticsearch.cli.ToolProvider | 1 + libs/cli/build.gradle | 27 ++++++++++++++----- .../java/org/elasticsearch/cli}/Launcher.java | 15 ++++++----- .../org/elasticsearch/cli}/ToolProvider.java | 4 +-- .../org/elasticsearch/launcher/Launcher.java | 2 +- server/build.gradle | 3 ++- settings.gradle | 1 + .../DateTimeFormatterTimestampConverter.java | 2 +- .../autodetect/AutodetectProcessManager.java | 4 +-- x-pack/plugin/security/build.gradle | 1 + x-pack/plugin/security/cli/build.gradle | 1 + x-pack/plugin/watcher/build.gradle | 2 ++ 15 files changed, 55 insertions(+), 29 deletions(-) create mode 100644 distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider rename {distribution/tools/launcher/src/main/java/org/elasticsearch/launcher => libs/cli/src/main/java/org/elasticsearch/cli}/Launcher.java (75%) rename {distribution/tools/launcher/src/main/java/org/elasticsearch/launcher => libs/cli/src/main/java/org/elasticsearch/cli}/ToolProvider.java (83%) rename {distribution/tools/launcher => libs/cli}/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java (97%) diff --git a/distribution/build.gradle b/distribution/build.gradle index b87b5ebef480b..2e64c13da8c45 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -230,7 +230,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { * Properties to expand when copying packaging files * *****************************************************************************/ configurations { - ['libs', 'libsLauncher', 'libsLaunchers', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { + ['libs', 'libsCli', 'libsLauncher', 'libsLaunchers', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { create(it) { canBeConsumed = false canBeResolved = true @@ -250,7 +250,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { dependencies { libs project(':server') - libsLauncher project(':distribution:tools:launcher') + libsCli project(':libs:elasticsearch-cli') libsLaunchers project(':distribution:tools:java-version-checker') libsLaunchers project(':distribution:tools:launchers') libsAnsiConsole project(':distribution:tools:ansi-console') @@ -269,8 +269,11 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { copySpec { // Delay by using closures, since they have not yet been configured, so no jar task exists yet. from(configurations.libs) - into('launcher') { - from(configurations.libsLauncher) + into('cli') { + from(configurations.libsCli) + into('plugin') { + from(configurations.libsPluginCli) + } } into('launchers') { from(configurations.libsLaunchers) diff --git a/distribution/src/bin/elasticsearch-cli-new b/distribution/src/bin/elasticsearch-cli-new index e683c42b29fa4..2faa36abe7518 100644 --- a/distribution/src/bin/elasticsearch-cli-new +++ b/distribution/src/bin/elasticsearch-cli-new @@ -64,11 +64,12 @@ if [ ! -z "$JAVA_HOME" ]; then echo "warning: ignoring JAVA_HOME=$JAVA_HOME; using $JAVA_TYPE" >&2 fi -LAUNCHER_CLASSPATH=$ES_HOME/lib/launcher/* +LAUNCHER_CLASSPATH=$ES_HOME/lib/cli/* exec \ "$JAVA" \ + $JAVA_OPTS \ -Des.tool="$ES_TOOLNAME" \ -cp "$LAUNCHER_CLASSPATH" \ - org.elasticsearch.launcher.Launcher \ + org.elasticsearch.cli.Launcher \ "${ARG_LIST[@]}" \ diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java index f5e5b6136a5b4..7e4d1865767f3 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java @@ -10,6 +10,7 @@ import org.elasticsearch.cli.Command; import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.ToolProvider; import org.elasticsearch.common.cli.LoggingAwareMultiCommand; import org.elasticsearch.core.internal.io.IOUtils; @@ -20,11 +21,11 @@ /** * A cli tool for adding, removing and listing plugins for elasticsearch. */ -public class PluginCli extends LoggingAwareMultiCommand { +public class PluginCli extends LoggingAwareMultiCommand implements ToolProvider { private final Collection commands; - private PluginCli() { + public PluginCli() { super("A tool for managing installed elasticsearch plugins"); subcommands.put("list", new ListPluginsCommand()); subcommands.put("install", new InstallPluginCommand()); diff --git a/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..e49cc18c4f6cf --- /dev/null +++ b/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1 @@ +org.elasticsearch.plugins.cli.PluginCli diff --git a/libs/cli/build.gradle b/libs/cli/build.gradle index c12ae87ee65fe..f95bf91419902 100644 --- a/libs/cli/build.gradle +++ b/libs/cli/build.gradle @@ -5,18 +5,31 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -apply plugin: 'elasticsearch.build' -apply plugin: 'elasticsearch.publish' +apply plugin: 'elasticsearch.java' dependencies { api 'net.sf.jopt-simple:jopt-simple:5.0.2' api project(':libs:elasticsearch-core') } -tasks.named("test").configure { enabled = false } -// Since CLI does not depend on :server, it cannot run the jarHell task -tasks.named("jarHell").configure { enabled = false } +sourceSets { + unsupportedJdkVersionEntrypoint +} + +tasks.named('compileUnsupportedJdkVersionEntrypointJava').configure { + targetCompatibility = JavaVersion.VERSION_1_7 +} + +tasks.named("jar") { + manifest { + attributes("Multi-Release": "true") + } -tasks.named('forbiddenApisMain').configure { - replaceSignatureFiles 'jdk-signatures' + from(sourceSets.unsupportedJdkVersionEntrypoint.output) + eachFile { details -> + if (details.path.equals("org/elasticsearch/cli/Launcher.class") && + sourceSets.main.output.asFileTree.contains(details.file)) { + details.relativePath = details.relativePath.prepend("META-INF/versions/17") + } + } } diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/libs/cli/src/main/java/org/elasticsearch/cli/Launcher.java similarity index 75% rename from distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java rename to libs/cli/src/main/java/org/elasticsearch/cli/Launcher.java index 0b4581aa3d51c..4b1e5cbbd4e77 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Launcher.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.launcher; +package org.elasticsearch.cli; import java.io.IOException; import java.net.MalformedURLException; @@ -15,11 +15,12 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ServiceLoader; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; -public class Launcher { +class Launcher { private static Function MAP_PATH_TO_URL = p -> { try { @@ -31,7 +32,7 @@ public class Launcher { private static Predicate JAR_PREDICATE = p -> p.getFileName().toString().endsWith(".jar"); // TODO: don't throw, catch this and give a nice error message - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws Exception { String toolname = System.getProperty("es.tool"); Path homeDir = Paths.get("").toAbsolutePath(); @@ -40,9 +41,11 @@ public static void main(String[] args) throws IOException { System.out.println("tool: " + toolname); Path libDir = homeDir.resolve("lib"); - ClassLoader serverLoader = loadJars(libDir, null); - ClassLoader cliLoader = loadJars(libDir.resolve("tools").resolve(toolname), serverLoader); - + ClassLoader serverLoader = loadJars(libDir, ClassLoader.getSystemClassLoader()); + ClassLoader cliLoader = loadJars(libDir.resolve("cli").resolve(toolname), serverLoader); + ServiceLoader toolProvider = ServiceLoader.load(ToolProvider.class, cliLoader); + ToolProvider tool = toolProvider.findFirst().orElseThrow(); + System.exit(tool.main(args, Terminal.DEFAULT)); } private static ClassLoader loadJars(Path dir, ClassLoader parent) throws IOException { diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/ToolProvider.java b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java similarity index 83% rename from distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/ToolProvider.java rename to libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java index 7a80c752f523d..4072991d195dc 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/ToolProvider.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -package org.elasticsearch.launcher; +package org.elasticsearch.cli; public interface ToolProvider { // TODO: merge launcher and cli lib, move to subdir in distro, make compileOnly dep in server // TODO: this is temporary - void main(String[] args, Terminal terminal) throws Exception; + int main(String[] args, Terminal terminal) throws Exception; } diff --git a/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java b/libs/cli/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java similarity index 97% rename from distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java rename to libs/cli/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java index 2814b6be093ec..c5929b7242047 100644 --- a/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java +++ b/libs/cli/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java @@ -10,7 +10,7 @@ import java.util.Locale; -public class Launcher { +class Launcher { public static void main(String[] args) { final String message = String.format( Locale.ROOT, diff --git a/server/build.gradle b/server/build.gradle index fdd4f83cb1a0a..bf47134363d07 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -48,7 +48,8 @@ dependencies { api "org.apache.lucene:lucene-suggest:${versions.lucene}" // utilities - api project(":libs:elasticsearch-cli") +// TODO: move this out! + compileOnly project(":libs:elasticsearch-cli") implementation 'com.carrotsearch:hppc:0.8.1' // percentiles aggregation diff --git a/settings.gradle b/settings.gradle index e66e217085a0a..fcc984b34f50b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -53,6 +53,7 @@ List projects = [ 'distribution:bwc:minor', 'distribution:bwc:staged', 'distribution:tools:java-version-checker', + 'distribution:tools:main', 'distribution:tools:launcher', 'distribution:tools:launchers', 'distribution:tools:plugin-cli', diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/time/DateTimeFormatterTimestampConverter.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/time/DateTimeFormatterTimestampConverter.java index f0f9d54d092f1..88abd1279e175 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/time/DateTimeFormatterTimestampConverter.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/time/DateTimeFormatterTimestampConverter.java @@ -6,7 +6,7 @@ */ package org.elasticsearch.xpack.core.ml.utils.time; -import org.elasticsearch.cli.SuppressForbidden; +import org.elasticsearch.core.SuppressForbidden; import java.time.DateTimeException; import java.time.Instant; diff --git a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java index d6a1175862351..2ac929e9ac91b 100644 --- a/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java +++ b/x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/job/process/autodetect/AutodetectProcessManager.java @@ -6,8 +6,6 @@ */ package org.elasticsearch.xpack.ml.job.process.autodetect; -import joptsimple.internal.Strings; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; @@ -438,7 +436,7 @@ public void onFailure(Exception e) { if (filterIds.isEmpty()) { filtersListener.onResponse(null); } else { - GetFiltersAction.Request getFilterRequest = new GetFiltersAction.Request(Strings.join(filterIds, ",")); + GetFiltersAction.Request getFilterRequest = new GetFiltersAction.Request(String.join(",", filterIds)); getFilterRequest.setPageParams(new PageParams(0, filterIds.size())); executeAsyncWithOrigin( client, diff --git a/x-pack/plugin/security/build.gradle b/x-pack/plugin/security/build.gradle index 597fdd0bb3b84..b792dc536bae6 100644 --- a/x-pack/plugin/security/build.gradle +++ b/x-pack/plugin/security/build.gradle @@ -14,6 +14,7 @@ esplugin { archivesBaseName = 'x-pack-security' dependencies { + compileOnly project(':libs:elasticsearch-cli') compileOnly project(path: xpackModule('core')) api project(path: ':modules:transport-netty4') api project(path: ':plugins:transport-nio') diff --git a/x-pack/plugin/security/cli/build.gradle b/x-pack/plugin/security/cli/build.gradle index e2684ce8d54f7..8b64f04622b37 100644 --- a/x-pack/plugin/security/cli/build.gradle +++ b/x-pack/plugin/security/cli/build.gradle @@ -7,6 +7,7 @@ archivesBaseName = 'elasticsearch-security-cli' dependencies { compileOnly project(":server") + compileOnly project(":libs:elasticsearch-cli") compileOnly project(path: xpackModule('core')) api "org.bouncycastle:bcpkix-jdk15on:${versions.bouncycastle}" api "org.bouncycastle:bcprov-jdk15on:${versions.bouncycastle}" diff --git a/x-pack/plugin/watcher/build.gradle b/x-pack/plugin/watcher/build.gradle index 93f4015586181..7ee8237475c81 100644 --- a/x-pack/plugin/watcher/build.gradle +++ b/x-pack/plugin/watcher/build.gradle @@ -20,6 +20,8 @@ tasks.named("dependencyLicenses").configure { dependencies { compileOnly project(':server') compileOnly project(':modules:lang-painless:spi') +// TODO: move watcher tool to another project + compileOnly project(':libs:elasticsearch-cli') compileOnly project(path: xpackModule('core')) compileOnly project(path: ':modules:transport-netty4') compileOnly project(path: ':plugins:transport-nio') From 3500ef7d30286f7f2f9c4cfaec25e48117d60506 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 2 Apr 2022 08:30:14 -0700 Subject: [PATCH 004/166] plugin cli done (on nix) --- distribution/build.gradle | 11 +++----- distribution/src/bin/elasticsearch-cli-new | 7 +++-- distribution/src/bin/elasticsearch-plugin | 7 +++-- distribution/tools/launcher/build.gradle | 4 +++ .../org/elasticsearch/launcher}/Launcher.java | 12 ++++++--- .../org/elasticsearch/launcher/Launcher.java | 0 libs/cli/build.gradle | 27 +++++-------------- server/build.gradle | 3 +-- 8 files changed, 30 insertions(+), 41 deletions(-) rename {libs/cli/src/main/java/org/elasticsearch/cli => distribution/tools/launcher/src/main/java/org/elasticsearch/launcher}/Launcher.java (83%) rename {libs/cli => distribution/tools/launcher}/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java (100%) diff --git a/distribution/build.gradle b/distribution/build.gradle index 2e64c13da8c45..b87b5ebef480b 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -230,7 +230,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { * Properties to expand when copying packaging files * *****************************************************************************/ configurations { - ['libs', 'libsCli', 'libsLauncher', 'libsLaunchers', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { + ['libs', 'libsLauncher', 'libsLaunchers', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { create(it) { canBeConsumed = false canBeResolved = true @@ -250,7 +250,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { dependencies { libs project(':server') - libsCli project(':libs:elasticsearch-cli') + libsLauncher project(':distribution:tools:launcher') libsLaunchers project(':distribution:tools:java-version-checker') libsLaunchers project(':distribution:tools:launchers') libsAnsiConsole project(':distribution:tools:ansi-console') @@ -269,11 +269,8 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { copySpec { // Delay by using closures, since they have not yet been configured, so no jar task exists yet. from(configurations.libs) - into('cli') { - from(configurations.libsCli) - into('plugin') { - from(configurations.libsPluginCli) - } + into('launcher') { + from(configurations.libsLauncher) } into('launchers') { from(configurations.libsLaunchers) diff --git a/distribution/src/bin/elasticsearch-cli-new b/distribution/src/bin/elasticsearch-cli-new index 2faa36abe7518..3402c848915b9 100644 --- a/distribution/src/bin/elasticsearch-cli-new +++ b/distribution/src/bin/elasticsearch-cli-new @@ -64,12 +64,11 @@ if [ ! -z "$JAVA_HOME" ]; then echo "warning: ignoring JAVA_HOME=$JAVA_HOME; using $JAVA_TYPE" >&2 fi -LAUNCHER_CLASSPATH=$ES_HOME/lib/cli/* +LAUNCHER_CLASSPATH=$ES_HOME/lib/*:$ES_HOME/lib/launcher/* exec \ "$JAVA" \ - $JAVA_OPTS \ - -Des.tool="$ES_TOOLNAME" \ + $LAUNCHER_JAVA_OPTS \ -cp "$LAUNCHER_CLASSPATH" \ - org.elasticsearch.cli.Launcher \ + org.elasticsearch.launcher.Launcher \ "${ARG_LIST[@]}" \ diff --git a/distribution/src/bin/elasticsearch-plugin b/distribution/src/bin/elasticsearch-plugin index a5fa482e22886..855b0d3acd913 100755 --- a/distribution/src/bin/elasticsearch-plugin +++ b/distribution/src/bin/elasticsearch-plugin @@ -1,7 +1,6 @@ #!/bin/bash -ES_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED $ES_JAVA_OPTS" \ - ES_MAIN_CLASS=org.elasticsearch.plugins.cli.PluginCli \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/plugin-cli \ - "`dirname "$0"`"/elasticsearch-cli \ +LAUNCHER_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED" \ + LAUNCHER_TOOLNAME=plugin-cli \ + "`dirname "$0"`"/elasticsearch-cli-new \ "$@" diff --git a/distribution/tools/launcher/build.gradle b/distribution/tools/launcher/build.gradle index a85e24ac2238a..0ce04dcc25bcd 100644 --- a/distribution/tools/launcher/build.gradle +++ b/distribution/tools/launcher/build.gradle @@ -1,6 +1,10 @@ apply plugin: 'elasticsearch.java' +dependencies { + compileOnly project(':server') +} + sourceSets { unsupportedJdkVersionEntrypoint } diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java similarity index 83% rename from libs/cli/src/main/java/org/elasticsearch/cli/Launcher.java rename to distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index 4b1e5cbbd4e77..50b205d655a14 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -package org.elasticsearch.cli; +package org.elasticsearch.launcher; + +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.ToolProvider; import java.io.IOException; import java.net.MalformedURLException; @@ -33,16 +36,17 @@ class Launcher { // TODO: don't throw, catch this and give a nice error message public static void main(String[] args) throws Exception { - String toolname = System.getProperty("es.tool"); Path homeDir = Paths.get("").toAbsolutePath(); + String toolname = System.getenv("LAUNCHER_TOOLNAME"); + String libs = System.getenv("LAUNCHER_LIBS"); System.out.println("Running ES cli"); System.out.println("ES_HOME=" + homeDir); System.out.println("tool: " + toolname); + System.out.println("libs: " + libs); Path libDir = homeDir.resolve("lib"); - ClassLoader serverLoader = loadJars(libDir, ClassLoader.getSystemClassLoader()); - ClassLoader cliLoader = loadJars(libDir.resolve("cli").resolve(toolname), serverLoader); + ClassLoader cliLoader = loadJars(libDir.resolve("tools").resolve(toolname), ClassLoader.getSystemClassLoader()); ServiceLoader toolProvider = ServiceLoader.load(ToolProvider.class, cliLoader); ToolProvider tool = toolProvider.findFirst().orElseThrow(); System.exit(tool.main(args, Terminal.DEFAULT)); diff --git a/libs/cli/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java similarity index 100% rename from libs/cli/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java rename to distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java diff --git a/libs/cli/build.gradle b/libs/cli/build.gradle index f95bf91419902..c12ae87ee65fe 100644 --- a/libs/cli/build.gradle +++ b/libs/cli/build.gradle @@ -5,31 +5,18 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -apply plugin: 'elasticsearch.java' +apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.publish' dependencies { api 'net.sf.jopt-simple:jopt-simple:5.0.2' api project(':libs:elasticsearch-core') } -sourceSets { - unsupportedJdkVersionEntrypoint -} - -tasks.named('compileUnsupportedJdkVersionEntrypointJava').configure { - targetCompatibility = JavaVersion.VERSION_1_7 -} - -tasks.named("jar") { - manifest { - attributes("Multi-Release": "true") - } +tasks.named("test").configure { enabled = false } +// Since CLI does not depend on :server, it cannot run the jarHell task +tasks.named("jarHell").configure { enabled = false } - from(sourceSets.unsupportedJdkVersionEntrypoint.output) - eachFile { details -> - if (details.path.equals("org/elasticsearch/cli/Launcher.class") && - sourceSets.main.output.asFileTree.contains(details.file)) { - details.relativePath = details.relativePath.prepend("META-INF/versions/17") - } - } +tasks.named('forbiddenApisMain').configure { + replaceSignatureFiles 'jdk-signatures' } diff --git a/server/build.gradle b/server/build.gradle index bf47134363d07..fdd4f83cb1a0a 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -48,8 +48,7 @@ dependencies { api "org.apache.lucene:lucene-suggest:${versions.lucene}" // utilities -// TODO: move this out! - compileOnly project(":libs:elasticsearch-cli") + api project(":libs:elasticsearch-cli") implementation 'com.carrotsearch:hppc:0.8.1' // percentiles aggregation From 42f1bdecfe246d3e1c46abec763e363b06b3ea3b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 2 Apr 2022 09:37:55 -0700 Subject: [PATCH 005/166] certutil converted --- distribution/src/bin/elasticsearch-cli | 94 ++++++++++++++----- distribution/src/bin/elasticsearch-cli-new | 74 --------------- .../src/bin/elasticsearch-cli-new.bat | 0 distribution/src/bin/elasticsearch-cli-old | 33 +++++++ .../src/bin/elasticsearch-cli-old.bat | 29 ++++++ distribution/src/bin/elasticsearch-cli.bat | 29 ------ distribution/src/bin/elasticsearch-plugin | 5 +- .../org/elasticsearch/launcher/Launcher.java | 32 +++++-- .../elasticsearch/plugins/cli/PluginCli.java | 4 +- .../plugins/cli/PluginCliProvider.java | 25 +++++ .../org.elasticsearch.cli.ToolProvider | 2 +- .../org/elasticsearch/cli/ToolProvider.java | 3 +- .../security/cli/CertificateToolProvider.java | 24 +++++ .../org.elasticsearch.cli.ToolProvider | 1 + .../src/main/bin/elasticsearch-certutil | 5 +- 15 files changed, 216 insertions(+), 144 deletions(-) delete mode 100644 distribution/src/bin/elasticsearch-cli-new delete mode 100644 distribution/src/bin/elasticsearch-cli-new.bat create mode 100644 distribution/src/bin/elasticsearch-cli-old create mode 100644 distribution/src/bin/elasticsearch-cli-old.bat create mode 100644 distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCliProvider.java create mode 100644 x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateToolProvider.java create mode 100644 x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index 6f03456eb0122..152c6f803d0cd 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -2,32 +2,82 @@ set -e -o pipefail -source "`dirname "$0"`"/elasticsearch-env +CDPATH="" -IFS=';' read -r -a additional_sources <<< "$ES_ADDITIONAL_SOURCES" -for additional_source in "${additional_sources[@]}" -do - source "$ES_HOME"/bin/$additional_source +SCRIPT="$0" + +# SCRIPT might be an arbitrarily deep series of symbolic links; loop until we +# have the concrete path +while [ -h "$SCRIPT" ] ; do + ls=`ls -ld "$SCRIPT"` + # Drop everything prior to -> + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=`dirname "$SCRIPT"`/"$link" + fi done -IFS=';' read -r -a additional_classpath_directories <<< "$ES_ADDITIONAL_CLASSPATH_DIRECTORIES" -for additional_classpath_directory in "${additional_classpath_directories[@]}" -do - ES_CLASSPATH="$ES_CLASSPATH:$ES_HOME/$additional_classpath_directory/*" +# determine Elasticsearch home; to do this, we strip from the path until we find +# bin, and then strip bin (there is an assumption here that there is no nested +# directory under bin also named bin) +ES_HOME=`dirname "$SCRIPT"` + +# now make ES_HOME absolute +ES_HOME=`cd "$ES_HOME"; pwd` + +while [ "`basename "$ES_HOME"`" != "bin" ]; do + ES_HOME=`dirname "$ES_HOME"` done +ES_HOME=`dirname "$ES_HOME"` +cd $ES_HOME + +# now set the path to java +if [ ! -z "$ES_JAVA_HOME" ]; then + JAVA="$ES_JAVA_HOME/bin/java" + JAVA_TYPE="ES_JAVA_HOME" +else + # use the bundled JDK (default) + if [ "$(uname -s)" = "Darwin" ]; then + # macOS has a different structure + JAVA="$ES_HOME/jdk.app/Contents/Home/bin/java" + else + JAVA="$ES_HOME/jdk/bin/java" + fi + JAVA_TYPE="bundled JDK" +fi + +if [ ! -x "$JAVA" ]; then + echo "could not find java in $JAVA_TYPE at $JAVA" >&2 + exit 1 +fi + +# do not let JAVA_TOOL_OPTIONS slip in (as the JVM does by default) +if [ ! -z "$JAVA_TOOL_OPTIONS" ]; then + echo "warning: ignoring JAVA_TOOL_OPTIONS=$JAVA_TOOL_OPTIONS" + unset JAVA_TOOL_OPTIONS +fi + +# warn that we are not observing the value of JAVA_HOME +if [ ! -z "$JAVA_HOME" ]; then + echo "warning: ignoring JAVA_HOME=$JAVA_HOME; using $JAVA_TYPE" >&2 +fi -# use a small heap size for the CLI tools, and thus the serial collector to -# avoid stealing many CPU cycles; a user can override by setting ES_JAVA_OPTS -ES_JAVA_OPTS="-Xms4m -Xmx64m -XX:+UseSerialGC ${ES_JAVA_OPTS}" +@source.path.env@ +ES_DISTRIBUTION_FLAVOR=@es.distribution.flavor@ +ES_DISTRIBUTION_TYPE=@es.distribution.type@ +ES_BUNDLED_JDK=@es.bundled_jdk@ +LAUNCHER_CLASSPATH=$ES_HOME/lib/*:$ES_HOME/lib/launcher/* exec \ - "$JAVA" \ - "$XSHARE" \ - $ES_JAVA_OPTS \ - -Des.path.home="$ES_HOME" \ - -Des.path.conf="$ES_PATH_CONF" \ - -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ - -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ - -cp "$ES_CLASSPATH" \ - "$ES_MAIN_CLASS" \ - "$@" + "$JAVA" \ + $LAUNCHER_JAVA_OPTS \ + -Des.path.home="$ES_HOME" \ + -Des.path.conf="$ES_PATH_CONF" \ + -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ + -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ + -Des.bundled_jdk="$ES_BUNDLED_JDK" \ + -cp "$LAUNCHER_CLASSPATH" \ + org.elasticsearch.launcher.Launcher \ + "$@" diff --git a/distribution/src/bin/elasticsearch-cli-new b/distribution/src/bin/elasticsearch-cli-new deleted file mode 100644 index 3402c848915b9..0000000000000 --- a/distribution/src/bin/elasticsearch-cli-new +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -set -e -o pipefail - -CDPATH="" - -SCRIPT="$0" - -# SCRIPT might be an arbitrarily deep series of symbolic links; loop until we -# have the concrete path -while [ -h "$SCRIPT" ] ; do - ls=`ls -ld "$SCRIPT"` - # Drop everything prior to -> - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - SCRIPT="$link" - else - SCRIPT=`dirname "$SCRIPT"`/"$link" - fi -done - -# determine Elasticsearch home; to do this, we strip from the path until we find -# bin, and then strip bin (there is an assumption here that there is no nested -# directory under bin also named bin) -ES_HOME=`dirname "$SCRIPT"` - -# now make ES_HOME absolute -ES_HOME=`cd "$ES_HOME"; pwd` - -while [ "`basename "$ES_HOME"`" != "bin" ]; do - ES_HOME=`dirname "$ES_HOME"` -done -ES_HOME=`dirname "$ES_HOME"` -cd $ES_HOME - -# now set the path to java -if [ ! -z "$ES_JAVA_HOME" ]; then - JAVA="$ES_JAVA_HOME/bin/java" - JAVA_TYPE="ES_JAVA_HOME" -else - # use the bundled JDK (default) - if [ "$(uname -s)" = "Darwin" ]; then - # macOS has a different structure - JAVA="$ES_HOME/jdk.app/Contents/Home/bin/java" - else - JAVA="$ES_HOME/jdk/bin/java" - fi - JAVA_TYPE="bundled JDK" -fi - -if [ ! -x "$JAVA" ]; then - echo "could not find java in $JAVA_TYPE at $JAVA" >&2 - exit 1 -fi - -# do not let JAVA_TOOL_OPTIONS slip in (as the JVM does by default) -if [ ! -z "$JAVA_TOOL_OPTIONS" ]; then - echo "warning: ignoring JAVA_TOOL_OPTIONS=$JAVA_TOOL_OPTIONS" - unset JAVA_TOOL_OPTIONS -fi - -# warn that we are not observing the value of JAVA_HOME -if [ ! -z "$JAVA_HOME" ]; then - echo "warning: ignoring JAVA_HOME=$JAVA_HOME; using $JAVA_TYPE" >&2 -fi - -LAUNCHER_CLASSPATH=$ES_HOME/lib/*:$ES_HOME/lib/launcher/* - -exec \ - "$JAVA" \ - $LAUNCHER_JAVA_OPTS \ - -cp "$LAUNCHER_CLASSPATH" \ - org.elasticsearch.launcher.Launcher \ - "${ARG_LIST[@]}" \ diff --git a/distribution/src/bin/elasticsearch-cli-new.bat b/distribution/src/bin/elasticsearch-cli-new.bat deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/distribution/src/bin/elasticsearch-cli-old b/distribution/src/bin/elasticsearch-cli-old new file mode 100644 index 0000000000000..6f03456eb0122 --- /dev/null +++ b/distribution/src/bin/elasticsearch-cli-old @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e -o pipefail + +source "`dirname "$0"`"/elasticsearch-env + +IFS=';' read -r -a additional_sources <<< "$ES_ADDITIONAL_SOURCES" +for additional_source in "${additional_sources[@]}" +do + source "$ES_HOME"/bin/$additional_source +done + +IFS=';' read -r -a additional_classpath_directories <<< "$ES_ADDITIONAL_CLASSPATH_DIRECTORIES" +for additional_classpath_directory in "${additional_classpath_directories[@]}" +do + ES_CLASSPATH="$ES_CLASSPATH:$ES_HOME/$additional_classpath_directory/*" +done + +# use a small heap size for the CLI tools, and thus the serial collector to +# avoid stealing many CPU cycles; a user can override by setting ES_JAVA_OPTS +ES_JAVA_OPTS="-Xms4m -Xmx64m -XX:+UseSerialGC ${ES_JAVA_OPTS}" + +exec \ + "$JAVA" \ + "$XSHARE" \ + $ES_JAVA_OPTS \ + -Des.path.home="$ES_HOME" \ + -Des.path.conf="$ES_PATH_CONF" \ + -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ + -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ + -cp "$ES_CLASSPATH" \ + "$ES_MAIN_CLASS" \ + "$@" diff --git a/distribution/src/bin/elasticsearch-cli-old.bat b/distribution/src/bin/elasticsearch-cli-old.bat new file mode 100644 index 0000000000000..866e8efc6689b --- /dev/null +++ b/distribution/src/bin/elasticsearch-cli-old.bat @@ -0,0 +1,29 @@ +call "%~dp0elasticsearch-env.bat" || exit /b 1 + +if defined ES_ADDITIONAL_SOURCES ( + for %%a in ("%ES_ADDITIONAL_SOURCES:;=","%") do ( + call "%~dp0%%a" + ) +) + +if defined ES_ADDITIONAL_CLASSPATH_DIRECTORIES ( + for %%a in ("%ES_ADDITIONAL_CLASSPATH_DIRECTORIES:;=","%") do ( + set ES_CLASSPATH=!ES_CLASSPATH!;!ES_HOME!/%%a/* + ) +) + +rem use a small heap size for the CLI tools, and thus the serial collector to +rem avoid stealing many CPU cycles; a user can override by setting ES_JAVA_OPTS +set ES_JAVA_OPTS=-Xms4m -Xmx64m -XX:+UseSerialGC %ES_JAVA_OPTS% + +%JAVA% ^ + %ES_JAVA_OPTS% ^ + -Des.path.home="%ES_HOME%" ^ + -Des.path.conf="%ES_PATH_CONF%" ^ + -Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%" ^ + -Des.distribution.type="%ES_DISTRIBUTION_TYPE%" ^ + -cp "%ES_CLASSPATH%" ^ + "%ES_MAIN_CLASS%" ^ + %* + +exit /b %ERRORLEVEL% diff --git a/distribution/src/bin/elasticsearch-cli.bat b/distribution/src/bin/elasticsearch-cli.bat index 866e8efc6689b..e69de29bb2d1d 100644 --- a/distribution/src/bin/elasticsearch-cli.bat +++ b/distribution/src/bin/elasticsearch-cli.bat @@ -1,29 +0,0 @@ -call "%~dp0elasticsearch-env.bat" || exit /b 1 - -if defined ES_ADDITIONAL_SOURCES ( - for %%a in ("%ES_ADDITIONAL_SOURCES:;=","%") do ( - call "%~dp0%%a" - ) -) - -if defined ES_ADDITIONAL_CLASSPATH_DIRECTORIES ( - for %%a in ("%ES_ADDITIONAL_CLASSPATH_DIRECTORIES:;=","%") do ( - set ES_CLASSPATH=!ES_CLASSPATH!;!ES_HOME!/%%a/* - ) -) - -rem use a small heap size for the CLI tools, and thus the serial collector to -rem avoid stealing many CPU cycles; a user can override by setting ES_JAVA_OPTS -set ES_JAVA_OPTS=-Xms4m -Xmx64m -XX:+UseSerialGC %ES_JAVA_OPTS% - -%JAVA% ^ - %ES_JAVA_OPTS% ^ - -Des.path.home="%ES_HOME%" ^ - -Des.path.conf="%ES_PATH_CONF%" ^ - -Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%" ^ - -Des.distribution.type="%ES_DISTRIBUTION_TYPE%" ^ - -cp "%ES_CLASSPATH%" ^ - "%ES_MAIN_CLASS%" ^ - %* - -exit /b %ERRORLEVEL% diff --git a/distribution/src/bin/elasticsearch-plugin b/distribution/src/bin/elasticsearch-plugin index 855b0d3acd913..ae86a103a2c93 100755 --- a/distribution/src/bin/elasticsearch-plugin +++ b/distribution/src/bin/elasticsearch-plugin @@ -1,6 +1,7 @@ #!/bin/bash LAUNCHER_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED" \ - LAUNCHER_TOOLNAME=plugin-cli \ - "`dirname "$0"`"/elasticsearch-cli-new \ + LAUNCHER_LIBS=lib/tools/plugin-cli \ + LAUNCHER_TOOLNAME=plugin \ + "`dirname "$0"`"/elasticsearch-cli \ "$@" diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index 50b205d655a14..4d81227eccedb 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -8,6 +8,7 @@ package org.elasticsearch.launcher; +import org.elasticsearch.cli.Command; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.ToolProvider; @@ -18,10 +19,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.ServiceLoader; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Stream; +import java.util.stream.StreamSupport; class Launcher { @@ -44,19 +49,26 @@ public static void main(String[] args) throws Exception { System.out.println("ES_HOME=" + homeDir); System.out.println("tool: " + toolname); System.out.println("libs: " + libs); + System.out.println("args: " + Arrays.asList(args)); - Path libDir = homeDir.resolve("lib"); - ClassLoader cliLoader = loadJars(libDir.resolve("tools").resolve(toolname), ClassLoader.getSystemClassLoader()); - ServiceLoader toolProvider = ServiceLoader.load(ToolProvider.class, cliLoader); - ToolProvider tool = toolProvider.findFirst().orElseThrow(); - System.exit(tool.main(args, Terminal.DEFAULT)); + List libsToLoad = Stream.of(libs.split(",")).map(homeDir::resolve).toList(); + + ClassLoader cliLoader = loadJars(libsToLoad); + ServiceLoader toolFinder = ServiceLoader.load(ToolProvider.class, cliLoader); + ToolProvider tool = StreamSupport.stream(toolFinder.spliterator(), false) + .filter(p -> p.name().equals(toolname)) + .findFirst().orElseThrow(); + Command toolCommand = tool.create(); + System.exit(toolCommand.main(args, Terminal.DEFAULT)); } - private static ClassLoader loadJars(Path dir, ClassLoader parent) throws IOException { - final URL[] urls; - try (Stream jarFiles = Files.list(dir)) { - urls = jarFiles.filter(JAR_PREDICATE).map(MAP_PATH_TO_URL).toArray(URL[]::new); + private static ClassLoader loadJars(List dirs) throws IOException { + final List urls = new ArrayList<>(); + for (var dir : dirs) { + try (Stream jarFiles = Files.list(dir)) { + jarFiles.filter(JAR_PREDICATE).map(MAP_PATH_TO_URL).forEach(urls::add); + } } - return URLClassLoader.newInstance(urls, parent); + return URLClassLoader.newInstance(urls.toArray(URL[]::new)); } } diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java index 7e4d1865767f3..138ee87724b96 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java @@ -21,11 +21,11 @@ /** * A cli tool for adding, removing and listing plugins for elasticsearch. */ -public class PluginCli extends LoggingAwareMultiCommand implements ToolProvider { +class PluginCli extends LoggingAwareMultiCommand { private final Collection commands; - public PluginCli() { + PluginCli() { super("A tool for managing installed elasticsearch plugins"); subcommands.put("list", new ListPluginsCommand()); subcommands.put("install", new InstallPluginCommand()); diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCliProvider.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCliProvider.java new file mode 100644 index 0000000000000..ef14af4d5e4f0 --- /dev/null +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCliProvider.java @@ -0,0 +1,25 @@ +/* + * 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.plugins.cli; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class PluginCliProvider implements ToolProvider { + + @Override + public String name() { + return "plugin"; + } + + @Override + public Command create() { + return new PluginCli(); + } +} diff --git a/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider index e49cc18c4f6cf..473a3cb9456d9 100644 --- a/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ b/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -1 +1 @@ -org.elasticsearch.plugins.cli.PluginCli +org.elasticsearch.plugins.cli.PluginCliProvider diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java index 4072991d195dc..d02dd20d2a299 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java @@ -11,7 +11,8 @@ public interface ToolProvider { // TODO: merge launcher and cli lib, move to subdir in distro, make compileOnly dep in server + String name(); // TODO: this is temporary - int main(String[] args, Terminal terminal) throws Exception; + Command create(); } diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateToolProvider.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateToolProvider.java new file mode 100644 index 0000000000000..83574655a6cfe --- /dev/null +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateToolProvider.java @@ -0,0 +1,24 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.cli; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class CertificateToolProvider implements ToolProvider { + + @Override + public String name() { + return "certutil"; + } + + @Override + public Command create() { + return new CertificateTool(); + } +} diff --git a/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..ed539b3c67cd6 --- /dev/null +++ b/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1 @@ +org.elasticsearch.xpack.security.cli.CertificateToolProvider diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil index 4e1e194626876..2c06b6e6b8d15 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil @@ -5,8 +5,7 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ +LAUNCHER_TOOLNAME=certutil \ + LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" \ "`dirname "$0"`"/elasticsearch-cli \ "$@" From 483577f3dab22702c9467567cc584dcf63842a19 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 08:41:35 -0700 Subject: [PATCH 006/166] use exec --- distribution/src/bin/elasticsearch-plugin | 9 ++++----- .../org/elasticsearch/launcher/Launcher.java | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/distribution/src/bin/elasticsearch-plugin b/distribution/src/bin/elasticsearch-plugin index ae86a103a2c93..c58acdbe0032c 100755 --- a/distribution/src/bin/elasticsearch-plugin +++ b/distribution/src/bin/elasticsearch-plugin @@ -1,7 +1,6 @@ #!/bin/bash -LAUNCHER_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED" \ - LAUNCHER_LIBS=lib/tools/plugin-cli \ - LAUNCHER_TOOLNAME=plugin \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +export LAUNCHER_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED" +export LAUNCHER_LIBS=lib/tools/plugin-cli +export LAUNCHER_TOOLNAME=plugin +exec "`dirname "$0"`"/elasticsearch-cli "$@" diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index 4d81227eccedb..94068b58cd759 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -21,10 +21,14 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.ServiceLoader; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; @@ -41,9 +45,13 @@ class Launcher { // TODO: don't throw, catch this and give a nice error message public static void main(String[] args) throws Exception { + // TODO: change signature of Command to take in sysprops and env + Map sysprops = convertPropertiesToMap(System.getProperties()); + Map env = new HashMap<>(System.getenv()); + Path homeDir = Paths.get("").toAbsolutePath(); - String toolname = System.getenv("LAUNCHER_TOOLNAME"); - String libs = System.getenv("LAUNCHER_LIBS"); + String toolname = env.get("LAUNCHER_TOOLNAME"); + String libs = env.get("LAUNCHER_LIBS"); System.out.println("Running ES cli"); System.out.println("ES_HOME=" + homeDir); @@ -62,6 +70,11 @@ public static void main(String[] args) throws Exception { System.exit(toolCommand.main(args, Terminal.DEFAULT)); } + private static Map convertPropertiesToMap(Properties properties) { + return properties.entrySet().stream().collect( + Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString())); + } + private static ClassLoader loadJars(List dirs) throws IOException { final List urls = new ArrayList<>(); for (var dir : dirs) { From b49edb4ae0cbadff0f6c96eac591d4b040e06638 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 09:17:14 -0700 Subject: [PATCH 007/166] use exec in certutil, remove readJvmOptionsFiles(final Path config) throws IOException, JvmOptio Reader reader = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(reader) ) { - parse(JavaVersion.majorVersion(JavaVersion.CURRENT), br, jvmOptions::add, invalidLines::put); + parse(Runtime.version().feature(), br, jvmOptions::add, invalidLines::put); } if (invalidLines.isEmpty() == false) { throw new JvmOptionsFileParserException(jvmOptionsFile, invalidLines); diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemJvmOptions.java b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemJvmOptions.java index 15e9eda273fe1..13f31f2c3793c 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemJvmOptions.java +++ b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemJvmOptions.java @@ -8,8 +8,6 @@ package org.elasticsearch.tools.launchers; -import org.elasticsearch.tools.java_version_checker.JavaVersion; - import java.util.List; import java.util.stream.Collectors; @@ -44,8 +42,8 @@ static List systemJvmOptions() { * debugging. */ "-XX:-OmitStackTraceInFastThrow", - // enable helpful NullPointerExceptions (https://openjdk.java.net/jeps/358), if they are supported - maybeShowCodeDetailsInExceptionMessages(), + // enable helpful NullPointerExceptions (https://openjdk.java.net/jeps/358) + "-XX:+ShowCodeDetailsInExceptionMessages", // flags to configure Netty "-Dio.netty.noUnsafe=true", "-Dio.netty.noKeySetOptimization=true", @@ -68,13 +66,4 @@ static List systemJvmOptions() { "--add-opens=java.base/java.io=ALL-UNNAMED" ).stream().filter(e -> e.isEmpty() == false).collect(Collectors.toList()); } - - private static String maybeShowCodeDetailsInExceptionMessages() { - if (JavaVersion.majorVersion(JavaVersion.CURRENT) >= 14) { - return "-XX:+ShowCodeDetailsInExceptionMessages"; - } else { - return ""; - } - } - } diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil index 2c06b6e6b8d15..21ada363a92cc 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil @@ -5,7 +5,6 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -LAUNCHER_TOOLNAME=certutil \ - LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +export LAUNCHER_TOOLNAME=certutil +export LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" +exec "`dirname "$0"`"/elasticsearch-cli "$@" From abefb362820b14fe0d6eaf11d4caa35ef08f2ccd Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 10:20:16 -0700 Subject: [PATCH 008/166] move java version checking, use source instead of exec --- distribution/src/bin/elasticsearch-cli | 6 ++ distribution/src/bin/elasticsearch-plugin | 8 +-- .../tools/java-version-checker/build.gradle | 25 +++++--- .../java_version_checker/JavaVersion.java | 60 ------------------- .../JavaVersionChecker.java | 39 +----------- .../JavaVersionChecker.java} | 15 ++++- distribution/tools/launcher/build.gradle | 22 ------- .../launchers/DefaultSystemMemoryInfo.java | 2 - .../tools/launchers/Launchers.java | 2 - .../tools/launchers}/SuppressForbidden.java | 2 +- .../src/main/bin/elasticsearch-certutil | 6 +- 11 files changed, 47 insertions(+), 140 deletions(-) delete mode 100644 distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersion.java rename distribution/tools/{launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java => java-version-checker/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java} (67%) rename distribution/tools/{java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker => launchers/src/main/java/org/elasticsearch/tools/launchers}/SuppressForbidden.java (93%) diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index 152c6f803d0cd..75cb3d9ecf946 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -37,6 +37,8 @@ cd $ES_HOME if [ ! -z "$ES_JAVA_HOME" ]; then JAVA="$ES_JAVA_HOME/bin/java" JAVA_TYPE="ES_JAVA_HOME" + + "$JAVA" -cp "$ES_HOME/tools/java_version_checker/*" org.elasticsearch.tools.java_version_checker.JavaVersionChecker else # use the bundled JDK (default) if [ "$(uname -s)" = "Darwin" ]; then @@ -70,6 +72,10 @@ ES_DISTRIBUTION_TYPE=@es.distribution.type@ ES_BUNDLED_JDK=@es.bundled_jdk@ LAUNCHER_CLASSPATH=$ES_HOME/lib/*:$ES_HOME/lib/launcher/* +# temporary hack to force these into the launcher, but do we want to pollute env? +export LAUNCHER_TOOLNAME +export LAUNCHER_LIBS + exec \ "$JAVA" \ $LAUNCHER_JAVA_OPTS \ diff --git a/distribution/src/bin/elasticsearch-plugin b/distribution/src/bin/elasticsearch-plugin index c58acdbe0032c..20f2c1431257a 100755 --- a/distribution/src/bin/elasticsearch-plugin +++ b/distribution/src/bin/elasticsearch-plugin @@ -1,6 +1,6 @@ #!/bin/bash -export LAUNCHER_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED" -export LAUNCHER_LIBS=lib/tools/plugin-cli -export LAUNCHER_TOOLNAME=plugin -exec "`dirname "$0"`"/elasticsearch-cli "$@" +LAUNCHER_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED" +LAUNCHER_LIBS=lib/tools/plugin-cli +LAUNCHER_TOOLNAME=plugin +source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/tools/java-version-checker/build.gradle b/distribution/tools/java-version-checker/build.gradle index 8aa9096eb5d31..e6f902fc1d61b 100644 --- a/distribution/tools/java-version-checker/build.gradle +++ b/distribution/tools/java-version-checker/build.gradle @@ -1,12 +1,23 @@ -apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.java' -targetCompatibility = JavaVersion.VERSION_1_7 +sourceSets { + unsupportedJdkVersionEntrypoint +} -// java_version_checker do not depend on core so only JDK signatures should be checked -tasks.named('forbiddenApisMain').configure { - replaceSignatureFiles 'jdk-signatures' +tasks.named('compileUnsupportedJdkVersionEntrypointJava').configure { + targetCompatibility = JavaVersion.VERSION_1_7 } -["test", "javadoc", "loggerUsageCheck", "jarHell"].each { - tasks.named(it).configure { enabled = false } +tasks.named("jar") { + manifest { + attributes("Multi-Release": "true") + } + + from(sourceSets.unsupportedJdkVersionEntrypoint.output) + eachFile { details -> + if (details.path.equals("org/elasticsearch/tools/java_version_checker/JavaVersionChecker.class") && + sourceSets.main.output.asFileTree.contains(details.file)) { + details.relativePath = details.relativePath.prepend("META-INF/versions/17") + } + } } diff --git a/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersion.java b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersion.java deleted file mode 100644 index dd4cc4bb075d6..0000000000000 --- a/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersion.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.tools.java_version_checker; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -public class JavaVersion { - - public static final List CURRENT = parse(System.getProperty("java.specification.version")); - public static final List JAVA_17 = parse("17"); - - private JavaVersion() {} - - static List parse(final String value) { - if (value.matches("^0*[0-9]+(\\.[0-9]+)*$") == false) { - throw new IllegalArgumentException(value); - } - - final List version = new ArrayList(); - final String[] components = value.split("\\."); - for (final String component : components) { - version.add(Integer.valueOf(component)); - } - return version; - } - - public static int majorVersion(final List javaVersion) { - Objects.requireNonNull(javaVersion); - if (javaVersion.get(0) > 1) { - return javaVersion.get(0); - } else { - return javaVersion.get(1); - } - } - - static int compare(final List left, final List right) { - // lexicographically compare two lists, treating missing entries as zeros - final int len = Math.max(left.size(), right.size()); - for (int i = 0; i < len; i++) { - final int l = (i < left.size()) ? left.get(i) : 0; - final int r = (i < right.size()) ? right.get(i) : 0; - if (l < r) { - return -1; - } - if (r < l) { - return 1; - } - } - return 0; - } - -} diff --git a/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java index b62701d74bee0..dd74ea7231db4 100644 --- a/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java +++ b/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java @@ -12,52 +12,17 @@ import java.util.Locale; /** - * Simple program that checks if the runtime Java version is at least 17. + * Java 17 compatible main which exits with no-error. */ final class JavaVersionChecker { private JavaVersionChecker() {} - /** - * The main entry point. The exit code is 0 if the Java version is at least 17, otherwise the exit code is 1. - * - * @param args the args to the program which are rejected if not empty - */ public static void main(final String[] args) { // no leniency! if (args.length != 0) { throw new IllegalArgumentException("expected zero arguments but was " + Arrays.toString(args)); } - if (JavaVersion.compare(JavaVersion.CURRENT, JavaVersion.JAVA_17) < 0) { - final String message = String.format( - Locale.ROOT, - "The minimum required Java version is 17; your Java version from [%s] does not meet this requirement", - System.getProperty("java.home") - ); - errPrintln(message); - exit(1); - } - exit(0); - } - - /** - * Prints a string and terminates the line on standard error. - * - * @param message the message to print - */ - @SuppressForbidden(reason = "System#err") - static void errPrintln(final String message) { - System.err.println(message); + System.exit(0); } - - /** - * Exit the VM with the specified status. - * - * @param status the status - */ - @SuppressForbidden(reason = "System#exit") - static void exit(final int status) { - System.exit(status); - } - } diff --git a/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/java-version-checker/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java similarity index 67% rename from distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java rename to distribution/tools/java-version-checker/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java index c5929b7242047..2fddf1bc164b3 100644 --- a/distribution/tools/launcher/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/java-version-checker/src/unsupportedJdkVersionEntrypoint/java/org/elasticsearch/tools/java_version_checker/JavaVersionChecker.java @@ -6,12 +6,23 @@ * Side Public License, v 1. */ -package org.elasticsearch.launcher; +package org.elasticsearch.tools.java_version_checker; +import java.util.Arrays; import java.util.Locale; -class Launcher { +/** + * Java 7 compatible main which exits with an error. + */ +final class JavaVersionChecker { + + private JavaVersionChecker() {} + public static void main(String[] args) { + // no leniency! + if (args.length != 0) { + throw new IllegalArgumentException("expected zero arguments but was " + Arrays.toString(args)); + } final String message = String.format( Locale.ROOT, "The minimum required Java version is 17; your Java version %s from [%s] does not meet that requirement.", diff --git a/distribution/tools/launcher/build.gradle b/distribution/tools/launcher/build.gradle index 0ce04dcc25bcd..0dd9cdedcfcdb 100644 --- a/distribution/tools/launcher/build.gradle +++ b/distribution/tools/launcher/build.gradle @@ -4,25 +4,3 @@ apply plugin: 'elasticsearch.java' dependencies { compileOnly project(':server') } - -sourceSets { - unsupportedJdkVersionEntrypoint -} - -tasks.named('compileUnsupportedJdkVersionEntrypointJava').configure { - targetCompatibility = JavaVersion.VERSION_1_7 -} - -tasks.named("jar") { - manifest { - attributes("Multi-Release": "true") - } - - from(sourceSets.unsupportedJdkVersionEntrypoint.output) - eachFile { details -> - if (details.path.equals("org/elasticsearch/launcher/Launcher.class") && - sourceSets.main.output.asFileTree.contains(details.file)) { - details.relativePath = details.relativePath.prepend("META-INF/versions/17") - } - } -} diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/DefaultSystemMemoryInfo.java b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/DefaultSystemMemoryInfo.java index d56afadd023b1..27572e6db6c10 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/DefaultSystemMemoryInfo.java +++ b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/DefaultSystemMemoryInfo.java @@ -10,8 +10,6 @@ import com.sun.management.OperatingSystemMXBean; -import org.elasticsearch.tools.java_version_checker.SuppressForbidden; - import java.lang.management.ManagementFactory; /** diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java index 606aa2b9646fe..725fcb041ad8b 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java +++ b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/Launchers.java @@ -8,8 +8,6 @@ package org.elasticsearch.tools.launchers; -import org.elasticsearch.tools.java_version_checker.SuppressForbidden; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/SuppressForbidden.java b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SuppressForbidden.java similarity index 93% rename from distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/SuppressForbidden.java rename to distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SuppressForbidden.java index 4ec39921fb345..2dbeb3dc0fb91 100644 --- a/distribution/tools/java-version-checker/src/main/java/org/elasticsearch/tools/java_version_checker/SuppressForbidden.java +++ b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SuppressForbidden.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.java_version_checker; +package org.elasticsearch.tools.launchers; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil index 21ada363a92cc..072ee2425f158 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil @@ -5,6 +5,6 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -export LAUNCHER_TOOLNAME=certutil -export LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" -exec "`dirname "$0"`"/elasticsearch-cli "$@" +LAUNCHER_TOOLNAME=certutil +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" +source "`dirname "$0"`"/elasticsearch-cli From e11e6a3a6e6780ca898aa823c087d5db3d0f27e7 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 10:56:25 -0700 Subject: [PATCH 009/166] Fix version checker --- distribution/build.gradle | 7 +++++-- distribution/src/bin/elasticsearch-cli | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/distribution/build.gradle b/distribution/build.gradle index b87b5ebef480b..c4a0a52e098da 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -230,7 +230,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { * Properties to expand when copying packaging files * *****************************************************************************/ configurations { - ['libs', 'libsLauncher', 'libsLaunchers', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { + ['libs', 'libsLauncher', 'libsLaunchers', 'libsVersionChecker', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { create(it) { canBeConsumed = false canBeResolved = true @@ -251,7 +251,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { libs project(':server') libsLauncher project(':distribution:tools:launcher') - libsLaunchers project(':distribution:tools:java-version-checker') + libsVersionChecker project(':distribution:tools:java-version-checker') libsLaunchers project(':distribution:tools:launchers') libsAnsiConsole project(':distribution:tools:ansi-console') libsPluginCli project(':distribution:tools:plugin-cli') @@ -272,6 +272,9 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { into('launcher') { from(configurations.libsLauncher) } + into('java-version-checker') { + from(configurations.libsVersionChecker) + } into('launchers') { from(configurations.libsLaunchers) } diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index 75cb3d9ecf946..664da55c216b2 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -38,7 +38,7 @@ if [ ! -z "$ES_JAVA_HOME" ]; then JAVA="$ES_JAVA_HOME/bin/java" JAVA_TYPE="ES_JAVA_HOME" - "$JAVA" -cp "$ES_HOME/tools/java_version_checker/*" org.elasticsearch.tools.java_version_checker.JavaVersionChecker + "$JAVA" -cp "$ES_HOME/lib/java-version-checker/*" org.elasticsearch.tools.java_version_checker.JavaVersionChecker else # use the bundled JDK (default) if [ "$(uname -s)" = "Darwin" ]; then From 2ba7d99c74c9988f27ff986d9c4341d21f1296c6 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 11:02:28 -0700 Subject: [PATCH 010/166] convert geoip cli --- distribution/src/bin/elasticsearch-geoip | 7 +++--- .../elasticsearch/geoip/GeoIpCliProvider.java | 24 +++++++++++++++++++ .../org.elasticsearch.cli.ToolProvider | 1 + 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCliProvider.java create mode 100644 distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/distribution/src/bin/elasticsearch-geoip b/distribution/src/bin/elasticsearch-geoip index 47cf3adfa75dc..56d751b6b7615 100755 --- a/distribution/src/bin/elasticsearch-geoip +++ b/distribution/src/bin/elasticsearch-geoip @@ -1,6 +1,5 @@ #!/bin/bash -ES_MAIN_CLASS=org.elasticsearch.geoip.GeoIpCli \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/geoip-cli \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_TOOLNAME=geoip +LAUNCHER_LIBS=lib/tools/geoip-cli +source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCliProvider.java b/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCliProvider.java new file mode 100644 index 0000000000000..a17afcf1ee628 --- /dev/null +++ b/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCliProvider.java @@ -0,0 +1,24 @@ +/* + * 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.geoip; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class GeoIpCliProvider implements ToolProvider { + @Override + public String name() { + return "geoip"; + } + + @Override + public Command create() { + return new GeoIpCli(); + } +} diff --git a/distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..bfbc25e9cad21 --- /dev/null +++ b/distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1 @@ +org.elasticsearch.geoip.GeoIpCliProvider From b3dc89018c5077ec11401d61210e61187019c835 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 11:06:35 -0700 Subject: [PATCH 011/166] convert keystore cli --- distribution/src/bin/elasticsearch-keystore | 7 +++--- .../cli/keystore/KeyStoreCli.java | 10 ++------ .../cli/keystore/KeyStoreCliProvider.java | 24 +++++++++++++++++++ .../org.elasticsearch.cli.ToolProvider | 1 + 4 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java create mode 100644 distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/distribution/src/bin/elasticsearch-keystore b/distribution/src/bin/elasticsearch-keystore index c24eb47536e25..6c7139f28059c 100755 --- a/distribution/src/bin/elasticsearch-keystore +++ b/distribution/src/bin/elasticsearch-keystore @@ -1,6 +1,5 @@ #!/bin/bash -ES_MAIN_CLASS=org.elasticsearch.cli.keystore.KeyStoreCli \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/keystore-cli \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_TOOLNAME=keystore +LAUNCHER_LIBS=lib/tools/keystore-cli +source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java index 710531a1999ab..81acf200d7848 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java @@ -8,15 +8,14 @@ package org.elasticsearch.cli.keystore; -import org.elasticsearch.cli.Terminal; import org.elasticsearch.common.cli.LoggingAwareMultiCommand; /** * A cli tool for managing secrets in the elasticsearch keystore. */ -public class KeyStoreCli extends LoggingAwareMultiCommand { +class KeyStoreCli extends LoggingAwareMultiCommand { - private KeyStoreCli() { + KeyStoreCli() { super("A tool for managing settings stored in the elasticsearch keystore"); subcommands.put("create", new CreateKeyStoreCommand()); subcommands.put("list", new ListKeyStoreCommand()); @@ -28,9 +27,4 @@ private KeyStoreCli() { subcommands.put("passwd", new ChangeKeyStorePasswordCommand()); subcommands.put("has-passwd", new HasPasswordKeyStoreCommand()); } - - public static void main(String[] args) throws Exception { - exit(new KeyStoreCli().main(args, Terminal.DEFAULT)); - } - } diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java new file mode 100644 index 0000000000000..894109ec8948b --- /dev/null +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java @@ -0,0 +1,24 @@ +/* + * 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.cli.keystore; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class KeyStoreCliProvider implements ToolProvider { + @Override + public String name() { + return "keystore"; + } + + @Override + public Command create() { + return new KeyStoreCli(); + } +} diff --git a/distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..6d546090b5635 --- /dev/null +++ b/distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1 @@ +org.elasticsearch.cli.keystore.KeyStoreCliProvider From eeff48546c7f2f09343d607081ec582b5add7ea4 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 11:20:46 -0700 Subject: [PATCH 012/166] introspect toolname --- distribution/src/bin/elasticsearch-cli | 3 ++- distribution/src/bin/elasticsearch-geoip | 1 - distribution/src/bin/elasticsearch-keystore | 1 - distribution/src/bin/elasticsearch-plugin | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index 664da55c216b2..dc3d88a27ca2c 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -72,8 +72,9 @@ ES_DISTRIBUTION_TYPE=@es.distribution.type@ ES_BUNDLED_JDK=@es.bundled_jdk@ LAUNCHER_CLASSPATH=$ES_HOME/lib/*:$ES_HOME/lib/launcher/* +SCRIPTNAME=$(basename "$0") # temporary hack to force these into the launcher, but do we want to pollute env? -export LAUNCHER_TOOLNAME +export LAUNCHER_TOOLNAME=${SCRIPTNAME#"elasticsearch-"} export LAUNCHER_LIBS exec \ diff --git a/distribution/src/bin/elasticsearch-geoip b/distribution/src/bin/elasticsearch-geoip index 56d751b6b7615..042d835f6d234 100755 --- a/distribution/src/bin/elasticsearch-geoip +++ b/distribution/src/bin/elasticsearch-geoip @@ -1,5 +1,4 @@ #!/bin/bash -LAUNCHER_TOOLNAME=geoip LAUNCHER_LIBS=lib/tools/geoip-cli source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/src/bin/elasticsearch-keystore b/distribution/src/bin/elasticsearch-keystore index 6c7139f28059c..a884b2ab35372 100755 --- a/distribution/src/bin/elasticsearch-keystore +++ b/distribution/src/bin/elasticsearch-keystore @@ -1,5 +1,4 @@ #!/bin/bash -LAUNCHER_TOOLNAME=keystore LAUNCHER_LIBS=lib/tools/keystore-cli source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/src/bin/elasticsearch-plugin b/distribution/src/bin/elasticsearch-plugin index 20f2c1431257a..f861eabf13d38 100755 --- a/distribution/src/bin/elasticsearch-plugin +++ b/distribution/src/bin/elasticsearch-plugin @@ -2,5 +2,4 @@ LAUNCHER_JAVA_OPTS="--add-opens java.base/sun.security.provider=ALL-UNNAMED" LAUNCHER_LIBS=lib/tools/plugin-cli -LAUNCHER_TOOLNAME=plugin source "`dirname "$0"`"/elasticsearch-cli From 160bbcfae5b595d91fe5e0f775f369a4a392ce68 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 12:10:29 -0700 Subject: [PATCH 013/166] convert node and shard clis --- distribution/src/bin/elasticsearch-node | 4 +--- distribution/src/bin/elasticsearch-shard | 4 +--- .../org/elasticsearch/launcher/Launcher.java | 14 +++++++---- .../coordination/NodeToolCliProvider.java | 24 +++++++++++++++++++ .../index/shard/ShardToolCli.java | 8 +------ .../index/shard/ShardToolCliProvider.java | 24 +++++++++++++++++++ .../org.elasticsearch.cli.ToolProvider | 2 ++ 7 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCliProvider.java create mode 100644 server/src/main/java/org/elasticsearch/index/shard/ShardToolCliProvider.java create mode 100644 server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/distribution/src/bin/elasticsearch-node b/distribution/src/bin/elasticsearch-node index 29949486b5526..dc31376286750 100755 --- a/distribution/src/bin/elasticsearch-node +++ b/distribution/src/bin/elasticsearch-node @@ -1,5 +1,3 @@ #!/bin/bash -ES_MAIN_CLASS=org.elasticsearch.cluster.coordination.NodeToolCli \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/src/bin/elasticsearch-shard b/distribution/src/bin/elasticsearch-shard index 4c14a0434175b..dc31376286750 100755 --- a/distribution/src/bin/elasticsearch-shard +++ b/distribution/src/bin/elasticsearch-shard @@ -1,5 +1,3 @@ #!/bin/bash -ES_MAIN_CLASS=org.elasticsearch.index.shard.ShardToolCli \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index 94068b58cd759..994faa4442cad 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -59,13 +59,19 @@ public static void main(String[] args) throws Exception { System.out.println("libs: " + libs); System.out.println("args: " + Arrays.asList(args)); - List libsToLoad = Stream.of(libs.split(",")).map(homeDir::resolve).toList(); - - ClassLoader cliLoader = loadJars(libsToLoad); + final ClassLoader cliLoader; + if (libs != null) { + List libsToLoad = Stream.of(libs.split(",")).map(homeDir::resolve).toList(); + cliLoader = loadJars(libsToLoad); + } else { + cliLoader = ClassLoader.getSystemClassLoader(); + } ServiceLoader toolFinder = ServiceLoader.load(ToolProvider.class, cliLoader); + // TODO: assert only one tool is found? ToolProvider tool = StreamSupport.stream(toolFinder.spliterator(), false) .filter(p -> p.name().equals(toolname)) - .findFirst().orElseThrow(); + .findFirst() + .orElseThrow(() -> new AssertionError("ToolProvider [" + toolname + "] not found")); Command toolCommand = tool.create(); System.exit(toolCommand.main(args, Terminal.DEFAULT)); } diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCliProvider.java b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCliProvider.java new file mode 100644 index 0000000000000..1ea082bee4903 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCliProvider.java @@ -0,0 +1,24 @@ +/* + * 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.coordination; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class NodeToolCliProvider implements ToolProvider { + @Override + public String name() { + return "node"; + } + + @Override + public Command create() { + return new NodeToolCli(); + } +} diff --git a/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java index 306db0025c6e0..3ca59d78db3a1 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java +++ b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java @@ -7,7 +7,6 @@ */ package org.elasticsearch.index.shard; -import org.elasticsearch.cli.Terminal; import org.elasticsearch.common.cli.LoggingAwareMultiCommand; /** @@ -15,13 +14,8 @@ */ public class ShardToolCli extends LoggingAwareMultiCommand { - private ShardToolCli() { + ShardToolCli() { super("A CLI tool to remove corrupted parts of unrecoverable shards"); subcommands.put("remove-corrupted-data", new RemoveCorruptedShardDataCommand()); } - - public static void main(String[] args) throws Exception { - exit(new ShardToolCli().main(args, Terminal.DEFAULT)); - } - } diff --git a/server/src/main/java/org/elasticsearch/index/shard/ShardToolCliProvider.java b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCliProvider.java new file mode 100644 index 0000000000000..7ff61546ef2b1 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCliProvider.java @@ -0,0 +1,24 @@ +/* + * 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.index.shard; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class ShardToolCliProvider implements ToolProvider { + @Override + public String name() { + return "shard"; + } + + @Override + public Command create() { + return new ShardToolCli(); + } +} diff --git a/server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..fbc978252dae0 --- /dev/null +++ b/server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1,2 @@ +org.elasticsearch.cluster.coordination.NodeToolCliProvider +org.elasticsearch.index.shard.ShardToolCliProvider From eab9724504eed2101b91b560203808da12d352e4 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 12:46:03 -0700 Subject: [PATCH 014/166] convert most security tools --- .../security/cli/CertificateGenerateTool.java | 4 ---- .../cli/CertificateGenerateToolProvider.java | 23 +++++++++++++++++++ .../xpack/security/cli/CertificateTool.java | 4 ---- .../cli/ReconfigureNodeToolProvider.java | 23 +++++++++++++++++++ .../org.elasticsearch.cli.ToolProvider | 2 ++ .../src/main/bin/elasticsearch-certgen | 7 ++---- .../src/main/bin/elasticsearch-certutil | 1 - .../bin/elasticsearch-create-enrollment-token | 6 ++--- .../main/bin/elasticsearch-reconfigure-node | 8 +++---- .../src/main/bin/elasticsearch-reset-password | 6 ++--- .../src/main/bin/elasticsearch-saml-metadata | 6 ++--- .../src/main/bin/elasticsearch-service-tokens | 6 ++--- .../main/bin/elasticsearch-setup-passwords | 6 ++--- .../src/main/bin/elasticsearch-syskeygen | 6 ++--- .../security/src/main/bin/elasticsearch-users | 6 ++--- .../esnative/tool/ResetPasswordTool.java | 4 ---- .../tool/ResetPasswordToolProvider.java | 23 +++++++++++++++++++ .../esnative/tool/SetupPasswordTool.java | 5 ---- .../tool/SetupPasswordToolProvider.java | 23 +++++++++++++++++++ .../security/authc/file/tool/UsersTool.java | 4 ---- .../authc/file/tool/UsersToolProvider.java | 23 +++++++++++++++++++ .../authc/saml/SamlMetadataCommand.java | 4 ---- .../authc/saml/SamlMetadataToolProvider.java | 23 +++++++++++++++++++ .../authc/service/FileTokensTool.java | 4 ---- .../authc/service/FileTokensToolProvider.java | 23 +++++++++++++++++++ .../security/crypto/tool/SystemKeyTool.java | 8 ------- .../crypto/tool/SystemKeyToolProvider.java | 23 +++++++++++++++++++ .../tool/CreateEnrollmentTokenTool.java | 4 ---- .../CreateEnrollmentTokenToolProvider.java | 23 +++++++++++++++++++ .../org.elasticsearch.cli.ToolProvider | 7 ++++++ 30 files changed, 235 insertions(+), 80 deletions(-) create mode 100644 x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateToolProvider.java create mode 100644 x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenToolProvider.java create mode 100644 x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java index d51d2032617f6..e2882097bb304 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java @@ -159,10 +159,6 @@ private static class InputFileParser { .withOptionalArg(); } - public static void main(String[] args) throws Exception { - exit(new CertificateGenerateTool().main(args, Terminal.DEFAULT)); - } - @Override protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { final boolean csrOnly = options.has(csrSpec); diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateToolProvider.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateToolProvider.java new file mode 100644 index 0000000000000..c2fae3c90b6c5 --- /dev/null +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.cli; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class CertificateGenerateToolProvider implements ToolProvider { + @Override + public String name() { + return "certgen"; + } + + @Override + public Command create() { + return new CertificateGenerateTool(); + } +} diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java index c6d6f71b25690..eccd44fd0b99c 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java @@ -145,10 +145,6 @@ private static class CertificateToolParser { } } - public static void main(String[] args) throws Exception { - exit(new CertificateTool().main(args, Terminal.DEFAULT)); - } - CertificateTool() { super(DESCRIPTION); subcommands.put("csr", new SigningRequestCommand()); diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java new file mode 100644 index 0000000000000..0ae222c332dc0 --- /dev/null +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.cli; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class ReconfigureNodeToolProvider implements ToolProvider { + @Override + public String name() { + return "reconfigure-node"; + } + + @Override + public Command create() { + return new AutoConfigureNode(); + } +} diff --git a/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider index ed539b3c67cd6..ec7da2dc694f9 100644 --- a/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ b/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -1 +1,3 @@ org.elasticsearch.xpack.security.cli.CertificateToolProvider +org.elasticsearch.xpack.security.cli.CertificateGenerateToolProvider +org.elasticsearch.xpack.security.cli.ReconfigureNodeToolProvider diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen index a99515c1e8478..511ca4885dfb2 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen @@ -5,8 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateGenerateTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil index 072ee2425f158..511ca4885dfb2 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil @@ -5,6 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -LAUNCHER_TOOLNAME=certutil LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token index ce17ab6022c6c..eb326a746a303 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.enrollment.tool.CreateEnrollmentTokenTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node index 5204adf7e5b62..39c266fae5359 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node @@ -5,8 +5,6 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ - "`dirname "$0"`"/elasticsearch-cli \ - --reconfigure "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" +# TODO: pass this hardcoded arg somehow!? --reconfigure "$@" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password index 6f50bc54440dc..eb326a746a303 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.esnative.tool.ResetPasswordTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata index a09bd6be3b220..eb326a746a303 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.saml.SamlMetadataCommand \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens index c9cdbd78bc8f0..eb326a746a303 100755 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.service.FileTokensTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords index 16b4823892981..eb326a746a303 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.esnative.tool.SetupPasswordTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen index 9e0c3ccf5d192..eb326a746a303 100755 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.crypto.tool.SystemKeyTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-users b/x-pack/plugin/security/src/main/bin/elasticsearch-users index bf4a4b3395234..eb326a746a303 100755 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-users +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-users @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.file.tool.UsersTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordTool.java index f52d8f8add031..1d3a0f39ef33c 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordTool.java @@ -46,10 +46,6 @@ public ResetPasswordTool() { this(CommandLineHttpClient::new, environment -> KeyStoreWrapper.load(environment.configFile())); } - public static void main(String[] args) throws Exception { - exit(new ResetPasswordTool().main(args, Terminal.DEFAULT)); - } - protected ResetPasswordTool( Function clientFunction, CheckedFunction keyStoreFunction diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordToolProvider.java new file mode 100644 index 0000000000000..075912908642a --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/ResetPasswordToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.authc.esnative.tool; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class ResetPasswordToolProvider implements ToolProvider { + @Override + public String name() { + return "reset-password"; + } + + @Override + public Command create() { + return new ResetPasswordTool(); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java index 3bcfec26652da..f907150ce4f61 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java @@ -55,7 +55,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.Function; - import javax.net.ssl.SSLException; import static java.util.Arrays.asList; @@ -124,10 +123,6 @@ protected InteractiveSetup newInteractiveSetup() { return new InteractiveSetup(); } - public static void main(String[] args) throws Exception { - exit(new SetupPasswordTool().main(args, Terminal.DEFAULT)); - } - // Visible for testing OptionParser getParser() { return this.parser; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordToolProvider.java new file mode 100644 index 0000000000000..408eb565a2f04 --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.authc.esnative.tool; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class SetupPasswordToolProvider implements ToolProvider { + @Override + public String name() { + return "setup-passwords"; + } + + @Override + public Command create() { + return new SetupPasswordTool(); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java index 11a61894bde4a..6a7245ae749a3 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java @@ -45,10 +45,6 @@ public class UsersTool extends LoggingAwareMultiCommand { - public static void main(String[] args) throws Exception { - exit(new UsersTool().main(args, Terminal.DEFAULT)); - } - UsersTool() { super("Manages elasticsearch file users"); subcommands.put("useradd", newAddUserCommand()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolProvider.java new file mode 100644 index 0000000000000..f3e5824a8c86e --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.authc.file.tool; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class UsersToolProvider implements ToolProvider { + @Override + public String name() { + return "users"; + } + + @Override + public Command create() { + return new UsersTool(); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java index 15ed84168ae96..726cecae76537 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java @@ -90,10 +90,6 @@ public class SamlMetadataCommand extends KeyStoreAwareCommand { private final CheckedFunction keyStoreFunction; private KeyStoreWrapper keyStoreWrapper; - public static void main(String[] args) throws Exception { - exit(new SamlMetadataCommand().main(args, Terminal.DEFAULT)); - } - public SamlMetadataCommand() { this((environment) -> { KeyStoreWrapper ksWrapper = KeyStoreWrapper.load(environment.configFile()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataToolProvider.java new file mode 100644 index 0000000000000..64b3c88ac4683 --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.authc.saml; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class SamlMetadataToolProvider implements ToolProvider { + @Override + public String name() { + return "saml-metadata"; + } + + @Override + public Command create() { + return new SamlMetadataCommand(); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java index 79e5f74c39b78..045e5fc226787 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java @@ -33,10 +33,6 @@ public class FileTokensTool extends LoggingAwareMultiCommand { - public static void main(String[] args) throws Exception { - exit(new FileTokensTool().main(args, Terminal.DEFAULT)); - } - public FileTokensTool() { super("Manages elasticsearch service account file-tokens"); subcommands.put("create", newCreateFileTokenCommand()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolProvider.java new file mode 100644 index 0000000000000..3fd0b50235ce4 --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.authc.service; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class FileTokensToolProvider implements ToolProvider { + @Override + public String name() { + return "service-tokens"; + } + + @Override + public Command create() { + return new FileTokensTool(); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java index 3da100aa82e1e..b00a55e42f4c0 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java @@ -49,14 +49,6 @@ public class SystemKeyTool extends EnvironmentAwareCommand { PosixFilePermission.OWNER_WRITE ); - public static void main(String[] args) throws Exception { - final SystemKeyTool tool = new SystemKeyTool(); - int status = main(tool, args, Terminal.DEFAULT); - if (status != ExitCodes.OK) { - exit(status); - } - } - static int main(SystemKeyTool tool, String[] args, Terminal terminal) throws Exception { return tool.main(args, terminal); } diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolProvider.java new file mode 100644 index 0000000000000..78feea41dd636 --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.crypto.tool; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class SystemKeyToolProvider implements ToolProvider { + @Override + public String name() { + return "syskeygen"; + } + + @Override + public Command create() { + return new SystemKeyTool(); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenTool.java index 954badc86e47a..2812d83f9a69e 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenTool.java @@ -55,10 +55,6 @@ public class CreateEnrollmentTokenTool extends BaseRunAsSuperuserCommand { .required(); } - public static void main(String[] args) throws Exception { - exit(new CreateEnrollmentTokenTool().main(args, Terminal.DEFAULT)); - } - @Override protected void validate(Terminal terminal, OptionSet options, Environment env) throws Exception { if (XPackSettings.ENROLLMENT_ENABLED.get(env.settings()) == false) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenToolProvider.java new file mode 100644 index 0000000000000..7fe88b8c37082 --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/CreateEnrollmentTokenToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.enrollment.tool; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class CreateEnrollmentTokenToolProvider implements ToolProvider { + @Override + public String name() { + return "create-enrollment-token"; + } + + @Override + public Command create() { + return new CreateEnrollmentTokenTool(); + } +} diff --git a/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..7515ba8fd568e --- /dev/null +++ b/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1,7 @@ +org.elasticsearch.xpack.security.enrollment.tool.CreateEnrollmentTokenToolProvider +org.elasticsearch.xpack.security.authc.esnative.tool.ResetPasswordToolProvider +org.elasticsearch.xpack.security.authc.esnative.tool.SetupPasswordToolProvider +org.elasticsearch.xpack.security.authc.saml.SamlMetadataToolProvider +org.elasticsearch.xpack.security.authc.service.FileTokensToolProvider +org.elasticsearch.xpack.security.crypto.tool.SystemKeyToolProvider +org.elasticsearch.xpack.security.authc.file.tool.UsersToolProvider From 271096ff99c1e6aa954e7a9acd82365a9f6125c3 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 12:49:56 -0700 Subject: [PATCH 015/166] convert croneval --- .../src/main/bin/elasticsearch-croneval | 6 ++--- .../trigger/schedule/tool/CronEvalTool.java | 4 ---- .../schedule/tool/CronEvalToolProvider.java | 23 +++++++++++++++++++ .../org.elasticsearch.cli.ToolProvider | 1 + 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolProvider.java create mode 100644 x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval index c2e7195428e12..12400ed4bc2ae 100755 --- a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval +++ b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval @@ -5,7 +5,5 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -ES_MAIN_CLASS=org.elasticsearch.xpack.watcher.trigger.schedule.tool.CronEvalTool \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-watcher-env" \ - "`dirname "$0"`"/elasticsearch-cli \ - "$@" +LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-watcher" +source "`dirname "$0"`"/elasticsearch-cli diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java index 45832d418ec94..54f5b12ff824f 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java @@ -26,10 +26,6 @@ public class CronEvalTool extends LoggingAwareCommand { - public static void main(String[] args) throws Exception { - exit(new CronEvalTool().main(args, Terminal.DEFAULT)); - } - private static final DateFormatter UTC_FORMATTER = DateFormatter.forPattern("EEE, d MMM yyyy HH:mm:ss") .withZone(ZoneOffset.UTC) .withLocale(Locale.ROOT); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolProvider.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolProvider.java new file mode 100644 index 0000000000000..dac72aeaa8251 --- /dev/null +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.watcher.trigger.schedule.tool; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class CronEvalToolProvider implements ToolProvider { + @Override + public String name() { + return "croneval"; + } + + @Override + public Command create() { + return new CronEvalTool(); + } +} diff --git a/x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..4ed62b4daa8e9 --- /dev/null +++ b/x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1 @@ +org.elasticsearch.xpack.watcher.trigger.schedule.tool.CronEvalToolProvider From e0fb885891f3b526822fff312733e50c7efbf3b7 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 13:38:37 -0700 Subject: [PATCH 016/166] use es-env script again, so sql-cli can still use it --- distribution/src/bin/elasticsearch-cli | 139 ++++++++++-------- distribution/src/bin/elasticsearch-env | 93 +----------- .../sql/src/main/bin/elasticsearch-sql-cli | 7 +- 3 files changed, 81 insertions(+), 158 deletions(-) diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index dc3d88a27ca2c..3a14870a1eaba 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -2,81 +2,98 @@ set -e -o pipefail -CDPATH="" +source "`dirname "$0"`"/elasticsearch-env -SCRIPT="$0" - -# SCRIPT might be an arbitrarily deep series of symbolic links; loop until we -# have the concrete path -while [ -h "$SCRIPT" ] ; do - ls=`ls -ld "$SCRIPT"` - # Drop everything prior to -> - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - SCRIPT="$link" - else - SCRIPT=`dirname "$SCRIPT"`/"$link" - fi -done - -# determine Elasticsearch home; to do this, we strip from the path until we find -# bin, and then strip bin (there is an assumption here that there is no nested -# directory under bin also named bin) -ES_HOME=`dirname "$SCRIPT"` - -# now make ES_HOME absolute -ES_HOME=`cd "$ES_HOME"; pwd` - -while [ "`basename "$ES_HOME"`" != "bin" ]; do - ES_HOME=`dirname "$ES_HOME"` -done -ES_HOME=`dirname "$ES_HOME"` -cd $ES_HOME - -# now set the path to java -if [ ! -z "$ES_JAVA_HOME" ]; then - JAVA="$ES_JAVA_HOME/bin/java" - JAVA_TYPE="ES_JAVA_HOME" - - "$JAVA" -cp "$ES_HOME/lib/java-version-checker/*" org.elasticsearch.tools.java_version_checker.JavaVersionChecker -else - # use the bundled JDK (default) - if [ "$(uname -s)" = "Darwin" ]; then - # macOS has a different structure - JAVA="$ES_HOME/jdk.app/Contents/Home/bin/java" - else - JAVA="$ES_HOME/jdk/bin/java" - fi - JAVA_TYPE="bundled JDK" -fi - -if [ ! -x "$JAVA" ]; then - echo "could not find java in $JAVA_TYPE at $JAVA" >&2 +@source.path.env@ +if [ -z "$ES_PATH_CONF" ]; then + echo "ES_PATH_CONF must be set to the configuration path" exit 1 fi -# do not let JAVA_TOOL_OPTIONS slip in (as the JVM does by default) -if [ ! -z "$JAVA_TOOL_OPTIONS" ]; then - echo "warning: ignoring JAVA_TOOL_OPTIONS=$JAVA_TOOL_OPTIONS" - unset JAVA_TOOL_OPTIONS -fi - -# warn that we are not observing the value of JAVA_HOME -if [ ! -z "$JAVA_HOME" ]; then - echo "warning: ignoring JAVA_HOME=$JAVA_HOME; using $JAVA_TYPE" >&2 -fi - -@source.path.env@ ES_DISTRIBUTION_FLAVOR=@es.distribution.flavor@ ES_DISTRIBUTION_TYPE=@es.distribution.type@ ES_BUNDLED_JDK=@es.bundled_jdk@ LAUNCHER_CLASSPATH=$ES_HOME/lib/*:$ES_HOME/lib/launcher/* +# TODO: move this to launcher +if [[ "$ES_BUNDLED_JDK" == "false" ]]; then + echo "warning: no-jdk distributions that do not bundle a JDK are deprecated and will be removed in a future release" >&2 +fi + SCRIPTNAME=$(basename "$0") # temporary hack to force these into the launcher, but do we want to pollute env? export LAUNCHER_TOOLNAME=${SCRIPTNAME#"elasticsearch-"} export LAUNCHER_LIBS +# TODO: move this to launcher +if [[ "$ES_DISTRIBUTION_TYPE" == "docker" ]]; then + # Allow environment variables to be set by creating a file with the + # contents, and setting an environment variable with the suffix _FILE to + # point to it. This can be used to provide secrets to a container, without + # the values being specified explicitly when running the container. + source "$ES_HOME/bin/elasticsearch-env-from-file" + + # Parse Docker env vars to customize Elasticsearch + # + # e.g. Setting the env var cluster.name=testcluster or ES_CLUSTER_NAME=testcluster + # + # will cause Elasticsearch to be invoked with -Ecluster.name=testcluster + # + # see https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#_setting_default_settings + + declare -a es_arg_array + + containsElement () { + local e match="$1" + shift + for e; do [[ "$e" == "$match" ]] && return 0; done + return 1 + } + + # Elasticsearch settings need to either: + # a. have at least two dot separated lower case words, e.g. `cluster.name`, or + while IFS='=' read -r envvar_key envvar_value; do + es_opt="" + if [[ -n "$envvar_value" ]]; then + es_opt="-E${envvar_key}=${envvar_value}" + fi + if [[ ! -z "${es_opt}" ]] && ! containsElement "${es_opt}" "$@" ; then + es_arg_array+=("${es_opt}") + fi + done <<< "$(env | grep -E '^[-a-z0-9_]+(\.[-a-z0-9_]+)+=')" + + # b. be upper cased with underscore separators and prefixed with `ES_SETTING_`, e.g. `ES_SETTING_CLUSTER_NAME`. + # Underscores in setting names are escaped by writing them as a double-underscore e.g. "__" + while IFS='=' read -r envvar_key envvar_value; do + es_opt="" + if [[ -n "$envvar_value" ]]; then + # The long-hand sed `y` command works in any sed variant. + envvar_key="$(echo "$envvar_key" | sed -e 's/^ES_SETTING_//; s/_/./g ; s/\.\./_/g; y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' )" + es_opt="-E${envvar_key}=${envvar_value}" + fi + if [[ ! -z "${es_opt}" ]] && ! containsElement "${es_opt}" "$@" ; then + es_arg_array+=("${es_opt}") + fi + done <<< "$(env | grep -E '^ES_SETTING(_{1,2}[A-Z]+)+=')" + + # Reset the positional parameters to the es_arg_array values and any existing positional params + set -- "$@" "${es_arg_array[@]}" + + # The virtual file /proc/self/cgroup should list the current cgroup + # membership. For each hierarchy, you can follow the cgroup path from + # this file to the cgroup filesystem (usually /sys/fs/cgroup/) and + # introspect the statistics for the cgroup for the given + # hierarchy. Alas, Docker breaks this by mounting the container + # statistics at the root while leaving the cgroup paths as the actual + # paths. Therefore, Elasticsearch provides a mechanism to override + # reading the cgroup path from /proc/self/cgroup and instead uses the + # cgroup path defined the JVM system property + # es.cgroups.hierarchy.override. Therefore, we set this value here so + # that cgroup statistics are available for the container this process + # will run in. + export ES_JAVA_OPTS="-Des.cgroups.hierarchy.override=/ $ES_JAVA_OPTS" +fi + exec \ "$JAVA" \ $LAUNCHER_JAVA_OPTS \ diff --git a/distribution/src/bin/elasticsearch-env b/distribution/src/bin/elasticsearch-env index b9b91c57dab26..4771d61120195 100644 --- a/distribution/src/bin/elasticsearch-env +++ b/distribution/src/bin/elasticsearch-env @@ -40,6 +40,8 @@ LAUNCHERS_CLASSPATH="$ES_CLASSPATH:$ES_HOME/lib/launchers/*" if [ ! -z "$ES_JAVA_HOME" ]; then JAVA="$ES_JAVA_HOME/bin/java" JAVA_TYPE="ES_JAVA_HOME" + # check the user supplied jdk version + "$JAVA" -cp "$ES_HOME/lib/java-version-checker/*" org.elasticsearch.tools.java_version_checker.JavaVersionChecker else # use the bundled JDK (default) if [ "$(uname -s)" = "Darwin" ]; then @@ -80,95 +82,4 @@ else XSHARE="-Xshare:auto" fi -# check the Java version -"$JAVA" "$XSHARE" -cp "$LAUNCHERS_CLASSPATH" org.elasticsearch.tools.java_version_checker.JavaVersionChecker - -export HOSTNAME=$HOSTNAME - -@source.path.env@ - -if [ -z "$ES_PATH_CONF" ]; then - echo "ES_PATH_CONF must be set to the configuration path" - exit 1 -fi - -# now make ES_PATH_CONF absolute -ES_PATH_CONF=`cd "$ES_PATH_CONF"; pwd` - -ES_DISTRIBUTION_FLAVOR=@es.distribution.flavor@ -ES_DISTRIBUTION_TYPE=@es.distribution.type@ -ES_BUNDLED_JDK=@es.bundled_jdk@ - -if [[ "$ES_BUNDLED_JDK" == "false" ]]; then - echo "warning: no-jdk distributions that do not bundle a JDK are deprecated and will be removed in a future release" >&2 -fi - -if [[ "$ES_DISTRIBUTION_TYPE" == "docker" ]]; then - # Allow environment variables to be set by creating a file with the - # contents, and setting an environment variable with the suffix _FILE to - # point to it. This can be used to provide secrets to a container, without - # the values being specified explicitly when running the container. - source "$ES_HOME/bin/elasticsearch-env-from-file" - - # Parse Docker env vars to customize Elasticsearch - # - # e.g. Setting the env var cluster.name=testcluster or ES_CLUSTER_NAME=testcluster - # - # will cause Elasticsearch to be invoked with -Ecluster.name=testcluster - # - # see https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#_setting_default_settings - - declare -a es_arg_array - - containsElement () { - local e match="$1" - shift - for e; do [[ "$e" == "$match" ]] && return 0; done - return 1 - } - - # Elasticsearch settings need to either: - # a. have at least two dot separated lower case words, e.g. `cluster.name`, or - while IFS='=' read -r envvar_key envvar_value; do - es_opt="" - if [[ -n "$envvar_value" ]]; then - es_opt="-E${envvar_key}=${envvar_value}" - fi - if [[ ! -z "${es_opt}" ]] && ! containsElement "${es_opt}" "$@" ; then - es_arg_array+=("${es_opt}") - fi - done <<< "$(env | grep -E '^[-a-z0-9_]+(\.[-a-z0-9_]+)+=')" - - # b. be upper cased with underscore separators and prefixed with `ES_SETTING_`, e.g. `ES_SETTING_CLUSTER_NAME`. - # Underscores in setting names are escaped by writing them as a double-underscore e.g. "__" - while IFS='=' read -r envvar_key envvar_value; do - es_opt="" - if [[ -n "$envvar_value" ]]; then - # The long-hand sed `y` command works in any sed variant. - envvar_key="$(echo "$envvar_key" | sed -e 's/^ES_SETTING_//; s/_/./g ; s/\.\./_/g; y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' )" - es_opt="-E${envvar_key}=${envvar_value}" - fi - if [[ ! -z "${es_opt}" ]] && ! containsElement "${es_opt}" "$@" ; then - es_arg_array+=("${es_opt}") - fi - done <<< "$(env | grep -E '^ES_SETTING(_{1,2}[A-Z]+)+=')" - - # Reset the positional parameters to the es_arg_array values and any existing positional params - set -- "$@" "${es_arg_array[@]}" - - # The virtual file /proc/self/cgroup should list the current cgroup - # membership. For each hierarchy, you can follow the cgroup path from - # this file to the cgroup filesystem (usually /sys/fs/cgroup/) and - # introspect the statistics for the cgroup for the given - # hierarchy. Alas, Docker breaks this by mounting the container - # statistics at the root while leaving the cgroup paths as the actual - # paths. Therefore, Elasticsearch provides a mechanism to override - # reading the cgroup path from /proc/self/cgroup and instead uses the - # cgroup path defined the JVM system property - # es.cgroups.hierarchy.override. Therefore, we set this value here so - # that cgroup statistics are available for the container this process - # will run in. - export ES_JAVA_OPTS="-Des.cgroups.hierarchy.override=/ $ES_JAVA_OPTS" -fi - cd "$ES_HOME" diff --git a/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli b/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli index 257eff658cfb5..50d464ff8ef4e 100755 --- a/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli +++ b/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli @@ -5,16 +5,11 @@ # 2.0; you may not use this file except in compliance with the Elastic License # 2.0. -CLI_PARAMETERS="$@" - source "`dirname "$0"`"/elasticsearch-env -source "$ES_HOME"/bin/x-pack-env - CLI_JAR=$(ls "$ES_HOME"/bin/elasticsearch-sql-cli-*.jar) - exec \ "$JAVA" \ "$XSHARE" \ -jar "$CLI_JAR" \ - $CLI_PARAMETERS + "$@" From fdc695fd370b3f81fc950063ea9370a58fdb6296 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 13:56:08 -0700 Subject: [PATCH 017/166] fix reconfigure tool --- .../xpack/security/cli/AutoConfigureNode.java | 13 +++++++------ .../security/cli/ReconfigureNodeToolProvider.java | 2 +- .../xpack/security/cli/AutoConfigureNodeTests.java | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java index a6b8f2d2adef9..d08b80ebb2728 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java @@ -138,29 +138,30 @@ public class AutoConfigureNode extends EnvironmentAwareCommand { private final OptionSpec enrollmentTokenParam = parser.accepts("enrollment-token", "The enrollment token to use") .withRequiredArg(); - private final OptionSpec reconfigure = parser.accepts("reconfigure"); + private final boolean inReconfigureMode; private final BiFunction clientFunction; - public AutoConfigureNode(BiFunction clientFunction) { + public AutoConfigureNode(boolean reconfigure, BiFunction clientFunction) { super("Generates all the necessary security configuration for a node in a secured cluster"); // This "cli utility" is invoked from the node startup script, where it is passed all the // node startup options unfiltered. It cannot consume most of them, but it does need to inspect the `-E` ones. parser.allowsUnrecognizedOptions(); + this.inReconfigureMode = reconfigure; this.clientFunction = clientFunction; } - public AutoConfigureNode() { - this(CommandLineHttpClient::new); + public AutoConfigureNode(boolean reconfigure) { + this(reconfigure, CommandLineHttpClient::new); } + // TODO: remove this once postinst is converted public static void main(String[] args) throws Exception { - exit(new AutoConfigureNode().main(args, Terminal.DEFAULT)); + exit(new AutoConfigureNode(false).main(args, Terminal.DEFAULT)); } @Override protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { final boolean inEnrollmentMode = options.has(enrollmentTokenParam); - final boolean inReconfigureMode = options.has(reconfigure); // skipping security auto-configuration because node considered as restarting. for (Path dataPath : env.dataFiles()) { diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java index 0ae222c332dc0..5f9ea0c3a413d 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/ReconfigureNodeToolProvider.java @@ -18,6 +18,6 @@ public String name() { @Override public Command create() { - return new AutoConfigureNode(); + return new AutoConfigureNode(true); } } diff --git a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeTests.java b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeTests.java index 8f3c1c7f6bce8..06848d3d555f0 100644 --- a/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeTests.java +++ b/x-pack/plugin/security/cli/src/test/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeTests.java @@ -262,7 +262,7 @@ private boolean checkGeneralNameSan(X509Certificate certificate, String generalN private X509Certificate runAutoConfigAndReturnHTTPCertificate(Path configDir, Settings settings) throws Exception { final Environment env = TestEnvironment.newEnvironment(Settings.builder().put("path.home", configDir).put(settings).build()); // runs the command to auto-generate the config files and the keystore - new AutoConfigureNode().execute(new MockTerminal(), new OptionParser().parse(), env); + new AutoConfigureNode(false).execute(new MockTerminal(), new OptionParser().parse(), env); KeyStoreWrapper nodeKeystore = KeyStoreWrapper.load(configDir.resolve("config")); nodeKeystore.decrypt(new char[0]); // the keystore is always bootstrapped with an empty password From 64c65c6dbace60ce604c98cab6f71478d3221489 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 4 Apr 2022 14:07:25 -0700 Subject: [PATCH 018/166] convert postinst --- .../packages/src/common/scripts/postinst | 10 ++++---- distribution/src/bin/elasticsearch-cli | 9 +++++--- .../xpack/security/cli/AutoConfigureNode.java | 5 ---- .../cli/AutoConfigureNodeToolProvider.java | 23 +++++++++++++++++++ ...AutoConfigGenerateElasticPasswordHash.java | 4 ---- ...nerateElasticPasswordHashToolProvider.java | 23 +++++++++++++++++++ .../org.elasticsearch.cli.ToolProvider | 1 + 7 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeToolProvider.java create mode 100644 x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHashToolProvider.java diff --git a/distribution/packages/src/common/scripts/postinst b/distribution/packages/src/common/scripts/postinst index 549a8753bab9e..d0fa76c416041 100644 --- a/distribution/packages/src/common/scripts/postinst +++ b/distribution/packages/src/common/scripts/postinst @@ -59,17 +59,15 @@ if [ "x$IS_UPGRADE" != "xtrue" ]; then # Don't exit immediately on error, we want to hopefully print some helpful banners set +e # Attempt to auto-configure security, this seems to be an installation - if ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ + if LAUNCHER_TOOLNAME="auto-configure-node" \ + LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" \ /usr/share/elasticsearch/bin/elasticsearch-cli <<< ""; then # Above command runs as root and TLS keystores are created group-owned by root. It's simple to correct the ownership here chown root:elasticsearch "${ES_PATH_CONF}"/certs/http.p12 chown root:elasticsearch "${ES_PATH_CONF}"/certs/http_ca.crt chown root:elasticsearch "${ES_PATH_CONF}"/certs/transport.p12 - if INITIAL_PASSWORD=$(ES_MAIN_CLASS=org.elasticsearch.xpack.security.enrollment.tool.AutoConfigGenerateElasticPasswordHash \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ + if INITIAL_PASSWORD=$(LAUNCHER_TOOLNAME=auto-config-gen-passwd \ + LAUNCHER_LIBS="modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli" \ /usr/share/elasticsearch/bin/elasticsearch-cli); then echo "--------------------------- Security autoconfiguration information ------------------------------" echo diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index 3a14870a1eaba..29f0af7328e44 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -20,9 +20,11 @@ if [[ "$ES_BUNDLED_JDK" == "false" ]]; then echo "warning: no-jdk distributions that do not bundle a JDK are deprecated and will be removed in a future release" >&2 fi -SCRIPTNAME=$(basename "$0") -# temporary hack to force these into the launcher, but do we want to pollute env? -export LAUNCHER_TOOLNAME=${SCRIPTNAME#"elasticsearch-"} +if [[ -z "$LAUNCHER_TOOLNAME" ]]; then + SCRIPTNAME=$(basename "$0") + # temporary hack to force these into the launcher, but do we want to pollute env? + export LAUNCHER_TOOLNAME=${SCRIPTNAME#"elasticsearch-"} +fi export LAUNCHER_LIBS # TODO: move this to launcher @@ -97,6 +99,7 @@ fi exec \ "$JAVA" \ $LAUNCHER_JAVA_OPTS \ + -Dcli.toolname="$LAUNCHER_TOOLNAME" -Des.path.home="$ES_HOME" \ -Des.path.conf="$ES_PATH_CONF" \ -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java index d08b80ebb2728..a3c1d887ab9c6 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java @@ -154,11 +154,6 @@ public AutoConfigureNode(boolean reconfigure) { this(reconfigure, CommandLineHttpClient::new); } - // TODO: remove this once postinst is converted - public static void main(String[] args) throws Exception { - exit(new AutoConfigureNode(false).main(args, Terminal.DEFAULT)); - } - @Override protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { final boolean inEnrollmentMode = options.has(enrollmentTokenParam); diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeToolProvider.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeToolProvider.java new file mode 100644 index 0000000000000..2f74d4aba395d --- /dev/null +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNodeToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.cli; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class AutoConfigureNodeToolProvider implements ToolProvider { + @Override + public String name() { + return "auto-configure-node"; + } + + @Override + public Command create() { + return new AutoConfigureNode(false); + } +} diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java index cfe1262057883..18265a72097d6 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java @@ -42,10 +42,6 @@ public AutoConfigGenerateElasticPasswordHash() { super("Generates a password hash for for the elastic user and stores it in elasticsearch.keystore"); } - public static void main(String[] args) throws Exception { - exit(new AutoConfigGenerateElasticPasswordHash().main(args, Terminal.DEFAULT)); - } - @Override protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { final Hasher hasher = Hasher.resolve(XPackSettings.PASSWORD_HASHING_ALGORITHM.get(env.settings())); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHashToolProvider.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHashToolProvider.java new file mode 100644 index 0000000000000..725a35a60f47a --- /dev/null +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHashToolProvider.java @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.security.enrollment.tool; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class AutoConfigGenerateElasticPasswordHashToolProvider implements ToolProvider { + @Override + public String name() { + return "auto-config-gen-passwd"; + } + + @Override + public Command create() { + return new AutoConfigGenerateElasticPasswordHash(); + } +} diff --git a/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider index 7515ba8fd568e..1ff00bb7edc9f 100644 --- a/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ b/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -5,3 +5,4 @@ org.elasticsearch.xpack.security.authc.saml.SamlMetadataToolProvider org.elasticsearch.xpack.security.authc.service.FileTokensToolProvider org.elasticsearch.xpack.security.crypto.tool.SystemKeyToolProvider org.elasticsearch.xpack.security.authc.file.tool.UsersToolProvider +org.elasticsearch.xpack.security.enrollment.tool.AutoConfigGenerateElasticPasswordHashToolProvider From 9129265009320f45ba55adf13c0256d8e7ca15e5 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 09:20:35 -0400 Subject: [PATCH 019/166] Update windows scripts to use launcher tool --- distribution/src/bin/elasticsearch-cli.bat | 68 +++++++++++++++++++ distribution/src/bin/elasticsearch-geoip.bat | 4 +- .../src/bin/elasticsearch-keystore.bat | 4 +- distribution/src/bin/elasticsearch-node.bat | 2 +- distribution/src/bin/elasticsearch-plugin.bat | 8 +-- distribution/src/bin/elasticsearch-shard.bat | 2 +- .../src/main/bin/elasticsearch-certgen.bat | 5 +- .../src/main/bin/elasticsearch-certutil.bat | 5 +- .../elasticsearch-create-enrollment-token.bat | 5 +- .../bin/elasticsearch-reconfigure-node.bat | 5 +- .../main/bin/elasticsearch-reset-password.bat | 5 +- .../main/bin/elasticsearch-saml-metadata.bat | 4 +- .../main/bin/elasticsearch-service-tokens.bat | 4 +- .../bin/elasticsearch-setup-passwords.bat | 4 +- .../src/main/bin/elasticsearch-syskeygen.bat | 4 +- .../src/main/bin/elasticsearch-users.bat | 4 +- 16 files changed, 98 insertions(+), 35 deletions(-) diff --git a/distribution/src/bin/elasticsearch-cli.bat b/distribution/src/bin/elasticsearch-cli.bat index e69de29bb2d1d..2fdbb265a3df3 100644 --- a/distribution/src/bin/elasticsearch-cli.bat +++ b/distribution/src/bin/elasticsearch-cli.bat @@ -0,0 +1,68 @@ +set SCRIPT=%0 + +rem determine Elasticsearch home; to do this, we strip from the path until we +rem find bin, and then strip bin (there is an assumption here that there is no +rem nested directory under bin also named bin) +for %%I in (%SCRIPT%) do set ES_HOME=%%~dpI + +:es_home_loop +for %%I in ("%ES_HOME:~1,-1%") do set DIRNAME=%%~nxI +if not "%DIRNAME%" == "bin" ( + for %%I in ("%ES_HOME%..") do set ES_HOME=%%~dpfI + goto es_home_loop +) +for %%I in ("%ES_HOME%..") do set ES_HOME=%%~dpfI + +cd /d "%ES_HOME%" + +rem now set the path to java, pass "nojava" arg to skip setting ES_JAVA_HOME and JAVA +if "%1" == "nojava" ( + exit /b +) + +rem comparing to empty string makes this equivalent to bash -v check on env var +rem and allows to effectively force use of the bundled jdk when launching ES +rem by setting ES_JAVA_HOME= +if defined ES_JAVA_HOME ( + set JAVA="%ES_JAVA_HOME%\bin\java.exe" + set JAVA_TYPE=ES_JAVA_HOME +) else ( + rem use the bundled JDK (default) + set JAVA="%ES_HOME%\jdk\bin\java.exe" + set "ES_JAVA_HOME=%ES_HOME%\jdk" + set JAVA_TYPE=bundled JDK +) + +if not exist !JAVA! ( + echo "could not find java in !JAVA_TYPE! at !JAVA!" >&2 + exit /b 1 +) + +rem do not let JAVA_TOOL_OPTIONS slip in (as the JVM does by default) +if defined JAVA_TOOL_OPTIONS ( + echo warning: ignoring JAVA_TOOL_OPTIONS=%JAVA_TOOL_OPTIONS% + set JAVA_TOOL_OPTIONS= +) + +rem warn that we are not observing the value of $JAVA_HOME +if defined JAVA_HOME ( + echo warning: ignoring JAVA_HOME=%JAVA_HOME%; using %JAVA_TYPE% >&2 +) + +set ES_DISTRIBUTION_FLAVOR=@es.distribution.flavor@ +set ES_DISTRIBUTION_TYPE=@es.distribution.type@ +set ES_BUNDLED_JDK=@es.bundled_jdk@ +set LAUNCHER_CLASSPATH=%ES_HOME%/lib/*;%ES_HOME%/lib/launcher/* + +%JAVA% ^ + %LAUNCHER_JAVA_OPTS% ^ + -Des.path.home="%ES_HOME%" ^ + -Des.path.conf="%ES_PATH_CONF%" ^ + -Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%" ^ + -Des.distribution.type="%ES_DISTRIBUTION_TYPE%" ^ + -Des.bundled_jdk="%ES_BUNDLED_JDK%" ^ + -cp "%LAUNCHER_CLASSPATH%" ^ + "org.elasticsearch.launcher.Launcher" ^ + %* + +exit /b %ERRORLEVEL% diff --git a/distribution/src/bin/elasticsearch-geoip.bat b/distribution/src/bin/elasticsearch-geoip.bat index a1d82ba16c571..05e9e18b690a9 100644 --- a/distribution/src/bin/elasticsearch-geoip.bat +++ b/distribution/src/bin/elasticsearch-geoip.bat @@ -3,8 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.geoip.GeoIpCli -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/geoip-cli +set LAUNCHER_TOOLNAME=geoip +set LAUNCHER_LIBS=lib/tools/geoip-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/distribution/src/bin/elasticsearch-keystore.bat b/distribution/src/bin/elasticsearch-keystore.bat index 59ca167ab519b..4f1e2e1a5f432 100644 --- a/distribution/src/bin/elasticsearch-keystore.bat +++ b/distribution/src/bin/elasticsearch-keystore.bat @@ -3,8 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.cli.keystore.KeyStoreCli -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/keystore-cli +set LAUNCHER_TOOLNAME=keystore +set LAUNCHER_LIBS=lib/tools/keystore-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/distribution/src/bin/elasticsearch-node.bat b/distribution/src/bin/elasticsearch-node.bat index b152331d5ef89..baf1ff048ae7e 100644 --- a/distribution/src/bin/elasticsearch-node.bat +++ b/distribution/src/bin/elasticsearch-node.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.cluster.coordination.NodeToolCli +set LAUNCHER_TOOLNAME=node call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/distribution/src/bin/elasticsearch-plugin.bat b/distribution/src/bin/elasticsearch-plugin.bat index 2da1d49b9b8ad..2ca77c3be60ef 100644 --- a/distribution/src/bin/elasticsearch-plugin.bat +++ b/distribution/src/bin/elasticsearch-plugin.bat @@ -3,13 +3,13 @@ setlocal enabledelayedexpansion setlocal enableextensions -set ES_JAVA_OPTS=--add-opens java.base/sun.security.provider=ALL-UNNAMED %ES_JAVA_OPTS% -set ES_MAIN_CLASS=org.elasticsearch.plugins.cli.PluginCli -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/plugin-cli +set LAUNCHER_TOOLNAME=plugin +set LAUNCHER_LIBS=lib/tools/plugin-cli +set LAUNCHER_JAVA_OPTS=--add-opens java.base/sun.security.provider=ALL-UNNAMED %ES_JAVA_OPTS% call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit - + endlocal endlocal diff --git a/distribution/src/bin/elasticsearch-shard.bat b/distribution/src/bin/elasticsearch-shard.bat index 4db48f141fd6c..dbcf28cca306e 100644 --- a/distribution/src/bin/elasticsearch-shard.bat +++ b/distribution/src/bin/elasticsearch-shard.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.index.shard.ShardToolCli +set LAUNCHER_TOOLNAME=shard call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat index fd851fd21ebf8..5dc9fc44c7013 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat @@ -8,9 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateGenerateTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli +set LAUNCHER_TOOLNAME=certgen +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat index 16740fe455ae2..fc25acbf28742 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat @@ -8,9 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.CertificateTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli +set LAUNCHER_TOOLNAME=certutil +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat index 39027ef697727..096269a2bb920 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat @@ -8,9 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.enrollment.tool.CreateEnrollmentTokenTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli +set LAUNCHER_TOOLNAME=create-enrollment-token +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat index 386812a663be5..894856dc1b8cb 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat @@ -8,9 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli +set LAUNCHER_TOOLNAME=reconfigure-node +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" "--reconfigure" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat index ec081b6548db3..4d60413861e1a 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat @@ -8,9 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.esnative.tool.ResetPasswordTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env -set ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli +set LAUNCHER_TOOLNAME=reset-password +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat index 0143b1b53c505..ec90e1a180a25 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat @@ -8,8 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.saml.SamlMetadataCommand -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env +set LAUNCHER_TOOLNAME=saml-metadata +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat index 6ca1260c2a6ab..4aa73982c3d42 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat @@ -8,8 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.service.FileTokensTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env +set LAUNCHER_TOOLNAME=service-tokens +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat index 54641bc49ba71..f0f2fa3199668 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat @@ -8,8 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.esnative.tool.SetupPasswordTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env +set LAUNCHER_TOOLNAME=setup-passwords +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat index dceff2294c4a5..36b3907d8b131 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat @@ -8,8 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.crypto.tool.SystemKeyTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env +set LAUNCHER_TOOLNAME=syskeygen +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat index e2e553ba41c20..0bf97de09b878 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat @@ -8,8 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.security.authc.file.tool.UsersTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-security-env +set LAUNCHER_TOOLNAME=users +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit From 92e02479f112d730cf6614c76d05b14168b8e34a Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 09:41:09 -0400 Subject: [PATCH 020/166] Convert croneval.bat --- x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat index bbc8f8d03e821..f42196eb8697a 100644 --- a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat +++ b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat @@ -8,8 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set ES_MAIN_CLASS=org.elasticsearch.xpack.watcher.trigger.schedule.tool.CronEvalTool -set ES_ADDITIONAL_SOURCES=x-pack-env;x-pack-watcher-env +set LAUNCHER_TOOLNAME=croneval +set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-watcher call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit From 9292c78a9fcb69533076fd44f9bc4869f3748c03 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 11:31:34 -0400 Subject: [PATCH 021/166] Set config directory in elasticsearch-cli.bat --- distribution/build.gradle | 1 + distribution/src/bin/elasticsearch-cli.bat | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/distribution/build.gradle b/distribution/build.gradle index c4a0a52e098da..592f5572cde28 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -488,6 +488,7 @@ subprojects { 'source.path.env': [ 'deb': 'source /etc/default/elasticsearch', 'rpm': 'source /etc/sysconfig/elasticsearch', + 'zip': 'if not defined ES_PATH_CONF set ES_PATH_CONF=!ES_HOME!\\config', 'def': 'if [ -z "$ES_PATH_CONF" ]; then ES_PATH_CONF="$ES_HOME"/config; fi', ], 'path.logs': [ diff --git a/distribution/src/bin/elasticsearch-cli.bat b/distribution/src/bin/elasticsearch-cli.bat index 2fdbb265a3df3..25c5cf4741f79 100644 --- a/distribution/src/bin/elasticsearch-cli.bat +++ b/distribution/src/bin/elasticsearch-cli.bat @@ -15,6 +15,13 @@ for %%I in ("%ES_HOME%..") do set ES_HOME=%%~dpfI cd /d "%ES_HOME%" +@source.path.env@ + +if not exist %ES_PATH_CONF% ( + echo "ES_PATH_CONF must be set to the configuration path" + exit /b 1 +) + rem now set the path to java, pass "nojava" arg to skip setting ES_JAVA_HOME and JAVA if "%1" == "nojava" ( exit /b From 3571c3524162f8097e9c58cdff856fbe30376716 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 5 Apr 2022 09:07:21 -0700 Subject: [PATCH 022/166] create server cli provider, move launcher stuff to it --- distribution/build.gradle | 6 +- distribution/src/bin/elasticsearch | 20 +- distribution/src/bin/elasticsearch-cli | 5 +- distribution/src/bin/elasticsearch-old | 133 ++++++++++++ .../tools/launchers/TempDirectory.java | 48 ---- distribution/tools/server-cli/build.gradle | 6 + .../server/cli}/BootstrapJvmOptions.java | 2 +- .../server/cli}/DefaultSystemMemoryInfo.java | 4 +- .../server/cli}/JvmErgonomics.java | 6 +- .../elasticsearch/server/cli}/JvmOption.java | 17 +- .../server/cli}/JvmOptionsParser.java | 49 ++--- .../server/cli}/MachineDependentHeap.java | 25 ++- .../cli}/OverridableSystemMemoryInfo.java | 2 +- .../elasticsearch/server/cli/ServerCli.java | 205 ++++++++++++++++++ .../server/cli/ServerCliProvider.java | 24 ++ .../server/cli}/SystemJvmOptions.java | 2 +- .../server/cli}/SystemMemoryInfo.java | 2 +- .../server/cli/TempDirectory.java | 66 ++++++ .../org.elasticsearch.cli.ToolProvider | 1 + .../bootstrap/Elasticsearch.java | 84 +------ .../common/cli/KeyStoreAwareCommand.java | 2 +- .../common/settings/KeyStoreWrapper.java | 2 + settings.gradle | 2 +- 23 files changed, 511 insertions(+), 202 deletions(-) create mode 100755 distribution/src/bin/elasticsearch-old delete mode 100644 distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/TempDirectory.java create mode 100644 distribution/tools/server-cli/build.gradle rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/BootstrapJvmOptions.java (98%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/DefaultSystemMemoryInfo.java (94%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/JvmErgonomics.java (97%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/JvmOption.java (91%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/JvmOptionsParser.java (90%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/MachineDependentHeap.java (92%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/OverridableSystemMemoryInfo.java (98%) create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCliProvider.java rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/SystemJvmOptions.java (98%) rename distribution/tools/{launchers/src/main/java/org/elasticsearch/tools/launchers => server-cli/src/main/java/org/elasticsearch/server/cli}/SystemMemoryInfo.java (96%) create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java create mode 100644 distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/distribution/build.gradle b/distribution/build.gradle index c4a0a52e098da..ec5e8e07310d2 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -230,7 +230,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { * Properties to expand when copying packaging files * *****************************************************************************/ configurations { - ['libs', 'libsLauncher', 'libsLaunchers', 'libsVersionChecker', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { + ['libs', 'libsLauncher', 'libsLaunchers', 'libsVersionChecker', 'libsServerCli', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { create(it) { canBeConsumed = false canBeResolved = true @@ -250,6 +250,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { dependencies { libs project(':server') + libsServerCli project(':distribution:tools:server-cli') libsLauncher project(':distribution:tools:launcher') libsVersionChecker project(':distribution:tools:java-version-checker') libsLaunchers project(':distribution:tools:launchers') @@ -278,6 +279,9 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { into('launchers') { from(configurations.libsLaunchers) } + into('tools/server-cli') { + from(configurations.libsServerCli) + } into('tools/geoip-cli') { from(configurations.libsGeoIpCli) } diff --git a/distribution/src/bin/elasticsearch b/distribution/src/bin/elasticsearch index 06279f90f5271..eef223be61ea7 100755 --- a/distribution/src/bin/elasticsearch +++ b/distribution/src/bin/elasticsearch @@ -1,19 +1,13 @@ #!/bin/bash -# CONTROLLING STARTUP: -# -# This script relies on a few environment variables to determine startup -# behavior, those variables are: -# -# ES_PATH_CONF -- Path to config directory -# ES_JAVA_OPTS -- External Java Opts on top of the defaults set -# -# Optionally, exact memory values can be set using the `ES_JAVA_OPTS`. Example -# values are "512m", and "10g". -# -# ES_JAVA_OPTS="-Xms8g -Xmx8g" ./bin/elasticsearch +LAUNCHER_TOOLNAME=server +LAUNCHER_LIBS=lib/tools/server-cli +source "`dirname "$0"`"/elasticsearch-cli -source "`dirname "$0"`"/elasticsearch-env +# NOT REACHABLE, TODO remove +exit 1 + + # END OF NEW SCRIPT CHECK_KEYSTORE=true ATTEMPT_SECURITY_AUTO_CONFIG=true diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index 29f0af7328e44..60a777a255313 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -23,8 +23,9 @@ fi if [[ -z "$LAUNCHER_TOOLNAME" ]]; then SCRIPTNAME=$(basename "$0") # temporary hack to force these into the launcher, but do we want to pollute env? - export LAUNCHER_TOOLNAME=${SCRIPTNAME#"elasticsearch-"} + LAUNCHER_TOOLNAME=${SCRIPTNAME#"elasticsearch-"} fi +export LAUNCHER_TOOLNAME export LAUNCHER_LIBS # TODO: move this to launcher @@ -99,7 +100,7 @@ fi exec \ "$JAVA" \ $LAUNCHER_JAVA_OPTS \ - -Dcli.toolname="$LAUNCHER_TOOLNAME" + -Dcli.toolname="$LAUNCHER_TOOLNAME" \ -Des.path.home="$ES_HOME" \ -Des.path.conf="$ES_PATH_CONF" \ -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ diff --git a/distribution/src/bin/elasticsearch-old b/distribution/src/bin/elasticsearch-old new file mode 100755 index 0000000000000..1ba2bb43ac584 --- /dev/null +++ b/distribution/src/bin/elasticsearch-old @@ -0,0 +1,133 @@ +#!/bin/bash + + +CHECK_KEYSTORE=true +ATTEMPT_SECURITY_AUTO_CONFIG=true +DAEMONIZE=false +ENROLL_TO_CLUSTER=false +# Store original arg array as we will be shifting through it below +ARG_LIST=("$@") + +while [ $# -gt 0 ]; do + if [[ $1 == "--enrollment-token" ]]; then + if [ $ENROLL_TO_CLUSTER = true ]; then + echo "Multiple --enrollment-token parameters are not allowed" 1>&2 + exit 1 + fi + ENROLL_TO_CLUSTER=true + ATTEMPT_SECURITY_AUTO_CONFIG=false + ENROLLMENT_TOKEN="$2" + shift + elif [[ $1 == "-h" || $1 == "--help" || $1 == "-V" || $1 == "--version" ]]; then + CHECK_KEYSTORE=false + ATTEMPT_SECURITY_AUTO_CONFIG=false + elif [[ $1 == "-d" || $1 == "--daemonize" ]]; then + DAEMONIZE=true + fi + if [[ $# -gt 0 ]]; then + shift + fi +done + +if [ -z "$ES_TMPDIR" ]; then + ES_TMPDIR=`"$JAVA" "$XSHARE" -cp "$LAUNCHERS_CLASSPATH" org.elasticsearch.tools.launchers.TempDirectory` +fi + +if [ -z "$LIBFFI_TMPDIR" ]; then + LIBFFI_TMPDIR="$ES_TMPDIR" + export LIBFFI_TMPDIR +fi + +# get keystore password before setting java options to avoid +# conflicting GC configurations for the keystore tools +unset KEYSTORE_PASSWORD +KEYSTORE_PASSWORD= +if [[ $CHECK_KEYSTORE = true ]] \ + && bin/elasticsearch-keystore has-passwd --silent +then + if ! read -s -r -p "Elasticsearch keystore password: " KEYSTORE_PASSWORD ; then + echo "Failed to read keystore password on console" 1>&2 + exit 1 + fi +fi + +if [[ $ENROLL_TO_CLUSTER = true ]]; then + ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ + ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ + ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ + bin/elasticsearch-cli "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD" +elif [[ $ATTEMPT_SECURITY_AUTO_CONFIG = true ]]; then + # It is possible that an auto-conf failure prevents the node from starting, but this is only the exceptional case (exit code 1). + # Most likely an auto-conf failure will leave the configuration untouched (exit codes 73, 78 and 80), optionally printing a message + # if the error is uncommon or unexpected, but it should otherwise let the node to start as usual. + # It is passed in all the command line options in order to read the node settings ones (-E), while the other parameters are ignored + # (a small caveat is that it also inspects the -v option in order to provide more information on how auto config went) + if ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ + ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ + ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ + bin/elasticsearch-cli "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD"; then + : + else + retval=$? + # these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual + # eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it + if [[ $retval -ne 80 ]] && [[ $retval -ne 73 ]] && [[ $retval -ne 78 ]]; then + exit $retval + fi + fi +fi + +# The JVM options parser produces the final JVM options to start Elasticsearch. +# It does this by incorporating JVM options in the following way: +# - first, system JVM options are applied (these are hardcoded options in the +# parser) +# - second, JVM options are read from jvm.options and jvm.options.d/*.options +# - third, JVM options from ES_JAVA_OPTS are applied +# - fourth, ergonomic JVM options are applied +ES_JAVA_OPTS=`export ES_TMPDIR; "$JAVA" "$XSHARE" -cp "$LAUNCHERS_CLASSPATH" org.elasticsearch.tools.launchers.JvmOptionsParser "$ES_PATH_CONF" "$ES_HOME/plugins"` + +# Remove enrollment related parameters before passing the arg list to Elasticsearch +for i in "${!ARG_LIST[@]}"; do + if [[ ${ARG_LIST[i]} = "--enrollment-token" || ${ARG_LIST[i]} = "$ENROLLMENT_TOKEN" ]]; then + unset 'ARG_LIST[i]' + fi +done + +# manual parsing to find out, if process should be detached +if [[ $DAEMONIZE = false ]]; then + exec \ + "$JAVA" \ + "$XSHARE" \ + $ES_JAVA_OPTS \ + -Des.path.home="$ES_HOME" \ + -Des.path.conf="$ES_PATH_CONF" \ + -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ + -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ + -Des.bundled_jdk="$ES_BUNDLED_JDK" \ + -cp "$ES_CLASSPATH" \ + org.elasticsearch.bootstrap.Elasticsearch \ + "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD" +else + exec \ + "$JAVA" \ + "$XSHARE" \ + $ES_JAVA_OPTS \ + -Des.path.home="$ES_HOME" \ + -Des.path.conf="$ES_PATH_CONF" \ + -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ + -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ + -Des.bundled_jdk="$ES_BUNDLED_JDK" \ + -cp "$ES_CLASSPATH" \ + org.elasticsearch.bootstrap.Elasticsearch \ + "${ARG_LIST[@]}" \ + <<<"$KEYSTORE_PASSWORD" & + retval=$? + pid=$! + [ $retval -eq 0 ] || exit $retval + if ! ps -p $pid > /dev/null ; then + exit 1 + fi + exit 0 +fi + +exit $? diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/TempDirectory.java b/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/TempDirectory.java deleted file mode 100644 index bfdb28ec78967..0000000000000 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/TempDirectory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.tools.launchers; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; - -/** - * Provides a path for a temporary directory. On non-Windows OS, this will be created as a sub-directory of the default temporary directory. - * Note that this causes the created temporary directory to be a private temporary directory. - */ -final class TempDirectory { - - /** - * The main entry point. The exit code is 0 if we successfully created a temporary directory as a sub-directory of the default - * temporary directory and printed the resulting path to the console. - * - * @param args the args to the program which should be empty - * @throws IOException if an I/O exception occurred while creating the temporary directory - */ - public static void main(final String[] args) throws IOException { - if (args.length != 0) { - throw new IllegalArgumentException("expected zero arguments but was " + Arrays.toString(args)); - } - /* - * On Windows, we avoid creating a unique temporary directory per invocation lest we pollute the temporary directory. On other - * operating systems, temporary directories will be cleaned automatically via various mechanisms (e.g., systemd, or restarts). - */ - final Path path; - if (System.getProperty("os.name").startsWith("Windows")) { - path = Paths.get(System.getProperty("java.io.tmpdir"), "elasticsearch"); - Files.createDirectories(path); - } else { - path = Launchers.createTempDirectory("elasticsearch-"); - } - Launchers.outPrintln(path.toString()); - } - -} diff --git a/distribution/tools/server-cli/build.gradle b/distribution/tools/server-cli/build.gradle new file mode 100644 index 0000000000000..3b1ace28c398f --- /dev/null +++ b/distribution/tools/server-cli/build.gradle @@ -0,0 +1,6 @@ +apply plugin: 'elasticsearch.build' + +dependencies { + compileOnly project(":server") + compileOnly project(":libs:elasticsearch-cli") +} diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/BootstrapJvmOptions.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/BootstrapJvmOptions.java similarity index 98% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/BootstrapJvmOptions.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/BootstrapJvmOptions.java index 3bccc5f769998..04d922ee3e624 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/BootstrapJvmOptions.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/BootstrapJvmOptions.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import java.io.IOException; import java.io.InputStream; diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/DefaultSystemMemoryInfo.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/DefaultSystemMemoryInfo.java similarity index 94% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/DefaultSystemMemoryInfo.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/DefaultSystemMemoryInfo.java index 27572e6db6c10..433e8d8f2ed9a 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/DefaultSystemMemoryInfo.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/DefaultSystemMemoryInfo.java @@ -6,10 +6,12 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import com.sun.management.OperatingSystemMXBean; +import org.elasticsearch.core.SuppressForbidden; + import java.lang.management.ManagementFactory; /** diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmErgonomics.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmErgonomics.java similarity index 97% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmErgonomics.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmErgonomics.java index 9d041949b4630..2fbebfdf8a259 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmErgonomics.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmErgonomics.java @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; + +import org.elasticsearch.cli.UserException; import java.io.IOException; import java.util.ArrayList; @@ -31,7 +33,7 @@ private JvmErgonomics() { * @param userDefinedJvmOptions A list of JVM options that have been defined by the user. * @return A list of additional JVM options to set. */ - static List choose(final List userDefinedJvmOptions) throws InterruptedException, IOException { + static List choose(final List userDefinedJvmOptions) throws UserException, IOException { final List ergonomicChoices = new ArrayList<>(); final Map finalJvmOptions = JvmOption.findFinalOptions(userDefinedJvmOptions); final long heapSize = JvmOption.extractMaxHeapSize(finalJvmOptions); diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOption.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOption.java similarity index 91% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOption.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOption.java index 5c62fbcd02626..c3fe9fe3d384c 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOption.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOption.java @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; + +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.UserException; import java.io.BufferedReader; import java.io.IOException; @@ -74,15 +77,14 @@ public static long extractMaxDirectMemorySize(final Map final /** * Determine the options present when invoking a JVM with the given user defined options. */ - public static Map findFinalOptions(final List userDefinedJvmOptions) throws InterruptedException, - IOException { + public static Map findFinalOptions(final List userDefinedJvmOptions) throws UserException, IOException { return flagsFinal(userDefinedJvmOptions).stream() .map(OPTION::matcher) .filter(Matcher::matches) .collect(Collectors.toUnmodifiableMap(m -> m.group("flag"), m -> new JvmOption(m.group("value"), m.group("origin")))); } - private static List flagsFinal(final List userDefinedJvmOptions) throws InterruptedException, IOException { + private static List flagsFinal(final List userDefinedJvmOptions) throws UserException, IOException { /* * To deduce the final set of JVM options that Elasticsearch is going to start with, we start a separate Java process with the JVM * options that we would pass on the command line. For this Java process we will add two additional flags, -XX:+PrintFlagsFinal and @@ -102,7 +104,12 @@ private static List flagsFinal(final List userDefinedJvmOptions) final Process process = new ProcessBuilder().command(command).start(); final List output = readLinesFromInputStream(process.getInputStream()); final List error = readLinesFromInputStream(process.getErrorStream()); - final int status = process.waitFor(); + final int status; + try { + status = process.waitFor(); + } catch (InterruptedException e) { + throw new UserException(ExitCodes.CODE_ERROR, "Interrupted while waiting for jvm options determiner"); + } if (status != 0) { final String message = String.format( Locale.ROOT, diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java similarity index 90% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOptionsParser.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index f4ce284a1bea8..44c223bc9aba5 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -6,13 +6,17 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; + +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.UserException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -59,36 +63,22 @@ SortedMap invalidLines() { } - /** - * The main entry point. The exit code is 0 if the JVM options were successfully parsed, otherwise the exit code is 1. If an improperly - * formatted line is discovered, the line is output to standard error. - * - * @param args the args to the program which should consist of a single option, the path to ES_PATH_CONF - */ - public static void main(final String[] args) throws InterruptedException, IOException { - if (args.length != 2) { - throw new IllegalArgumentException( - "Expected two arguments specifying path to ES_PATH_CONF and plugins directory, but was " + Arrays.toString(args) - ); - } - + static List determine(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws UserException { final JvmOptionsParser parser = new JvmOptionsParser(); final Map substitutions = new HashMap<>(); - substitutions.put("ES_TMPDIR", System.getenv("ES_TMPDIR")); - final String environmentPathConf = System.getenv("ES_PATH_CONF"); - if (environmentPathConf != null) { - substitutions.put("ES_PATH_CONF", environmentPathConf); - } + substitutions.put("ES_TMPDIR", tmpDir.toString()); + substitutions.put("ES_PATH_CONF", configDir.toString()); try { - final List jvmOptions = parser.jvmOptions( - Paths.get(args[0]), - Paths.get(args[1]), - System.getenv("ES_JAVA_OPTS"), + return parser.jvmOptions( + configDir, + pluginsDir, + envOptions, substitutions ); - Launchers.outPrintln(String.join(" ", jvmOptions)); + } catch (IOException e) { + throw new UncheckedIOException(e); } catch (final JvmOptionsFileParserException e) { final String errorMessage = String.format( Locale.ROOT, @@ -97,7 +87,7 @@ public static void main(final String[] args) throws InterruptedException, IOExce e.invalidLines().size() == 1 ? "" : "s", e.jvmOptionsFile() ); - Launchers.errPrintln(errorMessage); + StringBuilder msg = new StringBuilder(errorMessage); int count = 0; for (final Map.Entry entry : e.invalidLines().entrySet()) { count++; @@ -109,16 +99,15 @@ public static void main(final String[] args) throws InterruptedException, IOExce entry.getKey(), entry.getValue() ); - Launchers.errPrintln(message); + msg.append(System.lineSeparator()); + msg.append(message); } - Launchers.exit(1); + throw new UserException(ExitCodes.CONFIG, msg.toString()); } - - Launchers.exit(0); } private List jvmOptions(final Path config, Path plugins, final String esJavaOpts, final Map substitutions) - throws InterruptedException, IOException, JvmOptionsFileParserException { + throws UserException, IOException, JvmOptionsFileParserException { final List jvmOptions = readJvmOptionsFiles(config); diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/MachineDependentHeap.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/MachineDependentHeap.java similarity index 92% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/MachineDependentHeap.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/MachineDependentHeap.java index df025de60fb50..d74c08db92bd4 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/MachineDependentHeap.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/MachineDependentHeap.java @@ -6,11 +6,11 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; -import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.SafeConstructor; -import org.yaml.snakeyaml.error.YAMLException; +import org.elasticsearch.cli.UserException; +import org.elasticsearch.xcontent.XContentParserConfiguration; +import org.elasticsearch.xcontent.yaml.YamlXContent; import java.io.IOException; import java.io.InputStream; @@ -27,9 +27,9 @@ import static java.lang.Math.max; import static java.lang.Math.min; -import static org.elasticsearch.tools.launchers.JvmOption.isInitialHeapSpecified; -import static org.elasticsearch.tools.launchers.JvmOption.isMaxHeapSpecified; -import static org.elasticsearch.tools.launchers.JvmOption.isMinHeapSpecified; +import static org.elasticsearch.server.cli.JvmOption.isInitialHeapSpecified; +import static org.elasticsearch.server.cli.JvmOption.isMaxHeapSpecified; +import static org.elasticsearch.server.cli.JvmOption.isMinHeapSpecified; /** * Determines optimal default heap settings based on available system memory and assigned node roles. @@ -56,7 +56,7 @@ public MachineDependentHeap(SystemMemoryInfo systemMemoryInfo) { * @return final heap options, or an empty collection if user provided heap options are to be used * @throws IOException if unable to load elasticsearch.yml */ - public List determineHeapSettings(Path configDir, List userDefinedJvmOptions) throws IOException, InterruptedException { + public List determineHeapSettings(Path configDir, List userDefinedJvmOptions) throws UserException, IOException { // TODO: this could be more efficient, to only parse final options once final Map finalJvmOptions = JvmOption.findFinalOptions(userDefinedJvmOptions); if (isMaxHeapSpecified(finalJvmOptions) || isMinHeapSpecified(finalJvmOptions) || isInitialHeapSpecified(finalJvmOptions)) { @@ -102,16 +102,17 @@ static class NodeRoleParser { @SuppressWarnings("unchecked") public static MachineNodeRole parse(InputStream config) { - Yaml yaml = new Yaml(new SafeConstructor()); + Map root; try { - root = yaml.load(config); - } catch (YAMLException | ClassCastException ex) { + var parser = YamlXContent.yamlXContent.createParser(XContentParserConfiguration.EMPTY, config); + root = parser.map(); + } catch (IOException | ClassCastException ex) { // Strangely formatted config, so just return defaults and let startup settings validation catch the problem return MachineNodeRole.UNKNOWN; } - if (root != null) { + if (root.isEmpty() == false) { Map map = flatten(root, null); if (hasLegacySettings(map.keySet())) { diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/OverridableSystemMemoryInfo.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/OverridableSystemMemoryInfo.java similarity index 98% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/OverridableSystemMemoryInfo.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/OverridableSystemMemoryInfo.java index 118c68b2111b6..22a41802d1dc9 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/OverridableSystemMemoryInfo.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/OverridableSystemMemoryInfo.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import java.util.List; import java.util.Objects; diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java new file mode 100644 index 0000000000000..b0f33092a0682 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -0,0 +1,205 @@ +/* + * 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.server.cli; + +import joptsimple.OptionSet; + +import joptsimple.OptionSpec; +import joptsimple.OptionSpecBuilder; +import joptsimple.util.PathConverter; + +import org.elasticsearch.Build; +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; +import org.elasticsearch.common.cli.EnvironmentAwareCommand; +import org.elasticsearch.common.settings.KeyStoreWrapper; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.core.PathUtils; +import org.elasticsearch.env.Environment; +import org.elasticsearch.monitor.jvm.JvmInfo; +import org.elasticsearch.node.NodeValidationException; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +class ServerCli extends EnvironmentAwareCommand { + private final OptionSpecBuilder versionOption; + private final OptionSpecBuilder daemonizeOption; + private final OptionSpec pidfileOption; + private final OptionSpecBuilder quietOption; + private final OptionSpec enrollmentTokenOption; + + // visible for testing + ServerCli() { + super("Starts Elasticsearch", () -> {}); // we configure logging later so we override the base class from configuring logging + versionOption = parser.acceptsAll(Arrays.asList("V", "version"), "Prints Elasticsearch version information and exits"); + daemonizeOption = parser.acceptsAll(Arrays.asList("d", "daemonize"), "Starts Elasticsearch in the background") + .availableUnless(versionOption); + pidfileOption = parser.acceptsAll(Arrays.asList("p", "pidfile"), "Creates a pid file in the specified path on start") + .availableUnless(versionOption) + .withRequiredArg() + .withValuesConvertedBy(new PathConverter()); + quietOption = parser.acceptsAll(Arrays.asList("q", "quiet"), "Turns off standard output/error streams logging in console") + .availableUnless(versionOption) + .availableUnless(daemonizeOption); + enrollmentTokenOption = parser.accepts("enrollment-token", "An existing enrollment token for securely joining a cluster") + .availableUnless(versionOption) + .withRequiredArg(); + } + + /** + * Prints a message directing the user to look at the logs. A message is only printed if + * logging has been configured. + */ + static void printLogsSuggestion() { + final String basePath = System.getProperty("es.logs.base_path"); + // It's possible to fail before logging has been configured, in which case there's no point + // suggesting that the user look in the log file. + if (basePath != null) { + Terminal.DEFAULT.errorPrintln( + "ERROR: Elasticsearch did not exit normally - check the logs at " + + basePath + + System.getProperty("file.separator") + + System.getProperty("es.logs.cluster_name") + + ".log" + ); + } + } + + @Override + protected void execute(Terminal terminal, OptionSet options, Environment env) throws UserException { + if (options.nonOptionArguments().isEmpty() == false) { + throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments()); + } + if (options.has(versionOption)) { + final String versionOutput = String.format( + Locale.ROOT, + "Version: %s, Build: %s/%s/%s/%s, JVM: %s", + Build.CURRENT.qualifiedVersion(), + Build.CURRENT.flavor().displayName(), + Build.CURRENT.type().displayName(), + Build.CURRENT.hash(), + Build.CURRENT.date(), + JvmInfo.jvmInfo().version() + ); + terminal.println(versionOutput); + return; + } + + final boolean daemonize = options.has(daemonizeOption); + final Path pidFile = pidfileOption.value(options); + final boolean quiet = options.has(quietOption); + + Map envVars = new HashMap<>(System.getenv()); + Path tempDir = TempDirectory.initialize(envVars); + SecureString keystorePassword = null; + try { + KeyStoreWrapper keystore = KeyStoreWrapper.load(KeyStoreWrapper.keystorePath(env.configFile())); + if (keystore != null && keystore.hasPassword()) { + keystorePassword = new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT, KeyStoreWrapper.MAX_PASSPHRASE_LENGTH)); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + + if (options.has(enrollmentTokenOption)) { + final String enrollmentToken = enrollmentTokenOption.value(options); + + } + /* + if enrollment-token: auto configure node + else: attempt auto configure, exit on codes not in [80, 73, 78] + */ + + List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); + jvmOptions.add("-Des.path.conf=" + env.configFile()); + jvmOptions.add("-Des.distribution.flavor=" + System.getProperty("es.distribution.flavor")); + jvmOptions.add("-Des.distribution.type=" + System.getProperty("es.distribution.type")); + jvmOptions.add("-Des.bundled_jdk=" + System.getProperty("es.bundled_jdk")); + + /* + create process + - set command + 1. figure out java path + 2. add jvm options + 3. add classpath + 4. add main class + 5. add args + - set env + - set redirects + + launch java process + if (daemon) exit (TODO: wait until server is started) + wait on process + + */ + String esHome = System.getProperty("es.path.home"); + Path javaHome = PathUtils.get(System.getProperty("java.home")); + List command = new ArrayList<>(); + // TODO: fix this so it works on windows + command.add(javaHome.resolve("bin").resolve("java").toString()); + command.addAll(jvmOptions); + command.add("-cp"); + // TODO: fix this to work on windows + command.add(esHome + "/lib/*"); + command.add("org.elasticsearch.bootstrap.Elasticsearch"); + System.out.println("command: " + command); + + ProcessBuilder builder = new ProcessBuilder(command); + builder.environment().putAll(envVars); + builder.inheritIO(); + try { + Process process = builder.start(); + // TODO: handle daemonize flag + int code = process.waitFor(); + System.out.println("exited: " + code); + System.exit(code); + } catch (IOException e) { + throw new UncheckedIOException(e); + } catch (InterruptedException e) { + throw new UserException(ExitCodes.IO_ERROR, "Interrupted while waiting for Elasticsearch process"); + } + + // TODO: check the java.io.tmpdir + // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately + /*try { + env.validateTmpFile(); + } catch (IOException e) { + throw new UserException(ExitCodes.CONFIG, e.getMessage()); + } + + try { + init(daemonize, pidFile, quiet, env); + } catch (NodeValidationException e) { + throw new UserException(ExitCodes.CONFIG, e.getMessage()); + }*/ + } + + /** + * Required method that's called by Apache Commons procrun when + * running as a service on Windows, when the service is stopped. + * + * http://commons.apache.org/proper/commons-daemon/procrun.html + * + * NOTE: If this method is renamed and/or moved, make sure to + * update elasticsearch-service.bat! + */ + static void close(String[] args) throws IOException { + //Bootstrap.stop(); + } +} diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCliProvider.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCliProvider.java new file mode 100644 index 0000000000000..fc41a65cb7cf2 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCliProvider.java @@ -0,0 +1,24 @@ +/* + * 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.server.cli; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +public class ServerCliProvider implements ToolProvider { + @Override + public String name() { + return "server"; + } + + @Override + public Command create() { + return new ServerCli(); + } +} diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemJvmOptions.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java similarity index 98% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemJvmOptions.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java index 13f31f2c3793c..47a3781a44bca 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemJvmOptions.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import java.util.List; import java.util.stream.Collectors; diff --git a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemMemoryInfo.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemMemoryInfo.java similarity index 96% rename from distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemMemoryInfo.java rename to distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemMemoryInfo.java index 215204997893b..c0e7d3e8eb637 100644 --- a/distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/SystemMemoryInfo.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemMemoryInfo.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; /** * Determines available system memory that could be allocated for Elasticsearch, to include JVM heap and other native processes. diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java new file mode 100644 index 0000000000000..428bd0a073166 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java @@ -0,0 +1,66 @@ +/* + * 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.server.cli; + +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.UserException; +import org.elasticsearch.core.SuppressForbidden; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileAttribute; +import java.util.Arrays; +import java.util.Map; + +/** + * Provides a path for a temporary directory. On non-Windows OS, this will be created as a sub-directory of the default temporary directory. + * Note that this causes the created temporary directory to be a private temporary directory. + */ +final class TempDirectory { + + /** + * Ensures the environment map has ES_TMPDIR and LIBFFI_TMPDIR. + */ + public static Path initialize(Map env) throws UserException { + final Path path; + String existingTempDir = env.remove("ES_TMPDIR"); + if (existingTempDir != null) { + path = Paths.get(existingTempDir); + } else { + try { + if (System.getProperty("os.name").startsWith("Windows")) { + /* + * On Windows, we avoid creating a unique temporary directory per invocation lest we pollute the temporary directory. On other + * operating systems, temporary directories will be cleaned automatically via various mechanisms (e.g., systemd, or restarts). + */ + path = Paths.get(System.getProperty("java.io.tmpdir"), "elasticsearch"); + Files.createDirectories(path); + } else { + path = createTempDirectory("elasticsearch-"); + } + } catch (IOException e) { + // TODO: don't mask this exception, should we just propagate or try to summarize? in shell-land we would have printed it and exited + throw new UserException(ExitCodes.CONFIG, "Could not create temporary directory"); + } + } + + if (env.containsKey("LIBFFI_TMPDIR") == false) { + env.put("LIBFFI_TMPDIR", path.toString()); + } + return path; + } + + @SuppressForbidden(reason = "Files#createTempDirectory(String, FileAttribute...)") + private static Path createTempDirectory(final String prefix, final FileAttribute... attrs) throws IOException { + return Files.createTempDirectory(prefix, attrs); + } + +} diff --git a/distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider new file mode 100644 index 0000000000000..7a07f0225080a --- /dev/null +++ b/distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -0,0 +1 @@ +org.elasticsearch.server.cli.ServerCliProvider diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 03dd3fc1aa019..4426e51ce37d4 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -33,27 +33,7 @@ /** * This class starts elasticsearch. */ -class Elasticsearch extends EnvironmentAwareCommand { - - private final OptionSpecBuilder versionOption; - private final OptionSpecBuilder daemonizeOption; - private final OptionSpec pidfileOption; - private final OptionSpecBuilder quietOption; - - // visible for testing - Elasticsearch() { - super("Starts Elasticsearch", () -> {}); // we configure logging later so we override the base class from configuring logging - versionOption = parser.acceptsAll(Arrays.asList("V", "version"), "Prints Elasticsearch version information and exits"); - daemonizeOption = parser.acceptsAll(Arrays.asList("d", "daemonize"), "Starts Elasticsearch in the background") - .availableUnless(versionOption); - pidfileOption = parser.acceptsAll(Arrays.asList("p", "pidfile"), "Creates a pid file in the specified path on start") - .availableUnless(versionOption) - .withRequiredArg() - .withValuesConvertedBy(new PathConverter()); - quietOption = parser.acceptsAll(Arrays.asList("q", "quiet"), "Turns off standard output/error streams logging in console") - .availableUnless(versionOption) - .availableUnless(daemonizeOption); - } +class Elasticsearch { /** * Main entry point for starting elasticsearch @@ -77,11 +57,7 @@ public void checkPermission(Permission perm) { }); LogConfigurator.registerErrorListener(); final Elasticsearch elasticsearch = new Elasticsearch(); - int status = main(args, elasticsearch, Terminal.DEFAULT); - if (status != ExitCodes.OK) { - printLogsSuggestion(); - exit(status); - } + System.out.println("RUNNING ELASTICSEARCH"); } /** @@ -118,48 +94,6 @@ private static void overrideDnsCachePolicyProperties() { } } - static int main(final String[] args, final Elasticsearch elasticsearch, final Terminal terminal) throws Exception { - return elasticsearch.main(args, terminal); - } - - @Override - protected void execute(Terminal terminal, OptionSet options, Environment env) throws UserException { - if (options.nonOptionArguments().isEmpty() == false) { - throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments()); - } - if (options.has(versionOption)) { - final String versionOutput = String.format( - Locale.ROOT, - "Version: %s, Build: %s/%s/%s/%s, JVM: %s", - Build.CURRENT.qualifiedVersion(), - Build.CURRENT.flavor().displayName(), - Build.CURRENT.type().displayName(), - Build.CURRENT.hash(), - Build.CURRENT.date(), - JvmInfo.jvmInfo().version() - ); - terminal.println(versionOutput); - return; - } - - final boolean daemonize = options.has(daemonizeOption); - final Path pidFile = pidfileOption.value(options); - final boolean quiet = options.has(quietOption); - - // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately - try { - env.validateTmpFile(); - } catch (IOException e) { - throw new UserException(ExitCodes.CONFIG, e.getMessage()); - } - - try { - init(daemonize, pidFile, quiet, env); - } catch (NodeValidationException e) { - throw new UserException(ExitCodes.CONFIG, e.getMessage()); - } - } - void init(final boolean daemonize, final Path pidFile, final boolean quiet, Environment initialEnv) throws NodeValidationException, UserException { try { @@ -170,18 +104,4 @@ void init(final boolean daemonize, final Path pidFile, final boolean quiet, Envi throw new StartupException(e); } } - - /** - * Required method that's called by Apache Commons procrun when - * running as a service on Windows, when the service is stopped. - * - * http://commons.apache.org/proper/commons-daemon/procrun.html - * - * NOTE: If this method is renamed and/or moved, make sure to - * update elasticsearch-service.bat! - */ - static void close(String[] args) throws IOException { - Bootstrap.stop(); - } - } diff --git a/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java index 3067d477d9cb0..b43aceae14062 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java @@ -52,7 +52,7 @@ protected static SecureString readPassword(Terminal terminal, boolean withVerifi } Arrays.fill(passwordVerification, '\u0000'); } else { - passwordArray = terminal.readSecret("Enter password for the elasticsearch keystore : "); + passwordArray = terminal.readSecret(KeyStoreWrapper.PROMPT); } return new SecureString(passwordArray); } diff --git a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java index 083ca1e9aa8d6..c3de8ae47f2df 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java +++ b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java @@ -78,6 +78,8 @@ public class KeyStoreWrapper implements SecureSettings { /** Arbitrarily chosen maximum passphrase length */ public static final int MAX_PASSPHRASE_LENGTH = 128; + public static final String PROMPT = "Enter password for the elasticsearch keystore : "; + /** An identifier for the type of data that may be stored in a keystore entry. */ private enum EntryType { STRING, diff --git a/settings.gradle b/settings.gradle index fcc984b34f50b..a38c598df5af6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -53,13 +53,13 @@ List projects = [ 'distribution:bwc:minor', 'distribution:bwc:staged', 'distribution:tools:java-version-checker', - 'distribution:tools:main', 'distribution:tools:launcher', 'distribution:tools:launchers', 'distribution:tools:plugin-cli', 'distribution:tools:keystore-cli', 'distribution:tools:geoip-cli', 'distribution:tools:ansi-console', + 'distribution:tools:server-cli', 'server', 'server:cli', 'test:framework', From fa15e1e3f44bd1ca6e13fb122aab04492ec17d9f Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 15:22:53 -0400 Subject: [PATCH 023/166] Use elasticsearch-env.bat in elasticsearch-cli.bat and in sql cli --- distribution/src/bin/elasticsearch-cli.bat | 51 +------------------ distribution/src/bin/elasticsearch-env.bat | 23 +-------- .../src/main/bin/elasticsearch-sql-cli.bat | 4 +- 3 files changed, 3 insertions(+), 75 deletions(-) diff --git a/distribution/src/bin/elasticsearch-cli.bat b/distribution/src/bin/elasticsearch-cli.bat index 25c5cf4741f79..d24570dad3e73 100644 --- a/distribution/src/bin/elasticsearch-cli.bat +++ b/distribution/src/bin/elasticsearch-cli.bat @@ -1,19 +1,4 @@ -set SCRIPT=%0 - -rem determine Elasticsearch home; to do this, we strip from the path until we -rem find bin, and then strip bin (there is an assumption here that there is no -rem nested directory under bin also named bin) -for %%I in (%SCRIPT%) do set ES_HOME=%%~dpI - -:es_home_loop -for %%I in ("%ES_HOME:~1,-1%") do set DIRNAME=%%~nxI -if not "%DIRNAME%" == "bin" ( - for %%I in ("%ES_HOME%..") do set ES_HOME=%%~dpfI - goto es_home_loop -) -for %%I in ("%ES_HOME%..") do set ES_HOME=%%~dpfI - -cd /d "%ES_HOME%" +call "%~dp0elasticsearch-env.bat" || exit /b 1 @source.path.env@ @@ -22,40 +7,6 @@ if not exist %ES_PATH_CONF% ( exit /b 1 ) -rem now set the path to java, pass "nojava" arg to skip setting ES_JAVA_HOME and JAVA -if "%1" == "nojava" ( - exit /b -) - -rem comparing to empty string makes this equivalent to bash -v check on env var -rem and allows to effectively force use of the bundled jdk when launching ES -rem by setting ES_JAVA_HOME= -if defined ES_JAVA_HOME ( - set JAVA="%ES_JAVA_HOME%\bin\java.exe" - set JAVA_TYPE=ES_JAVA_HOME -) else ( - rem use the bundled JDK (default) - set JAVA="%ES_HOME%\jdk\bin\java.exe" - set "ES_JAVA_HOME=%ES_HOME%\jdk" - set JAVA_TYPE=bundled JDK -) - -if not exist !JAVA! ( - echo "could not find java in !JAVA_TYPE! at !JAVA!" >&2 - exit /b 1 -) - -rem do not let JAVA_TOOL_OPTIONS slip in (as the JVM does by default) -if defined JAVA_TOOL_OPTIONS ( - echo warning: ignoring JAVA_TOOL_OPTIONS=%JAVA_TOOL_OPTIONS% - set JAVA_TOOL_OPTIONS= -) - -rem warn that we are not observing the value of $JAVA_HOME -if defined JAVA_HOME ( - echo warning: ignoring JAVA_HOME=%JAVA_HOME%; using %JAVA_TYPE% >&2 -) - set ES_DISTRIBUTION_FLAVOR=@es.distribution.flavor@ set ES_DISTRIBUTION_TYPE=@es.distribution.type@ set ES_BUNDLED_JDK=@es.bundled_jdk@ diff --git a/distribution/src/bin/elasticsearch-env.bat b/distribution/src/bin/elasticsearch-env.bat index 1c6ac97b78c05..993708af0afd7 100644 --- a/distribution/src/bin/elasticsearch-env.bat +++ b/distribution/src/bin/elasticsearch-env.bat @@ -17,25 +17,6 @@ rem now set the classpath set ES_CLASSPATH=!ES_HOME!\lib\* set LAUNCHERS_CLASSPATH=!ES_CLASSPATH!;!ES_HOME!\lib\launchers\* -set HOSTNAME=%COMPUTERNAME% - -if not defined ES_PATH_CONF ( - set ES_PATH_CONF=!ES_HOME!\config -) - -rem now make ES_PATH_CONF absolute -for %%I in ("%ES_PATH_CONF%..") do set ES_PATH_CONF=%%~dpfI - -set ES_DISTRIBUTION_FLAVOR=@es.distribution.flavor@ -set ES_DISTRIBUTION_TYPE=@es.distribution.type@ -set ES_BUNDLED_JDK=@es.bundled_jdk@ - -if "%ES_BUNDLED_JDK%" == "false" ( - echo "warning: no-jdk distributions that do not bundle a JDK are deprecated and will be removed in a future release" >&2 -) - -cd /d "%ES_HOME%" - rem now set the path to java, pass "nojava" arg to skip setting ES_JAVA_HOME and JAVA if "%1" == "nojava" ( exit /b @@ -77,6 +58,4 @@ if defined JAVA_OPTS ( echo pass JVM parameters via ES_JAVA_OPTS ) -rem check the Java version -%JAVA% -cp "%LAUNCHERS_CLASSPATH%" "org.elasticsearch.tools.java_version_checker.JavaVersionChecker" || exit /b 1 - +cd %ES_HOME% diff --git a/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli.bat b/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli.bat index 62d694b6fe46d..47c24248faa6b 100644 --- a/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli.bat +++ b/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli.bat @@ -10,9 +10,7 @@ setlocal enableextensions call "%~dp0elasticsearch-env.bat" || exit /b 1 -call "%ES_HOME%/bin/x-pack-env.bat" || exit /b 1 - -set CLI_JAR=%ES_HOME%/bin/* +for %%a in (!ES_HOME!\bin\elasticsearch-sql-cli-*.jar) do set CLI_JAR=%%a %JAVA% ^ -cp "%CLI_JAR%" ^ From 4c6f9e8048b01ae7a824dc215f3e033e4a96d97d Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 15:27:25 -0400 Subject: [PATCH 024/166] Don't hardcode tool names for windows batch --- distribution/src/bin/elasticsearch-geoip.bat | 3 ++- distribution/src/bin/elasticsearch-keystore.bat | 3 ++- distribution/src/bin/elasticsearch-node.bat | 3 ++- distribution/src/bin/elasticsearch-plugin.bat | 3 ++- distribution/src/bin/elasticsearch-shard.bat | 3 ++- x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat | 3 ++- x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat | 3 ++- .../src/main/bin/elasticsearch-create-enrollment-token.bat | 3 ++- .../security/src/main/bin/elasticsearch-reconfigure-node.bat | 3 ++- .../security/src/main/bin/elasticsearch-reset-password.bat | 3 ++- .../security/src/main/bin/elasticsearch-saml-metadata.bat | 3 ++- .../security/src/main/bin/elasticsearch-service-tokens.bat | 3 ++- .../security/src/main/bin/elasticsearch-setup-passwords.bat | 3 ++- .../plugin/security/src/main/bin/elasticsearch-syskeygen.bat | 3 ++- x-pack/plugin/security/src/main/bin/elasticsearch-users.bat | 3 ++- x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat | 3 ++- 16 files changed, 32 insertions(+), 16 deletions(-) diff --git a/distribution/src/bin/elasticsearch-geoip.bat b/distribution/src/bin/elasticsearch-geoip.bat index 05e9e18b690a9..7c17cc8da685b 100644 --- a/distribution/src/bin/elasticsearch-geoip.bat +++ b/distribution/src/bin/elasticsearch-geoip.bat @@ -3,7 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=geoip +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=lib/tools/geoip-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/distribution/src/bin/elasticsearch-keystore.bat b/distribution/src/bin/elasticsearch-keystore.bat index 4f1e2e1a5f432..6ff51d30d5796 100644 --- a/distribution/src/bin/elasticsearch-keystore.bat +++ b/distribution/src/bin/elasticsearch-keystore.bat @@ -3,7 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=keystore +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=lib/tools/keystore-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/distribution/src/bin/elasticsearch-node.bat b/distribution/src/bin/elasticsearch-node.bat index baf1ff048ae7e..d51cd0a2feac2 100644 --- a/distribution/src/bin/elasticsearch-node.bat +++ b/distribution/src/bin/elasticsearch-node.bat @@ -3,7 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=node +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/distribution/src/bin/elasticsearch-plugin.bat b/distribution/src/bin/elasticsearch-plugin.bat index 2ca77c3be60ef..cdcbd186282a6 100644 --- a/distribution/src/bin/elasticsearch-plugin.bat +++ b/distribution/src/bin/elasticsearch-plugin.bat @@ -3,7 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=plugin +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=lib/tools/plugin-cli set LAUNCHER_JAVA_OPTS=--add-opens java.base/sun.security.provider=ALL-UNNAMED %ES_JAVA_OPTS% call "%~dp0elasticsearch-cli.bat" ^ diff --git a/distribution/src/bin/elasticsearch-shard.bat b/distribution/src/bin/elasticsearch-shard.bat index dbcf28cca306e..d51cd0a2feac2 100644 --- a/distribution/src/bin/elasticsearch-shard.bat +++ b/distribution/src/bin/elasticsearch-shard.bat @@ -3,7 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=shard +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat index 5dc9fc44c7013..37b929afc9c21 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=certgen +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat index fc25acbf28742..37b929afc9c21 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=certutil +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat index 096269a2bb920..38b29b1cc728f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=create-enrollment-token +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat index 894856dc1b8cb..6355328f657f1 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=reconfigure-node +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" "--reconfigure" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat index 4d60413861e1a..38b29b1cc728f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=reset-password +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat index ec90e1a180a25..38b29b1cc728f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=saml-metadata +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat index 4aa73982c3d42..38b29b1cc728f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=service-tokens +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat index f0f2fa3199668..38b29b1cc728f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=setup-passwords +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat index 36b3907d8b131..38b29b1cc728f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=syskeygen +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat index 0bf97de09b878..38b29b1cc728f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=users +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat index f42196eb8697a..e1f9869ea1b50 100644 --- a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat +++ b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat @@ -8,7 +8,8 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=croneval +set SCRIPT_NAME=%0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-watcher call "%~dp0elasticsearch-cli.bat" ^ %%* ^ From c76c8b7ea26e7c105f0d443e4572412fb1b42154 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 15:35:02 -0400 Subject: [PATCH 025/166] Just get the filename --- distribution/src/bin/elasticsearch-geoip.bat | 2 +- distribution/src/bin/elasticsearch-keystore.bat | 2 +- distribution/src/bin/elasticsearch-node.bat | 2 +- distribution/src/bin/elasticsearch-plugin.bat | 2 +- distribution/src/bin/elasticsearch-shard.bat | 2 +- x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat | 2 +- x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat | 2 +- .../src/main/bin/elasticsearch-create-enrollment-token.bat | 2 +- .../security/src/main/bin/elasticsearch-reconfigure-node.bat | 2 +- .../security/src/main/bin/elasticsearch-reset-password.bat | 2 +- .../security/src/main/bin/elasticsearch-saml-metadata.bat | 2 +- .../security/src/main/bin/elasticsearch-service-tokens.bat | 2 +- .../security/src/main/bin/elasticsearch-setup-passwords.bat | 2 +- x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat | 2 +- x-pack/plugin/security/src/main/bin/elasticsearch-users.bat | 2 +- x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/distribution/src/bin/elasticsearch-geoip.bat b/distribution/src/bin/elasticsearch-geoip.bat index 7c17cc8da685b..7ae707b94b9b3 100644 --- a/distribution/src/bin/elasticsearch-geoip.bat +++ b/distribution/src/bin/elasticsearch-geoip.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=lib/tools/geoip-cli call "%~dp0elasticsearch-cli.bat" ^ diff --git a/distribution/src/bin/elasticsearch-keystore.bat b/distribution/src/bin/elasticsearch-keystore.bat index 6ff51d30d5796..7f0f9cdf1656f 100644 --- a/distribution/src/bin/elasticsearch-keystore.bat +++ b/distribution/src/bin/elasticsearch-keystore.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=lib/tools/keystore-cli call "%~dp0elasticsearch-cli.bat" ^ diff --git a/distribution/src/bin/elasticsearch-node.bat b/distribution/src/bin/elasticsearch-node.bat index d51cd0a2feac2..70b0abdc41082 100644 --- a/distribution/src/bin/elasticsearch-node.bat +++ b/distribution/src/bin/elasticsearch-node.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/distribution/src/bin/elasticsearch-plugin.bat b/distribution/src/bin/elasticsearch-plugin.bat index cdcbd186282a6..a9d1accd1ee63 100644 --- a/distribution/src/bin/elasticsearch-plugin.bat +++ b/distribution/src/bin/elasticsearch-plugin.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=lib/tools/plugin-cli set LAUNCHER_JAVA_OPTS=--add-opens java.base/sun.security.provider=ALL-UNNAMED %ES_JAVA_OPTS% diff --git a/distribution/src/bin/elasticsearch-shard.bat b/distribution/src/bin/elasticsearch-shard.bat index d51cd0a2feac2..70b0abdc41082 100644 --- a/distribution/src/bin/elasticsearch-shard.bat +++ b/distribution/src/bin/elasticsearch-shard.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat index 37b929afc9c21..2244fcea0ca5f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certgen.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat index 37b929afc9c21..2244fcea0ca5f 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-certutil.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat index 38b29b1cc728f..bf80229faffd3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-create-enrollment-token.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat index 6355328f657f1..27cc3f0a453cc 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reconfigure-node.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli call "%~dp0elasticsearch-cli.bat" "--reconfigure" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat index 38b29b1cc728f..bf80229faffd3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-reset-password.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat index 38b29b1cc728f..bf80229faffd3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-saml-metadata.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat index 38b29b1cc728f..bf80229faffd3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-service-tokens.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat index 38b29b1cc728f..bf80229faffd3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-setup-passwords.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat index 38b29b1cc728f..bf80229faffd3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-syskeygen.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat b/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat index 38b29b1cc728f..bf80229faffd3 100644 --- a/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat +++ b/x-pack/plugin/security/src/main/bin/elasticsearch-users.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-security call "%~dp0elasticsearch-cli.bat" ^ diff --git a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat index e1f9869ea1b50..87edf5767611a 100644 --- a/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat +++ b/x-pack/plugin/watcher/src/main/bin/elasticsearch-croneval.bat @@ -8,7 +8,7 @@ rem 2.0. setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%0 +set SCRIPT_NAME=%~n0 set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% set LAUNCHER_LIBS=modules/x-pack-core,modules/x-pack-watcher call "%~dp0elasticsearch-cli.bat" ^ From 8ba04d03005c8804544735c64495cd2586623c69 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 5 Apr 2022 13:16:42 -0700 Subject: [PATCH 026/166] more server cleanup, simplified logging init for clis --- .../cli/keystore/KeyStoreCli.java | 6 +-- .../org/elasticsearch/launcher/Launcher.java | 16 +++++++ .../elasticsearch/plugins/cli/PluginCli.java | 7 ++- .../elasticsearch/server/cli/ServerCli.java | 12 ++++++ .../bootstrap/Elasticsearch.java | 20 ++++----- .../elasticsearch/bootstrap/ServerArgs.java | 43 +++++++++++++++++++ .../cluster/coordination/NodeToolCli.java | 9 ---- .../cli/CommandLoggingConfigurator.java | 32 -------------- .../common/cli/EnvironmentAwareCommand.java | 2 +- .../common/cli/LoggingAwareCommand.java | 29 ------------- .../common/cli/LoggingAwareMultiCommand.java | 30 ------------- .../index/shard/ShardToolCli.java | 6 +-- .../licensor/tools/KeyPairGeneratorTool.java | 6 +-- .../licensor/tools/LicenseGeneratorTool.java | 6 +-- .../tools/LicenseVerificationTool.java | 6 +-- .../xpack/security/cli/CertificateTool.java | 7 ++- .../esnative/tool/SetupPasswordTool.java | 6 +-- .../security/authc/file/tool/UsersTool.java | 6 +-- .../authc/service/FileTokensTool.java | 6 +-- .../trigger/schedule/tool/CronEvalTool.java | 6 +-- 20 files changed, 113 insertions(+), 148 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java delete mode 100644 server/src/main/java/org/elasticsearch/common/cli/CommandLoggingConfigurator.java delete mode 100644 server/src/main/java/org/elasticsearch/common/cli/LoggingAwareCommand.java delete mode 100644 server/src/main/java/org/elasticsearch/common/cli/LoggingAwareMultiCommand.java diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java index 81acf200d7848..65897d586721a 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java @@ -8,15 +8,15 @@ package org.elasticsearch.cli.keystore; -import org.elasticsearch.common.cli.LoggingAwareMultiCommand; +import org.elasticsearch.cli.MultiCommand; /** * A cli tool for managing secrets in the elasticsearch keystore. */ -class KeyStoreCli extends LoggingAwareMultiCommand { +class KeyStoreCli extends MultiCommand { KeyStoreCli() { - super("A tool for managing settings stored in the elasticsearch keystore"); + super("A tool for managing settings stored in the elasticsearch keystore", () -> {}); subcommands.put("create", new CreateKeyStoreCommand()); subcommands.put("list", new ListKeyStoreCommand()); subcommands.put("show", new ShowKeyStoreCommand()); diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index 994faa4442cad..ac7ea550ce69b 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -8,9 +8,12 @@ package org.elasticsearch.launcher; +import org.apache.logging.log4j.Level; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.ToolProvider; +import org.elasticsearch.common.logging.LogConfigurator; +import org.elasticsearch.common.settings.Settings; import java.io.IOException; import java.net.MalformedURLException; @@ -45,6 +48,8 @@ class Launcher { // TODO: don't throw, catch this and give a nice error message public static void main(String[] args) throws Exception { + configureLoggingWithoutConfig(); + // TODO: change signature of Command to take in sysprops and env Map sysprops = convertPropertiesToMap(System.getProperties()); Map env = new HashMap<>(System.getenv()); @@ -90,4 +95,15 @@ private static ClassLoader loadJars(List dirs) throws IOException { } return URLClassLoader.newInstance(urls.toArray(URL[]::new)); } + + /** + * Configures logging without Elasticsearch configuration files based on the system property "es.logger.level" only. As such, any + * logging will be written to the console. + */ + public static void configureLoggingWithoutConfig() { + // initialize default for es.logger.level because we will not read the log4j2.properties + final String loggerLevel = System.getProperty("es.logger.level", Level.INFO.name()); + final Settings settings = Settings.builder().put("logger.level", loggerLevel).build(); + LogConfigurator.configureWithoutConfig(settings); + } } diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java index 138ee87724b96..2ae3bd1ffd249 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java @@ -9,9 +9,8 @@ package org.elasticsearch.plugins.cli; import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; -import org.elasticsearch.cli.ToolProvider; -import org.elasticsearch.common.cli.LoggingAwareMultiCommand; import org.elasticsearch.core.internal.io.IOUtils; import java.io.IOException; @@ -21,12 +20,12 @@ /** * A cli tool for adding, removing and listing plugins for elasticsearch. */ -class PluginCli extends LoggingAwareMultiCommand { +class PluginCli extends MultiCommand { private final Collection commands; PluginCli() { - super("A tool for managing installed elasticsearch plugins"); + super("A tool for managing installed elasticsearch plugins", () -> {}); subcommands.put("list", new ListPluginsCommand()); subcommands.put("install", new InstallPluginCommand()); subcommands.put("remove", new RemovePluginCommand()); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index b0f33092a0682..838909c13d37c 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -15,10 +15,12 @@ import joptsimple.util.PathConverter; import org.elasticsearch.Build; +import org.elasticsearch.bootstrap.ServerArgs; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.cli.EnvironmentAwareCommand; +import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; import org.elasticsearch.common.settings.KeyStoreWrapper; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.core.PathUtils; @@ -26,6 +28,9 @@ import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.node.NodeValidationException; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedWriter; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; @@ -126,6 +131,9 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th else: attempt auto configure, exit on codes not in [80, 73, 78] */ + // TODO: this settings is wrong, needs to account for docker stuff through env and also auto enrollment, needs to be late binding + ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings()); + List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); jvmOptions.add("-Des.path.conf=" + env.configFile()); jvmOptions.add("-Des.distribution.flavor=" + System.getProperty("es.distribution.flavor")); @@ -163,8 +171,12 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th ProcessBuilder builder = new ProcessBuilder(command); builder.environment().putAll(envVars); builder.inheritIO(); + builder.redirectInput(ProcessBuilder.Redirect.PIPE); try { Process process = builder.start(); + try (var out = new OutputStreamStreamOutput(process.getOutputStream())) { + serverArgs.writeTo(out); + } // TODO: handle daemonize flag int code = process.waitFor(); System.out.println("exited: " + code); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 4426e51ce37d4..e4e9347843d38 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -8,27 +8,16 @@ package org.elasticsearch.bootstrap; -import joptsimple.OptionSet; -import joptsimple.OptionSpec; -import joptsimple.OptionSpecBuilder; -import joptsimple.util.PathConverter; - -import org.elasticsearch.Build; -import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; -import org.elasticsearch.common.cli.EnvironmentAwareCommand; +import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.env.Environment; -import org.elasticsearch.monitor.jvm.JvmInfo; import org.elasticsearch.node.NodeValidationException; -import java.io.IOException; import java.nio.file.Path; import java.security.Permission; import java.security.Security; -import java.util.Arrays; -import java.util.Locale; /** * This class starts elasticsearch. @@ -56,8 +45,15 @@ public void checkPermission(Permission perm) { }); LogConfigurator.registerErrorListener(); + final ServerArgs serverArgs; + try (var in = new InputStreamStreamInput(System.in)) { + serverArgs = new ServerArgs(in); + } + final Elasticsearch elasticsearch = new Elasticsearch(); System.out.println("RUNNING ELASTICSEARCH"); + System.out.println(serverArgs); + elasticsearch.init(serverArgs.daemonize(), serverArgs.pidFile(), false, new Environment(serverArgs.nodeSettings(), serverArgs.configDir())); } /** diff --git a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java new file mode 100644 index 0000000000000..e03be5f0d9a45 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java @@ -0,0 +1,43 @@ +/* + * 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.bootstrap; + +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.PathUtils; + +import java.io.IOException; +import java.nio.file.Path; + +public record ServerArgs(boolean daemonize, Path pidFile, Settings nodeSettings, Path configDir) implements Writeable { + + public ServerArgs(StreamInput in) throws IOException { + this( + in.readBoolean(), + readPidFile(in), + Settings.readSettingsFromStream(in), + PathUtils.get(in.readString()) + ); + } + + private static Path readPidFile(StreamInput in) throws IOException { + String pidFile = in.readOptionalString(); + return pidFile == null ? null : PathUtils.get(pidFile); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeBoolean(daemonize); + out.writeOptionalString(pidFile == null ? null : pidFile.toString()); + Settings.writeSettingsToStream(nodeSettings, out); + out.writeString(configDir.toString()); + } +} diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java index 89b2fde4ab38a..b12b3ee249525 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java @@ -9,22 +9,13 @@ import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; -import org.elasticsearch.common.cli.CommandLoggingConfigurator; import org.elasticsearch.env.NodeRepurposeCommand; import org.elasticsearch.env.OverrideNodeVersionCommand; -// NodeToolCli does not extend LoggingAwareCommand, because LoggingAwareCommand performs logging initialization -// after LoggingAwareCommand instance is constructed. -// It's too late for us, because before UnsafeBootstrapMasterCommand is added to the list of subcommands -// log4j2 initialization will happen, because it has static reference to Logger class. -// Even if we avoid making a static reference to Logger class, there is no nice way to avoid declaring -// UNSAFE_BOOTSTRAP, which depends on ClusterService, which in turn has static Logger. -// TODO execute CommandLoggingConfigurator.configureLoggingWithoutConfig() in the constructor of commands, not in beforeMain public class NodeToolCli extends MultiCommand { public NodeToolCli() { super("A CLI tool to do unsafe cluster and index manipulations on current node", () -> {}); - CommandLoggingConfigurator.configureLoggingWithoutConfig(); subcommands.put("repurpose", new NodeRepurposeCommand()); subcommands.put("unsafe-bootstrap", new UnsafeBootstrapMasterCommand()); subcommands.put("detach-cluster", new DetachClusterCommand()); diff --git a/server/src/main/java/org/elasticsearch/common/cli/CommandLoggingConfigurator.java b/server/src/main/java/org/elasticsearch/common/cli/CommandLoggingConfigurator.java deleted file mode 100644 index 41a077cd769f5..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/cli/CommandLoggingConfigurator.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.common.cli; - -import org.apache.logging.log4j.Level; -import org.elasticsearch.common.logging.LogConfigurator; -import org.elasticsearch.common.settings.Settings; - -/** - * Holder class for method to configure logging without Elasticsearch configuration files for use in CLI tools that will not read such - * files. - */ -public final class CommandLoggingConfigurator { - - /** - * Configures logging without Elasticsearch configuration files based on the system property "es.logger.level" only. As such, any - * logging will be written to the console. - */ - public static void configureLoggingWithoutConfig() { - // initialize default for es.logger.level because we will not read the log4j2.properties - final String loggerLevel = System.getProperty("es.logger.level", Level.INFO.name()); - final Settings settings = Settings.builder().put("logger.level", loggerLevel).build(); - LogConfigurator.configureWithoutConfig(settings); - } - -} diff --git a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java index d3b62416b802b..611a82384bc3d 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java @@ -39,7 +39,7 @@ public abstract class EnvironmentAwareCommand extends Command { * @param description the command description */ public EnvironmentAwareCommand(final String description) { - this(description, CommandLoggingConfigurator::configureLoggingWithoutConfig); + this(description, () -> {}); } /** diff --git a/server/src/main/java/org/elasticsearch/common/cli/LoggingAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/LoggingAwareCommand.java deleted file mode 100644 index 9682a5680eb05..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/cli/LoggingAwareCommand.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.common.cli; - -import org.elasticsearch.cli.Command; - -/** - * A command that is aware of logging. This class should be preferred over the base {@link Command} class for any CLI tools that depend on - * core Elasticsearch as they could directly or indirectly touch classes that touch logging and as such logging needs to be configured. - */ -public abstract class LoggingAwareCommand extends Command { - - /** - * Construct the command with the specified command description. This command will have logging configured without reading Elasticsearch - * configuration files. - * - * @param description the command description - */ - public LoggingAwareCommand(final String description) { - super(description, CommandLoggingConfigurator::configureLoggingWithoutConfig); - } - -} diff --git a/server/src/main/java/org/elasticsearch/common/cli/LoggingAwareMultiCommand.java b/server/src/main/java/org/elasticsearch/common/cli/LoggingAwareMultiCommand.java deleted file mode 100644 index d3996d815d1da..0000000000000 --- a/server/src/main/java/org/elasticsearch/common/cli/LoggingAwareMultiCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.common.cli; - -import org.elasticsearch.cli.MultiCommand; - -/** - * A multi-command that is aware of logging. This class should be preferred over the base {@link MultiCommand} class for any CLI tools that - * depend on core Elasticsearch as they could directly or indirectly touch classes that touch logging and as such logging needs to be - * configured. - */ -public abstract class LoggingAwareMultiCommand extends MultiCommand { - - /** - * Construct the command with the specified command description. This command will have logging configured without reading Elasticsearch - * configuration files. - * - * @param description the command description - */ - public LoggingAwareMultiCommand(final String description) { - super(description, CommandLoggingConfigurator::configureLoggingWithoutConfig); - } - -} diff --git a/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java index 3ca59d78db3a1..ea36f90c1728c 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java +++ b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java @@ -7,15 +7,15 @@ */ package org.elasticsearch.index.shard; -import org.elasticsearch.common.cli.LoggingAwareMultiCommand; +import org.elasticsearch.cli.MultiCommand; /** * Class encapsulating and dispatching commands from the {@code elasticsearch-shard} command line tool */ -public class ShardToolCli extends LoggingAwareMultiCommand { +public class ShardToolCli extends MultiCommand { ShardToolCli() { - super("A CLI tool to remove corrupted parts of unrecoverable shards"); + super("A CLI tool to remove corrupted parts of unrecoverable shards", () -> {}); subcommands.put("remove-corrupted-data", new RemoveCorruptedShardDataCommand()); } } diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java index 6cfb3184e73be..5d842fb9865a9 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java @@ -9,10 +9,10 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; +import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; -import org.elasticsearch.common.cli.LoggingAwareCommand; import org.elasticsearch.core.PathUtils; import org.elasticsearch.core.SuppressForbidden; @@ -24,13 +24,13 @@ import static org.elasticsearch.license.CryptUtils.writeEncryptedPrivateKey; -public class KeyPairGeneratorTool extends LoggingAwareCommand { +public class KeyPairGeneratorTool extends Command { private final OptionSpec publicKeyPathOption; private final OptionSpec privateKeyPathOption; public KeyPairGeneratorTool() { - super("Generates a key pair with RSA 2048-bit security"); + super("Generates a key pair with RSA 2048-bit security", () -> {}); // TODO: in jopt-simple 5.0 we can use a PathConverter to take Path instead of File this.publicKeyPathOption = parser.accepts("publicKeyPath", "public key path").withRequiredArg().required(); this.privateKeyPathOption = parser.accepts("privateKeyPath", "private key path").withRequiredArg().required(); diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java index 4dc1b17b2f9f6..0b609a4753612 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java @@ -9,12 +9,12 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; +import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.common.cli.LoggingAwareCommand; import org.elasticsearch.core.PathUtils; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.license.License; @@ -28,7 +28,7 @@ import java.nio.file.Files; import java.nio.file.Path; -public class LicenseGeneratorTool extends LoggingAwareCommand { +public class LicenseGeneratorTool extends Command { private final OptionSpec publicKeyPathOption; private final OptionSpec privateKeyPathOption; @@ -36,7 +36,7 @@ public class LicenseGeneratorTool extends LoggingAwareCommand { private final OptionSpec licenseFileOption; public LicenseGeneratorTool() { - super("Generates signed elasticsearch license(s) for a given license spec(s)"); + super("Generates signed elasticsearch license(s) for a given license spec(s)", () -> {}); publicKeyPathOption = parser.accepts("publicKeyPath", "path to public key file").withRequiredArg().required(); privateKeyPathOption = parser.accepts("privateKeyPath", "path to private key file").withRequiredArg().required(); // TODO: with jopt-simple 5.0, we can make these requiredUnless each other diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java index 1059b100fc396..d190b8ee52b01 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java @@ -9,12 +9,12 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; +import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; -import org.elasticsearch.common.cli.LoggingAwareCommand; import org.elasticsearch.core.PathUtils; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.license.CryptUtils; @@ -29,14 +29,14 @@ import java.nio.file.Files; import java.nio.file.Path; -public class LicenseVerificationTool extends LoggingAwareCommand { +public class LicenseVerificationTool extends Command { private final OptionSpec publicKeyPathOption; private final OptionSpec licenseOption; private final OptionSpec licenseFileOption; public LicenseVerificationTool() { - super("Generates signed elasticsearch license(s) for a given license spec(s)"); + super("Generates signed elasticsearch license(s) for a given license spec(s)", () -> {}); publicKeyPathOption = parser.accepts("publicKeyPath", "path to public key file").withRequiredArg().required(); // TODO: with jopt-simple 5.0, we can make these requiredUnless each other // which is effectively "one must be present" diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java index eccd44fd0b99c..d4bcea918c276 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java @@ -22,12 +22,12 @@ import org.bouncycastle.pkcs.PKCS10CertificationRequest; import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.Terminal.Verbosity; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.cli.EnvironmentAwareCommand; -import org.elasticsearch.common.cli.LoggingAwareMultiCommand; import org.elasticsearch.common.network.InetAddresses; import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.common.util.set.Sets; @@ -78,13 +78,12 @@ import java.util.stream.Stream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; - import javax.security.auth.x500.X500Principal; /** * CLI tool to make generation of certificates or certificate requests easier for users */ -public class CertificateTool extends LoggingAwareMultiCommand { +public class CertificateTool extends MultiCommand { private static final String AUTO_GEN_CA_DN = "CN=Elastic Certificate Tool Autogenerated CA"; private static final String DESCRIPTION = "Simplifies certificate creation for use with the Elastic Stack"; @@ -146,7 +145,7 @@ private static class CertificateToolParser { } CertificateTool() { - super(DESCRIPTION); + super(DESCRIPTION, () -> {}); subcommands.put("csr", new SigningRequestCommand()); subcommands.put("cert", new GenerateCertificateCommand()); subcommands.put("ca", new CertificateAuthorityCommand()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java index f907150ce4f61..85f96c4f4ef08 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java @@ -12,13 +12,13 @@ import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.Terminal.Verbosity; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.Strings; import org.elasticsearch.common.cli.KeyStoreAwareCommand; -import org.elasticsearch.common.cli.LoggingAwareMultiCommand; import org.elasticsearch.common.settings.KeyStoreWrapper; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; @@ -71,7 +71,7 @@ * elastic user and the ChangePassword API for setting the password of the rest of the built-in users when needed. */ @Deprecated -public class SetupPasswordTool extends LoggingAwareMultiCommand { +public class SetupPasswordTool extends MultiCommand { private static final char[] CHARS = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").toCharArray(); public static final List USERS = asList( @@ -108,7 +108,7 @@ public class SetupPasswordTool extends LoggingAwareMultiCommand { Function clientFunction, CheckedFunction keyStoreFunction ) { - super("Sets the passwords for reserved users"); + super("Sets the passwords for reserved users", () -> {}); subcommands.put("auto", newAutoSetup()); subcommands.put("interactive", newInteractiveSetup()); this.clientFunction = clientFunction; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java index 6a7245ae749a3..9c118030174c2 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java @@ -11,11 +11,11 @@ import org.elasticsearch.ElasticsearchException; import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.cli.EnvironmentAwareCommand; -import org.elasticsearch.common.cli.LoggingAwareMultiCommand; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.Maps; @@ -43,10 +43,10 @@ import java.util.Set; import java.util.stream.Collectors; -public class UsersTool extends LoggingAwareMultiCommand { +public class UsersTool extends MultiCommand { UsersTool() { - super("Manages elasticsearch file users"); + super("Manages elasticsearch file users", () -> {}); subcommands.put("useradd", newAddUserCommand()); subcommands.put("userdel", newDeleteUserCommand()); subcommands.put("passwd", newPasswordCommand()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java index 045e5fc226787..7a4d47967aa8c 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java @@ -11,11 +11,11 @@ import joptsimple.OptionSpec; import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.MultiCommand; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.cli.EnvironmentAwareCommand; -import org.elasticsearch.common.cli.LoggingAwareMultiCommand; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.env.Environment; import org.elasticsearch.xpack.core.XPackSettings; @@ -31,10 +31,10 @@ import java.util.TreeMap; import java.util.function.Predicate; -public class FileTokensTool extends LoggingAwareMultiCommand { +public class FileTokensTool extends MultiCommand { public FileTokensTool() { - super("Manages elasticsearch service account file-tokens"); + super("Manages elasticsearch service account file-tokens", () -> {}); subcommands.put("create", newCreateFileTokenCommand()); subcommands.put("delete", newDeleteFileTokenCommand()); subcommands.put("list", newListFileTokenCommand()); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java index 54f5b12ff824f..e560aaea09596 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java @@ -9,10 +9,10 @@ import joptsimple.OptionSet; import joptsimple.OptionSpec; +import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; -import org.elasticsearch.common.cli.LoggingAwareCommand; import org.elasticsearch.common.time.DateFormatter; import org.elasticsearch.xpack.core.scheduler.Cron; @@ -24,7 +24,7 @@ import java.util.List; import java.util.Locale; -public class CronEvalTool extends LoggingAwareCommand { +public class CronEvalTool extends Command { private static final DateFormatter UTC_FORMATTER = DateFormatter.forPattern("EEE, d MMM yyyy HH:mm:ss") .withZone(ZoneOffset.UTC) @@ -38,7 +38,7 @@ public class CronEvalTool extends LoggingAwareCommand { private final OptionSpec detailOption; CronEvalTool() { - super("Validates and evaluates a cron expression"); + super("Validates and evaluates a cron expression", () -> {}); this.countOption = parser.acceptsAll(Arrays.asList("c", "count"), "The number of future times this expression will be triggered") .withRequiredArg() .ofType(Integer.class) From 4af0543e9c813443d0ca053f4d01ba365b785ae0 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 5 Apr 2022 13:20:21 -0700 Subject: [PATCH 027/166] remove old main methods --- .../src/main/java/org/elasticsearch/geoip/GeoIpCli.java | 4 ---- .../main/java/org/elasticsearch/plugins/cli/PluginCli.java | 4 ---- .../org/elasticsearch/cluster/coordination/NodeToolCli.java | 5 ----- .../license/licensor/tools/KeyPairGeneratorTool.java | 4 ---- .../license/licensor/tools/LicenseGeneratorTool.java | 4 ---- .../license/licensor/tools/LicenseVerificationTool.java | 4 ---- .../xpack/security/crypto/tool/SystemKeyTool.java | 4 ---- 7 files changed, 29 deletions(-) diff --git a/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java b/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java index 6eb3aa01fc71b..6c6a2b0b03cad 100644 --- a/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java +++ b/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java @@ -154,8 +154,4 @@ private byte[] createTarHeader(String name, long size) { return buf; } - - public static void main(String[] args) throws Exception { - exit(new GeoIpCli().main(args, Terminal.DEFAULT)); - } } diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java index 2ae3bd1ffd249..1de382df8fd00 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java @@ -32,10 +32,6 @@ class PluginCli extends MultiCommand { commands = Collections.unmodifiableCollection(subcommands.values()); } - public static void main(String[] args) throws Exception { - exit(new PluginCli().main(args, Terminal.DEFAULT)); - } - @Override public void close() throws IOException { IOUtils.close(commands); diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java index b12b3ee249525..7044f96f05cd0 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java @@ -23,9 +23,4 @@ public NodeToolCli() { subcommands.put("remove-settings", new RemoveSettingsCommand()); subcommands.put("remove-customs", new RemoveCustomsCommand()); } - - public static void main(String[] args) throws Exception { - exit(new NodeToolCli().main(args, Terminal.DEFAULT)); - } - } diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java index 5d842fb9865a9..8ea7ce43ec8b5 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java @@ -36,10 +36,6 @@ public KeyPairGeneratorTool() { this.privateKeyPathOption = parser.accepts("privateKeyPath", "private key path").withRequiredArg().required(); } - public static void main(String[] args) throws Exception { - exit(new KeyPairGeneratorTool().main(args, Terminal.DEFAULT)); - } - @Override protected void printAdditionalHelp(Terminal terminal) { terminal.println("This tool generates and saves a key pair to the provided publicKeyPath"); diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java index 0b609a4753612..bb5ef5dc19095 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java @@ -45,10 +45,6 @@ public LicenseGeneratorTool() { licenseFileOption = parser.accepts("licenseFile", "license json spec file").withRequiredArg(); } - public static void main(String[] args) throws Exception { - exit(new LicenseGeneratorTool().main(args, Terminal.DEFAULT)); - } - @Override protected void printAdditionalHelp(Terminal terminal) { terminal.println("This tool generate elasticsearch license(s) for the provided"); diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java index d190b8ee52b01..51591e2f53979 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java @@ -44,10 +44,6 @@ public LicenseVerificationTool() { licenseFileOption = parser.accepts("licenseFile", "license json spec file").withRequiredArg(); } - public static void main(String[] args) throws Exception { - exit(new LicenseVerificationTool().main(args, Terminal.DEFAULT)); - } - @Override protected void execute(Terminal terminal, OptionSet options) throws Exception { Path publicKeyPath = parsePath(publicKeyPathOption.value(options)); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java index b00a55e42f4c0..5c94337e39ffe 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java @@ -49,10 +49,6 @@ public class SystemKeyTool extends EnvironmentAwareCommand { PosixFilePermission.OWNER_WRITE ); - static int main(SystemKeyTool tool, String[] args, Terminal terminal) throws Exception { - return tool.main(args, terminal); - } - @Override protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { final Path keyPath; From e0b0b57ea2a67b7a24b1e9e76ea94eb5f8dc138d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 5 Apr 2022 13:25:26 -0700 Subject: [PATCH 028/166] remove beforeMain hook --- .../main/java/org/elasticsearch/geoip/GeoIpCli.java | 2 +- .../org/elasticsearch/cli/keystore/KeyStoreCli.java | 2 +- .../org/elasticsearch/plugins/cli/PluginCli.java | 3 +-- .../main/java/org/elasticsearch/cli/Command.java | 10 ++-------- .../java/org/elasticsearch/cli/MultiCommand.java | 7 +++---- .../org/elasticsearch/cli/EvilCommandTests.java | 2 +- .../cluster/coordination/NodeToolCli.java | 3 +-- .../common/cli/EnvironmentAwareCommand.java | 13 +------------ .../org/elasticsearch/index/shard/ShardToolCli.java | 2 +- .../java/org/elasticsearch/cli/CommandTests.java | 6 +++--- .../org/elasticsearch/cli/MultiCommandTests.java | 8 ++++---- .../licensor/tools/KeyPairGeneratorTool.java | 2 +- .../licensor/tools/LicenseGeneratorTool.java | 2 +- .../licensor/tools/LicenseVerificationTool.java | 2 +- .../xpack/security/cli/CertificateTool.java | 2 +- .../authc/esnative/tool/SetupPasswordTool.java | 2 +- .../xpack/security/authc/file/tool/UsersTool.java | 2 +- .../security/authc/service/FileTokensTool.java | 2 +- .../java/org/elasticsearch/xpack/sql/cli/Cli.java | 2 +- .../watcher/trigger/schedule/tool/CronEvalTool.java | 2 +- 20 files changed, 28 insertions(+), 48 deletions(-) diff --git a/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java b/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java index 6c6a2b0b03cad..56aa9ad20b2a5 100644 --- a/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java +++ b/distribution/tools/geoip-cli/src/main/java/org/elasticsearch/geoip/GeoIpCli.java @@ -47,7 +47,7 @@ public class GeoIpCli extends Command { private final OptionSpec targetDirectory; public GeoIpCli() { - super("A CLI tool to prepare local GeoIp database service", () -> {}); + super("A CLI tool to prepare local GeoIp database service"); sourceDirectory = parser.acceptsAll(Arrays.asList("s", "source"), "Source directory").withRequiredArg().required(); targetDirectory = parser.acceptsAll(Arrays.asList("t", "target"), "Target directory").withRequiredArg(); diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java index 65897d586721a..99778a4750e07 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCli.java @@ -16,7 +16,7 @@ class KeyStoreCli extends MultiCommand { KeyStoreCli() { - super("A tool for managing settings stored in the elasticsearch keystore", () -> {}); + super("A tool for managing settings stored in the elasticsearch keystore"); subcommands.put("create", new CreateKeyStoreCommand()); subcommands.put("list", new ListKeyStoreCommand()); subcommands.put("show", new ShowKeyStoreCommand()); diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java index 1de382df8fd00..2d26080750225 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/PluginCli.java @@ -10,7 +10,6 @@ import org.elasticsearch.cli.Command; import org.elasticsearch.cli.MultiCommand; -import org.elasticsearch.cli.Terminal; import org.elasticsearch.core.internal.io.IOUtils; import java.io.IOException; @@ -25,7 +24,7 @@ class PluginCli extends MultiCommand { private final Collection commands; PluginCli() { - super("A tool for managing installed elasticsearch plugins", () -> {}); + super("A tool for managing installed elasticsearch plugins"); subcommands.put("list", new ListPluginsCommand()); subcommands.put("install", new InstallPluginCommand()); subcommands.put("remove", new RemovePluginCommand()); diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java index 1df7e9432f3a1..d133615f7ef25 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java @@ -27,8 +27,6 @@ public abstract class Command implements Closeable { /** A description of the command, used in the help output. */ protected final String description; - private final Runnable beforeMain; - /** The option parser for this command. */ protected final OptionParser parser = new OptionParser(); @@ -39,13 +37,11 @@ public abstract class Command implements Closeable { /** * Construct the command with the specified command description and runnable to execute before main is invoked. + * @param description the command description * - * @param description the command description - * @param beforeMain the before-main runnable */ - public Command(final String description, final Runnable beforeMain) { + public Command(final String description) { this.description = description; - this.beforeMain = beforeMain; } private Thread shutdownHookThread; @@ -71,8 +67,6 @@ public final int main(String[] args, Terminal terminal) throws Exception { Runtime.getRuntime().addShutdownHook(shutdownHookThread); } - beforeMain.run(); - try { mainWithoutErrorHandling(args, terminal); } catch (OptionException e) { diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/MultiCommand.java b/libs/cli/src/main/java/org/elasticsearch/cli/MultiCommand.java index acda0c70c592e..451392037132f 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/MultiCommand.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/MultiCommand.java @@ -34,12 +34,11 @@ public class MultiCommand extends Command { /** * Construct the multi-command with the specified command description and runnable to execute before main is invoked. + * @param description the multi-command description * - * @param description the multi-command description - * @param beforeMain the before-main runnable */ - public MultiCommand(final String description, final Runnable beforeMain) { - super(description, beforeMain); + public MultiCommand(final String description) { + super(description); this.settingOption = parser.accepts("E", "Configure a setting").withRequiredArg().ofType(KeyValuePair.class); parser.posixlyCorrect(true); } diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilCommandTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilCommandTests.java index 7fb22afd76d23..db7ec4a438d26 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilCommandTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilCommandTests.java @@ -24,7 +24,7 @@ public class EvilCommandTests extends ESTestCase { public void testCommandShutdownHook() throws Exception { final AtomicBoolean closed = new AtomicBoolean(); final boolean shouldThrow = randomBoolean(); - final Command command = new Command("test-command-shutdown-hook", () -> {}) { + final Command command = new Command("test-command-shutdown-hook") { @Override protected void execute(Terminal terminal, OptionSet options) throws Exception { diff --git a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java index 7044f96f05cd0..5b442cca9456e 100644 --- a/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java +++ b/server/src/main/java/org/elasticsearch/cluster/coordination/NodeToolCli.java @@ -8,14 +8,13 @@ package org.elasticsearch.cluster.coordination; import org.elasticsearch.cli.MultiCommand; -import org.elasticsearch.cli.Terminal; import org.elasticsearch.env.NodeRepurposeCommand; import org.elasticsearch.env.OverrideNodeVersionCommand; public class NodeToolCli extends MultiCommand { public NodeToolCli() { - super("A CLI tool to do unsafe cluster and index manipulations on current node", () -> {}); + super("A CLI tool to do unsafe cluster and index manipulations on current node"); subcommands.put("repurpose", new NodeRepurposeCommand()); subcommands.put("unsafe-bootstrap", new UnsafeBootstrapMasterCommand()); subcommands.put("detach-cluster", new DetachClusterCommand()); diff --git a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java index 611a82384bc3d..89b2b4e9d3d63 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java @@ -39,18 +39,7 @@ public abstract class EnvironmentAwareCommand extends Command { * @param description the command description */ public EnvironmentAwareCommand(final String description) { - this(description, () -> {}); - } - - /** - * Construct the command with the specified command description and runnable to execute before main is invoked. Commands constructed - * with this constructor must take ownership of configuring logging. - * - * @param description the command description - * @param beforeMain the before-main runnable - */ - public EnvironmentAwareCommand(final String description, final Runnable beforeMain) { - super(description, beforeMain); + super(description); this.settingOption = parser.accepts("E", "Configure a setting").withRequiredArg().ofType(KeyValuePair.class); } diff --git a/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java index ea36f90c1728c..69ad92372e5b7 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java +++ b/server/src/main/java/org/elasticsearch/index/shard/ShardToolCli.java @@ -15,7 +15,7 @@ public class ShardToolCli extends MultiCommand { ShardToolCli() { - super("A CLI tool to remove corrupted parts of unrecoverable shards", () -> {}); + super("A CLI tool to remove corrupted parts of unrecoverable shards"); subcommands.put("remove-corrupted-data", new RemoveCorruptedShardDataCommand()); } } diff --git a/server/src/test/java/org/elasticsearch/cli/CommandTests.java b/server/src/test/java/org/elasticsearch/cli/CommandTests.java index 27ea9b077a90c..ab6c56a2212d4 100644 --- a/server/src/test/java/org/elasticsearch/cli/CommandTests.java +++ b/server/src/test/java/org/elasticsearch/cli/CommandTests.java @@ -18,7 +18,7 @@ public class CommandTests extends ESTestCase { static class UserErrorCommand extends Command { UserErrorCommand() { - super("Throws a user error", () -> {}); + super("Throws a user error"); } @Override @@ -36,7 +36,7 @@ protected boolean addShutdownHook() { static class UsageErrorCommand extends Command { UsageErrorCommand() { - super("Throws a usage error", () -> {}); + super("Throws a usage error"); } @Override @@ -56,7 +56,7 @@ static class NoopCommand extends Command { boolean executed = false; NoopCommand() { - super("Does nothing", () -> {}); + super("Does nothing"); } @Override diff --git a/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java b/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java index 9191ff7f16324..26f2d716c765c 100644 --- a/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java +++ b/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java @@ -30,7 +30,7 @@ static class DummyMultiCommand extends MultiCommand { final AtomicBoolean closed = new AtomicBoolean(); DummyMultiCommand() { - super("A dummy multi command", () -> {}); + super("A dummy multi command"); } @Override @@ -51,7 +51,7 @@ static class DummySubCommand extends Command { } DummySubCommand(final boolean throwsExceptionOnClose) { - super("A dummy subcommand", () -> {}); + super("A dummy subcommand"); this.throwsExceptionOnClose = throwsExceptionOnClose; } @@ -198,7 +198,7 @@ public void testCloseWhenSubCommandCloseThrowsException() throws Exception { static class ErrorHandlingMultiCommand extends MultiCommand { ErrorHandlingMultiCommand() { - super("error catching", () -> {}); + super("error catching"); } @Override @@ -209,7 +209,7 @@ protected boolean addShutdownHook() { static class ErrorThrowingSubCommand extends Command { ErrorThrowingSubCommand() { - super("error throwing", () -> {}); + super("error throwing"); } @Override diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java index 8ea7ce43ec8b5..6ca381e8989c6 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/KeyPairGeneratorTool.java @@ -30,7 +30,7 @@ public class KeyPairGeneratorTool extends Command { private final OptionSpec privateKeyPathOption; public KeyPairGeneratorTool() { - super("Generates a key pair with RSA 2048-bit security", () -> {}); + super("Generates a key pair with RSA 2048-bit security"); // TODO: in jopt-simple 5.0 we can use a PathConverter to take Path instead of File this.publicKeyPathOption = parser.accepts("publicKeyPath", "public key path").withRequiredArg().required(); this.privateKeyPathOption = parser.accepts("privateKeyPath", "private key path").withRequiredArg().required(); diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java index bb5ef5dc19095..f4c8a090f65ed 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseGeneratorTool.java @@ -36,7 +36,7 @@ public class LicenseGeneratorTool extends Command { private final OptionSpec licenseFileOption; public LicenseGeneratorTool() { - super("Generates signed elasticsearch license(s) for a given license spec(s)", () -> {}); + super("Generates signed elasticsearch license(s) for a given license spec(s)"); publicKeyPathOption = parser.accepts("publicKeyPath", "path to public key file").withRequiredArg().required(); privateKeyPathOption = parser.accepts("privateKeyPath", "path to private key file").withRequiredArg().required(); // TODO: with jopt-simple 5.0, we can make these requiredUnless each other diff --git a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java index 51591e2f53979..01a78be981773 100644 --- a/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java +++ b/x-pack/license-tools/src/main/java/org/elasticsearch/license/licensor/tools/LicenseVerificationTool.java @@ -36,7 +36,7 @@ public class LicenseVerificationTool extends Command { private final OptionSpec licenseFileOption; public LicenseVerificationTool() { - super("Generates signed elasticsearch license(s) for a given license spec(s)", () -> {}); + super("Generates signed elasticsearch license(s) for a given license spec(s)"); publicKeyPathOption = parser.accepts("publicKeyPath", "path to public key file").withRequiredArg().required(); // TODO: with jopt-simple 5.0, we can make these requiredUnless each other // which is effectively "one must be present" diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java index d4bcea918c276..0b58c5950a8fe 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java @@ -145,7 +145,7 @@ private static class CertificateToolParser { } CertificateTool() { - super(DESCRIPTION, () -> {}); + super(DESCRIPTION); subcommands.put("csr", new SigningRequestCommand()); subcommands.put("cert", new GenerateCertificateCommand()); subcommands.put("ca", new CertificateAuthorityCommand()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java index 85f96c4f4ef08..8d7093afc0f49 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java @@ -108,7 +108,7 @@ public class SetupPasswordTool extends MultiCommand { Function clientFunction, CheckedFunction keyStoreFunction ) { - super("Sets the passwords for reserved users", () -> {}); + super("Sets the passwords for reserved users"); subcommands.put("auto", newAutoSetup()); subcommands.put("interactive", newInteractiveSetup()); this.clientFunction = clientFunction; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java index 9c118030174c2..1ccb536b76c98 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java @@ -46,7 +46,7 @@ public class UsersTool extends MultiCommand { UsersTool() { - super("Manages elasticsearch file users", () -> {}); + super("Manages elasticsearch file users"); subcommands.put("useradd", newAddUserCommand()); subcommands.put("userdel", newDeleteUserCommand()); subcommands.put("passwd", newPasswordCommand()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java index 7a4d47967aa8c..38985ab86c830 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java @@ -34,7 +34,7 @@ public class FileTokensTool extends MultiCommand { public FileTokensTool() { - super("Manages elasticsearch service account file-tokens", () -> {}); + super("Manages elasticsearch service account file-tokens"); subcommands.put("create", newCreateFileTokenCommand()); subcommands.put("delete", newDeleteFileTokenCommand()); subcommands.put("list", newListFileTokenCommand()); diff --git a/x-pack/plugin/sql/sql-cli/src/main/java/org/elasticsearch/xpack/sql/cli/Cli.java b/x-pack/plugin/sql/sql-cli/src/main/java/org/elasticsearch/xpack/sql/cli/Cli.java index 97d5bcc3da927..f780f4582803b 100644 --- a/x-pack/plugin/sql/sql-cli/src/main/java/org/elasticsearch/xpack/sql/cli/Cli.java +++ b/x-pack/plugin/sql/sql-cli/src/main/java/org/elasticsearch/xpack/sql/cli/Cli.java @@ -85,7 +85,7 @@ private static void configureJLineLogging() { * Build the CLI. */ public Cli(CliTerminal cliTerminal) { - super("Elasticsearch SQL CLI", () -> {}); + super("Elasticsearch SQL CLI"); this.cliTerminal = cliTerminal; parser.acceptsAll(Arrays.asList("d", "debug"), "Enable debug logging"); diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java index e560aaea09596..f0977ac862e9c 100644 --- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java +++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/trigger/schedule/tool/CronEvalTool.java @@ -38,7 +38,7 @@ public class CronEvalTool extends Command { private final OptionSpec detailOption; CronEvalTool() { - super("Validates and evaluates a cron expression", () -> {}); + super("Validates and evaluates a cron expression"); this.countOption = parser.acceptsAll(Arrays.asList("c", "count"), "The number of future times this expression will be triggered") .withRequiredArg() .ofType(Integer.class) From 8081db1af715cdec7394e5dab565a4c632ac2c15 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 16:52:13 -0400 Subject: [PATCH 029/166] Call launcher in elasticsearch.bat --- distribution/src/bin/elasticsearch.bat | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/distribution/src/bin/elasticsearch.bat b/distribution/src/bin/elasticsearch.bat index e6add5dac51e3..16f0b1732a123 100644 --- a/distribution/src/bin/elasticsearch.bat +++ b/distribution/src/bin/elasticsearch.bat @@ -3,6 +3,19 @@ setlocal enabledelayedexpansion setlocal enableextensions +rem START OF NEW SCRIPT + +set LAUNCHER_TOOLNAME=server +set LAUNCHER_LIBS=lib/tools/server-cli +call "%~dp0elasticsearch-cli.bat" ^ + %%* ^ + || goto exit + +exit /b 1 + +rem END OF NEW SCRIPT + + SET params='%*' SET checkpassword=Y SET enrolltocluster=N From 43ab03f870cc8b400400f6e51292ea960d632a17 Mon Sep 17 00:00:00 2001 From: William Brafford Date: Tue, 5 Apr 2022 22:02:43 -0400 Subject: [PATCH 030/166] Stub for windows service cli tool --- distribution/build.gradle | 6 +- .../src/bin/elasticsearch-service-old.bat | 291 ++++++++++++++++++ .../src/bin/elasticsearch-service.bat | 289 +---------------- .../tools/windows-service-cli/build.gradle | 6 + .../cli/WindowsServiceCli.java | 34 ++ .../cli/WindowsServiceCliProvider.java | 27 ++ .../org.elasticserach.cli.ToolProvider | 1 + settings.gradle | 1 + 8 files changed, 372 insertions(+), 283 deletions(-) create mode 100644 distribution/src/bin/elasticsearch-service-old.bat create mode 100644 distribution/tools/windows-service-cli/build.gradle create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCliProvider.java create mode 100644 distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider diff --git a/distribution/build.gradle b/distribution/build.gradle index cd04277a2e780..cb0edbfd11aed 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -230,7 +230,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { * Properties to expand when copying packaging files * *****************************************************************************/ configurations { - ['libs', 'libsLauncher', 'libsLaunchers', 'libsVersionChecker', 'libsServerCli', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { + ['libs', 'libsLauncher', 'libsLaunchers', 'libsVersionChecker', 'libsServerCli', 'libsWindowsServiceCli', 'libsPluginCli', 'libsKeystoreCli', 'libsSecurityCli', 'libsGeoIpCli', 'libsAnsiConsole'].each { create(it) { canBeConsumed = false canBeResolved = true @@ -251,6 +251,7 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { libs project(':server') libsServerCli project(':distribution:tools:server-cli') + libsWindowsServiceCli project(':distribution:tools:windows-service-cli') libsLauncher project(':distribution:tools:launcher') libsVersionChecker project(':distribution:tools:java-version-checker') libsLaunchers project(':distribution:tools:launchers') @@ -297,6 +298,9 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) { into('tools/ansi-console') { from(configurations.libsAnsiConsole) } + into('tools/windows-service-cli') { + from(configurations.libsWindowsServiceCli) + } } diff --git a/distribution/src/bin/elasticsearch-service-old.bat b/distribution/src/bin/elasticsearch-service-old.bat new file mode 100644 index 0000000000000..0ead2d3193771 --- /dev/null +++ b/distribution/src/bin/elasticsearch-service-old.bat @@ -0,0 +1,291 @@ +@echo off + +setlocal enabledelayedexpansion +setlocal enableextensions + +set NOJAVA=nojava +if /i "%1" == "install" set NOJAVA= + +call "%~dp0elasticsearch-env.bat" %NOJAVA% || exit /b 1 + +set EXECUTABLE=%ES_HOME%\bin\elasticsearch-service-x64.exe +if "%SERVICE_ID%" == "" set SERVICE_ID=elasticsearch-service-x64 +set ARCH=64-bit + +if EXIST "%EXECUTABLE%" goto okExe +echo elasticsearch-service-x64.exe was not found... +exit /B 1 + +:okExe +set ES_VERSION=@project.version@ + +if "%SERVICE_LOG_DIR%" == "" set SERVICE_LOG_DIR=%ES_HOME%\logs + +if "x%1x" == "xx" goto displayUsage +set SERVICE_CMD=%1 +shift +if "x%1x" == "xx" goto checkServiceCmd +set SERVICE_ID=%1 + +:checkServiceCmd + +if "%LOG_OPTS%" == "" set LOG_OPTS=--LogPath "%SERVICE_LOG_DIR%" --LogPrefix "%SERVICE_ID%" --StdError auto --StdOutput auto + +if /i %SERVICE_CMD% == install goto doInstall +if /i %SERVICE_CMD% == remove goto doRemove +if /i %SERVICE_CMD% == start goto doStart +if /i %SERVICE_CMD% == stop goto doStop +if /i %SERVICE_CMD% == manager goto doManagment +echo Unknown option "%SERVICE_CMD%" +exit /B 1 + +:displayUsage +echo. +echo Usage: elasticsearch-service.bat install^|remove^|start^|stop^|manager [SERVICE_ID] +goto:eof + +:doStart +"%EXECUTABLE%" //ES//%SERVICE_ID% %LOG_OPTS% +if not errorlevel 1 goto started +echo Failed starting '%SERVICE_ID%' service +exit /B 1 +goto:eof +:started +echo The service '%SERVICE_ID%' has been started +goto:eof + +:doStop +"%EXECUTABLE%" //SS//%SERVICE_ID% %LOG_OPTS% +if not errorlevel 1 goto stopped +echo Failed stopping '%SERVICE_ID%' service +exit /B 1 +goto:eof +:stopped +echo The service '%SERVICE_ID%' has been stopped +goto:eof + +:doManagment +set EXECUTABLE_MGR=%ES_HOME%\bin\elasticsearch-service-mgr +"%EXECUTABLE_MGR%" //ES//%SERVICE_ID% +if not errorlevel 1 goto managed +echo Failed starting service manager for '%SERVICE_ID%' +exit /B 1 +goto:eof +:managed +echo Successfully started service manager for '%SERVICE_ID%'. +goto:eof + +:doRemove +rem Remove the service +"%EXECUTABLE%" //DS//%SERVICE_ID% %LOG_OPTS% +if not errorlevel 1 goto removed +echo Failed removing '%SERVICE_ID%' service +exit /B 1 +goto:eof +:removed +echo The service '%SERVICE_ID%' has been removed +goto:eof + +:doInstall +echo Installing service : "%SERVICE_ID%" +echo Using ES_JAVA_HOME (%ARCH%): "%ES_JAVA_HOME%" + +rem Check JVM server dll first +if exist "%ES_JAVA_HOME%\jre\bin\server\jvm.dll" ( + set JVM_DLL=\jre\bin\server\jvm.dll + goto foundJVM +) + +rem Check 'server' JRE (JRE installed on Windows Server) +if exist "%ES_JAVA_HOME%\bin\server\jvm.dll" ( + set JVM_DLL=\bin\server\jvm.dll + goto foundJVM +) else ( + echo ES_JAVA_HOME ("%ES_JAVA_HOME%"^) points to an invalid Java installation (no jvm.dll found in "%ES_JAVA_HOME%\jre\bin\server" or "%ES_JAVA_HOME%\bin\server"^). Exiting... + goto:eof +) + +:foundJVM +if not defined ES_TMPDIR ( + for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!LAUNCHERS_CLASSPATH!" "org.elasticsearch.tools.launchers.TempDirectory"`) do set ES_TMPDIR=%%a +) + +rem The JVM options parser produces the final JVM options to start +rem Elasticsearch. It does this by incorporating JVM options in the following +rem way: +rem - first, system JVM options are applied (these are hardcoded options in +rem the parser) +rem - second, JVM options are read from jvm.options and +rem jvm.options.d/*.options +rem - third, JVM options from ES_JAVA_OPTS are applied +rem - fourth, ergonomic JVM options are applied + +@setlocal +for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!LAUNCHERS_CLASSPATH!" "org.elasticsearch.tools.launchers.JvmOptionsParser" "!ES_PATH_CONF!" "!ES_HOME!"/plugins ^|^| echo jvm_options_parser_failed`) do set ES_JAVA_OPTS=%%a +@endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%ES_JAVA_OPTS%" & set ES_JAVA_OPTS=%ES_JAVA_OPTS% + +if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( + exit /b 1 +) + +rem The output of the JVM options parses is space-delimited. We need to +rem convert to semicolon-delimited and avoid doubled semicolons. +@setlocal +if not "%ES_JAVA_OPTS%" == "" ( + set ES_JAVA_OPTS=!ES_JAVA_OPTS: =;! + set ES_JAVA_OPTS=!ES_JAVA_OPTS:;;=;! +) +@endlocal & set ES_JAVA_OPTS=%ES_JAVA_OPTS% + +if "%ES_JAVA_OPTS:~-1%"==";" set ES_JAVA_OPTS=%ES_JAVA_OPTS:~0,-1% + +echo %ES_JAVA_OPTS% + +@setlocal EnableDelayedExpansion +for %%a in ("%ES_JAVA_OPTS:;=","%") do ( + set var=%%a + set other_opt=true + if "!var:~1,4!" == "-Xms" ( + set XMS=!var:~5,-1! + set other_opt=false + call:convertxm !XMS! JVM_MS + ) + if "!var:~1,16!" == "-XX:MinHeapSize=" ( + set XMS=!var:~17,-1! + set other_opt=false + call:convertxm !XMS! JVM_MS + ) + if "!var:~1,4!" == "-Xmx" ( + set XMX=!var:~5,-1! + set other_opt=false + call:convertxm !XMX! JVM_MX + ) + if "!var:~1,16!" == "-XX:MaxHeapSize=" ( + set XMX=!var:~17,-1! + set other_opt=false + call:convertxm !XMX! JVM_MX + ) + if "!var:~1,4!" == "-Xss" ( + set XSS=!var:~5,-1! + set other_opt=false + call:convertxk !XSS! JVM_SS + ) + if "!var:~1,20!" == "-XX:ThreadStackSize=" ( + set XSS=!var:~21,-1! + set other_opt=false + call:convertxk !XSS! JVM_SS + ) + if "!other_opt!" == "true" set OTHER_JAVA_OPTS=!OTHER_JAVA_OPTS!;!var! +) +@endlocal & set JVM_MS=%JVM_MS% & set JVM_MX=%JVM_MX% & set JVM_SS=%JVM_SS% & set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS% + +if "%JVM_MS%" == "" ( + echo minimum heap size not set; configure using -Xms via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS + goto:eof +) +if "%JVM_MX%" == "" ( + echo maximum heap size not set; configure using -Xmx via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS + goto:eof +) +if "%JVM_SS%" == "" ( + echo thread stack size not set; configure using -Xss via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS + goto:eof +) +set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS:"=% +set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS:~1% + +set ES_PARAMS=-Delasticsearch;-Des.path.home="%ES_HOME%";-Des.path.conf="%ES_PATH_CONF%";-Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%";-Des.distribution.type="%ES_DISTRIBUTION_TYPE%";-Des.bundled_jdk="%ES_BUNDLED_JDK%" + +if "%ES_START_TYPE%" == "" set ES_START_TYPE=manual +if "%ES_STOP_TIMEOUT%" == "" set ES_STOP_TIMEOUT=0 + +if "%SERVICE_DISPLAY_NAME%" == "" set SERVICE_DISPLAY_NAME=Elasticsearch %ES_VERSION% (%SERVICE_ID%) +if "%SERVICE_DESCRIPTION%" == "" set SERVICE_DESCRIPTION=Elasticsearch %ES_VERSION% Windows Service - https://elastic.co + +if not "%SERVICE_USERNAME%" == "" ( + if not "%SERVICE_PASSWORD%" == "" ( + set SERVICE_PARAMS=%SERVICE_PARAMS% --ServiceUser "%SERVICE_USERNAME%" --ServicePassword "%SERVICE_PASSWORD%" + ) +) +"%EXECUTABLE%" //IS//%SERVICE_ID% --Startup %ES_START_TYPE% --StopTimeout %ES_STOP_TIMEOUT% --StartClass org.elasticsearch.bootstrap.Elasticsearch --StartMethod main ++StartParams --quiet --StopClass org.elasticsearch.bootstrap.Elasticsearch --StopMethod close --Classpath "%ES_CLASSPATH%" --JvmMs %JVM_MS% --JvmMx %JVM_MX% --JvmSs %JVM_SS% --JvmOptions %OTHER_JAVA_OPTS% ++JvmOptions %ES_PARAMS% %LOG_OPTS% --PidFile "%SERVICE_ID%.pid" --DisplayName "%SERVICE_DISPLAY_NAME%" --Description "%SERVICE_DESCRIPTION%" --Jvm "%ES_JAVA_HOME%%JVM_DLL%" --StartMode jvm --StopMode jvm --StartPath "%ES_HOME%" %SERVICE_PARAMS% ++Environment HOSTNAME="%%COMPUTERNAME%%" + +if not errorlevel 1 goto installed +echo Failed installing '%SERVICE_ID%' service +exit /B 1 +goto:eof + +:installed +echo The service '%SERVICE_ID%' has been installed. +goto:eof + +:err +echo ES_JAVA_HOME environment variable must be set! +pause +goto:eof + +rem --- +rem Function for converting Xm[s|x] values into MB which Commons Daemon accepts +rem --- +:convertxm +set value=%~1 +rem extract last char (unit) +set unit=%value:~-1% +rem assume the unit is specified +set conv=%value:~0,-1% + +if "%unit%" == "k" goto kilo +if "%unit%" == "K" goto kilo +if "%unit%" == "m" goto mega +if "%unit%" == "M" goto mega +if "%unit%" == "g" goto giga +if "%unit%" == "G" goto giga + +rem no unit found, must be bytes; consider the whole value +set conv=%value% +rem convert to KB +set /a conv=%conv% / 1024 +:kilo +rem convert to MB +set /a conv=%conv% / 1024 +goto mega +:giga +rem convert to MB +set /a conv=%conv% * 1024 +:mega +set "%~2=%conv%" +goto:eof + +:convertxk +set value=%~1 +rem extract last char (unit) +set unit=%value:~-1% +rem assume the unit is specified +set conv=%value:~0,-1% + +if "%unit%" == "k" goto kilo +if "%unit%" == "K" goto kilo +if "%unit%" == "m" goto mega +if "%unit%" == "M" goto mega +if "%unit%" == "g" goto giga +if "%unit%" == "G" goto giga + +rem no unit found, must be bytes; consider the whole value +set conv=%value% +rem convert to KB +set /a conv=%conv% / 1024 +goto kilo +:mega +rem convert to KB +set /a conv=%conv% * 1024 +goto kilo +:giga +rem convert to KB +set /a conv=%conv% * 1024 * 1024 +:kilo +set "%~2=%conv%" +goto:eof + +endlocal +endlocal + +exit /b %ERRORLEVEL% diff --git a/distribution/src/bin/elasticsearch-service.bat b/distribution/src/bin/elasticsearch-service.bat index 0ead2d3193771..ae3f0eb34d16e 100644 --- a/distribution/src/bin/elasticsearch-service.bat +++ b/distribution/src/bin/elasticsearch-service.bat @@ -3,289 +3,14 @@ setlocal enabledelayedexpansion setlocal enableextensions -set NOJAVA=nojava -if /i "%1" == "install" set NOJAVA= - -call "%~dp0elasticsearch-env.bat" %NOJAVA% || exit /b 1 - -set EXECUTABLE=%ES_HOME%\bin\elasticsearch-service-x64.exe -if "%SERVICE_ID%" == "" set SERVICE_ID=elasticsearch-service-x64 -set ARCH=64-bit - -if EXIST "%EXECUTABLE%" goto okExe -echo elasticsearch-service-x64.exe was not found... -exit /B 1 - -:okExe -set ES_VERSION=@project.version@ - -if "%SERVICE_LOG_DIR%" == "" set SERVICE_LOG_DIR=%ES_HOME%\logs - -if "x%1x" == "xx" goto displayUsage -set SERVICE_CMD=%1 -shift -if "x%1x" == "xx" goto checkServiceCmd -set SERVICE_ID=%1 - -:checkServiceCmd - -if "%LOG_OPTS%" == "" set LOG_OPTS=--LogPath "%SERVICE_LOG_DIR%" --LogPrefix "%SERVICE_ID%" --StdError auto --StdOutput auto - -if /i %SERVICE_CMD% == install goto doInstall -if /i %SERVICE_CMD% == remove goto doRemove -if /i %SERVICE_CMD% == start goto doStart -if /i %SERVICE_CMD% == stop goto doStop -if /i %SERVICE_CMD% == manager goto doManagment -echo Unknown option "%SERVICE_CMD%" -exit /B 1 - -:displayUsage -echo. -echo Usage: elasticsearch-service.bat install^|remove^|start^|stop^|manager [SERVICE_ID] -goto:eof - -:doStart -"%EXECUTABLE%" //ES//%SERVICE_ID% %LOG_OPTS% -if not errorlevel 1 goto started -echo Failed starting '%SERVICE_ID%' service -exit /B 1 -goto:eof -:started -echo The service '%SERVICE_ID%' has been started -goto:eof - -:doStop -"%EXECUTABLE%" //SS//%SERVICE_ID% %LOG_OPTS% -if not errorlevel 1 goto stopped -echo Failed stopping '%SERVICE_ID%' service -exit /B 1 -goto:eof -:stopped -echo The service '%SERVICE_ID%' has been stopped -goto:eof - -:doManagment -set EXECUTABLE_MGR=%ES_HOME%\bin\elasticsearch-service-mgr -"%EXECUTABLE_MGR%" //ES//%SERVICE_ID% -if not errorlevel 1 goto managed -echo Failed starting service manager for '%SERVICE_ID%' -exit /B 1 -goto:eof -:managed -echo Successfully started service manager for '%SERVICE_ID%'. -goto:eof - -:doRemove -rem Remove the service -"%EXECUTABLE%" //DS//%SERVICE_ID% %LOG_OPTS% -if not errorlevel 1 goto removed -echo Failed removing '%SERVICE_ID%' service -exit /B 1 -goto:eof -:removed -echo The service '%SERVICE_ID%' has been removed -goto:eof - -:doInstall -echo Installing service : "%SERVICE_ID%" -echo Using ES_JAVA_HOME (%ARCH%): "%ES_JAVA_HOME%" - -rem Check JVM server dll first -if exist "%ES_JAVA_HOME%\jre\bin\server\jvm.dll" ( - set JVM_DLL=\jre\bin\server\jvm.dll - goto foundJVM -) - -rem Check 'server' JRE (JRE installed on Windows Server) -if exist "%ES_JAVA_HOME%\bin\server\jvm.dll" ( - set JVM_DLL=\bin\server\jvm.dll - goto foundJVM -) else ( - echo ES_JAVA_HOME ("%ES_JAVA_HOME%"^) points to an invalid Java installation (no jvm.dll found in "%ES_JAVA_HOME%\jre\bin\server" or "%ES_JAVA_HOME%\bin\server"^). Exiting... - goto:eof -) - -:foundJVM -if not defined ES_TMPDIR ( - for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!LAUNCHERS_CLASSPATH!" "org.elasticsearch.tools.launchers.TempDirectory"`) do set ES_TMPDIR=%%a -) - -rem The JVM options parser produces the final JVM options to start -rem Elasticsearch. It does this by incorporating JVM options in the following -rem way: -rem - first, system JVM options are applied (these are hardcoded options in -rem the parser) -rem - second, JVM options are read from jvm.options and -rem jvm.options.d/*.options -rem - third, JVM options from ES_JAVA_OPTS are applied -rem - fourth, ergonomic JVM options are applied - -@setlocal -for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!LAUNCHERS_CLASSPATH!" "org.elasticsearch.tools.launchers.JvmOptionsParser" "!ES_PATH_CONF!" "!ES_HOME!"/plugins ^|^| echo jvm_options_parser_failed`) do set ES_JAVA_OPTS=%%a -@endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%ES_JAVA_OPTS%" & set ES_JAVA_OPTS=%ES_JAVA_OPTS% - -if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( - exit /b 1 -) - -rem The output of the JVM options parses is space-delimited. We need to -rem convert to semicolon-delimited and avoid doubled semicolons. -@setlocal -if not "%ES_JAVA_OPTS%" == "" ( - set ES_JAVA_OPTS=!ES_JAVA_OPTS: =;! - set ES_JAVA_OPTS=!ES_JAVA_OPTS:;;=;! -) -@endlocal & set ES_JAVA_OPTS=%ES_JAVA_OPTS% - -if "%ES_JAVA_OPTS:~-1%"==";" set ES_JAVA_OPTS=%ES_JAVA_OPTS:~0,-1% - -echo %ES_JAVA_OPTS% - -@setlocal EnableDelayedExpansion -for %%a in ("%ES_JAVA_OPTS:;=","%") do ( - set var=%%a - set other_opt=true - if "!var:~1,4!" == "-Xms" ( - set XMS=!var:~5,-1! - set other_opt=false - call:convertxm !XMS! JVM_MS - ) - if "!var:~1,16!" == "-XX:MinHeapSize=" ( - set XMS=!var:~17,-1! - set other_opt=false - call:convertxm !XMS! JVM_MS - ) - if "!var:~1,4!" == "-Xmx" ( - set XMX=!var:~5,-1! - set other_opt=false - call:convertxm !XMX! JVM_MX - ) - if "!var:~1,16!" == "-XX:MaxHeapSize=" ( - set XMX=!var:~17,-1! - set other_opt=false - call:convertxm !XMX! JVM_MX - ) - if "!var:~1,4!" == "-Xss" ( - set XSS=!var:~5,-1! - set other_opt=false - call:convertxk !XSS! JVM_SS - ) - if "!var:~1,20!" == "-XX:ThreadStackSize=" ( - set XSS=!var:~21,-1! - set other_opt=false - call:convertxk !XSS! JVM_SS - ) - if "!other_opt!" == "true" set OTHER_JAVA_OPTS=!OTHER_JAVA_OPTS!;!var! -) -@endlocal & set JVM_MS=%JVM_MS% & set JVM_MX=%JVM_MX% & set JVM_SS=%JVM_SS% & set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS% - -if "%JVM_MS%" == "" ( - echo minimum heap size not set; configure using -Xms via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS - goto:eof -) -if "%JVM_MX%" == "" ( - echo maximum heap size not set; configure using -Xmx via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS - goto:eof -) -if "%JVM_SS%" == "" ( - echo thread stack size not set; configure using -Xss via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS - goto:eof -) -set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS:"=% -set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS:~1% - -set ES_PARAMS=-Delasticsearch;-Des.path.home="%ES_HOME%";-Des.path.conf="%ES_PATH_CONF%";-Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%";-Des.distribution.type="%ES_DISTRIBUTION_TYPE%";-Des.bundled_jdk="%ES_BUNDLED_JDK%" - -if "%ES_START_TYPE%" == "" set ES_START_TYPE=manual -if "%ES_STOP_TIMEOUT%" == "" set ES_STOP_TIMEOUT=0 - -if "%SERVICE_DISPLAY_NAME%" == "" set SERVICE_DISPLAY_NAME=Elasticsearch %ES_VERSION% (%SERVICE_ID%) -if "%SERVICE_DESCRIPTION%" == "" set SERVICE_DESCRIPTION=Elasticsearch %ES_VERSION% Windows Service - https://elastic.co - -if not "%SERVICE_USERNAME%" == "" ( - if not "%SERVICE_PASSWORD%" == "" ( - set SERVICE_PARAMS=%SERVICE_PARAMS% --ServiceUser "%SERVICE_USERNAME%" --ServicePassword "%SERVICE_PASSWORD%" - ) -) -"%EXECUTABLE%" //IS//%SERVICE_ID% --Startup %ES_START_TYPE% --StopTimeout %ES_STOP_TIMEOUT% --StartClass org.elasticsearch.bootstrap.Elasticsearch --StartMethod main ++StartParams --quiet --StopClass org.elasticsearch.bootstrap.Elasticsearch --StopMethod close --Classpath "%ES_CLASSPATH%" --JvmMs %JVM_MS% --JvmMx %JVM_MX% --JvmSs %JVM_SS% --JvmOptions %OTHER_JAVA_OPTS% ++JvmOptions %ES_PARAMS% %LOG_OPTS% --PidFile "%SERVICE_ID%.pid" --DisplayName "%SERVICE_DISPLAY_NAME%" --Description "%SERVICE_DESCRIPTION%" --Jvm "%ES_JAVA_HOME%%JVM_DLL%" --StartMode jvm --StopMode jvm --StartPath "%ES_HOME%" %SERVICE_PARAMS% ++Environment HOSTNAME="%%COMPUTERNAME%%" - -if not errorlevel 1 goto installed -echo Failed installing '%SERVICE_ID%' service -exit /B 1 -goto:eof - -:installed -echo The service '%SERVICE_ID%' has been installed. -goto:eof - -:err -echo ES_JAVA_HOME environment variable must be set! -pause -goto:eof - -rem --- -rem Function for converting Xm[s|x] values into MB which Commons Daemon accepts -rem --- -:convertxm -set value=%~1 -rem extract last char (unit) -set unit=%value:~-1% -rem assume the unit is specified -set conv=%value:~0,-1% - -if "%unit%" == "k" goto kilo -if "%unit%" == "K" goto kilo -if "%unit%" == "m" goto mega -if "%unit%" == "M" goto mega -if "%unit%" == "g" goto giga -if "%unit%" == "G" goto giga - -rem no unit found, must be bytes; consider the whole value -set conv=%value% -rem convert to KB -set /a conv=%conv% / 1024 -:kilo -rem convert to MB -set /a conv=%conv% / 1024 -goto mega -:giga -rem convert to MB -set /a conv=%conv% * 1024 -:mega -set "%~2=%conv%" -goto:eof - -:convertxk -set value=%~1 -rem extract last char (unit) -set unit=%value:~-1% -rem assume the unit is specified -set conv=%value:~0,-1% - -if "%unit%" == "k" goto kilo -if "%unit%" == "K" goto kilo -if "%unit%" == "m" goto mega -if "%unit%" == "M" goto mega -if "%unit%" == "g" goto giga -if "%unit%" == "G" goto giga - -rem no unit found, must be bytes; consider the whole value -set conv=%value% -rem convert to KB -set /a conv=%conv% / 1024 -goto kilo -:mega -rem convert to KB -set /a conv=%conv% * 1024 -goto kilo -:giga -rem convert to KB -set /a conv=%conv% * 1024 * 1024 -:kilo -set "%~2=%conv%" -goto:eof +set SCRIPT_NAME=%~n0 +set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% +set LAUNCHER_LIBS=lib/tools/windows-service-cli +call "%~dp0elasticsearch-cli.bat" ^ + %%* ^ + || goto exit endlocal endlocal - +:exit exit /b %ERRORLEVEL% diff --git a/distribution/tools/windows-service-cli/build.gradle b/distribution/tools/windows-service-cli/build.gradle new file mode 100644 index 0000000000000..3b1ace28c398f --- /dev/null +++ b/distribution/tools/windows-service-cli/build.gradle @@ -0,0 +1,6 @@ +apply plugin: 'elasticsearch.build' + +dependencies { + compileOnly project(":server") + compileOnly project(":libs:elasticsearch-cli") +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java new file mode 100644 index 0000000000000..dcea7563e675c --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java @@ -0,0 +1,34 @@ +/* + * 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.elasticserach.windows_service.cli; + +import joptsimple.OptionSet; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.MultiCommand; +import org.elasticsearch.cli.Terminal; + +class WindowsServiceCli extends MultiCommand { + + private static Command tmpCommand = new Command("temp") { + @Override + protected void execute(Terminal terminal, OptionSet options) throws Exception { + return; + } + }; + + WindowsServiceCli() { + super("A tool for managing Elasticsearch as a Windows service"); + subcommands.put("install", tmpCommand); + subcommands.put("remove", tmpCommand); + subcommands.put("start", tmpCommand); + subcommands.put("stop", tmpCommand); + subcommands.put("manager", tmpCommand); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCliProvider.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCliProvider.java new file mode 100644 index 0000000000000..c3fcb1b16adf8 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCliProvider.java @@ -0,0 +1,27 @@ +/* + * 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.elasticserach.windows_service.cli; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ToolProvider; + +/** + * Provides a tool for managing an Elasticsearch service on Windows + */ +public class WindowsServiceCliProvider implements ToolProvider { + @Override + public String name() { + return "windows-service"; + } + + @Override + public Command create() { + return new WindowsServiceCli(); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider new file mode 100644 index 0000000000000..74a44ea867b56 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider @@ -0,0 +1 @@ +org.elasticsearch.windows_service.cli.WindowsServiceCliProvider diff --git a/settings.gradle b/settings.gradle index a38c598df5af6..fea8cc790e37c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -60,6 +60,7 @@ List projects = [ 'distribution:tools:geoip-cli', 'distribution:tools:ansi-console', 'distribution:tools:server-cli', + 'distribution:tools:windows-service-cli', 'server', 'server:cli', 'test:framework', From 7401099eece409b61d96cd7ca5a6a1d96f052bec Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 5 Apr 2022 21:30:53 -0700 Subject: [PATCH 031/166] server sort of works, it starts! --- distribution/src/bin/elasticsearch-cli | 50 ------- distribution/src/bin/elasticsearch-env | 3 + .../org/elasticsearch/launcher/Launcher.java | 36 +---- distribution/tools/server-cli/build.gradle | 6 + .../server/cli/KeystorePasswordTerminal.java | 54 ++++++++ .../elasticsearch/server/cli/ServerCli.java | 40 ++++-- .../server/cli}/BootstrapJvmOptionsTests.java | 5 +- .../server/cli}/JvmErgonomicsTests.java | 4 +- .../server/cli}/JvmOptionsParserTests.java | 2 +- .../server/cli}/LaunchersTestCase.java | 2 +- .../cli}/MachineDependentHeapTests.java | 2 +- .../server/cli}/NodeRoleParserTests.java | 10 +- .../OverridableSystemMemoryInfoTests.java | 4 +- .../java/org/elasticsearch/cli/Terminal.java | 124 ++++++++---------- .../org/elasticsearch/cli/ToolProvider.java | 50 +++++++ .../bootstrap/plugins/LoggerTerminal.java | 2 +- .../common/cli/EnvironmentAwareCommand.java | 56 ++++++-- .../org/elasticsearch/cli/TerminalTests.java | 7 - .../bootstrap/ESElasticsearchCliTestCase.java | 4 +- .../xpack/security/cli/AutoConfigureNode.java | 6 +- .../org.elasticsearch.cli.ToolProvider | 1 + 21 files changed, 263 insertions(+), 205 deletions(-) create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java rename distribution/tools/{launchers/src/test/java/org/elasticsearch/tools/launchers => server-cli/src/test/java/org/elasticsearch/server/cli}/BootstrapJvmOptionsTests.java (95%) rename distribution/tools/{launchers/src/test/java/org/elasticsearch/tools/launchers => server-cli/src/test/java/org/elasticsearch/server/cli}/JvmErgonomicsTests.java (98%) rename distribution/tools/{launchers/src/test/java/org/elasticsearch/tools/launchers => server-cli/src/test/java/org/elasticsearch/server/cli}/JvmOptionsParserTests.java (99%) rename distribution/tools/{launchers/src/test/java/org/elasticsearch/tools/launchers => server-cli/src/test/java/org/elasticsearch/server/cli}/LaunchersTestCase.java (97%) rename distribution/tools/{launchers/src/test/java/org/elasticsearch/tools/launchers => server-cli/src/test/java/org/elasticsearch/server/cli}/MachineDependentHeapTests.java (99%) rename distribution/tools/{launchers/src/test/java/org/elasticsearch/tools/launchers => server-cli/src/test/java/org/elasticsearch/server/cli}/NodeRoleParserTests.java (91%) rename distribution/tools/{launchers/src/test/java/org/elasticsearch/tools/launchers => server-cli/src/test/java/org/elasticsearch/server/cli}/OverridableSystemMemoryInfoTests.java (96%) diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index 60a777a255313..a2cd5f4b769f6 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -30,57 +30,7 @@ export LAUNCHER_LIBS # TODO: move this to launcher if [[ "$ES_DISTRIBUTION_TYPE" == "docker" ]]; then - # Allow environment variables to be set by creating a file with the - # contents, and setting an environment variable with the suffix _FILE to - # point to it. This can be used to provide secrets to a container, without - # the values being specified explicitly when running the container. - source "$ES_HOME/bin/elasticsearch-env-from-file" - # Parse Docker env vars to customize Elasticsearch - # - # e.g. Setting the env var cluster.name=testcluster or ES_CLUSTER_NAME=testcluster - # - # will cause Elasticsearch to be invoked with -Ecluster.name=testcluster - # - # see https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html#_setting_default_settings - - declare -a es_arg_array - - containsElement () { - local e match="$1" - shift - for e; do [[ "$e" == "$match" ]] && return 0; done - return 1 - } - - # Elasticsearch settings need to either: - # a. have at least two dot separated lower case words, e.g. `cluster.name`, or - while IFS='=' read -r envvar_key envvar_value; do - es_opt="" - if [[ -n "$envvar_value" ]]; then - es_opt="-E${envvar_key}=${envvar_value}" - fi - if [[ ! -z "${es_opt}" ]] && ! containsElement "${es_opt}" "$@" ; then - es_arg_array+=("${es_opt}") - fi - done <<< "$(env | grep -E '^[-a-z0-9_]+(\.[-a-z0-9_]+)+=')" - - # b. be upper cased with underscore separators and prefixed with `ES_SETTING_`, e.g. `ES_SETTING_CLUSTER_NAME`. - # Underscores in setting names are escaped by writing them as a double-underscore e.g. "__" - while IFS='=' read -r envvar_key envvar_value; do - es_opt="" - if [[ -n "$envvar_value" ]]; then - # The long-hand sed `y` command works in any sed variant. - envvar_key="$(echo "$envvar_key" | sed -e 's/^ES_SETTING_//; s/_/./g ; s/\.\./_/g; y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' )" - es_opt="-E${envvar_key}=${envvar_value}" - fi - if [[ ! -z "${es_opt}" ]] && ! containsElement "${es_opt}" "$@" ; then - es_arg_array+=("${es_opt}") - fi - done <<< "$(env | grep -E '^ES_SETTING(_{1,2}[A-Z]+)+=')" - - # Reset the positional parameters to the es_arg_array values and any existing positional params - set -- "$@" "${es_arg_array[@]}" # The virtual file /proc/self/cgroup should list the current cgroup # membership. For each hierarchy, you can follow the cgroup path from diff --git a/distribution/src/bin/elasticsearch-env b/distribution/src/bin/elasticsearch-env index 4771d61120195..bc4dc93b7a6a7 100644 --- a/distribution/src/bin/elasticsearch-env +++ b/distribution/src/bin/elasticsearch-env @@ -82,4 +82,7 @@ else XSHARE="-Xshare:auto" fi +# TODO: move this into Launcher +export HOSTNAME + cd "$ES_HOME" diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index ac7ea550ce69b..607a782b57106 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -37,15 +37,6 @@ class Launcher { - private static Function MAP_PATH_TO_URL = p -> { - try { - return p.toUri().toURL(); - } catch (MalformedURLException e) { - throw new AssertionError(e); - } - }; - private static Predicate JAR_PREDICATE = p -> p.getFileName().toString().endsWith(".jar"); - // TODO: don't throw, catch this and give a nice error message public static void main(String[] args) throws Exception { configureLoggingWithoutConfig(); @@ -64,20 +55,7 @@ public static void main(String[] args) throws Exception { System.out.println("libs: " + libs); System.out.println("args: " + Arrays.asList(args)); - final ClassLoader cliLoader; - if (libs != null) { - List libsToLoad = Stream.of(libs.split(",")).map(homeDir::resolve).toList(); - cliLoader = loadJars(libsToLoad); - } else { - cliLoader = ClassLoader.getSystemClassLoader(); - } - ServiceLoader toolFinder = ServiceLoader.load(ToolProvider.class, cliLoader); - // TODO: assert only one tool is found? - ToolProvider tool = StreamSupport.stream(toolFinder.spliterator(), false) - .filter(p -> p.name().equals(toolname)) - .findFirst() - .orElseThrow(() -> new AssertionError("ToolProvider [" + toolname + "] not found")); - Command toolCommand = tool.create(); + Command toolCommand = ToolProvider.loadTool(toolname, libs).create(); System.exit(toolCommand.main(args, Terminal.DEFAULT)); } @@ -86,21 +64,11 @@ private static Map convertPropertiesToMap(Properties properties) Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString())); } - private static ClassLoader loadJars(List dirs) throws IOException { - final List urls = new ArrayList<>(); - for (var dir : dirs) { - try (Stream jarFiles = Files.list(dir)) { - jarFiles.filter(JAR_PREDICATE).map(MAP_PATH_TO_URL).forEach(urls::add); - } - } - return URLClassLoader.newInstance(urls.toArray(URL[]::new)); - } - /** * Configures logging without Elasticsearch configuration files based on the system property "es.logger.level" only. As such, any * logging will be written to the console. */ - public static void configureLoggingWithoutConfig() { + private static void configureLoggingWithoutConfig() { // initialize default for es.logger.level because we will not read the log4j2.properties final String loggerLevel = System.getProperty("es.logger.level", Level.INFO.name()); final Settings settings = Settings.builder().put("logger.level", loggerLevel).build(); diff --git a/distribution/tools/server-cli/build.gradle b/distribution/tools/server-cli/build.gradle index 3b1ace28c398f..0bccf5b55f5a5 100644 --- a/distribution/tools/server-cli/build.gradle +++ b/distribution/tools/server-cli/build.gradle @@ -1,6 +1,12 @@ +import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis + apply plugin: 'elasticsearch.build' dependencies { compileOnly project(":server") compileOnly project(":libs:elasticsearch-cli") + + testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" + testImplementation "junit:junit:${versions.junit}" + testImplementation "org.hamcrest:hamcrest:${versions.hamcrest}" } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java new file mode 100644 index 0000000000000..5c48ae0400387 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -0,0 +1,54 @@ +/* + * 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.server.cli; + +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.common.settings.SecureString; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Reader; + +public class KeystorePasswordTerminal extends Terminal { + + private final Terminal delegate; + + protected KeystorePasswordTerminal(Terminal delegate, SecureString password) { + super(newPasswordReader(password), delegate.getWriter(), delegate.getErrorWriter(), delegate.getLineSeparator()); + this.delegate = delegate; + } + + private static Reader newPasswordReader(final SecureString password) { + return new Reader() { + int ndx = 0; + + @Override + public int read(char[] cbuf, int off, int len) { + int charsLeft = password.length() - ndx; + if (charsLeft == 0) { + return -1; + } + int toCopy = Math.min(charsLeft, len); + System.arraycopy(password.getChars(), ndx, cbuf, off, toCopy); + ndx += toCopy; + return toCopy; + } + + @Override + public void close() { + password.close(); + } + }; + } + + @Override + public OutputStream getOutputStream() { + return delegate.getOutputStream(); + } +} diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 838909c13d37c..c39528e5d8e05 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -16,8 +16,10 @@ import org.elasticsearch.Build; import org.elasticsearch.bootstrap.ServerArgs; +import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.ToolProvider; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.cli.EnvironmentAwareCommand; import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; @@ -50,7 +52,7 @@ class ServerCli extends EnvironmentAwareCommand { // visible for testing ServerCli() { - super("Starts Elasticsearch", () -> {}); // we configure logging later so we override the base class from configuring logging + super("Starts Elasticsearch"); // we configure logging later so we override the base class from configuring logging versionOption = parser.acceptsAll(Arrays.asList("V", "version"), "Prints Elasticsearch version information and exits"); daemonizeOption = parser.acceptsAll(Arrays.asList("d", "daemonize"), "Starts Elasticsearch in the background") .availableUnless(versionOption); @@ -86,7 +88,7 @@ static void printLogsSuggestion() { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env) throws UserException { + protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { if (options.nonOptionArguments().isEmpty() == false) { throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments()); } @@ -111,28 +113,44 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th Map envVars = new HashMap<>(System.getenv()); Path tempDir = TempDirectory.initialize(envVars); - SecureString keystorePassword = null; + final SecureString keystorePassword; try { KeyStoreWrapper keystore = KeyStoreWrapper.load(KeyStoreWrapper.keystorePath(env.configFile())); if (keystore != null && keystore.hasPassword()) { keystorePassword = new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT, KeyStoreWrapper.MAX_PASSPHRASE_LENGTH)); + } else { + keystorePassword = new SecureString(new char[0]); } } catch (IOException e) { throw new UncheckedIOException(e); } - + KeystorePasswordTerminal autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); + Command autoConfigNode; + try { + autoConfigNode = ToolProvider.loadTool("auto-configure-node", "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli").create(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } if (options.has(enrollmentTokenOption)) { final String enrollmentToken = enrollmentTokenOption.value(options); - + // TODO: add -E params + int ret = autoConfigNode.main(new String[] { enrollmentTokenOption.options().get(0), enrollmentToken}, autoConfigTerminal); + if (ret != 0) { + throw new UserException(ret, "Auto security enrollment failed"); + } + } else { + // TODO: add -E params + int ret = autoConfigNode.main(new String[0], autoConfigTerminal); + switch (ret) { + case ExitCodes.OK, ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: break; + default: throw new UserException(ret, "Auto security enrollment failed"); + } } - /* - if enrollment-token: auto configure node - else: attempt auto configure, exit on codes not in [80, 73, 78] - */ - // TODO: this settings is wrong, needs to account for docker stuff through env and also auto enrollment, needs to be late binding - ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings()); + // reload settings now that auto configuration has occurred + env = createEnv(options); + ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings(), env.configFile()); List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); jvmOptions.add("-Des.path.conf=" + env.configFile()); diff --git a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/BootstrapJvmOptionsTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/BootstrapJvmOptionsTests.java similarity index 95% rename from distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/BootstrapJvmOptionsTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/BootstrapJvmOptionsTests.java index 3fb9e532db428..cb02408c752f1 100644 --- a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/BootstrapJvmOptionsTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/BootstrapJvmOptionsTests.java @@ -6,9 +6,10 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; -import org.elasticsearch.tools.launchers.BootstrapJvmOptions.PluginInfo; +import org.elasticsearch.server.cli.BootstrapJvmOptions.PluginInfo; +import org.hamcrest.Matchers; import java.util.List; import java.util.Properties; diff --git a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmErgonomicsTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java similarity index 98% rename from distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmErgonomicsTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java index ed657b9adf130..33e4a669770e4 100644 --- a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmErgonomicsTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import java.io.IOException; import java.util.Arrays; @@ -159,7 +159,7 @@ public void testMaxDirectMemorySizeChoice() throws InterruptedException, IOExcep "8G", Long.toString((8L << 30) / 2) ); - final String heapSize = randomFrom(heapMaxDirectMemorySize.keySet().toArray(String[]::new)); + final String heapSize = RandomizedTest.randomFrom(heapMaxDirectMemorySize.keySet().toArray(String[]::new)); assertThat( JvmErgonomics.choose(Arrays.asList("-Xms" + heapSize, "-Xmx" + heapSize)), hasItem("-XX:MaxDirectMemorySize=" + heapMaxDirectMemorySize.get(heapSize)) diff --git a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmOptionsParserTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmOptionsParserTests.java similarity index 99% rename from distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmOptionsParserTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmOptionsParserTests.java index fd2ddef5ef378..838c92a205fc5 100644 --- a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmOptionsParserTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmOptionsParserTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import java.io.BufferedReader; import java.io.IOException; diff --git a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/LaunchersTestCase.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/LaunchersTestCase.java similarity index 97% rename from distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/LaunchersTestCase.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/LaunchersTestCase.java index 3a66e6872b725..98cb8e3a28c1f 100644 --- a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/LaunchersTestCase.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/LaunchersTestCase.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import com.carrotsearch.randomizedtesting.JUnit3MethodProvider; import com.carrotsearch.randomizedtesting.MixWithSuiteName; diff --git a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/MachineDependentHeapTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/MachineDependentHeapTests.java similarity index 99% rename from distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/MachineDependentHeapTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/MachineDependentHeapTests.java index 2649c1e5aa11a..d843b28b3382a 100644 --- a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/MachineDependentHeapTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/MachineDependentHeapTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/NodeRoleParserTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/NodeRoleParserTests.java similarity index 91% rename from distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/NodeRoleParserTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/NodeRoleParserTests.java index 8e1950fb92f29..900e996058706 100644 --- a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/NodeRoleParserTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/NodeRoleParserTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -14,10 +14,10 @@ import java.nio.charset.StandardCharsets; import java.util.function.Consumer; -import static org.elasticsearch.tools.launchers.MachineDependentHeap.MachineNodeRole.DATA; -import static org.elasticsearch.tools.launchers.MachineDependentHeap.MachineNodeRole.MASTER_ONLY; -import static org.elasticsearch.tools.launchers.MachineDependentHeap.MachineNodeRole.ML_ONLY; -import static org.elasticsearch.tools.launchers.MachineDependentHeap.MachineNodeRole.UNKNOWN; +import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.DATA; +import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.MASTER_ONLY; +import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.ML_ONLY; +import static org.elasticsearch.server.cli.MachineDependentHeap.MachineNodeRole.UNKNOWN; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; diff --git a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/OverridableSystemMemoryInfoTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/OverridableSystemMemoryInfoTests.java similarity index 96% rename from distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/OverridableSystemMemoryInfoTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/OverridableSystemMemoryInfoTests.java index f56db17422578..086cddead4be7 100644 --- a/distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/OverridableSystemMemoryInfoTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/OverridableSystemMemoryInfoTests.java @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -package org.elasticsearch.tools.launchers; +package org.elasticsearch.server.cli; -import org.elasticsearch.tools.launchers.SystemMemoryInfo.SystemMemoryInfoException; +import org.elasticsearch.server.cli.SystemMemoryInfo.SystemMemoryInfoException; import java.util.List; diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java index ddc9e459b877a..d74bfb08a09f2 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java @@ -54,10 +54,19 @@ public enum Verbosity { /** The current verbosity for the terminal, defaulting to {@link Verbosity#NORMAL}. */ private Verbosity currentVerbosity = Verbosity.NORMAL; + private final BufferedReader reader; + + private final PrintWriter outWriter; + + private final PrintWriter errWriter; + /** The newline used when calling println. */ private final String lineSeparator; - protected Terminal(String lineSeparator) { + protected Terminal(Reader reader, PrintWriter outWriter, PrintWriter errWriter, String lineSeparator) { + this.reader = reader == null ? null : new BufferedReader(reader); + this.outWriter = outWriter; + this.errWriter = errWriter; this.lineSeparator = lineSeparator; } @@ -67,36 +76,49 @@ public void setVerbosity(Verbosity verbosity) { } /** Reads clear text from the terminal input. See {@link Console#readLine()}. */ - public abstract String readText(String prompt); + public String readText(String prompt) { + errWriter.print(prompt); // prompts should go to standard error to avoid mixing with list output + try { + final String line = reader.readLine(); + if (line == null) { + throw new IllegalStateException("unable to read from standard input; is standard input open and a tty attached?"); + } + return line; + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } /** Reads password text from the terminal input. See {@link Console#readPassword()}}. */ - public abstract char[] readSecret(String prompt); + public char[] readSecret(String prompt) { + return readText(prompt).toCharArray(); + } /** Read password text form terminal input up to a maximum length. */ - public char[] readSecret(String prompt, int maxLength) { - char[] result = readSecret(prompt); - if (result.length > maxLength) { - Arrays.fill(result, '\0'); - throw new IllegalStateException("Secret exceeded maximum length of " + maxLength); - } - return result; + public char[] readSecret(String text, int maxLength) { + errWriter.println(text); + return readLineToCharArray(reader, maxLength); } - /** Returns a Writer which can be used to write to the terminal directly using standard output. */ - public abstract PrintWriter getWriter(); + /** Returns the separate used for a new line when writing to either output or error writers. */ + public String getLineSeparator() { + return lineSeparator; + } - /** - * Returns an OutputStream which can be used to write to the terminal directly using standard output. - * May return {@code null} if this Terminal is not capable of binary output - */ - @Nullable - public abstract OutputStream getOutputStream(); + /** Returns a Writer which can be used to write to the terminal directly using standard output. */ + public PrintWriter getWriter() { + return outWriter; + } /** Returns a Writer which can be used to write to the terminal directly using standard error. */ public PrintWriter getErrorWriter() { - return ERROR_WRITER; + return errWriter; } + /** Returns an OutputStream for writing bytes directly, or null if not supported */ + @Nullable + public abstract OutputStream getOutputStream(); + /** Prints a line to the terminal at {@link Verbosity#NORMAL} verbosity level. */ public final void println(CharSequence msg) { println(Verbosity.NORMAL, msg); @@ -202,8 +224,8 @@ public static char[] readLineToCharArray(Reader reader, int maxLength) { } public void flush() { - this.getWriter().flush(); - this.getErrorWriter().flush(); + outWriter.flush(); + errWriter.flush(); } /** @@ -221,18 +243,13 @@ private static class ConsoleTerminal extends Terminal { private static final Console CONSOLE = System.console(); ConsoleTerminal() { - super(System.lineSeparator()); + super(CONSOLE.reader(), CONSOLE.writer(), ERROR_WRITER, System.lineSeparator()); } static boolean isSupported() { return CONSOLE != null; } - @Override - public PrintWriter getWriter() { - return CONSOLE.writer(); - } - @Override public OutputStream getOutputStream() { return null; @@ -252,61 +269,24 @@ public char[] readSecret(String prompt) { /** visible for testing */ static class SystemTerminal extends Terminal { - private static final PrintWriter WRITER = newWriter(); - - private BufferedReader reader; - SystemTerminal() { - super(System.lineSeparator()); - } - - @SuppressForbidden(reason = "Writer for System.out") - private static PrintWriter newWriter() { - return new PrintWriter(System.out); - } - - /** visible for testing */ - BufferedReader getReader() { - if (reader == null) { - reader = new BufferedReader(new InputStreamReader(System.in, Charset.defaultCharset())); - } - return reader; - } - - @Override - public PrintWriter getWriter() { - return WRITER; + super(newReader(), newWriter(), ERROR_WRITER, System.lineSeparator()); } + @SuppressForbidden(reason = "Writing to System.out") @Override - @SuppressForbidden(reason = "Use system.out in CLI framework") public OutputStream getOutputStream() { return System.out; } - @Override - public String readText(String text) { - getErrorWriter().print(text); // prompts should go to standard error to avoid mixing with list output - try { - final String line = getReader().readLine(); - if (line == null) { - throw new IllegalStateException("unable to read from standard input; is standard input open and a tty attached?"); - } - return line; - } catch (IOException ioe) { - throw new RuntimeException(ioe); - } + @SuppressForbidden(reason = "Reader for System.in") + private static Reader newReader() { + return new InputStreamReader(System.in); } - @Override - public char[] readSecret(String text) { - return readText(text).toCharArray(); - } - - @Override - public char[] readSecret(String text, int maxLength) { - getErrorWriter().println(text); - return readLineToCharArray(getReader(), maxLength); + @SuppressForbidden(reason = "Writer for System.out") + private static PrintWriter newWriter() { + return new PrintWriter(System.out); } } } diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java index d02dd20d2a299..686b8944ed700 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java @@ -8,6 +8,21 @@ package org.elasticsearch.cli; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + public interface ToolProvider { // TODO: merge launcher and cli lib, move to subdir in distro, make compileOnly dep in server @@ -15,4 +30,39 @@ public interface ToolProvider { // TODO: this is temporary Command create(); + + static ToolProvider loadTool(String toolname, String libs) throws IOException { + // the ES homedir is always our working dir + Path homeDir = Paths.get("").toAbsolutePath(); + final ClassLoader cliLoader; + if (libs != null) { + List libsToLoad = Stream.of(libs.split(",")).map(homeDir::resolve).toList(); + cliLoader = loadJars(libsToLoad); + } else { + cliLoader = ClassLoader.getSystemClassLoader(); + } + + ServiceLoader toolFinder = ServiceLoader.load(ToolProvider.class, cliLoader); + // TODO: assert only one tool is found? + return StreamSupport.stream(toolFinder.spliterator(), false) + .filter(p -> p.name().equals(toolname)) + .findFirst() + .orElseThrow(() -> new AssertionError("ToolProvider [" + toolname + "] not found")); + } + + private static ClassLoader loadJars(List dirs) throws IOException { + final List urls = new ArrayList<>(); + for (var dir : dirs) { + try (Stream jarFiles = Files.list(dir)) { + jarFiles.filter(p -> p.getFileName().toString().endsWith(".jar")).map(p -> { + try { + return p.toUri().toURL(); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + }).forEach(urls::add); + } + } + return URLClassLoader.newInstance(urls.toArray(URL[]::new)); + } } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/plugins/LoggerTerminal.java b/server/src/main/java/org/elasticsearch/bootstrap/plugins/LoggerTerminal.java index 32e10a1bd6c66..f2de43df6a6b9 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/plugins/LoggerTerminal.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/plugins/LoggerTerminal.java @@ -24,7 +24,7 @@ public final class LoggerTerminal extends Terminal { private static final String FQCN = LoggerTerminal.class.getName(); private LoggerTerminal(final Logger logger) { - super(System.lineSeparator()); + super(null, null, null, System.lineSeparator()); this.logger = new ExtendedLoggerWrapper((AbstractLogger) logger, logger.getName(), logger.getMessageFactory()); } diff --git a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java index 89b2b4e9d3d63..3c903e8f9a616 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java @@ -12,6 +12,7 @@ import joptsimple.OptionSpec; import joptsimple.util.KeyValuePair; +import org.elasticsearch.Build; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.Terminal; @@ -45,6 +46,45 @@ public EnvironmentAwareCommand(final String description) { @Override protected void execute(Terminal terminal, OptionSet options) throws Exception { + execute(terminal, options, createEnv(options)); + } + + // Note, isUpperCase is used so that non-letters are considered lowercase + private static boolean isLowerCase(String s) { + for (int i = 0; i < s.length(); ++i) { + if (Character.isUpperCase(s.charAt(i))) { + return false; + } + } + return true; + } + + private static final String DOCKER_SETTING_PREFIX = "ES_SETTING_"; + + private void putDockerEnvSettings(Map settings, Map envVars) { + + for (var envVar : envVars.entrySet()) { + String key = envVar.getKey(); + if (isLowerCase(key)) { + // all lowercase, like cluster.name, so just put directly + settings.put(key, envVar.getValue()); + } else if (key.startsWith(DOCKER_SETTING_PREFIX)) { + // remove prefix + key = key.substring(DOCKER_SETTING_PREFIX.length()); + // insert dots for underscores + key = key.replace('_', '.'); + // unescape double dots, which were originally double underscores + key = key.replace("..", "_"); + // lowercase the whole thing + key = key.toLowerCase(Locale.ROOT); + + settings.put(key, envVar.getValue()); + } + } + } + + /** Create an {@link Environment} for the command to use. Overrideable for tests. */ + protected Environment createEnv(OptionSet options) throws UserException { final Map settings = new HashMap<>(); for (final KeyValuePair kvp : settingOption.values(options)) { if (kvp.value.isEmpty()) { @@ -63,26 +103,20 @@ protected void execute(Terminal terminal, OptionSet options) throws Exception { settings.put(kvp.key, kvp.value); } + if (Build.CURRENT.type() == Build.Type.DOCKER) { + putDockerEnvSettings(settings, System.getenv()); + } + putSystemPropertyIfSettingIsMissing(settings, "path.data", "es.path.data"); putSystemPropertyIfSettingIsMissing(settings, "path.home", "es.path.home"); putSystemPropertyIfSettingIsMissing(settings, "path.logs", "es.path.logs"); - execute(terminal, options, createEnv(settings)); - } - - /** Create an {@link Environment} for the command to use. Overrideable for tests. */ - protected Environment createEnv(final Map settings) throws UserException { - return createEnv(Settings.EMPTY, settings); - } - - /** Create an {@link Environment} for the command to use. Overrideable for tests. */ - protected static Environment createEnv(final Settings baseSettings, final Map settings) throws UserException { final String esPathConf = System.getProperty("es.path.conf"); if (esPathConf == null) { throw new UserException(ExitCodes.CONFIG, "the system property [es.path.conf] must be set"); } return InternalSettingsPreparer.prepareEnvironment( - baseSettings, + Settings.EMPTY, settings, getConfigPath(esPathConf), // HOSTNAME is set by elasticsearch-env and elasticsearch-env.bat so it is always available diff --git a/server/src/test/java/org/elasticsearch/cli/TerminalTests.java b/server/src/test/java/org/elasticsearch/cli/TerminalTests.java index 1d3394327debe..6084d610ccff0 100644 --- a/server/src/test/java/org/elasticsearch/cli/TerminalTests.java +++ b/server/src/test/java/org/elasticsearch/cli/TerminalTests.java @@ -102,13 +102,6 @@ public void testMaxSecretLength() throws Exception { ); } - public void testTerminalReusesBufferedReaders() throws Exception { - Terminal.SystemTerminal terminal = new Terminal.SystemTerminal(); - BufferedReader reader1 = terminal.getReader(); - BufferedReader reader2 = terminal.getReader(); - assertSame("System terminal should not create multiple buffered readers", reader1, reader2); - } - private void assertPrinted(MockTerminal logTerminal, Terminal.Verbosity verbosity, String text) throws Exception { logTerminal.println(verbosity, text); String output = logTerminal.getOutput(); diff --git a/test/framework/src/main/java/org/elasticsearch/bootstrap/ESElasticsearchCliTestCase.java b/test/framework/src/main/java/org/elasticsearch/bootstrap/ESElasticsearchCliTestCase.java index e820bf3aa5699..bd3b6f0226bf3 100644 --- a/test/framework/src/main/java/org/elasticsearch/bootstrap/ESElasticsearchCliTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/bootstrap/ESElasticsearchCliTestCase.java @@ -38,7 +38,7 @@ void runTest( final Path home = createTempDir(); try { final AtomicBoolean init = new AtomicBoolean(); - final int status = Elasticsearch.main(args, new Elasticsearch() { + final int status = 0; /*Elasticsearch.main(args, new Elasticsearch() { @Override protected Environment createEnv(final Map settings) throws UserException { Settings.Builder builder = Settings.builder().put("path.home", home); @@ -57,7 +57,7 @@ void init(final boolean daemonize, final Path pidFile, final boolean quiet, Envi protected boolean addShutdownHook() { return false; } - }, terminal); + }, terminal);*/ assertThat(status, equalTo(expectedStatus)); assertThat(init.get(), equalTo(expectedInit)); outputConsumer.accept(terminal.getOutput(), terminal.getErrorOutput()); diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java index a3c1d887ab9c6..c102ed3e1196b 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java @@ -206,7 +206,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th if (false == inEnrollmentMode) { throw new UserException(ExitCodes.USAGE, "enrollment-token is a mandatory parameter when reconfiguring the node"); } - env = possiblyReconfigureNode(env, terminal); + env = possiblyReconfigureNode(env, terminal, options); } // only perform auto-configuration if the existing configuration is not conflicting (eg Security already enabled) @@ -874,7 +874,7 @@ protected static boolean anyRemoteHostNodeAddress(List allNodesTransport return false; } - private Environment possiblyReconfigureNode(Environment env, Terminal terminal) throws UserException { + private Environment possiblyReconfigureNode(Environment env, Terminal terminal, OptionSet options) throws UserException { // We remove the existing auto-configuration stanza from elasticsearch.yml, the elastisearch.keystore and // the directory with the auto-configured TLS key material, and then proceed as if elasticsearch is started // with --enrolment-token token, in the first place. @@ -917,7 +917,7 @@ private Environment possiblyReconfigureNode(Environment env, Terminal terminal) ); } // rebuild the environment after removing the settings that were added in auto-configuration. - return createEnv(Map.of("path.home", env.settings().get("path.home"))); + return createEnv(options); } else { throw new UserException( ExitCodes.USAGE, diff --git a/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider index ec7da2dc694f9..087e4da126a10 100644 --- a/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ b/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider @@ -1,3 +1,4 @@ org.elasticsearch.xpack.security.cli.CertificateToolProvider org.elasticsearch.xpack.security.cli.CertificateGenerateToolProvider org.elasticsearch.xpack.security.cli.ReconfigureNodeToolProvider +org.elasticsearch.xpack.security.cli.AutoConfigureNodeToolProvider From 38b518d151fd3d817a49d79e9754d86488587b6d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 5 Apr 2022 21:35:44 -0700 Subject: [PATCH 032/166] remove old files --- distribution/src/bin/elasticsearch | 136 ------------------ distribution/src/bin/elasticsearch-cli-old | 33 ----- .../src/bin/elasticsearch-cli-old.bat | 29 ---- distribution/src/bin/elasticsearch-old | 133 ----------------- 4 files changed, 331 deletions(-) delete mode 100644 distribution/src/bin/elasticsearch-cli-old delete mode 100644 distribution/src/bin/elasticsearch-cli-old.bat delete mode 100755 distribution/src/bin/elasticsearch-old diff --git a/distribution/src/bin/elasticsearch b/distribution/src/bin/elasticsearch index eef223be61ea7..c060f8d82916e 100755 --- a/distribution/src/bin/elasticsearch +++ b/distribution/src/bin/elasticsearch @@ -3,139 +3,3 @@ LAUNCHER_TOOLNAME=server LAUNCHER_LIBS=lib/tools/server-cli source "`dirname "$0"`"/elasticsearch-cli - -# NOT REACHABLE, TODO remove -exit 1 - - # END OF NEW SCRIPT - -CHECK_KEYSTORE=true -ATTEMPT_SECURITY_AUTO_CONFIG=true -DAEMONIZE=false -ENROLL_TO_CLUSTER=false -# Store original arg array as we will be shifting through it below -ARG_LIST=("$@") - -while [ $# -gt 0 ]; do - if [[ $1 == "--enrollment-token" ]]; then - if [ $ENROLL_TO_CLUSTER = true ]; then - echo "Multiple --enrollment-token parameters are not allowed" 1>&2 - exit 1 - fi - ENROLL_TO_CLUSTER=true - ATTEMPT_SECURITY_AUTO_CONFIG=false - ENROLLMENT_TOKEN="$2" - shift - elif [[ $1 == "-h" || $1 == "--help" || $1 == "-V" || $1 == "--version" ]]; then - CHECK_KEYSTORE=false - ATTEMPT_SECURITY_AUTO_CONFIG=false - elif [[ $1 == "-d" || $1 == "--daemonize" ]]; then - DAEMONIZE=true - fi - if [[ $# -gt 0 ]]; then - shift - fi -done - -if [ -z "$ES_TMPDIR" ]; then - ES_TMPDIR=`"$JAVA" "$XSHARE" -cp "$LAUNCHERS_CLASSPATH" org.elasticsearch.tools.launchers.TempDirectory` -fi - -if [ -z "$LIBFFI_TMPDIR" ]; then - LIBFFI_TMPDIR="$ES_TMPDIR" - export LIBFFI_TMPDIR -fi - -# get keystore password before setting java options to avoid -# conflicting GC configurations for the keystore tools -unset KEYSTORE_PASSWORD -KEYSTORE_PASSWORD= -if [[ $CHECK_KEYSTORE = true ]] \ - && bin/elasticsearch-keystore has-passwd --silent -then - if ! read -s -r -p "Elasticsearch keystore password: " KEYSTORE_PASSWORD ; then - echo "Failed to read keystore password on console" 1>&2 - exit 1 - fi -fi - -if [[ $ENROLL_TO_CLUSTER = true ]]; then - ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ - bin/elasticsearch-cli "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD" -elif [[ $ATTEMPT_SECURITY_AUTO_CONFIG = true ]]; then - # It is possible that an auto-conf failure prevents the node from starting, but this is only the exceptional case (exit code 1). - # Most likely an auto-conf failure will leave the configuration untouched (exit codes 73, 78 and 80), optionally printing a message - # if the error is uncommon or unexpected, but it should otherwise let the node to start as usual. - # It is passed in all the command line options in order to read the node settings ones (-E), while the other parameters are ignored - # (a small caveat is that it also inspects the -v option in order to provide more information on how auto config went) - if ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ - bin/elasticsearch-cli "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD"; then - : - else - retval=$? - # these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual - # eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it - if [[ $retval -ne 80 ]] && [[ $retval -ne 73 ]] && [[ $retval -ne 78 ]]; then - exit $retval - fi - fi -fi - -# The JVM options parser produces the final JVM options to start Elasticsearch. -# It does this by incorporating JVM options in the following way: -# - first, system JVM options are applied (these are hardcoded options in the -# parser) -# - second, JVM options are read from jvm.options and jvm.options.d/*.options -# - third, JVM options from ES_JAVA_OPTS are applied -# - fourth, ergonomic JVM options are applied -ES_JAVA_OPTS=`export ES_TMPDIR; "$JAVA" "$XSHARE" -cp "$LAUNCHERS_CLASSPATH" org.elasticsearch.tools.launchers.JvmOptionsParser "$ES_PATH_CONF" "$ES_HOME/plugins"` - -# Remove enrollment related parameters before passing the arg list to Elasticsearch -for i in "${!ARG_LIST[@]}"; do - if [[ ${ARG_LIST[i]} = "--enrollment-token" || ${ARG_LIST[i]} = "$ENROLLMENT_TOKEN" ]]; then - unset 'ARG_LIST[i]' - fi -done - -# manual parsing to find out, if process should be detached -if [[ $DAEMONIZE = false ]]; then - exec \ - "$JAVA" \ - "$XSHARE" \ - $ES_JAVA_OPTS \ - -Des.path.home="$ES_HOME" \ - -Des.path.conf="$ES_PATH_CONF" \ - -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ - -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ - -Des.bundled_jdk="$ES_BUNDLED_JDK" \ - -cp "$ES_CLASSPATH" \ - org.elasticsearch.bootstrap.Elasticsearch \ - "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD" -else - exec \ - "$JAVA" \ - "$XSHARE" \ - $ES_JAVA_OPTS \ - -Des.path.home="$ES_HOME" \ - -Des.path.conf="$ES_PATH_CONF" \ - -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ - -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ - -Des.bundled_jdk="$ES_BUNDLED_JDK" \ - -cp "$ES_CLASSPATH" \ - org.elasticsearch.bootstrap.Elasticsearch \ - "${ARG_LIST[@]}" \ - <<<"$KEYSTORE_PASSWORD" & - retval=$? - pid=$! - [ $retval -eq 0 ] || exit $retval - if ! ps -p $pid > /dev/null ; then - exit 1 - fi - exit 0 -fi - -exit $? diff --git a/distribution/src/bin/elasticsearch-cli-old b/distribution/src/bin/elasticsearch-cli-old deleted file mode 100644 index 6f03456eb0122..0000000000000 --- a/distribution/src/bin/elasticsearch-cli-old +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -e -o pipefail - -source "`dirname "$0"`"/elasticsearch-env - -IFS=';' read -r -a additional_sources <<< "$ES_ADDITIONAL_SOURCES" -for additional_source in "${additional_sources[@]}" -do - source "$ES_HOME"/bin/$additional_source -done - -IFS=';' read -r -a additional_classpath_directories <<< "$ES_ADDITIONAL_CLASSPATH_DIRECTORIES" -for additional_classpath_directory in "${additional_classpath_directories[@]}" -do - ES_CLASSPATH="$ES_CLASSPATH:$ES_HOME/$additional_classpath_directory/*" -done - -# use a small heap size for the CLI tools, and thus the serial collector to -# avoid stealing many CPU cycles; a user can override by setting ES_JAVA_OPTS -ES_JAVA_OPTS="-Xms4m -Xmx64m -XX:+UseSerialGC ${ES_JAVA_OPTS}" - -exec \ - "$JAVA" \ - "$XSHARE" \ - $ES_JAVA_OPTS \ - -Des.path.home="$ES_HOME" \ - -Des.path.conf="$ES_PATH_CONF" \ - -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ - -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ - -cp "$ES_CLASSPATH" \ - "$ES_MAIN_CLASS" \ - "$@" diff --git a/distribution/src/bin/elasticsearch-cli-old.bat b/distribution/src/bin/elasticsearch-cli-old.bat deleted file mode 100644 index 866e8efc6689b..0000000000000 --- a/distribution/src/bin/elasticsearch-cli-old.bat +++ /dev/null @@ -1,29 +0,0 @@ -call "%~dp0elasticsearch-env.bat" || exit /b 1 - -if defined ES_ADDITIONAL_SOURCES ( - for %%a in ("%ES_ADDITIONAL_SOURCES:;=","%") do ( - call "%~dp0%%a" - ) -) - -if defined ES_ADDITIONAL_CLASSPATH_DIRECTORIES ( - for %%a in ("%ES_ADDITIONAL_CLASSPATH_DIRECTORIES:;=","%") do ( - set ES_CLASSPATH=!ES_CLASSPATH!;!ES_HOME!/%%a/* - ) -) - -rem use a small heap size for the CLI tools, and thus the serial collector to -rem avoid stealing many CPU cycles; a user can override by setting ES_JAVA_OPTS -set ES_JAVA_OPTS=-Xms4m -Xmx64m -XX:+UseSerialGC %ES_JAVA_OPTS% - -%JAVA% ^ - %ES_JAVA_OPTS% ^ - -Des.path.home="%ES_HOME%" ^ - -Des.path.conf="%ES_PATH_CONF%" ^ - -Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%" ^ - -Des.distribution.type="%ES_DISTRIBUTION_TYPE%" ^ - -cp "%ES_CLASSPATH%" ^ - "%ES_MAIN_CLASS%" ^ - %* - -exit /b %ERRORLEVEL% diff --git a/distribution/src/bin/elasticsearch-old b/distribution/src/bin/elasticsearch-old deleted file mode 100755 index 1ba2bb43ac584..0000000000000 --- a/distribution/src/bin/elasticsearch-old +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/bash - - -CHECK_KEYSTORE=true -ATTEMPT_SECURITY_AUTO_CONFIG=true -DAEMONIZE=false -ENROLL_TO_CLUSTER=false -# Store original arg array as we will be shifting through it below -ARG_LIST=("$@") - -while [ $# -gt 0 ]; do - if [[ $1 == "--enrollment-token" ]]; then - if [ $ENROLL_TO_CLUSTER = true ]; then - echo "Multiple --enrollment-token parameters are not allowed" 1>&2 - exit 1 - fi - ENROLL_TO_CLUSTER=true - ATTEMPT_SECURITY_AUTO_CONFIG=false - ENROLLMENT_TOKEN="$2" - shift - elif [[ $1 == "-h" || $1 == "--help" || $1 == "-V" || $1 == "--version" ]]; then - CHECK_KEYSTORE=false - ATTEMPT_SECURITY_AUTO_CONFIG=false - elif [[ $1 == "-d" || $1 == "--daemonize" ]]; then - DAEMONIZE=true - fi - if [[ $# -gt 0 ]]; then - shift - fi -done - -if [ -z "$ES_TMPDIR" ]; then - ES_TMPDIR=`"$JAVA" "$XSHARE" -cp "$LAUNCHERS_CLASSPATH" org.elasticsearch.tools.launchers.TempDirectory` -fi - -if [ -z "$LIBFFI_TMPDIR" ]; then - LIBFFI_TMPDIR="$ES_TMPDIR" - export LIBFFI_TMPDIR -fi - -# get keystore password before setting java options to avoid -# conflicting GC configurations for the keystore tools -unset KEYSTORE_PASSWORD -KEYSTORE_PASSWORD= -if [[ $CHECK_KEYSTORE = true ]] \ - && bin/elasticsearch-keystore has-passwd --silent -then - if ! read -s -r -p "Elasticsearch keystore password: " KEYSTORE_PASSWORD ; then - echo "Failed to read keystore password on console" 1>&2 - exit 1 - fi -fi - -if [[ $ENROLL_TO_CLUSTER = true ]]; then - ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ - bin/elasticsearch-cli "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD" -elif [[ $ATTEMPT_SECURITY_AUTO_CONFIG = true ]]; then - # It is possible that an auto-conf failure prevents the node from starting, but this is only the exceptional case (exit code 1). - # Most likely an auto-conf failure will leave the configuration untouched (exit codes 73, 78 and 80), optionally printing a message - # if the error is uncommon or unexpected, but it should otherwise let the node to start as usual. - # It is passed in all the command line options in order to read the node settings ones (-E), while the other parameters are ignored - # (a small caveat is that it also inspects the -v option in order to provide more information on how auto config went) - if ES_MAIN_CLASS=org.elasticsearch.xpack.security.cli.AutoConfigureNode \ - ES_ADDITIONAL_SOURCES="x-pack-env;x-pack-security-env" \ - ES_ADDITIONAL_CLASSPATH_DIRECTORIES=lib/tools/security-cli \ - bin/elasticsearch-cli "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD"; then - : - else - retval=$? - # these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual - # eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it - if [[ $retval -ne 80 ]] && [[ $retval -ne 73 ]] && [[ $retval -ne 78 ]]; then - exit $retval - fi - fi -fi - -# The JVM options parser produces the final JVM options to start Elasticsearch. -# It does this by incorporating JVM options in the following way: -# - first, system JVM options are applied (these are hardcoded options in the -# parser) -# - second, JVM options are read from jvm.options and jvm.options.d/*.options -# - third, JVM options from ES_JAVA_OPTS are applied -# - fourth, ergonomic JVM options are applied -ES_JAVA_OPTS=`export ES_TMPDIR; "$JAVA" "$XSHARE" -cp "$LAUNCHERS_CLASSPATH" org.elasticsearch.tools.launchers.JvmOptionsParser "$ES_PATH_CONF" "$ES_HOME/plugins"` - -# Remove enrollment related parameters before passing the arg list to Elasticsearch -for i in "${!ARG_LIST[@]}"; do - if [[ ${ARG_LIST[i]} = "--enrollment-token" || ${ARG_LIST[i]} = "$ENROLLMENT_TOKEN" ]]; then - unset 'ARG_LIST[i]' - fi -done - -# manual parsing to find out, if process should be detached -if [[ $DAEMONIZE = false ]]; then - exec \ - "$JAVA" \ - "$XSHARE" \ - $ES_JAVA_OPTS \ - -Des.path.home="$ES_HOME" \ - -Des.path.conf="$ES_PATH_CONF" \ - -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ - -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ - -Des.bundled_jdk="$ES_BUNDLED_JDK" \ - -cp "$ES_CLASSPATH" \ - org.elasticsearch.bootstrap.Elasticsearch \ - "${ARG_LIST[@]}" <<<"$KEYSTORE_PASSWORD" -else - exec \ - "$JAVA" \ - "$XSHARE" \ - $ES_JAVA_OPTS \ - -Des.path.home="$ES_HOME" \ - -Des.path.conf="$ES_PATH_CONF" \ - -Des.distribution.flavor="$ES_DISTRIBUTION_FLAVOR" \ - -Des.distribution.type="$ES_DISTRIBUTION_TYPE" \ - -Des.bundled_jdk="$ES_BUNDLED_JDK" \ - -cp "$ES_CLASSPATH" \ - org.elasticsearch.bootstrap.Elasticsearch \ - "${ARG_LIST[@]}" \ - <<<"$KEYSTORE_PASSWORD" & - retval=$? - pid=$! - [ $retval -eq 0 ] || exit $retval - if ! ps -p $pid > /dev/null ; then - exit 1 - fi - exit 0 -fi - -exit $? From d49e11b99cb21359f6952161e916e15df144cf8c Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 6 Apr 2022 21:10:16 -0700 Subject: [PATCH 033/166] daemonization works --- .../org/elasticsearch/launcher/Launcher.java | 19 ++++- .../elasticsearch/server/cli/ServerCli.java | 85 +++++++++---------- .../elasticsearch/bootstrap/Bootstrap.java | 27 +++--- .../bootstrap/Elasticsearch.java | 33 +++++-- ...ElasticsearchUncaughtExceptionHandler.java | 10 +-- .../org/elasticsearch/bootstrap/Security.java | 2 +- 6 files changed, 107 insertions(+), 69 deletions(-) diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java index 607a782b57106..8b919cf0a26b0 100644 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java @@ -37,6 +37,8 @@ class Launcher { + private static volatile Command command; + // TODO: don't throw, catch this and give a nice error message public static void main(String[] args) throws Exception { configureLoggingWithoutConfig(); @@ -55,8 +57,8 @@ public static void main(String[] args) throws Exception { System.out.println("libs: " + libs); System.out.println("args: " + Arrays.asList(args)); - Command toolCommand = ToolProvider.loadTool(toolname, libs).create(); - System.exit(toolCommand.main(args, Terminal.DEFAULT)); + command = ToolProvider.loadTool(toolname, libs).create(); + System.exit(command.main(args, Terminal.DEFAULT)); } private static Map convertPropertiesToMap(Properties properties) { @@ -74,4 +76,17 @@ private static void configureLoggingWithoutConfig() { final Settings settings = Settings.builder().put("logger.level", loggerLevel).build(); LogConfigurator.configureWithoutConfig(settings); } + + /** + * Required method that's called by Apache Commons procrun when + * running as a service on Windows, when the service is stopped. + * + * http://commons.apache.org/proper/commons-daemon/procrun.html + * + * NOTE: If this method is renamed and/or moved, make sure to + * update elasticsearch-service.bat! + */ + static void close(String[] args) throws IOException { + command.close(); + } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index c39528e5d8e05..c2af7ee866b39 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -9,7 +9,6 @@ package org.elasticsearch.server.cli; import joptsimple.OptionSet; - import joptsimple.OptionSpec; import joptsimple.OptionSpecBuilder; import joptsimple.util.PathConverter; @@ -28,12 +27,11 @@ import org.elasticsearch.core.PathUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; -import org.elasticsearch.node.NodeValidationException; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedWriter; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.ArrayList; @@ -68,25 +66,6 @@ class ServerCli extends EnvironmentAwareCommand { .withRequiredArg(); } - /** - * Prints a message directing the user to look at the logs. A message is only printed if - * logging has been configured. - */ - static void printLogsSuggestion() { - final String basePath = System.getProperty("es.logs.base_path"); - // It's possible to fail before logging has been configured, in which case there's no point - // suggesting that the user look in the log file. - if (basePath != null) { - Terminal.DEFAULT.errorPrintln( - "ERROR: Elasticsearch did not exit normally - check the logs at " - + basePath - + System.getProperty("file.separator") - + System.getProperty("es.logs.cluster_name") - + ".log" - ); - } - } - @Override protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { if (options.nonOptionArguments().isEmpty() == false) { @@ -128,7 +107,8 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th KeystorePasswordTerminal autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); Command autoConfigNode; try { - autoConfigNode = ToolProvider.loadTool("auto-configure-node", "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli").create(); + String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; + autoConfigNode = ToolProvider.loadTool("auto-configure-node", autoConfigLibs).create(); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -148,6 +128,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th } } + // reload settings now that auto configuration has occurred env = createEnv(options); ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings(), env.configFile()); @@ -188,23 +169,54 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th ProcessBuilder builder = new ProcessBuilder(command); builder.environment().putAll(envVars); - builder.inheritIO(); - builder.redirectInput(ProcessBuilder.Redirect.PIPE); + builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); + try { - Process process = builder.start(); + final Process process = builder.start(); try (var out = new OutputStreamStreamOutput(process.getOutputStream())) { serverArgs.writeTo(out); } - // TODO: handle daemonize flag + String userExceptionMsg = null; + boolean ready = false; + InputStream err = process.getErrorStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(err)); + try { + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty() == false && line.charAt(0) == '\24') { + userExceptionMsg = line.substring(1); + } else if (line.isEmpty() == false && line.charAt(0) == '\21') { + ready = true; + break; + } else { + terminal.getErrorWriter().println(line); + } + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + if (ready && daemonize) { + process.getOutputStream().close(); + process.getErrorStream().close(); + process.getInputStream().close(); + return; + } + int code = process.waitFor(); + terminal.flush(); System.out.println("exited: " + code); - System.exit(code); + if (code != ExitCodes.OK) { + throw new UserException(code, userExceptionMsg); + } } catch (IOException e) { throw new UncheckedIOException(e); } catch (InterruptedException e) { throw new UserException(ExitCodes.IO_ERROR, "Interrupted while waiting for Elasticsearch process"); } + + // TODO: add ctrl-c handler so we can wait for subprocess // TODO: check the java.io.tmpdir // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately /*try { @@ -219,17 +231,4 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th throw new UserException(ExitCodes.CONFIG, e.getMessage()); }*/ } - - /** - * Required method that's called by Apache Commons procrun when - * running as a service on Windows, when the service is stopped. - * - * http://commons.apache.org/proper/commons-daemon/procrun.html - * - * NOTE: If this method is renamed and/or moved, make sure to - * update elasticsearch-service.bat! - */ - static void close(String[] args) throws IOException { - //Bootstrap.stop(); - } } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index ba94dcb965f41..cbef7e489d22a 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -8,6 +8,7 @@ package org.elasticsearch.bootstrap; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.Appender; @@ -44,7 +45,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintStream; +import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.nio.file.Path; @@ -323,16 +326,6 @@ static void init(final boolean foreground, final Path pidFile, final boolean qui } try { - final boolean closeStandardStreams = (foreground == false) || quiet; - if (closeStandardStreams) { - final Logger rootLogger = LogManager.getRootLogger(); - final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class); - if (maybeConsoleAppender != null) { - Loggers.removeAppender(rootLogger, maybeConsoleAppender); - } - sysOutCloser.run(); - } - // fail if somebody replaced the lucene jars checkLucene(); @@ -372,6 +365,14 @@ static void init(final boolean foreground, final Path pidFile, final boolean qui // `--quiet`, not `-d`, so we want users to be able to see // startup errors via journalctl. if (foreground == false) { + System.err.println("CLOSING STREAMS"); + final Logger rootLogger = LogManager.getRootLogger(); + rootLogger.log(Level.INFO, "CLOSING STREAMS"); + final Appender maybeConsoleAppender = Loggers.findAppender(rootLogger, ConsoleAppender.class); + if (maybeConsoleAppender != null) { + Loggers.removeAppender(rootLogger, maybeConsoleAppender); + } + sysOutCloser.run(); sysErrorCloser.run(); } @@ -428,7 +429,11 @@ private static Runnable getSysOutCloser() { @SuppressForbidden(reason = "System#err") private static Runnable getSysErrorCloser() { - return System.err::close; + final PrintStream err = System.err; + return () -> { + err.println('\21'); + err.close(); + }; } private static void checkLucene() { diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index e4e9347843d38..2eeafdee306c4 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -8,13 +8,14 @@ package org.elasticsearch.bootstrap; -import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.env.Environment; import org.elasticsearch.node.NodeValidationException; +import java.io.PrintStream; import java.nio.file.Path; import java.security.Permission; import java.security.Security; @@ -46,26 +47,44 @@ public void checkPermission(Permission perm) { }); LogConfigurator.registerErrorListener(); final ServerArgs serverArgs; - try (var in = new InputStreamStreamInput(System.in)) { - serverArgs = new ServerArgs(in); - } + var in = new InputStreamStreamInput(System.in); + serverArgs = new ServerArgs(in); final Elasticsearch elasticsearch = new Elasticsearch(); System.out.println("RUNNING ELASTICSEARCH"); System.out.println(serverArgs); - elasticsearch.init(serverArgs.daemonize(), serverArgs.pidFile(), false, new Environment(serverArgs.nodeSettings(), serverArgs.configDir())); + PrintStream err = System.err; + int exitCode = 0; + try { + elasticsearch.init(serverArgs.daemonize(), + serverArgs.pidFile(), + false, + new Environment(serverArgs.nodeSettings(), serverArgs.configDir())); + } catch (NodeValidationException e) { + exitCode = ExitCodes.CONFIG; + err.print('\24'); + err.println(e.getMessage()); + } catch (UserException e) { + exitCode = e.exitCode; + err.print('\24'); + err.println(e.getMessage()); + } + if (exitCode != ExitCodes.OK) { + printLogsSuggestion(err); + System.exit(exitCode); + } } /** * Prints a message directing the user to look at the logs. A message is only printed if * logging has been configured. */ - static void printLogsSuggestion() { + static void printLogsSuggestion(PrintStream err) { final String basePath = System.getProperty("es.logs.base_path"); // It's possible to fail before logging has been configured, in which case there's no point // suggesting that the user look in the log file. if (basePath != null) { - Terminal.DEFAULT.errorPrintln( + err.println( "ERROR: Elasticsearch did not exit normally - check the logs at " + basePath + System.getProperty("file.separator") diff --git a/server/src/main/java/org/elasticsearch/bootstrap/ElasticsearchUncaughtExceptionHandler.java b/server/src/main/java/org/elasticsearch/bootstrap/ElasticsearchUncaughtExceptionHandler.java index a56e074a47c09..4ffdeac4633a4 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/ElasticsearchUncaughtExceptionHandler.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/ElasticsearchUncaughtExceptionHandler.java @@ -54,21 +54,21 @@ static boolean isFatalUncaught(Throwable e) { void onFatalUncaught(final String threadName, final Throwable t) { final String message = "fatal error in thread [" + threadName + "], exiting"; logger.error(message, t); - Terminal.DEFAULT.errorPrintln(message); + /*Terminal.DEFAULT.errorPrintln(message); t.printStackTrace(Terminal.DEFAULT.getErrorWriter()); // Without a final flush, the stacktrace may not be shown before ES exits - Terminal.DEFAULT.flush(); + Terminal.DEFAULT.flush();*/ - Elasticsearch.printLogsSuggestion(); + //Elasticsearch.printLogsSuggestion(); } void onNonFatalUncaught(final String threadName, final Throwable t) { final String message = "uncaught exception in thread [" + threadName + "]"; logger.error(message, t); - Terminal.DEFAULT.errorPrintln(message); + /*Terminal.DEFAULT.errorPrintln(message); t.printStackTrace(Terminal.DEFAULT.getErrorWriter()); // Without a final flush, the stacktrace may not be shown if ES goes on to exit - Terminal.DEFAULT.flush(); + Terminal.DEFAULT.flush();*/ } void halt(int status) { diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Security.java b/server/src/main/java/org/elasticsearch/bootstrap/Security.java index 30909e8723d2e..c9b15ae6859f8 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Security.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -128,7 +128,7 @@ static void configure(Environment environment, boolean filterBadDefaults) throws final String[] classesThatCanExit = new String[] { // SecureSM matches class names as regular expressions so we escape the $ that arises from the nested class name ElasticsearchUncaughtExceptionHandler.PrivilegedHaltAction.class.getName().replace("$", "\\$"), - Command.class.getName() }; + Elasticsearch.class.getName() }; setSecurityManager(new SecureSM(classesThatCanExit)); // do some basic tests From d026aa7b46fa50d36f831c8ed1634a32b8b19688 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 6 Apr 2022 21:23:11 -0700 Subject: [PATCH 034/166] some cleanup --- .../elasticsearch/server/cli/ServerCli.java | 127 +++++++++--------- 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index c2af7ee866b39..b3680a5981616 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -72,17 +72,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments()); } if (options.has(versionOption)) { - final String versionOutput = String.format( - Locale.ROOT, - "Version: %s, Build: %s/%s/%s/%s, JVM: %s", - Build.CURRENT.qualifiedVersion(), - Build.CURRENT.flavor().displayName(), - Build.CURRENT.type().displayName(), - Build.CURRENT.hash(), - Build.CURRENT.date(), - JvmInfo.jvmInfo().version() - ); - terminal.println(versionOutput); + printVersion(terminal); return; } @@ -92,45 +82,12 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th Map envVars = new HashMap<>(System.getenv()); Path tempDir = TempDirectory.initialize(envVars); - final SecureString keystorePassword; - try { - KeyStoreWrapper keystore = KeyStoreWrapper.load(KeyStoreWrapper.keystorePath(env.configFile())); - if (keystore != null && keystore.hasPassword()) { - keystorePassword = new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT, KeyStoreWrapper.MAX_PASSPHRASE_LENGTH)); - } else { - keystorePassword = new SecureString(new char[0]); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - KeystorePasswordTerminal autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); - Command autoConfigNode; - try { - String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; - autoConfigNode = ToolProvider.loadTool("auto-configure-node", autoConfigLibs).create(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - if (options.has(enrollmentTokenOption)) { - final String enrollmentToken = enrollmentTokenOption.value(options); - // TODO: add -E params - int ret = autoConfigNode.main(new String[] { enrollmentTokenOption.options().get(0), enrollmentToken}, autoConfigTerminal); - if (ret != 0) { - throw new UserException(ret, "Auto security enrollment failed"); - } - } else { - // TODO: add -E params - int ret = autoConfigNode.main(new String[0], autoConfigTerminal); - switch (ret) { - case ExitCodes.OK, ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: break; - default: throw new UserException(ret, "Auto security enrollment failed"); - } - } - + final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); + autoConfigureSecurity(terminal, options, keystorePassword); - // reload settings now that auto configuration has occurred + // reload settings since auto security might have changed them env = createEnv(options); + // TODO: add keystore password to server args ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings(), env.configFile()); List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); @@ -139,31 +96,14 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th jvmOptions.add("-Des.distribution.type=" + System.getProperty("es.distribution.type")); jvmOptions.add("-Des.bundled_jdk=" + System.getProperty("es.bundled_jdk")); - /* - create process - - set command - 1. figure out java path - 2. add jvm options - 3. add classpath - 4. add main class - 5. add args - - set env - - set redirects - - launch java process - if (daemon) exit (TODO: wait until server is started) - wait on process - - */ - String esHome = System.getProperty("es.path.home"); + Path esHome = PathUtils.get(System.getProperty("es.path.home")); Path javaHome = PathUtils.get(System.getProperty("java.home")); List command = new ArrayList<>(); // TODO: fix this so it works on windows command.add(javaHome.resolve("bin").resolve("java").toString()); command.addAll(jvmOptions); command.add("-cp"); - // TODO: fix this to work on windows - command.add(esHome + "/lib/*"); + command.add(esHome.resolve("lib").resolve("*").toString()); command.add("org.elasticsearch.bootstrap.Elasticsearch"); System.out.println("command: " + command); @@ -231,4 +171,57 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th throw new UserException(ExitCodes.CONFIG, e.getMessage()); }*/ } + + private void printVersion(Terminal terminal) { + final String versionOutput = String.format( + Locale.ROOT, + "Version: %s, Build: %s/%s/%s/%s, JVM: %s", + Build.CURRENT.qualifiedVersion(), + Build.CURRENT.flavor().displayName(), + Build.CURRENT.type().displayName(), + Build.CURRENT.hash(), + Build.CURRENT.date(), + JvmInfo.jvmInfo().version() + ); + terminal.println(versionOutput); + } + + private SecureString getKeystorePassword(Path configDir, Terminal terminal) { + try { + KeyStoreWrapper keystore = KeyStoreWrapper.load(KeyStoreWrapper.keystorePath(configDir)); + if (keystore != null && keystore.hasPassword()) { + return new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT, KeyStoreWrapper.MAX_PASSPHRASE_LENGTH)); + } else { + return new SecureString(new char[0]); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void autoConfigureSecurity(Terminal terminal, OptionSet options, SecureString keystorePassword) throws UserException { + KeystorePasswordTerminal autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); + Command autoConfigNode; + try { + String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; + autoConfigNode = ToolProvider.loadTool("auto-configure-node", autoConfigLibs).create(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + if (options.has(enrollmentTokenOption)) { + final String enrollmentToken = enrollmentTokenOption.value(options); + // TODO: add -E params + int ret = autoConfigNode.main(new String[] { enrollmentTokenOption.options().get(0), enrollmentToken}, autoConfigTerminal); + if (ret != 0) { + throw new UserException(ret, "Auto security enrollment failed"); + } + } else { + // TODO: add -E params + int ret = autoConfigNode.main(new String[0], autoConfigTerminal); + switch (ret) { + case ExitCodes.OK, ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: break; + default: throw new UserException(ret, "Auto security enrollment failed"); + } + } + } } From aae9eb4bcb3700f4f51d8ca6f7eaad6873a1cd0d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 6 Apr 2022 22:15:10 -0700 Subject: [PATCH 035/166] pass -E through to autoconfigurenode --- .../elasticsearch/server/cli/ServerCli.java | 37 +++++++++++-------- .../common/cli/EnvironmentAwareCommand.java | 2 +- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index b3680a5981616..a4cfe5d9bb4e0 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.settings.KeyStoreWrapper; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.core.PathUtils; +import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; @@ -137,9 +138,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th } if (ready && daemonize) { - process.getOutputStream().close(); - process.getErrorStream().close(); - process.getInputStream().close(); + closeStreams(process); return; } @@ -172,6 +171,10 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th }*/ } + private void closeStreams(Process process) throws IOException { + IOUtils.close(process.getOutputStream(), process.getInputStream(), process.getErrorStream()); + } + private void printVersion(Terminal terminal) { final String versionOutput = String.format( Locale.ROOT, @@ -199,25 +202,29 @@ private SecureString getKeystorePassword(Path configDir, Terminal terminal) { } } - private void autoConfigureSecurity(Terminal terminal, OptionSet options, SecureString keystorePassword) throws UserException { + private void autoConfigureSecurity(Terminal terminal, OptionSet options, SecureString keystorePassword) throws Exception { KeystorePasswordTerminal autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); - Command autoConfigNode; - try { - String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; - autoConfigNode = ToolProvider.loadTool("auto-configure-node", autoConfigLibs).create(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + + // reconstitute command lines + List settingValues = options.asMap().get(settingOption); + List args = new ArrayList<>(); + settingValues.forEach(v -> { + args.add("-E"); + args.add(v.toString()); + }); + + String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; + Command autoConfigNode = ToolProvider.loadTool("auto-configure-node", autoConfigLibs).create(); if (options.has(enrollmentTokenOption)) { final String enrollmentToken = enrollmentTokenOption.value(options); - // TODO: add -E params - int ret = autoConfigNode.main(new String[] { enrollmentTokenOption.options().get(0), enrollmentToken}, autoConfigTerminal); + args.add("--enrollment-token"); + args.add(enrollmentToken); + int ret = autoConfigNode.main(args.toArray(new String[0]), autoConfigTerminal); if (ret != 0) { throw new UserException(ret, "Auto security enrollment failed"); } } else { - // TODO: add -E params - int ret = autoConfigNode.main(new String[0], autoConfigTerminal); + int ret = autoConfigNode.main(args.toArray(new String[0]), autoConfigTerminal); switch (ret) { case ExitCodes.OK, ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: break; default: throw new UserException(ret, "Auto security enrollment failed"); diff --git a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java index 3c903e8f9a616..cf640293c4120 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java @@ -31,7 +31,7 @@ /** A cli command which requires an {@link org.elasticsearch.env.Environment} to use current paths and settings. */ public abstract class EnvironmentAwareCommand extends Command { - private final OptionSpec settingOption; + protected final OptionSpec settingOption; /** * Construct the command with the specified command description. This command will have logging configured without reading Elasticsearch From 444f6d474bec4f52bab2de97a9315fdd3269ac3d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 7 Apr 2022 06:19:53 -0700 Subject: [PATCH 036/166] add comment --- .../main/java/org/elasticsearch/server/cli/ServerCli.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index a4cfe5d9bb4e0..024d826ba254c 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -92,7 +92,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings(), env.configFile()); List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); - jvmOptions.add("-Des.path.conf=" + env.configFile()); + //jvmOptions.add("-Des.path.conf=" + env.configFile()); jvmOptions.add("-Des.distribution.flavor=" + System.getProperty("es.distribution.flavor")); jvmOptions.add("-Des.distribution.type=" + System.getProperty("es.distribution.type")); jvmOptions.add("-Des.bundled_jdk=" + System.getProperty("es.bundled_jdk")); @@ -128,6 +128,9 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th userExceptionMsg = line.substring(1); } else if (line.isEmpty() == false && line.charAt(0) == '\21') { ready = true; + // The server closes stderr right after this message, but for some unknown reason + // the pipe closing does not close this end of the pipe, so we must explicitly + // break out of this loop, or we will block forever on the next read. break; } else { terminal.getErrorWriter().println(line); From 82cf49521316f5b5be27a47aad581a57088bc2df Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 7 Apr 2022 13:32:02 -0700 Subject: [PATCH 037/166] windows service cli --- distribution/src/bin/elasticsearch-cli | 4 + distribution/src/bin/elasticsearch-cli.bat | 4 + distribution/src/bin/elasticsearch-env.bat | 4 + distribution/tools/server-cli/build.gradle | 2 +- .../tools/windows-service-cli/build.gradle | 2 +- .../windows_service/ProcrunCommand.java | 104 ++++++++++++ .../windows_service/WindowsServiceCli.java | 159 ++++++++++++++++++ .../WindowsServiceCliProvider.java | 2 +- .../cli/WindowsServiceCli.java | 34 ---- .../org.elasticserach.cli.ToolProvider | 2 +- 10 files changed, 279 insertions(+), 38 deletions(-) create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java rename distribution/tools/windows-service-cli/src/main/java/org/{elasticserach/windows_service/cli => elasticsearch/windows_service}/WindowsServiceCliProvider.java (94%) delete mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java diff --git a/distribution/src/bin/elasticsearch-cli b/distribution/src/bin/elasticsearch-cli index a2cd5f4b769f6..25263a03429f1 100644 --- a/distribution/src/bin/elasticsearch-cli +++ b/distribution/src/bin/elasticsearch-cli @@ -15,6 +15,10 @@ ES_DISTRIBUTION_TYPE=@es.distribution.type@ ES_BUNDLED_JDK=@es.bundled_jdk@ LAUNCHER_CLASSPATH=$ES_HOME/lib/*:$ES_HOME/lib/launcher/* +# use a small heap size for the CLI tools, and thus the serial collector to +# avoid stealing many CPU cycles; a user can override by setting LAUNCHER_JAVA_OPTS +LAUNCHER_JAVA_OPTS="-Xms4m -Xmx64m -XX:+UseSerialGC ${LAUNCHER_JAVA_OPTS}" + # TODO: move this to launcher if [[ "$ES_BUNDLED_JDK" == "false" ]]; then echo "warning: no-jdk distributions that do not bundle a JDK are deprecated and will be removed in a future release" >&2 diff --git a/distribution/src/bin/elasticsearch-cli.bat b/distribution/src/bin/elasticsearch-cli.bat index d24570dad3e73..e7afdc894737e 100644 --- a/distribution/src/bin/elasticsearch-cli.bat +++ b/distribution/src/bin/elasticsearch-cli.bat @@ -12,6 +12,10 @@ set ES_DISTRIBUTION_TYPE=@es.distribution.type@ set ES_BUNDLED_JDK=@es.bundled_jdk@ set LAUNCHER_CLASSPATH=%ES_HOME%/lib/*;%ES_HOME%/lib/launcher/* +rem use a small heap size for the CLI tools, and thus the serial collector to +rem avoid stealing many CPU cycles; a user can override by setting LAUNCHER_JAVA_OPTS +set LAUNCHER_JAVA_OPTS=-Xms4m -Xmx64m -XX:+UseSerialGC %LAUNCHER_JAVA_OPTS% + %JAVA% ^ %LAUNCHER_JAVA_OPTS% ^ -Des.path.home="%ES_HOME%" ^ diff --git a/distribution/src/bin/elasticsearch-env.bat b/distribution/src/bin/elasticsearch-env.bat index 993708af0afd7..cbff9d42e9df5 100644 --- a/distribution/src/bin/elasticsearch-env.bat +++ b/distribution/src/bin/elasticsearch-env.bat @@ -17,6 +17,7 @@ rem now set the classpath set ES_CLASSPATH=!ES_HOME!\lib\* set LAUNCHERS_CLASSPATH=!ES_CLASSPATH!;!ES_HOME!\lib\launchers\* +rem TODO: remove this, no longer needed rem now set the path to java, pass "nojava" arg to skip setting ES_JAVA_HOME and JAVA if "%1" == "nojava" ( exit /b @@ -28,6 +29,9 @@ rem by setting ES_JAVA_HOME= if defined ES_JAVA_HOME ( set JAVA="%ES_JAVA_HOME%\bin\java.exe" set JAVA_TYPE=ES_JAVA_HOME + + rem TODO: add version check + rem TODO: add dll check ) else ( rem use the bundled JDK (default) set JAVA="%ES_HOME%\jdk\bin\java.exe" diff --git a/distribution/tools/server-cli/build.gradle b/distribution/tools/server-cli/build.gradle index 0bccf5b55f5a5..8968ed5cdf0c8 100644 --- a/distribution/tools/server-cli/build.gradle +++ b/distribution/tools/server-cli/build.gradle @@ -1,6 +1,6 @@ import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis -apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.java' dependencies { compileOnly project(":server") diff --git a/distribution/tools/windows-service-cli/build.gradle b/distribution/tools/windows-service-cli/build.gradle index 3b1ace28c398f..ad158ec05961b 100644 --- a/distribution/tools/windows-service-cli/build.gradle +++ b/distribution/tools/windows-service-cli/build.gradle @@ -1,4 +1,4 @@ -apply plugin: 'elasticsearch.build' +apply plugin: 'elasticsearch.java' dependencies { compileOnly project(":server") diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java new file mode 100644 index 0000000000000..cb95452d62df5 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java @@ -0,0 +1,104 @@ +/* + * 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.windows_service; + +import joptsimple.OptionSet; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + * Base command for interacting with Apache procrun executable. + */ +abstract class ProcrunCommand extends Command { + private final Path procrun; + private final String cmd; + + protected ProcrunCommand(String desc, String cmd) { + super(desc); + this.procrun = Paths.get("").resolve("bin").resolve(getExecutable()); + if (Files.exists(procrun) == false) { + throw new IllegalStateException("Missing procrun exe: " + procrun); + } + this.cmd = cmd; + } + + protected String getExecutable() { + return "elasticsearch-service-x64.exe"; + } + + @Override + protected void execute(Terminal terminal, OptionSet options) throws Exception { + Map env = System.getenv(); + Path esHome = Paths.get(""); // TODO: this should be passed through execute + String serviceId = getServiceId(options, env); + preExecute(terminal, serviceId); + + List procrunCmd = new ArrayList<>(); + procrunCmd.add(procrun.toString()); + procrunCmd.add(getLogArgs(serviceId, esHome, env)); + procrunCmd.add(getAdditionalArgs(serviceId, esHome, env)); + + ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/C", String.join(" ", procrunCmd).trim()); + Process process = processBuilder.start(); + int ret = process.waitFor(); + if (ret != ExitCodes.OK) { + throw new UserException(ret, getFailureMessage(serviceId)); + } else { + terminal.println(getSuccessMessage(serviceId)); + } + } + + private String getServiceId(OptionSet options, Map env) throws UserException { + List args = options.nonOptionArguments(); + if (args.size() > 1) { + throw new UserException(ExitCodes.USAGE, null); + } + final String serviceId; + if (args.size() > 0) { + serviceId = args.get(0).toString(); + } else { + serviceId = env.getOrDefault("SERVICE_ID", "elasticsearch-service-x64"); + } + return serviceId; + } + + private String getLogArgs(String serviceId, Path esHome, Map env) { + String logArgs = env.get("LOG_OPTS"); + if (logArgs != null && logArgs.isBlank() == false) { + return logArgs; + } + String logsDir = env.get("SERVICE_LOG_DIR"); + if (logsDir == null || logsDir.isBlank()) { + logsDir = esHome.resolve("logs").toString(); + } + String logArgsFormat = "--LogPath \"%s\" --LogPrefix \"%s\" --StdError auto --StdOutput auto"; + return String.format(Locale.ROOT, logArgsFormat, logsDir, serviceId); + } + + protected String getAdditionalArgs(String serviceId, Path esHome, Map env) { + return ""; + } + + protected void preExecute(Terminal terminal, String serviceId) throws UserException {} + + protected abstract String getSuccessMessage(String serviceId); + + protected abstract String getFailureMessage(String serviceId); +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java new file mode 100644 index 0000000000000..9ef282fdf35a3 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java @@ -0,0 +1,159 @@ +/* + * 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.windows_service; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.MultiCommand; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +class WindowsServiceCli extends MultiCommand { + + private static final Command installCommand = new ProcrunCommand("Install Elasticsearch as a Windows Service", "install") { + private final Path javaHome; + private final Path javaDll; + { + javaHome = Paths.get(System.getProperty("java.home")); + Path dll = javaHome.resolve("jre/bin/server/jvm.dll"); + if (Files.exists(dll) == false) { + dll = javaHome.resolve("bin/server/jvm.dll"); + } + javaDll = dll; + } + + @Override + protected String getAdditionalArgs(String serviceId, Path esHome, Map env) { + List args = new ArrayList<>(); + addArg(args, "--Startup", env.getOrDefault("ES_START_TYPE", "manual")); + addArg(args, "--StopTimeout", env.getOrDefault("ES_STOP_TIMEOUT", "0")); + addArg(args, "--StartClass", "org.elasticsearch.cli.Launcher"); + addArg(args, "--StartMethod", "main"); + addArg(args, "++StartParams", "--quiet"); + addArg(args, "--StopClass", "org.elasticsearch.cli.Launcher"); + addArg(args, "--StopMethod", "close"); + addArg(args, "--Classpath", "\"%s\"".formatted(System.getProperty("java.class.path"))); + addArg(args, "--JvmSx", "4m"); + addArg(args, "--JvmMx", "64m"); + addArg(args, "--JvmOptions", "-XX:+UseSerialGC"); + addArg(args, "--PidFile", "\"%s.pid\"".formatted(serviceId)); + // TODO: get ES version + addArg(args, "--DisplayName", + env.getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch ES_VERSION (%s)".formatted(serviceId))); + addArg(args, "--Description", + env.getOrDefault("SERVICE_DESCRIPTION", "Elasticsearch ES_VERSION Windows Service - https://elastic.co")); + addArg(args, "--Jvm", javaDll.toString()); + addArg(args, "--StartMode", "jvm"); + addArg(args, "--StartPath", esHome.toString()); + addArg(args, "++Environment", "LAUNCHER_TOOLNAME=server-cli"); + addArg(args, "++Environment", "LAUNCHER_LIBS=lib/tools/server-cli"); + String serviceParams = env.get("SERVICE_PARAMS"); + if (serviceParams != null) { + args.add(serviceParams); + } + return String.join(" ", args); + } + + private static void addArg(List args, String arg, String value) { + args.add(arg); + args.add(value); + } + + + @Override + protected void preExecute(Terminal terminal, String serviceId) throws UserException { + terminal.println("Installing service : %s".formatted(serviceId)); + terminal.println("Using ES_JAVA_HOME : %s".formatted(javaHome.toString())); + + if (Files.exists(javaDll) == false) { + throw new UserException(ExitCodes.CONFIG, + "Invalid java installation (no jvm.dll found in %s\\jre\\bin\\server\\ or %s\\bin\\server\"). Exiting..." + .formatted(javaHome.toString(), javaHome.toString())); + } + } + + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been installed".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed installing '%s' service".formatted(serviceId); + } + }; + + private static final Command removeCommand = new ProcrunCommand("Remove the Elasticsearch Windows Service", "delete") { + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been removed".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed removing '%s' service".formatted(serviceId); + } + }; + + private static final Command startCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service", "start") { + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been started".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed starting '%s' service".formatted(serviceId); + } + }; + + private static final Command stopCommand = new ProcrunCommand("Stops the Elasticsearch Windows Service", "stop") { + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been stopped".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed stopping '%s' service".formatted(serviceId); + } + }; + + private static final Command managerCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service manager", "manage") { + @Override + protected String getExecutable() { + return "elasticsearch-service-mgr"; + } + @Override + protected String getSuccessMessage(String serviceId) { + return "Successfully started service manager for '%s'".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed starting service manager for '%s'".formatted(serviceId); + } + }; + + WindowsServiceCli() { + super("A tool for managing Elasticsearch as a Windows service"); + subcommands.put("install", installCommand); + subcommands.put("remove", removeCommand); + subcommands.put("start", startCommand); + subcommands.put("stop", stopCommand); + subcommands.put("manager", managerCommand); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCliProvider.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCliProvider.java similarity index 94% rename from distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCliProvider.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCliProvider.java index c3fcb1b16adf8..3b1572d7b3ec0 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCliProvider.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCliProvider.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticserach.windows_service.cli; +package org.elasticsearch.windows_service; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ToolProvider; diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java deleted file mode 100644 index dcea7563e675c..0000000000000 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticserach/windows_service/cli/WindowsServiceCli.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.elasticserach.windows_service.cli; - -import joptsimple.OptionSet; - -import org.elasticsearch.cli.Command; -import org.elasticsearch.cli.MultiCommand; -import org.elasticsearch.cli.Terminal; - -class WindowsServiceCli extends MultiCommand { - - private static Command tmpCommand = new Command("temp") { - @Override - protected void execute(Terminal terminal, OptionSet options) throws Exception { - return; - } - }; - - WindowsServiceCli() { - super("A tool for managing Elasticsearch as a Windows service"); - subcommands.put("install", tmpCommand); - subcommands.put("remove", tmpCommand); - subcommands.put("start", tmpCommand); - subcommands.put("stop", tmpCommand); - subcommands.put("manager", tmpCommand); - } -} diff --git a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider index 74a44ea867b56..d99aea9958202 100644 --- a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider +++ b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider @@ -1 +1 @@ -org.elasticsearch.windows_service.cli.WindowsServiceCliProvider +org.elasticsearch.windows_service.WindowsServiceCliProvider From dc9764ad927a6fb2e86b3e811e33fcdd5085858f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 7 Apr 2022 14:00:12 -0700 Subject: [PATCH 038/166] windows service fixes --- distribution/src/bin/elasticsearch-service.bat | 3 +-- .../org/elasticsearch/windows_service/WindowsServiceCli.java | 2 +- .../src/main/java/org/elasticsearch/cli/ToolProvider.java | 5 ++++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/distribution/src/bin/elasticsearch-service.bat b/distribution/src/bin/elasticsearch-service.bat index ae3f0eb34d16e..9cc957af8ecf1 100644 --- a/distribution/src/bin/elasticsearch-service.bat +++ b/distribution/src/bin/elasticsearch-service.bat @@ -3,8 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set SCRIPT_NAME=%~n0 -set LAUNCHER_TOOLNAME=%SCRIPT_NAME:elasticsearch-=% +set LAUNCHER_TOOLNAME=windows-service set LAUNCHER_LIBS=lib/tools/windows-service-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java index 9ef282fdf35a3..4e4d7b7ccce49 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java @@ -135,7 +135,7 @@ protected String getFailureMessage(String serviceId) { private static final Command managerCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service manager", "manage") { @Override protected String getExecutable() { - return "elasticsearch-service-mgr"; + return "elasticsearch-service-mgr.exe"; } @Override protected String getSuccessMessage(String serviceId) { diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java index 686b8944ed700..75c162ca17d90 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/ToolProvider.java @@ -47,7 +47,10 @@ static ToolProvider loadTool(String toolname, String libs) throws IOException { return StreamSupport.stream(toolFinder.spliterator(), false) .filter(p -> p.name().equals(toolname)) .findFirst() - .orElseThrow(() -> new AssertionError("ToolProvider [" + toolname + "] not found")); + .orElseThrow(() -> { + var names = StreamSupport.stream(toolFinder.spliterator(), false).map(ToolProvider::name).toList(); + return new AssertionError("ToolProvider [" + toolname + "] not found, available names are " + names); + }); } private static ClassLoader loadJars(List dirs) throws IOException { From dbe21c49142e6f6df0f26f783f4349d010e9c23b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 7 Apr 2022 16:11:22 -0700 Subject: [PATCH 039/166] more windows service fixes --- .../windows_service/ProcrunCommand.java | 7 +++- .../windows_service/WindowsServiceCli.java | 37 +++++++++++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java index cb95452d62df5..165b232fcd823 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java @@ -32,7 +32,7 @@ abstract class ProcrunCommand extends Command { protected ProcrunCommand(String desc, String cmd) { super(desc); - this.procrun = Paths.get("").resolve("bin").resolve(getExecutable()); + this.procrun = Paths.get("").resolve("bin").resolve(getExecutable()).toAbsolutePath(); if (Files.exists(procrun) == false) { throw new IllegalStateException("Missing procrun exe: " + procrun); } @@ -46,16 +46,19 @@ protected String getExecutable() { @Override protected void execute(Terminal terminal, OptionSet options) throws Exception { Map env = System.getenv(); - Path esHome = Paths.get(""); // TODO: this should be passed through execute + Path esHome = Paths.get("").toAbsolutePath(); // TODO: this should be passed through execute String serviceId = getServiceId(options, env); preExecute(terminal, serviceId); List procrunCmd = new ArrayList<>(); procrunCmd.add(procrun.toString()); + procrunCmd.add(cmd); + procrunCmd.add(serviceId); procrunCmd.add(getLogArgs(serviceId, esHome, env)); procrunCmd.add(getAdditionalArgs(serviceId, esHome, env)); ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/C", String.join(" ", procrunCmd).trim()); + processBuilder.inheritIO(); Process process = processBuilder.start(); int ret = process.waitFor(); if (ret != ExitCodes.OK) { diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java index 4e4d7b7ccce49..67d0d25c61ace 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java @@ -40,16 +40,16 @@ protected String getAdditionalArgs(String serviceId, Path esHome, Map args = new ArrayList<>(); addArg(args, "--Startup", env.getOrDefault("ES_START_TYPE", "manual")); addArg(args, "--StopTimeout", env.getOrDefault("ES_STOP_TIMEOUT", "0")); - addArg(args, "--StartClass", "org.elasticsearch.cli.Launcher"); + addArg(args, "--StartClass", "org.elasticsearch.launcher.Launcher"); addArg(args, "--StartMethod", "main"); addArg(args, "++StartParams", "--quiet"); - addArg(args, "--StopClass", "org.elasticsearch.cli.Launcher"); + addArg(args, "--StopClass", "org.elasticsearch.launcher.Launcher"); addArg(args, "--StopMethod", "close"); - addArg(args, "--Classpath", "\"%s\"".formatted(System.getProperty("java.class.path"))); - addArg(args, "--JvmSx", "4m"); + addArg(args, "--Classpath", System.getProperty("java.class.path")); + addArg(args, "--JvmMs", "4m"); addArg(args, "--JvmMx", "64m"); - addArg(args, "--JvmOptions", "-XX:+UseSerialGC"); - addArg(args, "--PidFile", "\"%s.pid\"".formatted(serviceId)); + addArg(args, "--JvmOptions", getJvmOptions()); + addArg(args, "--PidFile", "%s.pid".formatted(serviceId)); // TODO: get ES version addArg(args, "--DisplayName", env.getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch ES_VERSION (%s)".formatted(serviceId))); @@ -58,20 +58,43 @@ protected String getAdditionalArgs(String serviceId, Path esHome, Map args, String arg, String value) { args.add(arg); + if (value.contains(" ")) { + value = "\"%s\"".formatted(value); + } args.add(value); } + private static String getJvmOptions() { + List jvmOptions = new ArrayList<>(); + jvmOptions.add("-XX:+UseSerialGC"); + // passthrough these properties + for (var prop : List.of("es.path.home", "es.path.conf", "es.distribution.flavor", "es.distribution.type", "es.bundled_jdk")) { + jvmOptions.add("-D%s=%s".formatted(prop, System.getProperty(prop))); + } + return String.join(";", jvmOptions); + } @Override protected void preExecute(Terminal terminal, String serviceId) throws UserException { From 4b0d62b1cf9b13e648726b08bacbad9784cca90f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 7 Apr 2022 16:14:52 -0700 Subject: [PATCH 040/166] and the toolprovider filename too --- ...serach.cli.ToolProvider => org.elasticsearch.cli.ToolProvider} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename distribution/tools/windows-service-cli/src/main/resources/META-INF/services/{org.elasticserach.cli.ToolProvider => org.elasticsearch.cli.ToolProvider} (100%) diff --git a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider similarity index 100% rename from distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticserach.cli.ToolProvider rename to distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider From 76c32d572132caf4f244161b0a2e043ae128cf24 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 7 Apr 2022 16:17:45 -0700 Subject: [PATCH 041/166] remove old --- .../src/bin/elasticsearch-service-old.bat | 291 ------------------ 1 file changed, 291 deletions(-) delete mode 100644 distribution/src/bin/elasticsearch-service-old.bat diff --git a/distribution/src/bin/elasticsearch-service-old.bat b/distribution/src/bin/elasticsearch-service-old.bat deleted file mode 100644 index 0ead2d3193771..0000000000000 --- a/distribution/src/bin/elasticsearch-service-old.bat +++ /dev/null @@ -1,291 +0,0 @@ -@echo off - -setlocal enabledelayedexpansion -setlocal enableextensions - -set NOJAVA=nojava -if /i "%1" == "install" set NOJAVA= - -call "%~dp0elasticsearch-env.bat" %NOJAVA% || exit /b 1 - -set EXECUTABLE=%ES_HOME%\bin\elasticsearch-service-x64.exe -if "%SERVICE_ID%" == "" set SERVICE_ID=elasticsearch-service-x64 -set ARCH=64-bit - -if EXIST "%EXECUTABLE%" goto okExe -echo elasticsearch-service-x64.exe was not found... -exit /B 1 - -:okExe -set ES_VERSION=@project.version@ - -if "%SERVICE_LOG_DIR%" == "" set SERVICE_LOG_DIR=%ES_HOME%\logs - -if "x%1x" == "xx" goto displayUsage -set SERVICE_CMD=%1 -shift -if "x%1x" == "xx" goto checkServiceCmd -set SERVICE_ID=%1 - -:checkServiceCmd - -if "%LOG_OPTS%" == "" set LOG_OPTS=--LogPath "%SERVICE_LOG_DIR%" --LogPrefix "%SERVICE_ID%" --StdError auto --StdOutput auto - -if /i %SERVICE_CMD% == install goto doInstall -if /i %SERVICE_CMD% == remove goto doRemove -if /i %SERVICE_CMD% == start goto doStart -if /i %SERVICE_CMD% == stop goto doStop -if /i %SERVICE_CMD% == manager goto doManagment -echo Unknown option "%SERVICE_CMD%" -exit /B 1 - -:displayUsage -echo. -echo Usage: elasticsearch-service.bat install^|remove^|start^|stop^|manager [SERVICE_ID] -goto:eof - -:doStart -"%EXECUTABLE%" //ES//%SERVICE_ID% %LOG_OPTS% -if not errorlevel 1 goto started -echo Failed starting '%SERVICE_ID%' service -exit /B 1 -goto:eof -:started -echo The service '%SERVICE_ID%' has been started -goto:eof - -:doStop -"%EXECUTABLE%" //SS//%SERVICE_ID% %LOG_OPTS% -if not errorlevel 1 goto stopped -echo Failed stopping '%SERVICE_ID%' service -exit /B 1 -goto:eof -:stopped -echo The service '%SERVICE_ID%' has been stopped -goto:eof - -:doManagment -set EXECUTABLE_MGR=%ES_HOME%\bin\elasticsearch-service-mgr -"%EXECUTABLE_MGR%" //ES//%SERVICE_ID% -if not errorlevel 1 goto managed -echo Failed starting service manager for '%SERVICE_ID%' -exit /B 1 -goto:eof -:managed -echo Successfully started service manager for '%SERVICE_ID%'. -goto:eof - -:doRemove -rem Remove the service -"%EXECUTABLE%" //DS//%SERVICE_ID% %LOG_OPTS% -if not errorlevel 1 goto removed -echo Failed removing '%SERVICE_ID%' service -exit /B 1 -goto:eof -:removed -echo The service '%SERVICE_ID%' has been removed -goto:eof - -:doInstall -echo Installing service : "%SERVICE_ID%" -echo Using ES_JAVA_HOME (%ARCH%): "%ES_JAVA_HOME%" - -rem Check JVM server dll first -if exist "%ES_JAVA_HOME%\jre\bin\server\jvm.dll" ( - set JVM_DLL=\jre\bin\server\jvm.dll - goto foundJVM -) - -rem Check 'server' JRE (JRE installed on Windows Server) -if exist "%ES_JAVA_HOME%\bin\server\jvm.dll" ( - set JVM_DLL=\bin\server\jvm.dll - goto foundJVM -) else ( - echo ES_JAVA_HOME ("%ES_JAVA_HOME%"^) points to an invalid Java installation (no jvm.dll found in "%ES_JAVA_HOME%\jre\bin\server" or "%ES_JAVA_HOME%\bin\server"^). Exiting... - goto:eof -) - -:foundJVM -if not defined ES_TMPDIR ( - for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!LAUNCHERS_CLASSPATH!" "org.elasticsearch.tools.launchers.TempDirectory"`) do set ES_TMPDIR=%%a -) - -rem The JVM options parser produces the final JVM options to start -rem Elasticsearch. It does this by incorporating JVM options in the following -rem way: -rem - first, system JVM options are applied (these are hardcoded options in -rem the parser) -rem - second, JVM options are read from jvm.options and -rem jvm.options.d/*.options -rem - third, JVM options from ES_JAVA_OPTS are applied -rem - fourth, ergonomic JVM options are applied - -@setlocal -for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!LAUNCHERS_CLASSPATH!" "org.elasticsearch.tools.launchers.JvmOptionsParser" "!ES_PATH_CONF!" "!ES_HOME!"/plugins ^|^| echo jvm_options_parser_failed`) do set ES_JAVA_OPTS=%%a -@endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%ES_JAVA_OPTS%" & set ES_JAVA_OPTS=%ES_JAVA_OPTS% - -if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( - exit /b 1 -) - -rem The output of the JVM options parses is space-delimited. We need to -rem convert to semicolon-delimited and avoid doubled semicolons. -@setlocal -if not "%ES_JAVA_OPTS%" == "" ( - set ES_JAVA_OPTS=!ES_JAVA_OPTS: =;! - set ES_JAVA_OPTS=!ES_JAVA_OPTS:;;=;! -) -@endlocal & set ES_JAVA_OPTS=%ES_JAVA_OPTS% - -if "%ES_JAVA_OPTS:~-1%"==";" set ES_JAVA_OPTS=%ES_JAVA_OPTS:~0,-1% - -echo %ES_JAVA_OPTS% - -@setlocal EnableDelayedExpansion -for %%a in ("%ES_JAVA_OPTS:;=","%") do ( - set var=%%a - set other_opt=true - if "!var:~1,4!" == "-Xms" ( - set XMS=!var:~5,-1! - set other_opt=false - call:convertxm !XMS! JVM_MS - ) - if "!var:~1,16!" == "-XX:MinHeapSize=" ( - set XMS=!var:~17,-1! - set other_opt=false - call:convertxm !XMS! JVM_MS - ) - if "!var:~1,4!" == "-Xmx" ( - set XMX=!var:~5,-1! - set other_opt=false - call:convertxm !XMX! JVM_MX - ) - if "!var:~1,16!" == "-XX:MaxHeapSize=" ( - set XMX=!var:~17,-1! - set other_opt=false - call:convertxm !XMX! JVM_MX - ) - if "!var:~1,4!" == "-Xss" ( - set XSS=!var:~5,-1! - set other_opt=false - call:convertxk !XSS! JVM_SS - ) - if "!var:~1,20!" == "-XX:ThreadStackSize=" ( - set XSS=!var:~21,-1! - set other_opt=false - call:convertxk !XSS! JVM_SS - ) - if "!other_opt!" == "true" set OTHER_JAVA_OPTS=!OTHER_JAVA_OPTS!;!var! -) -@endlocal & set JVM_MS=%JVM_MS% & set JVM_MX=%JVM_MX% & set JVM_SS=%JVM_SS% & set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS% - -if "%JVM_MS%" == "" ( - echo minimum heap size not set; configure using -Xms via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS - goto:eof -) -if "%JVM_MX%" == "" ( - echo maximum heap size not set; configure using -Xmx via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS - goto:eof -) -if "%JVM_SS%" == "" ( - echo thread stack size not set; configure using -Xss via "%ES_PATH_CONF%/jvm.options.d", or ES_JAVA_OPTS - goto:eof -) -set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS:"=% -set OTHER_JAVA_OPTS=%OTHER_JAVA_OPTS:~1% - -set ES_PARAMS=-Delasticsearch;-Des.path.home="%ES_HOME%";-Des.path.conf="%ES_PATH_CONF%";-Des.distribution.flavor="%ES_DISTRIBUTION_FLAVOR%";-Des.distribution.type="%ES_DISTRIBUTION_TYPE%";-Des.bundled_jdk="%ES_BUNDLED_JDK%" - -if "%ES_START_TYPE%" == "" set ES_START_TYPE=manual -if "%ES_STOP_TIMEOUT%" == "" set ES_STOP_TIMEOUT=0 - -if "%SERVICE_DISPLAY_NAME%" == "" set SERVICE_DISPLAY_NAME=Elasticsearch %ES_VERSION% (%SERVICE_ID%) -if "%SERVICE_DESCRIPTION%" == "" set SERVICE_DESCRIPTION=Elasticsearch %ES_VERSION% Windows Service - https://elastic.co - -if not "%SERVICE_USERNAME%" == "" ( - if not "%SERVICE_PASSWORD%" == "" ( - set SERVICE_PARAMS=%SERVICE_PARAMS% --ServiceUser "%SERVICE_USERNAME%" --ServicePassword "%SERVICE_PASSWORD%" - ) -) -"%EXECUTABLE%" //IS//%SERVICE_ID% --Startup %ES_START_TYPE% --StopTimeout %ES_STOP_TIMEOUT% --StartClass org.elasticsearch.bootstrap.Elasticsearch --StartMethod main ++StartParams --quiet --StopClass org.elasticsearch.bootstrap.Elasticsearch --StopMethod close --Classpath "%ES_CLASSPATH%" --JvmMs %JVM_MS% --JvmMx %JVM_MX% --JvmSs %JVM_SS% --JvmOptions %OTHER_JAVA_OPTS% ++JvmOptions %ES_PARAMS% %LOG_OPTS% --PidFile "%SERVICE_ID%.pid" --DisplayName "%SERVICE_DISPLAY_NAME%" --Description "%SERVICE_DESCRIPTION%" --Jvm "%ES_JAVA_HOME%%JVM_DLL%" --StartMode jvm --StopMode jvm --StartPath "%ES_HOME%" %SERVICE_PARAMS% ++Environment HOSTNAME="%%COMPUTERNAME%%" - -if not errorlevel 1 goto installed -echo Failed installing '%SERVICE_ID%' service -exit /B 1 -goto:eof - -:installed -echo The service '%SERVICE_ID%' has been installed. -goto:eof - -:err -echo ES_JAVA_HOME environment variable must be set! -pause -goto:eof - -rem --- -rem Function for converting Xm[s|x] values into MB which Commons Daemon accepts -rem --- -:convertxm -set value=%~1 -rem extract last char (unit) -set unit=%value:~-1% -rem assume the unit is specified -set conv=%value:~0,-1% - -if "%unit%" == "k" goto kilo -if "%unit%" == "K" goto kilo -if "%unit%" == "m" goto mega -if "%unit%" == "M" goto mega -if "%unit%" == "g" goto giga -if "%unit%" == "G" goto giga - -rem no unit found, must be bytes; consider the whole value -set conv=%value% -rem convert to KB -set /a conv=%conv% / 1024 -:kilo -rem convert to MB -set /a conv=%conv% / 1024 -goto mega -:giga -rem convert to MB -set /a conv=%conv% * 1024 -:mega -set "%~2=%conv%" -goto:eof - -:convertxk -set value=%~1 -rem extract last char (unit) -set unit=%value:~-1% -rem assume the unit is specified -set conv=%value:~0,-1% - -if "%unit%" == "k" goto kilo -if "%unit%" == "K" goto kilo -if "%unit%" == "m" goto mega -if "%unit%" == "M" goto mega -if "%unit%" == "g" goto giga -if "%unit%" == "G" goto giga - -rem no unit found, must be bytes; consider the whole value -set conv=%value% -rem convert to KB -set /a conv=%conv% / 1024 -goto kilo -:mega -rem convert to KB -set /a conv=%conv% * 1024 -goto kilo -:giga -rem convert to KB -set /a conv=%conv% * 1024 * 1024 -:kilo -set "%~2=%conv%" -goto:eof - -endlocal -endlocal - -exit /b %ERRORLEVEL% From e28acc63a411cd26f1e6f1577edc09868ee8a05f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 19 Apr 2022 16:50:54 -0700 Subject: [PATCH 042/166] spotless --- .../server/cli/JvmOptionsParser.java | 8 +------ .../server/cli/KeystorePasswordTerminal.java | 1 - .../elasticsearch/server/cli/ServerCli.java | 9 ++++---- .../server/cli/TempDirectory.java | 4 ++-- .../windows_service/WindowsServiceCli.java | 21 ++++++++++++------- .../bootstrap/Elasticsearch.java | 6 ++++-- ...ElasticsearchUncaughtExceptionHandler.java | 3 +-- .../org/elasticsearch/bootstrap/Security.java | 1 - .../elasticsearch/bootstrap/ServerArgs.java | 7 +------ .../org/elasticsearch/cli/TerminalTests.java | 1 - .../xpack/security/cli/CertificateTool.java | 1 + .../esnative/tool/SetupPasswordTool.java | 1 + 12 files changed, 30 insertions(+), 33 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index 44c223bc9aba5..ad11ee4e18793 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -21,7 +21,6 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -71,12 +70,7 @@ static List determine(Path configDir, Path pluginsDir, Path tmpDir, Stri substitutions.put("ES_PATH_CONF", configDir.toString()); try { - return parser.jvmOptions( - configDir, - pluginsDir, - envOptions, - substitutions - ); + return parser.jvmOptions(configDir, pluginsDir, envOptions, substitutions); } catch (IOException e) { throw new UncheckedIOException(e); } catch (final JvmOptionsFileParserException e) { diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java index 5c48ae0400387..496e1267e9ae9 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -11,7 +11,6 @@ import org.elasticsearch.cli.Terminal; import org.elasticsearch.common.settings.SecureString; -import java.io.IOException; import java.io.OutputStream; import java.io.Reader; diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 491b83881e676..f310717769578 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -92,7 +92,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings(), env.configFile()); List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); - //jvmOptions.add("-Des.path.conf=" + env.configFile()); + // jvmOptions.add("-Des.path.conf=" + env.configFile()); jvmOptions.add("-Des.distribution.flavor=" + System.getProperty("es.distribution.flavor")); jvmOptions.add("-Des.distribution.type=" + System.getProperty("es.distribution.type")); jvmOptions.add("-Des.bundled_jdk=" + System.getProperty("es.bundled_jdk")); @@ -157,7 +157,6 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th throw new UserException(ExitCodes.IO_ERROR, "Interrupted while waiting for Elasticsearch process"); } - // TODO: add ctrl-c handler so we can wait for subprocess // TODO: check the java.io.tmpdir // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately @@ -228,8 +227,10 @@ private void autoConfigureSecurity(Terminal terminal, OptionSet options, SecureS } else { int ret = autoConfigNode.main(args.toArray(new String[0]), autoConfigTerminal); switch (ret) { - case ExitCodes.OK, ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: break; - default: throw new UserException(ret, "Auto security enrollment failed"); + case ExitCodes.OK, ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: + break; + default: + throw new UserException(ret, "Auto security enrollment failed"); } } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java index 428bd0a073166..06a33fd1ab84c 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java @@ -17,7 +17,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileAttribute; -import java.util.Arrays; import java.util.Map; /** @@ -47,7 +46,8 @@ public static Path initialize(Map env) throws UserException { path = createTempDirectory("elasticsearch-"); } } catch (IOException e) { - // TODO: don't mask this exception, should we just propagate or try to summarize? in shell-land we would have printed it and exited + // TODO: don't mask this exception, should we just propagate or try to summarize? in shell-land we would have printed it and + // exited throw new UserException(ExitCodes.CONFIG, "Could not create temporary directory"); } } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java index 9ba9ce9aed1e7..b63c328339453 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java @@ -52,10 +52,12 @@ protected String getAdditionalArgs(String serviceId, Path esHome, Map Date: Tue, 19 Apr 2022 16:51:53 -0700 Subject: [PATCH 043/166] remove old launcher --- distribution/tools/launcher/build.gradle | 6 -- .../org/elasticsearch/launcher/Launcher.java | 92 ------------------- 2 files changed, 98 deletions(-) delete mode 100644 distribution/tools/launcher/build.gradle delete mode 100644 distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java diff --git a/distribution/tools/launcher/build.gradle b/distribution/tools/launcher/build.gradle deleted file mode 100644 index 0dd9cdedcfcdb..0000000000000 --- a/distribution/tools/launcher/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ - -apply plugin: 'elasticsearch.java' - -dependencies { - compileOnly project(':server') -} diff --git a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java b/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java deleted file mode 100644 index 8b919cf0a26b0..0000000000000 --- a/distribution/tools/launcher/src/main/java/org/elasticsearch/launcher/Launcher.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.launcher; - -import org.apache.logging.log4j.Level; -import org.elasticsearch.cli.Command; -import org.elasticsearch.cli.Terminal; -import org.elasticsearch.cli.ToolProvider; -import org.elasticsearch.common.logging.LogConfigurator; -import org.elasticsearch.common.settings.Settings; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.ServiceLoader; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -class Launcher { - - private static volatile Command command; - - // TODO: don't throw, catch this and give a nice error message - public static void main(String[] args) throws Exception { - configureLoggingWithoutConfig(); - - // TODO: change signature of Command to take in sysprops and env - Map sysprops = convertPropertiesToMap(System.getProperties()); - Map env = new HashMap<>(System.getenv()); - - Path homeDir = Paths.get("").toAbsolutePath(); - String toolname = env.get("LAUNCHER_TOOLNAME"); - String libs = env.get("LAUNCHER_LIBS"); - - System.out.println("Running ES cli"); - System.out.println("ES_HOME=" + homeDir); - System.out.println("tool: " + toolname); - System.out.println("libs: " + libs); - System.out.println("args: " + Arrays.asList(args)); - - command = ToolProvider.loadTool(toolname, libs).create(); - System.exit(command.main(args, Terminal.DEFAULT)); - } - - private static Map convertPropertiesToMap(Properties properties) { - return properties.entrySet().stream().collect( - Collectors.toMap(e -> e.getKey().toString(), e -> e.getValue().toString())); - } - - /** - * Configures logging without Elasticsearch configuration files based on the system property "es.logger.level" only. As such, any - * logging will be written to the console. - */ - private static void configureLoggingWithoutConfig() { - // initialize default for es.logger.level because we will not read the log4j2.properties - final String loggerLevel = System.getProperty("es.logger.level", Level.INFO.name()); - final Settings settings = Settings.builder().put("logger.level", loggerLevel).build(); - LogConfigurator.configureWithoutConfig(settings); - } - - /** - * Required method that's called by Apache Commons procrun when - * running as a service on Windows, when the service is stopped. - * - * http://commons.apache.org/proper/commons-daemon/procrun.html - * - * NOTE: If this method is renamed and/or moved, make sure to - * update elasticsearch-service.bat! - */ - static void close(String[] args) throws IOException { - command.close(); - } -} From 0fdb858bd6e6ed5b2870c3f0971088d6098c5c98 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 19 Apr 2022 16:56:38 -0700 Subject: [PATCH 044/166] fix windows service --- .../launcher/CliToolLauncher.java | 18 +++++++++++++++++- .../windows_service/WindowsServiceCli.java | 8 ++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java index 6f001258e3fb4..72d9eadcfe242 100644 --- a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java +++ b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java @@ -16,6 +16,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.SuppressForbidden; +import java.io.IOException; import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; @@ -28,6 +29,8 @@ class CliToolLauncher { private static final String SCRIPT_PREFIX = "elasticsearch-"; + private static volatile Command command; + /** * Runs a CLI tool. * @@ -53,7 +56,7 @@ public static void main(String[] args) throws Exception { String toolname = getToolName(sysprops); String libs = sysprops.getOrDefault("cli.libs", ""); - Command command = CliToolProvider.load(toolname, libs).create(); + command = CliToolProvider.load(toolname, libs).create(); exit(command.main(args, Terminal.DEFAULT)); } @@ -94,4 +97,17 @@ private static void configureLoggingWithoutConfig(Map sysprops) final Settings settings = Settings.builder().put("logger.level", loggerLevel).build(); LogConfigurator.configureWithoutConfig(settings); } + + /** + * Required method that's called by Apache Commons procrun when + * running as a service on Windows, when the service is stopped. + * + * http://commons.apache.org/proper/commons-daemon/procrun.html + * + * NOTE: If this method is renamed and/or moved, make sure to + * update elasticsearch-service.bat! + */ + static void close(String[] args) throws IOException { + command.close(); + } } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java index b63c328339453..192d4e462d45b 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java @@ -8,6 +8,7 @@ package org.elasticsearch.windows_service; +import org.elasticsearch.Version; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.MultiCommand; @@ -43,7 +44,6 @@ protected String getAdditionalArgs(String serviceId, Path esHome, Map jvmOptions = new ArrayList<>(); jvmOptions.add("-XX:+UseSerialGC"); // passthrough these properties - for (var prop : List.of("es.path.home", "es.path.conf", "es.distribution.flavor", "es.distribution.type", "es.bundled_jdk")) { + for (var prop : List.of("es.path.home", "es.path.conf", "es.distribution.type")) { jvmOptions.add("-D%s=%s".formatted(prop, System.getProperty(prop))); } return String.join(";", jvmOptions); From 603c2f0ab5061829831d5821f2b5af481bd8a00c Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 19 Apr 2022 17:11:30 -0700 Subject: [PATCH 045/166] cleanup --- distribution/src/bin/elasticsearch.bat | 162 +----------------- .../org.elasticsearch.cli.ToolProvider | 1 - .../cli/keystore/KeyStoreCliProvider.java | 1 - .../org.elasticsearch.cli.ToolProvider | 1 - .../org.elasticsearch.cli.ToolProvider | 1 - .../server/cli/KeystorePasswordTerminal.java | 2 +- .../org.elasticsearch.cli.ToolProvider | 2 - .../org.elasticsearch.cli.ToolProvider | 4 - .../org.elasticsearch.cli.ToolProvider | 8 - .../org.elasticsearch.cli.ToolProvider | 1 - 10 files changed, 2 insertions(+), 181 deletions(-) delete mode 100644 distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider delete mode 100644 distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider delete mode 100644 distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider delete mode 100644 server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider delete mode 100644 x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider delete mode 100644 x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider delete mode 100644 x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider diff --git a/distribution/src/bin/elasticsearch.bat b/distribution/src/bin/elasticsearch.bat index 6928ebd802d0e..4f3ea1f5ba37d 100644 --- a/distribution/src/bin/elasticsearch.bat +++ b/distribution/src/bin/elasticsearch.bat @@ -3,171 +3,11 @@ setlocal enabledelayedexpansion setlocal enableextensions -rem START OF NEW SCRIPT - set LAUNCHER_TOOLNAME=server set LAUNCHER_LIBS=lib/tools/server-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit -exit /b 1 - -rem END OF NEW SCRIPT - - -SET params='%*' -SET checkpassword=Y -SET enrolltocluster=N -SET attemptautoconfig=Y - -:loop -FOR /F "usebackq tokens=1* delims= " %%A IN (!params!) DO ( - SET previous=!current! - SET current=%%A - SET params='%%B' - SET silent=N - IF "!current!" == "-s" ( - SET silent=Y - ) - IF "!current!" == "--silent" ( - SET silent=Y - ) - - IF "!current!" == "-h" ( - SET checkpassword=N - SET attemptautoconfig=N - ) - IF "!current!" == "--help" ( - SET checkpassword=N - SET attemptautoconfig=N - ) - - IF "!current!" == "-V" ( - SET checkpassword=N - SET attemptautoconfig=N - ) - IF "!current!" == "--version" ( - SET checkpassword=N - SET attemptautoconfig=N - ) - - IF "!current!" == "--enrollment-token" ( - IF "!enrolltocluster!" == "Y" ( - ECHO "Multiple --enrollment-token parameters are not allowed" 1>&2 - goto exitwithone - ) - SET enrolltocluster=Y - SET attemptautoconfig=N - ) - - IF "!previous!" == "--enrollment-token" ( - SET enrollmenttoken="!current!" - ) - - IF "!silent!" == "Y" ( - SET nopauseonerror=Y - ) ELSE ( - SET SHOULD_SKIP=false - IF "!previous!" == "--enrollment-token" SET SHOULD_SKIP=true - IF "!current!" == "--enrollment-token" SET SHOULD_SKIP=true - IF "!SHOULD_SKIP!" == "false" ( - IF "x!newparams!" NEQ "x" ( - SET newparams=!newparams! !current! - ) ELSE ( - SET newparams=!current! - ) - ) - - ) - - IF "x!params!" NEQ "x" ( - GOTO loop - ) -) - -CALL "%~dp0elasticsearch-env.bat" || exit /b 1 -IF ERRORLEVEL 1 ( - IF NOT DEFINED nopauseonerror ( - PAUSE - ) - EXIT /B %ERRORLEVEL% -) - -SET KEYSTORE_PASSWORD= -IF "%checkpassword%"=="Y" ( - CALL "%~dp0elasticsearch-keystore.bat" has-passwd --silent - IF !ERRORLEVEL! EQU 0 ( - SET /P KEYSTORE_PASSWORD=Elasticsearch keystore password: - IF !ERRORLEVEL! NEQ 0 ( - ECHO Failed to read keystore password on standard input - EXIT /B !ERRORLEVEL! - ) - ) -) - -rem windows batch pipe will choke on special characters in strings -SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^^=^^^^! -SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^&=^^^&! -SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^|=^^^|! -SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^<=^^^=^^^>! -SET KEYSTORE_PASSWORD=!KEYSTORE_PASSWORD:^\=^^^\! - -IF "%attemptautoconfig%"=="Y" ( - SET CLI_NAME=auto-configure-node - SET CLI_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli - ECHO.!KEYSTORE_PASSWORD!|call "%~dp0elasticsearch-cli.bat" !newparams! - SET SHOULDEXIT=Y - IF !ERRORLEVEL! EQU 0 SET SHOULDEXIT=N - IF !ERRORLEVEL! EQU 73 SET SHOULDEXIT=N - IF !ERRORLEVEL! EQU 78 SET SHOULDEXIT=N - IF !ERRORLEVEL! EQU 80 SET SHOULDEXIT=N - IF "!SHOULDEXIT!"=="Y" ( - exit /b !ERRORLEVEL! - ) -) - -IF "!enrolltocluster!"=="Y" ( - SET CLI_NAME=auto-configure-node - SET CLI_LIBS=modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli - ECHO.!KEYSTORE_PASSWORD!|call "%~dp0elasticsearch-cli.bat" !newparams! --enrollment-token %enrollmenttoken% - IF !ERRORLEVEL! NEQ 0 ( - exit /b !ERRORLEVEL! - ) -) - -if not defined ES_TMPDIR ( - for /f "tokens=* usebackq" %%a in (`CALL %JAVA% -cp "!SERVER_CLI_CLASSPATH!" "org.elasticsearch.server.cli.TempDirectory"`) do set ES_TMPDIR=%%a -) - -rem The JVM options parser produces the final JVM options to start -rem Elasticsearch. It does this by incorporating JVM options in the following -rem way: -rem - first, system JVM options are applied (these are hardcoded options in -rem the parser) -rem - second, JVM options are read from jvm.options and -rem jvm.options.d/*.options -rem - third, JVM options from ES_JAVA_OPTS are applied -rem - fourth, ergonomic JVM options are applied -@setlocal -for /F "usebackq delims=" %%a in (`CALL %JAVA% -cp "!SERVER_CLI_CLASSPATH!" "org.elasticsearch.server.cli.JvmOptionsParser" "!ES_PATH_CONF!" "!ES_HOME!"/plugins ^|^| echo jvm_options_parser_failed`) do set ES_JAVA_OPTS=%%a -@endlocal & set "MAYBE_JVM_OPTIONS_PARSER_FAILED=%ES_JAVA_OPTS%" & set ES_JAVA_OPTS=%ES_JAVA_OPTS% - -if "%MAYBE_JVM_OPTIONS_PARSER_FAILED%" == "jvm_options_parser_failed" ( - exit /b 1 -) - -ECHO.!KEYSTORE_PASSWORD!| %JAVA% %ES_JAVA_OPTS% -Delasticsearch ^ - -Des.path.home="%ES_HOME%" -Des.path.conf="%ES_PATH_CONF%" ^ - -Des.distribution.type="%ES_DISTRIBUTION_TYPE%" ^ - -cp "%ES_CLASSPATH%" "org.elasticsearch.bootstrap.Elasticsearch" !newparams! - -endlocal -endlocal +:exit exit /b %ERRORLEVEL% - -rem this hack is ugly but necessary because we can't exit with /b X from within the argument parsing loop. -rem exit 1 (without /b) would work for powershell but it will terminate the cmd process when run in cmd -:exitwithone - exit /b 1 diff --git a/distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider deleted file mode 100644 index bfbc25e9cad21..0000000000000 --- a/distribution/tools/geoip-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ /dev/null @@ -1 +0,0 @@ -org.elasticsearch.geoip.GeoIpCliProvider diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java index f05f5e81eb275..5d64bc21015ac 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/KeyStoreCliProvider.java @@ -12,7 +12,6 @@ import org.elasticsearch.cli.Command; public class KeyStoreCliProvider implements CliToolProvider { - @Override public String name() { return "keystore"; diff --git a/distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider deleted file mode 100644 index 6d546090b5635..0000000000000 --- a/distribution/tools/keystore-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ /dev/null @@ -1 +0,0 @@ -org.elasticsearch.cli.keystore.KeyStoreCliProvider diff --git a/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider deleted file mode 100644 index 473a3cb9456d9..0000000000000 --- a/distribution/tools/plugin-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ /dev/null @@ -1 +0,0 @@ -org.elasticsearch.plugins.cli.PluginCliProvider diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java index 496e1267e9ae9..121516c3fc210 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -14,7 +14,7 @@ import java.io.OutputStream; import java.io.Reader; -public class KeystorePasswordTerminal extends Terminal { +class KeystorePasswordTerminal extends Terminal { private final Terminal delegate; diff --git a/server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider deleted file mode 100644 index fbc978252dae0..0000000000000 --- a/server/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ /dev/null @@ -1,2 +0,0 @@ -org.elasticsearch.cluster.coordination.NodeToolCliProvider -org.elasticsearch.index.shard.ShardToolCliProvider diff --git a/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider deleted file mode 100644 index 087e4da126a10..0000000000000 --- a/x-pack/plugin/security/cli/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ /dev/null @@ -1,4 +0,0 @@ -org.elasticsearch.xpack.security.cli.CertificateToolProvider -org.elasticsearch.xpack.security.cli.CertificateGenerateToolProvider -org.elasticsearch.xpack.security.cli.ReconfigureNodeToolProvider -org.elasticsearch.xpack.security.cli.AutoConfigureNodeToolProvider diff --git a/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider deleted file mode 100644 index 1ff00bb7edc9f..0000000000000 --- a/x-pack/plugin/security/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ /dev/null @@ -1,8 +0,0 @@ -org.elasticsearch.xpack.security.enrollment.tool.CreateEnrollmentTokenToolProvider -org.elasticsearch.xpack.security.authc.esnative.tool.ResetPasswordToolProvider -org.elasticsearch.xpack.security.authc.esnative.tool.SetupPasswordToolProvider -org.elasticsearch.xpack.security.authc.saml.SamlMetadataToolProvider -org.elasticsearch.xpack.security.authc.service.FileTokensToolProvider -org.elasticsearch.xpack.security.crypto.tool.SystemKeyToolProvider -org.elasticsearch.xpack.security.authc.file.tool.UsersToolProvider -org.elasticsearch.xpack.security.enrollment.tool.AutoConfigGenerateElasticPasswordHashToolProvider diff --git a/x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider b/x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider deleted file mode 100644 index 4ed62b4daa8e9..0000000000000 --- a/x-pack/plugin/watcher/src/main/resources/META-INF/services/org.elasticsearch.cli.ToolProvider +++ /dev/null @@ -1 +0,0 @@ -org.elasticsearch.xpack.watcher.trigger.schedule.tool.CronEvalToolProvider From 6060da482fc11333e8ad3a5e16f306ad14d48bbb Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 19 Apr 2022 20:23:17 -0700 Subject: [PATCH 046/166] move tests --- distribution/tools/server-cli/build.gradle | 4 +-- .../elasticsearch/server/cli/ServerCli.java | 2 -- .../server/cli}/ElasticsearchCliTests.java | 33 ++++++++++--------- .../server/cli/JvmErgonomicsTests.java | 32 +++++++++--------- .../org/elasticsearch/cli/MockTerminal.java | 2 +- 5 files changed, 35 insertions(+), 38 deletions(-) rename {server/src/test/java/org/elasticsearch/bootstrap => distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli}/ElasticsearchCliTests.java (84%) diff --git a/distribution/tools/server-cli/build.gradle b/distribution/tools/server-cli/build.gradle index 83b864c663bb6..b553154de30db 100644 --- a/distribution/tools/server-cli/build.gradle +++ b/distribution/tools/server-cli/build.gradle @@ -13,9 +13,7 @@ dependencies { compileOnly project(":server") compileOnly project(":libs:elasticsearch-cli") - testImplementation "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" - testImplementation "junit:junit:${versions.junit}" - testImplementation "org.hamcrest:hamcrest:${versions.hamcrest}" + testImplementation project(":test:framework") } tasks.withType(CheckForbiddenApis).configureEach { diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index f310717769578..7b7ade263f73f 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -93,9 +93,7 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); // jvmOptions.add("-Des.path.conf=" + env.configFile()); - jvmOptions.add("-Des.distribution.flavor=" + System.getProperty("es.distribution.flavor")); jvmOptions.add("-Des.distribution.type=" + System.getProperty("es.distribution.type")); - jvmOptions.add("-Des.bundled_jdk=" + System.getProperty("es.bundled_jdk")); Path esHome = PathUtils.get(System.getProperty("es.path.home")); Path javaHome = PathUtils.get(System.getProperty("java.home")); diff --git a/server/src/test/java/org/elasticsearch/bootstrap/ElasticsearchCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java similarity index 84% rename from server/src/test/java/org/elasticsearch/bootstrap/ElasticsearchCliTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java index b16625448c7fb..30f90fbf9ac4d 100644 --- a/server/src/test/java/org/elasticsearch/bootstrap/ElasticsearchCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.bootstrap; +package org.elasticsearch.server.cli; import org.elasticsearch.Build; import org.elasticsearch.cli.Command; @@ -16,6 +16,7 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; import org.hamcrest.Matcher; +import org.junit.Assert; import org.junit.Before; import java.nio.file.Path; @@ -38,17 +39,17 @@ private void assertOk(String... args) throws Exception { private void assertOkWithOutput(Matcher matcher, String... args) throws Exception { terminal.reset(); int status = executeMain(args); - assertThat(status, equalTo(ExitCodes.OK)); - assertThat(terminal.getErrorOutput(), emptyString()); - assertThat(terminal.getOutput(), matcher); + Assert.assertThat(status, equalTo(ExitCodes.OK)); + Assert.assertThat(terminal.getErrorOutput(), emptyString()); + Assert.assertThat(terminal.getOutput(), matcher); } private void assertUsage(Matcher matcher, String... args) throws Exception { terminal.reset(); initCallback = FAIL_INIT; int status = executeMain(args); - assertThat(status, equalTo(ExitCodes.USAGE)); - assertThat(terminal.getErrorOutput(), matcher); + Assert.assertThat(status, equalTo(ExitCodes.USAGE)); + Assert.assertThat(terminal.getErrorOutput(), matcher); } private void assertMutuallyExclusiveOptions(String... args) throws Exception { @@ -94,7 +95,7 @@ public void testPidFile() throws Exception { Path tmpDir = createTempDir(); Path pidFileArg = tmpDir.resolve("pid"); assertUsage(containsString("Option p/pidfile requires an argument"), "-p"); - initCallback = (daemonize, pidFile, quiet, env) -> { assertThat(pidFile.toString(), equalTo(pidFileArg.toString())); }; + initCallback = (daemonize, pidFile, quiet, env) -> { Assert.assertThat(pidFile.toString(), equalTo(pidFileArg.toString())); }; terminal.reset(); assertOk("-p", pidFileArg.toString()); terminal.reset(); @@ -103,7 +104,7 @@ public void testPidFile() throws Exception { public void testDaemonize() throws Exception { AtomicBoolean expectDaemonize = new AtomicBoolean(true); - initCallback = (d, p, q, e) -> assertThat(d, equalTo(expectDaemonize.get())); + initCallback = (d, p, q, e) -> Assert.assertThat(d, equalTo(expectDaemonize.get())); assertOk("-d"); assertOk("--daemonize"); expectDaemonize.set(false); @@ -112,7 +113,7 @@ public void testDaemonize() throws Exception { public void testQuiet() throws Exception { AtomicBoolean expectQuiet = new AtomicBoolean(true); - initCallback = (d, p, q, e) -> assertThat(q, equalTo(expectQuiet.get())); + initCallback = (d, p, q, e) -> Assert.assertThat(q, equalTo(expectQuiet.get())); assertOk("-q"); assertOk("--quiet"); expectQuiet.set(false); @@ -122,8 +123,8 @@ public void testQuiet() throws Exception { public void testElasticsearchSettings() throws Exception { initCallback = (d, p, q, e) -> { Settings settings = e.settings(); - assertThat(settings.get("foo"), equalTo("bar")); - assertThat(settings.get("baz"), equalTo("qux")); + Assert.assertThat(settings.get("foo"), equalTo("bar")); + Assert.assertThat(settings.get("baz"), equalTo("qux")); }; assertOk("-Efoo=bar", "-E", "baz=qux"); } @@ -145,8 +146,8 @@ public void testPathHome() throws Exception { expectedHomeDir.set(homeDir.toString()); initCallback = (d, p, q, e) -> { Settings settings = e.settings(); - assertThat(settings.get("path.home"), equalTo(expectedHomeDir.get())); - assertThat(settings.keySet(), hasItem("path.logs")); // added by env initialization + Assert.assertThat(settings.get("path.home"), equalTo(expectedHomeDir.get())); + Assert.assertThat(settings.keySet(), hasItem("path.logs")); // added by env initialization }; assertOk(); homeDir = null; @@ -161,7 +162,7 @@ interface InitMethod { Path homeDir; InitMethod initCallback; - final InitMethod FAIL_INIT = (d, p, q, e) -> fail("Did not expect to run init"); + final InitMethod FAIL_INIT = (d, p, q, e) -> Assert.fail("Did not expect to run init"); @Before public void resetCommand() { @@ -171,13 +172,13 @@ public void resetCommand() { @Override protected Command newCommand() { - return new Elasticsearch() { + return new ServerCli() { @Override protected Map captureSystemProperties() { if (homeDir == null) { return Map.of("es.path.conf", createTempDir().toString()); } - return mockSystemProperties(homeDir); + return CommandTestCase.mockSystemProperties(homeDir); } @Override diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java index 33e4a669770e4..83af1c8b133ab 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java @@ -30,22 +30,22 @@ public class JvmErgonomicsTests extends LaunchersTestCase { - public void testExtractValidHeapSizeUsingXmx() throws InterruptedException, IOException { + public void testExtractValidHeapSizeUsingXmx() throws Exception { assertThat(JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.singletonList("-Xmx2g"))), equalTo(2L << 30)); } - public void testExtractValidHeapSizeUsingMaxHeapSize() throws InterruptedException, IOException { + public void testExtractValidHeapSizeUsingMaxHeapSize() throws Exception { assertThat( JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.singletonList("-XX:MaxHeapSize=2g"))), equalTo(2L << 30) ); } - public void testExtractValidHeapSizeNoOptionPresent() throws InterruptedException, IOException { + public void testExtractValidHeapSizeNoOptionPresent() throws Exception { assertThat(JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.emptyList())), greaterThan(0L)); } - public void testHeapSizeInvalid() throws InterruptedException, IOException { + public void testHeapSizeInvalid() throws Exception { try { JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.singletonList("-Xmx2Z"))); fail("expected starting java to fail"); @@ -55,7 +55,7 @@ public void testHeapSizeInvalid() throws InterruptedException, IOException { } } - public void testHeapSizeTooSmall() throws InterruptedException, IOException { + public void testHeapSizeTooSmall() throws Exception { try { JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.singletonList("-Xmx1024"))); fail("expected starting java to fail"); @@ -65,7 +65,7 @@ public void testHeapSizeTooSmall() throws InterruptedException, IOException { } } - public void testHeapSizeWithSpace() throws InterruptedException, IOException { + public void testHeapSizeWithSpace() throws Exception { try { JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.singletonList("-Xmx 1024"))); fail("expected starting java to fail"); @@ -75,11 +75,11 @@ public void testHeapSizeWithSpace() throws InterruptedException, IOException { } } - public void testMaxDirectMemorySizeUnset() throws InterruptedException, IOException { + public void testMaxDirectMemorySizeUnset() throws Exception { assertThat(JvmOption.extractMaxDirectMemorySize(JvmOption.findFinalOptions(Collections.singletonList("-Xmx1g"))), equalTo(0L)); } - public void testMaxDirectMemorySizeSet() throws InterruptedException, IOException { + public void testMaxDirectMemorySizeSet() throws Exception { assertThat( JvmOption.extractMaxDirectMemorySize(JvmOption.findFinalOptions(Arrays.asList("-Xmx1g", "-XX:MaxDirectMemorySize=512m"))), equalTo(512L << 20) @@ -98,14 +98,14 @@ public void testExtractSystemProperties() { assertEquals(expectedSystemProperties, parsedSystemProperties); } - public void testG1GOptionsForSmallHeap() throws InterruptedException, IOException { + public void testG1GOptionsForSmallHeap() throws Exception { List jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseG1GC")); assertThat(jvmErgonomics, hasItem("-XX:G1HeapRegionSize=4m")); assertThat(jvmErgonomics, hasItem("-XX:InitiatingHeapOccupancyPercent=30")); assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=15")); } - public void testG1GOptionsForSmallHeapWhenTuningSet() throws InterruptedException, IOException { + public void testG1GOptionsForSmallHeapWhenTuningSet() throws Exception { List jvmErgonomics = JvmErgonomics.choose( Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseG1GC", "-XX:G1HeapRegionSize=4m", "-XX:InitiatingHeapOccupancyPercent=45") ); @@ -114,21 +114,21 @@ public void testG1GOptionsForSmallHeapWhenTuningSet() throws InterruptedExceptio assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=15")); } - public void testG1GOptionsForLargeHeap() throws InterruptedException, IOException { + public void testG1GOptionsForLargeHeap() throws Exception { List jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms8g", "-Xmx8g", "-XX:+UseG1GC")); assertThat(jvmErgonomics, hasItem("-XX:InitiatingHeapOccupancyPercent=30")); assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=25")); assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize=")))); } - public void testG1GOptionsForSmallHeapWhenOtherGCSet() throws InterruptedException, IOException { + public void testG1GOptionsForSmallHeapWhenOtherGCSet() throws Exception { List jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseParallelGC")); assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize=")))); assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent=")))); assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent=")))); } - public void testG1GOptionsForLargeHeapWhenTuningSet() throws InterruptedException, IOException { + public void testG1GOptionsForLargeHeapWhenTuningSet() throws Exception { List jvmErgonomics = JvmErgonomics.choose( Arrays.asList("-Xms8g", "-Xmx8g", "-XX:+UseG1GC", "-XX:InitiatingHeapOccupancyPercent=60", "-XX:G1ReservePercent=10") ); @@ -142,7 +142,7 @@ public void testExtractNoSystemProperties() { assertTrue(parsedSystemProperties.isEmpty()); } - public void testMaxDirectMemorySizeChoice() throws InterruptedException, IOException { + public void testMaxDirectMemorySizeChoice() throws Exception { final Map heapMaxDirectMemorySize = Map.of( "64M", Long.toString((64L << 20) / 2), @@ -159,14 +159,14 @@ public void testMaxDirectMemorySizeChoice() throws InterruptedException, IOExcep "8G", Long.toString((8L << 30) / 2) ); - final String heapSize = RandomizedTest.randomFrom(heapMaxDirectMemorySize.keySet().toArray(String[]::new)); + final String heapSize = randomFrom(heapMaxDirectMemorySize.keySet().toArray(String[]::new)); assertThat( JvmErgonomics.choose(Arrays.asList("-Xms" + heapSize, "-Xmx" + heapSize)), hasItem("-XX:MaxDirectMemorySize=" + heapMaxDirectMemorySize.get(heapSize)) ); } - public void testMaxDirectMemorySizeChoiceWhenSet() throws InterruptedException, IOException { + public void testMaxDirectMemorySizeChoiceWhenSet() throws Exception { assertThat( JvmErgonomics.choose(Arrays.asList("-Xms1g", "-Xmx1g", "-XX:MaxDirectMemorySize=1g")), everyItem(not(startsWith("-XX:MaxDirectMemorySize="))) diff --git a/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java b/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java index 83708a2f8c4bb..e2b2198e45b2c 100644 --- a/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java +++ b/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java @@ -42,7 +42,7 @@ public class MockTerminal extends Terminal { private boolean hasOutputStream = true; public MockTerminal() { - super("\n"); // always *nix newlines for tests + super(null, null, null, "\n"); // always *nix newlines for tests } @Override From 8f71d351e88af3e32aa675bb65a89950bd14ed87 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 25 Apr 2022 14:48:12 -0700 Subject: [PATCH 047/166] fix scripts to use correct env var --- distribution/src/bin/elasticsearch | 4 ++-- distribution/src/bin/elasticsearch-service.bat | 2 +- distribution/src/bin/elasticsearch.bat | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/distribution/src/bin/elasticsearch b/distribution/src/bin/elasticsearch index c060f8d82916e..4f48aa8453d73 100755 --- a/distribution/src/bin/elasticsearch +++ b/distribution/src/bin/elasticsearch @@ -1,5 +1,5 @@ #!/bin/bash -LAUNCHER_TOOLNAME=server -LAUNCHER_LIBS=lib/tools/server-cli +CLI_NAME=server +CLI_LIBS=lib/tools/server-cli source "`dirname "$0"`"/elasticsearch-cli diff --git a/distribution/src/bin/elasticsearch-service.bat b/distribution/src/bin/elasticsearch-service.bat index 65ded53429a81..d57b0dc324f1f 100644 --- a/distribution/src/bin/elasticsearch-service.bat +++ b/distribution/src/bin/elasticsearch-service.bat @@ -3,7 +3,7 @@ setlocal enabledelayedexpansion setlocal enableextensions -set CLI_TOOLNAME=windows-service +set CLI_NAME=windows-service set CLI_LIBS=lib/tools/windows-service-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ diff --git a/distribution/src/bin/elasticsearch.bat b/distribution/src/bin/elasticsearch.bat index 4f3ea1f5ba37d..18c2a4b26c4ce 100644 --- a/distribution/src/bin/elasticsearch.bat +++ b/distribution/src/bin/elasticsearch.bat @@ -3,8 +3,8 @@ setlocal enabledelayedexpansion setlocal enableextensions -set LAUNCHER_TOOLNAME=server -set LAUNCHER_LIBS=lib/tools/server-cli +set CLI_NAME=server +set CLI_LIBS=lib/tools/server-cli call "%~dp0elasticsearch-cli.bat" ^ %%* ^ || goto exit From fcde65f262d559dde0cb5fa73871315fd63ea456 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 27 Apr 2022 22:29:46 -0700 Subject: [PATCH 048/166] cleanups --- .../server/cli/JvmOptionsParser.java | 2 +- .../server/cli/KeystorePasswordTerminal.java | 10 +- .../elasticsearch/server/cli/ServerCli.java | 149 ++++++++---------- .../server/cli/TempDirectory.java | 23 ++- .../elasticsearch/bootstrap/Bootstrap.java | 2 +- .../bootstrap/BootstrapInfo.java | 6 + .../bootstrap/Elasticsearch.java | 6 +- .../elasticsearch/bootstrap/ServerArgs.java | 7 +- 8 files changed, 106 insertions(+), 99 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index ad11ee4e18793..61800fac14c0b 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -62,7 +62,7 @@ SortedMap invalidLines() { } - static List determine(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws UserException { + static List determineOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws UserException { final JvmOptionsParser parser = new JvmOptionsParser(); final Map substitutions = new HashMap<>(); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java index 121516c3fc210..ca304edd2c3bf 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -19,23 +19,23 @@ class KeystorePasswordTerminal extends Terminal { private final Terminal delegate; protected KeystorePasswordTerminal(Terminal delegate, SecureString password) { - super(newPasswordReader(password), delegate.getWriter(), delegate.getErrorWriter(), delegate.getLineSeparator()); + super(newPasswordReader(password), delegate.getWriter(), delegate.getErrorWriter()); this.delegate = delegate; } private static Reader newPasswordReader(final SecureString password) { return new Reader() { - int ndx = 0; + int pos = 0; @Override public int read(char[] cbuf, int off, int len) { - int charsLeft = password.length() - ndx; + int charsLeft = password.length() - pos; if (charsLeft == 0) { return -1; } int toCopy = Math.min(charsLeft, len); - System.arraycopy(password.getChars(), ndx, cbuf, off, toCopy); - ndx += toCopy; + System.arraycopy(password.getChars(), pos, cbuf, off, toCopy); + pos += toCopy; return toCopy; } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 7b7ade263f73f..6f65a9d174aed 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -24,8 +24,8 @@ import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; import org.elasticsearch.common.settings.KeyStoreWrapper; import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.PathUtils; -import org.elasticsearch.core.internal.io.IOUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; @@ -42,6 +42,9 @@ import java.util.Locale; import java.util.Map; +import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; +import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; + class ServerCli extends EnvironmentAwareCommand { private final OptionSpecBuilder versionOption; private final OptionSpecBuilder daemonizeOption; @@ -77,98 +80,56 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th return; } - final boolean daemonize = options.has(daemonizeOption); - final Path pidFile = pidfileOption.value(options); - final boolean quiet = options.has(quietOption); - - Map envVars = new HashMap<>(System.getenv()); - Path tempDir = TempDirectory.initialize(envVars); + // setup security final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); autoConfigureSecurity(terminal, options, keystorePassword); - // reload settings since auto security might have changed them + // TODO: don't recreate if security settings were not changed env = createEnv(options); - // TODO: add keystore password to server args - ServerArgs serverArgs = new ServerArgs(daemonize, pidFile, env.settings(), env.configFile()); - List jvmOptions = JvmOptionsParser.determine(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); + // determine process environment and arguments + Map envVars = new HashMap<>(this.envVars); + Path tempDir = TempDirectory.setup(envVars); + List jvmOptions = JvmOptionsParser.determineOptions(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); // jvmOptions.add("-Des.path.conf=" + env.configFile()); - jvmOptions.add("-Des.distribution.type=" + System.getProperty("es.distribution.type")); - - Path esHome = PathUtils.get(System.getProperty("es.path.home")); - Path javaHome = PathUtils.get(System.getProperty("java.home")); - List command = new ArrayList<>(); - // TODO: fix this so it works on windows - command.add(javaHome.resolve("bin").resolve("java").toString()); - command.addAll(jvmOptions); - command.add("-cp"); - command.add(esHome.resolve("lib").resolve("*").toString()); - command.add("org.elasticsearch.bootstrap.Elasticsearch"); - System.out.println("command: " + command); + jvmOptions.add("-Des.distribution.type=" + sysprops.get("es.distribution.type")); + var args = createArgs(options, keystorePassword, env); - ProcessBuilder builder = new ProcessBuilder(command); - builder.environment().putAll(envVars); - builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); - - try { - final Process process = builder.start(); - try (var out = new OutputStreamStreamOutput(process.getOutputStream())) { - serverArgs.writeTo(out); - } - String userExceptionMsg = null; - boolean ready = false; - InputStream err = process.getErrorStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(err)); - try { - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty() == false && line.charAt(0) == '\24') { - userExceptionMsg = line.substring(1); - } else if (line.isEmpty() == false && line.charAt(0) == '\21') { - ready = true; - // The server closes stderr right after this message, but for some unknown reason - // the pipe closing does not close this end of the pipe, so we must explicitly - // break out of this loop, or we will block forever on the next read. - break; - } else { - terminal.getErrorWriter().println(line); - } - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - if (ready && daemonize) { - closeStreams(process); - return; - } - - int code = process.waitFor(); - terminal.flush(); - System.out.println("exited: " + code); - if (code != ExitCodes.OK) { - throw new UserException(code, userExceptionMsg); + final Process process = createProcess(jvmOptions, envVars); + try (var out = new OutputStreamStreamOutput(process.getOutputStream())) { + args.writeTo(out); + } + String userExceptionMsg = null; + boolean ready = false; + InputStream err = process.getErrorStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(err)); + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { + userExceptionMsg = line.substring(1); + break; + } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { + ready = true; + // The server closes stderr right after this message, but for some unknown reason + // the pipe closing does not close this end of the pipe, so we must explicitly + // break out of this loop, or we will block forever on the next read. + break; + } else { + terminal.getErrorWriter().println(line); } - } catch (IOException e) { - throw new UncheckedIOException(e); - } catch (InterruptedException e) { - throw new UserException(ExitCodes.IO_ERROR, "Interrupted while waiting for Elasticsearch process"); } - // TODO: add ctrl-c handler so we can wait for subprocess - // TODO: check the java.io.tmpdir - // a misconfigured java.io.tmpdir can cause hard-to-diagnose problems later, so reject it immediately - /*try { - env.validateTmpFile(); - } catch (IOException e) { - throw new UserException(ExitCodes.CONFIG, e.getMessage()); + if (ready && args.daemonize()) { + closeStreams(process); + return; } - try { - init(daemonize, pidFile, quiet, env); - } catch (NodeValidationException e) { - throw new UserException(ExitCodes.CONFIG, e.getMessage()); - }*/ + int code = process.waitFor(); + terminal.flush(); + System.out.println("exited: " + code); + if (code != ExitCodes.OK) { + throw new UserException(code, userExceptionMsg); + } } private void closeStreams(Process process) throws IOException { @@ -232,4 +193,30 @@ private void autoConfigureSecurity(Terminal terminal, OptionSet options, SecureS } } } + + private ServerArgs createArgs(OptionSet options, SecureString keystorePassword, Environment env) { + final boolean daemonize = options.has(daemonizeOption); + final boolean quiet = options.has(quietOption); + final Path pidFile = pidfileOption.value(options); + + return new ServerArgs(daemonize, quiet, pidFile, keystorePassword, env.settings(), env.configFile()); + } + + private Process createProcess(List jvmOptions, Map envVars) throws IOException { + Path esHome = PathUtils.get(""); + Path javaHome = PathUtils.get(sysprops.get("java.home")); + List command = new ArrayList<>(); + boolean isWindows = sysprops.get("os.name").startsWith("Windows"); + command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); + command.addAll(jvmOptions); + command.add("-cp"); + command.add(esHome.resolve("lib").resolve("*").toString()); + command.add("org.elasticsearch.bootstrap.Elasticsearch"); + + var builder = new ProcessBuilder(command); + builder.environment().putAll(envVars); + builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); + + return builder.start(); + } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java index 06a33fd1ab84c..7ed02802121e4 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java @@ -13,6 +13,7 @@ import org.elasticsearch.core.SuppressForbidden; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -26,19 +27,29 @@ final class TempDirectory { /** - * Ensures the environment map has ES_TMPDIR and LIBFFI_TMPDIR. + * Ensures the environment map has {@code ES_TMPDIR} and {@code LIBFFI_TMPDIR}. + * + * @param env the current environment variables that will be passed to Elasticsearch + * @return the temporary directory */ - public static Path initialize(Map env) throws UserException { + public static Path setup(Map env) throws UserException { final Path path; String existingTempDir = env.remove("ES_TMPDIR"); if (existingTempDir != null) { path = Paths.get(existingTempDir); + if (Files.exists(path) == false) { + throw new UserException(ExitCodes.CONFIG, "Temporary directory [" + path + "] does not exist or is not accessible"); + } + if (Files.isDirectory(path) == false) { + throw new UserException(ExitCodes.CONFIG, "Temporary directory [" + path + "] is not a directory"); + } } else { try { if (System.getProperty("os.name").startsWith("Windows")) { /* - * On Windows, we avoid creating a unique temporary directory per invocation lest we pollute the temporary directory. On other - * operating systems, temporary directories will be cleaned automatically via various mechanisms (e.g., systemd, or restarts). + * On Windows, we avoid creating a unique temporary directory per invocation lest + * we pollute the temporary directory. On other operating systems, temporary directories + * will be cleaned automatically via various mechanisms (e.g., systemd, or restarts). */ path = Paths.get(System.getProperty("java.io.tmpdir"), "elasticsearch"); Files.createDirectories(path); @@ -46,9 +57,7 @@ public static Path initialize(Map env) throws UserException { path = createTempDirectory("elasticsearch-"); } } catch (IOException e) { - // TODO: don't mask this exception, should we just propagate or try to summarize? in shell-land we would have printed it and - // exited - throw new UserException(ExitCodes.CONFIG, "Could not create temporary directory"); + throw new UncheckedIOException(e); } } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index e0fee9abf0a68..1f4c78545d157 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -418,7 +418,7 @@ private static Runnable getSysOutCloser() { private static Runnable getSysErrorCloser() { final PrintStream err = System.err; return () -> { - err.println('\21'); + err.println(BootstrapInfo.SERVER_READY_MARKER); err.close(); }; } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java index 0b63211f2851a..e2d7637de7f11 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java @@ -63,6 +63,12 @@ public static ConsoleLoader.Console getConsole() { */ public static final String UNTRUSTED_CODEBASE = "/untrusted"; + // TODO: document + public static final char USER_EXCEPTION_MARKER = '\24'; + + // TODO: document + public static final char SERVER_READY_MARKER = '\21'; + // create a view of sysprops map that does not allow modifications // this must be done this way (e.g. versus an actual typed map), because // some test methods still change properties, so whitelisted changes must diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 299611fd4724e..263d6689042ba 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -22,6 +22,8 @@ import java.security.Permission; import java.security.Security; +import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; + /** * This class starts elasticsearch. */ @@ -65,11 +67,11 @@ public void checkPermission(Permission perm) { ); } catch (NodeValidationException e) { exitCode = ExitCodes.CONFIG; - err.print('\24'); + err.print(USER_EXCEPTION_MARKER); err.println(e.getMessage()); } catch (UserException e) { exitCode = e.exitCode; - err.print('\24'); + err.print(USER_EXCEPTION_MARKER); err.println(e.getMessage()); } catch (Exception e) { exitCode = 1; // mimic JDK exit code on exception diff --git a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java index 00906f260b874..9605672874b37 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java @@ -11,16 +11,17 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.PathUtils; import java.io.IOException; import java.nio.file.Path; -public record ServerArgs(boolean daemonize, Path pidFile, Settings nodeSettings, Path configDir) implements Writeable { +public record ServerArgs(boolean daemonize, boolean quiet, Path pidFile, SecureString keystorePassword, Settings nodeSettings, Path configDir) implements Writeable { public ServerArgs(StreamInput in) throws IOException { - this(in.readBoolean(), readPidFile(in), Settings.readSettingsFromStream(in), PathUtils.get(in.readString())); + this(in.readBoolean(), in.readBoolean(), readPidFile(in), in.readSecureString(), Settings.readSettingsFromStream(in), PathUtils.get(in.readString())); } private static Path readPidFile(StreamInput in) throws IOException { @@ -31,7 +32,9 @@ private static Path readPidFile(StreamInput in) throws IOException { @Override public void writeTo(StreamOutput out) throws IOException { out.writeBoolean(daemonize); + out.writeBoolean(quiet); out.writeOptionalString(pidFile == null ? null : pidFile.toString()); + out.writeSecureString(keystorePassword); Settings.writeSettingsToStream(nodeSettings, out); out.writeString(configDir.toString()); } From 468569f42047d45e399c2d3bc1e55c0502f1eff5 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 28 Apr 2022 06:06:27 -0700 Subject: [PATCH 049/166] use quiet flag --- .../main/java/org/elasticsearch/bootstrap/Elasticsearch.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 263d6689042ba..61ad6f5145f13 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -62,7 +62,7 @@ public void checkPermission(Permission perm) { elasticsearch.init( serverArgs.daemonize(), serverArgs.pidFile(), - false, + serverArgs.quiet(), new Environment(serverArgs.nodeSettings(), serverArgs.configDir()) ); } catch (NodeValidationException e) { From 5551b3d14ae58057e0fb43dc33417441493bba43 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 28 Apr 2022 06:08:10 -0700 Subject: [PATCH 050/166] cleanup project config --- settings.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/settings.gradle b/settings.gradle index 6a05aacfe8028..770cdad0721de 100644 --- a/settings.gradle +++ b/settings.gradle @@ -55,12 +55,11 @@ List projects = [ 'distribution:tools:java-version-checker', 'distribution:tools:cli-launcher', 'distribution:tools:server-cli', + 'distribution:tools:windows-service-cli', 'distribution:tools:plugin-cli', 'distribution:tools:keystore-cli', 'distribution:tools:geoip-cli', 'distribution:tools:ansi-console', - 'distribution:tools:server-cli', - 'distribution:tools:windows-service-cli', 'server', 'server:cli', 'test:framework', From 4ca5bbd79eef3eb81653d6b03ef4f07422edd45d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 28 Apr 2022 06:09:23 -0700 Subject: [PATCH 051/166] remove direct dep on cli since it is still part of server --- x-pack/plugin/security/build.gradle | 1 - x-pack/plugin/security/cli/build.gradle | 1 - x-pack/plugin/watcher/build.gradle | 2 -- 3 files changed, 4 deletions(-) diff --git a/x-pack/plugin/security/build.gradle b/x-pack/plugin/security/build.gradle index 398726e2e928e..952047f6dedfa 100644 --- a/x-pack/plugin/security/build.gradle +++ b/x-pack/plugin/security/build.gradle @@ -14,7 +14,6 @@ esplugin { archivesBaseName = 'x-pack-security' dependencies { - compileOnly project(':libs:elasticsearch-cli') compileOnly project(path: xpackModule('core')) api project(path: ':modules:transport-netty4') diff --git a/x-pack/plugin/security/cli/build.gradle b/x-pack/plugin/security/cli/build.gradle index 8b64f04622b37..e2684ce8d54f7 100644 --- a/x-pack/plugin/security/cli/build.gradle +++ b/x-pack/plugin/security/cli/build.gradle @@ -7,7 +7,6 @@ archivesBaseName = 'elasticsearch-security-cli' dependencies { compileOnly project(":server") - compileOnly project(":libs:elasticsearch-cli") compileOnly project(path: xpackModule('core')) api "org.bouncycastle:bcpkix-jdk15on:${versions.bouncycastle}" api "org.bouncycastle:bcprov-jdk15on:${versions.bouncycastle}" diff --git a/x-pack/plugin/watcher/build.gradle b/x-pack/plugin/watcher/build.gradle index 6332c5e075bb9..21b174a523ce5 100644 --- a/x-pack/plugin/watcher/build.gradle +++ b/x-pack/plugin/watcher/build.gradle @@ -20,8 +20,6 @@ tasks.named("dependencyLicenses").configure { dependencies { compileOnly project(':server') compileOnly project(':modules:lang-painless:spi') -// TODO: move watcher tool to another project - compileOnly project(':libs:elasticsearch-cli') compileOnly project(path: xpackModule('core')) compileOnly project(path: ':modules:transport-netty4') From 81fa06d6f49472516a5e0f895c4782a688925b29 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 28 Apr 2022 07:06:09 -0700 Subject: [PATCH 052/166] simplify checked exceptions --- .../server/cli/JvmErgonomics.java | 4 +--- .../elasticsearch/server/cli/JvmOption.java | 15 ++++----------- .../server/cli/JvmOptionsParser.java | 8 +++----- .../elasticsearch/server/cli/ServerCli.java | 7 ++++++- .../server/cli/JvmErgonomicsTests.java | 1 - .../windows_service/WindowsServiceCli.java | 7 +++++-- .../elasticsearch/bootstrap/ServerArgs.java | 18 ++++++++++++++++-- 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmErgonomics.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmErgonomics.java index 2fbebfdf8a259..46e3da3ced90b 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmErgonomics.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmErgonomics.java @@ -8,8 +8,6 @@ package org.elasticsearch.server.cli; -import org.elasticsearch.cli.UserException; - import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -33,7 +31,7 @@ private JvmErgonomics() { * @param userDefinedJvmOptions A list of JVM options that have been defined by the user. * @return A list of additional JVM options to set. */ - static List choose(final List userDefinedJvmOptions) throws UserException, IOException { + static List choose(final List userDefinedJvmOptions) throws InterruptedException, IOException { final List ergonomicChoices = new ArrayList<>(); final Map finalJvmOptions = JvmOption.findFinalOptions(userDefinedJvmOptions); final long heapSize = JvmOption.extractMaxHeapSize(finalJvmOptions); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOption.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOption.java index b6a176434ef8f..27f74d0f0fa4b 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOption.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOption.java @@ -8,9 +8,6 @@ package org.elasticsearch.server.cli; -import org.elasticsearch.cli.ExitCodes; -import org.elasticsearch.cli.UserException; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -78,14 +75,15 @@ public static long extractMaxDirectMemorySize(final Map final /** * Determine the options present when invoking a JVM with the given user defined options. */ - public static Map findFinalOptions(final List userDefinedJvmOptions) throws UserException, IOException { + public static Map findFinalOptions(final List userDefinedJvmOptions) throws InterruptedException, + IOException { return flagsFinal(userDefinedJvmOptions).stream() .map(OPTION::matcher) .filter(Matcher::matches) .collect(Collectors.toUnmodifiableMap(m -> m.group("flag"), m -> new JvmOption(m.group("value"), m.group("origin")))); } - private static List flagsFinal(final List userDefinedJvmOptions) throws UserException, IOException { + private static List flagsFinal(final List userDefinedJvmOptions) throws InterruptedException, IOException { /* * To deduce the final set of JVM options that Elasticsearch is going to start with, we start a separate Java process with the JVM * options that we would pass on the command line. For this Java process we will add two additional flags, -XX:+PrintFlagsFinal and @@ -103,12 +101,7 @@ private static List flagsFinal(final List userDefinedJvmOptions) final Process process = new ProcessBuilder().command(command).start(); final List output = readLinesFromInputStream(process.getInputStream()); final List error = readLinesFromInputStream(process.getErrorStream()); - final int status; - try { - status = process.waitFor(); - } catch (InterruptedException e) { - throw new UserException(ExitCodes.CODE_ERROR, "Interrupted while waiting for jvm options determiner"); - } + final int status = process.waitFor(); if (status != 0) { final String message = String.format( Locale.ROOT, diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index 61800fac14c0b..3c13cca10963f 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -16,7 +16,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; -import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -62,7 +61,8 @@ SortedMap invalidLines() { } - static List determineOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws UserException { + static List determineOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, + IOException, UserException { final JvmOptionsParser parser = new JvmOptionsParser(); final Map substitutions = new HashMap<>(); @@ -71,8 +71,6 @@ static List determineOptions(Path configDir, Path pluginsDir, Path tmpDi try { return parser.jvmOptions(configDir, pluginsDir, envOptions, substitutions); - } catch (IOException e) { - throw new UncheckedIOException(e); } catch (final JvmOptionsFileParserException e) { final String errorMessage = String.format( Locale.ROOT, @@ -101,7 +99,7 @@ static List determineOptions(Path configDir, Path pluginsDir, Path tmpDi } private List jvmOptions(final Path config, Path plugins, final String esJavaOpts, final Map substitutions) - throws UserException, IOException, JvmOptionsFileParserException { + throws UserException, IOException, InterruptedException, JvmOptionsFileParserException { final List jvmOptions = readJvmOptionsFiles(config); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 6f65a9d174aed..7da729f5ac5f0 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -90,7 +90,12 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th // determine process environment and arguments Map envVars = new HashMap<>(this.envVars); Path tempDir = TempDirectory.setup(envVars); - List jvmOptions = JvmOptionsParser.determineOptions(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); + List jvmOptions = JvmOptionsParser.determineOptions( + env.configFile(), + env.pluginsFile(), + tempDir, + envVars.get("ES_JAVA_OPTS") + ); // jvmOptions.add("-Des.path.conf=" + env.configFile()); jvmOptions.add("-Des.distribution.type=" + sysprops.get("es.distribution.type")); var args = createArgs(options, keystorePassword, env); diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java index 83af1c8b133ab..5840746c7cf94 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java @@ -8,7 +8,6 @@ package org.elasticsearch.server.cli; -import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java index 192d4e462d45b..4f594df5e987d 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java @@ -51,8 +51,11 @@ protected String getAdditionalArgs(String serviceId, Path esHome, Map Date: Thu, 28 Apr 2022 08:26:02 -0700 Subject: [PATCH 053/166] imports --- .../server/cli/ElasticsearchCliTests.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java index 30f90fbf9ac4d..6baa8c6c002b9 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java @@ -16,7 +16,6 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; import org.hamcrest.Matcher; -import org.junit.Assert; import org.junit.Before; import java.nio.file.Path; @@ -39,17 +38,17 @@ private void assertOk(String... args) throws Exception { private void assertOkWithOutput(Matcher matcher, String... args) throws Exception { terminal.reset(); int status = executeMain(args); - Assert.assertThat(status, equalTo(ExitCodes.OK)); - Assert.assertThat(terminal.getErrorOutput(), emptyString()); - Assert.assertThat(terminal.getOutput(), matcher); + assertThat(status, equalTo(ExitCodes.OK)); + assertThat(terminal.getErrorOutput(), emptyString()); + assertThat(terminal.getOutput(), matcher); } private void assertUsage(Matcher matcher, String... args) throws Exception { terminal.reset(); initCallback = FAIL_INIT; int status = executeMain(args); - Assert.assertThat(status, equalTo(ExitCodes.USAGE)); - Assert.assertThat(terminal.getErrorOutput(), matcher); + assertThat(status, equalTo(ExitCodes.USAGE)); + assertThat(terminal.getErrorOutput(), matcher); } private void assertMutuallyExclusiveOptions(String... args) throws Exception { @@ -95,7 +94,7 @@ public void testPidFile() throws Exception { Path tmpDir = createTempDir(); Path pidFileArg = tmpDir.resolve("pid"); assertUsage(containsString("Option p/pidfile requires an argument"), "-p"); - initCallback = (daemonize, pidFile, quiet, env) -> { Assert.assertThat(pidFile.toString(), equalTo(pidFileArg.toString())); }; + initCallback = (daemonize, pidFile, quiet, env) -> { assertThat(pidFile.toString(), equalTo(pidFileArg.toString())); }; terminal.reset(); assertOk("-p", pidFileArg.toString()); terminal.reset(); @@ -104,7 +103,7 @@ public void testPidFile() throws Exception { public void testDaemonize() throws Exception { AtomicBoolean expectDaemonize = new AtomicBoolean(true); - initCallback = (d, p, q, e) -> Assert.assertThat(d, equalTo(expectDaemonize.get())); + initCallback = (d, p, q, e) -> assertThat(d, equalTo(expectDaemonize.get())); assertOk("-d"); assertOk("--daemonize"); expectDaemonize.set(false); @@ -113,7 +112,7 @@ public void testDaemonize() throws Exception { public void testQuiet() throws Exception { AtomicBoolean expectQuiet = new AtomicBoolean(true); - initCallback = (d, p, q, e) -> Assert.assertThat(q, equalTo(expectQuiet.get())); + initCallback = (d, p, q, e) -> assertThat(q, equalTo(expectQuiet.get())); assertOk("-q"); assertOk("--quiet"); expectQuiet.set(false); @@ -123,8 +122,8 @@ public void testQuiet() throws Exception { public void testElasticsearchSettings() throws Exception { initCallback = (d, p, q, e) -> { Settings settings = e.settings(); - Assert.assertThat(settings.get("foo"), equalTo("bar")); - Assert.assertThat(settings.get("baz"), equalTo("qux")); + assertThat(settings.get("foo"), equalTo("bar")); + assertThat(settings.get("baz"), equalTo("qux")); }; assertOk("-Efoo=bar", "-E", "baz=qux"); } @@ -146,8 +145,8 @@ public void testPathHome() throws Exception { expectedHomeDir.set(homeDir.toString()); initCallback = (d, p, q, e) -> { Settings settings = e.settings(); - Assert.assertThat(settings.get("path.home"), equalTo(expectedHomeDir.get())); - Assert.assertThat(settings.keySet(), hasItem("path.logs")); // added by env initialization + assertThat(settings.get("path.home"), equalTo(expectedHomeDir.get())); + assertThat(settings.keySet(), hasItem("path.logs")); // added by env initialization }; assertOk(); homeDir = null; @@ -162,7 +161,7 @@ interface InitMethod { Path homeDir; InitMethod initCallback; - final InitMethod FAIL_INIT = (d, p, q, e) -> Assert.fail("Did not expect to run init"); + final InitMethod FAIL_INIT = (d, p, q, e) -> fail("Did not expect to run init"); @Before public void resetCommand() { From 55a08652b7d559134348fce9677a905b796fc1c2 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 28 Apr 2022 14:37:40 -0700 Subject: [PATCH 054/166] abstract start --- .../src/main/java/org/elasticsearch/server/cli/ServerCli.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 7da729f5ac5f0..2d839f9d0f129 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -222,6 +222,10 @@ private Process createProcess(List jvmOptions, Map envVa builder.environment().putAll(envVars); builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); + return startProcess(builder); + } + + Process startProcess(ProcessBuilder builder) { return builder.start(); } } From 9ac647e217890af0f5095d4f2f77b825bf5dc02d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 29 Apr 2022 14:51:53 -0700 Subject: [PATCH 055/166] trying to improve tests... --- .../elasticsearch/server/cli/ServerCli.java | 21 ++++++++++++------- .../server/cli/ElasticsearchCliTests.java | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 2d839f9d0f129..90fede3897d78 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -18,6 +18,7 @@ import org.elasticsearch.cli.CliToolProvider; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.cli.EnvironmentAwareCommand; @@ -71,7 +72,7 @@ class ServerCli extends EnvironmentAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception { + protected void execute(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env) throws Exception { if (options.nonOptionArguments().isEmpty() == false) { throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments()); } @@ -82,13 +83,13 @@ protected void execute(Terminal terminal, OptionSet options, Environment env) th // setup security final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); - autoConfigureSecurity(terminal, options, keystorePassword); + autoConfigureSecurity(terminal, options, keystorePassword, processInfo); // reload settings since auto security might have changed them // TODO: don't recreate if security settings were not changed - env = createEnv(options); + env = createEnv(options, processInfo); // determine process environment and arguments - Map envVars = new HashMap<>(this.envVars); + Map envVars = new HashMap<>(processInfo.envVars()); Path tempDir = TempDirectory.setup(envVars); List jvmOptions = JvmOptionsParser.determineOptions( env.configFile(), @@ -179,12 +180,18 @@ private void autoConfigureSecurity(Terminal terminal, OptionSet options, SecureS }); String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; - Command autoConfigNode = CliToolProvider.load("auto-configure-node", autoConfigLibs).create(); + Command cmd = CliToolProvider.load("auto-configure-node", autoConfigLibs).create(); + assert cmd instanceof EnvironmentAwareCommand; + @SuppressWarnings("raw") + var autoConfigNode = (EnvironmentAwareCommand) cmd; + OptionSet newOptions = new Op() + + if (options.has(enrollmentTokenOption)) { final String enrollmentToken = enrollmentTokenOption.value(options); args.add("--enrollment-token"); args.add(enrollmentToken); - int ret = autoConfigNode.main(args.toArray(new String[0]), autoConfigTerminal); + int ret = autoConfigNode.(args.toArray(new String[0]), autoConfigTerminal); if (ret != 0) { throw new UserException(ret, "Auto security enrollment failed"); } @@ -225,7 +232,7 @@ private Process createProcess(List jvmOptions, Map envVa return startProcess(builder); } - Process startProcess(ProcessBuilder builder) { + protected Process startProcess(ProcessBuilder builder) throws IOException { return builder.start(); } } diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java index d885c3baba628..666d7aa43cd62 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java @@ -171,7 +171,7 @@ protected Command newCommand() { return new ServerCli() { @Override - Process startProcess(ProcessBuilder processBuilder) { + protected Process startProcess(ProcessBuilder processBuilder) { if (initCallback != null) { initCallback.init(daemonize, pidFile, quiet, initialEnv); } From f836b1796de2735bc9595bf0de11f151a866dcb3 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 29 Apr 2022 14:53:44 -0700 Subject: [PATCH 056/166] Make env aware CLI command publicly runnable The EnvironmentAwareCommand is the most common base class for CLI tools. Sometimes it is desirable for one tool to call into another. With the new CliToolLauncher, this becomes easier to do. However, that only gives access to the base Command class, which means the Environment would be reparsed if the implementation is an EnvironmentAwareCommand. This commit makes the execute method of EnvironmentAwareCommand public so that other tools can pass the already parsed env. relates #85758 --- .../cli/keystore/BaseKeyStoreCommand.java | 2 +- .../cli/keystore/CreateKeyStoreCommand.java | 2 +- .../cli/keystore/HasPasswordKeyStoreCommand.java | 2 +- .../plugins/cli/InstallPluginCommand.java | 2 +- .../elasticsearch/plugins/cli/ListPluginsCommand.java | 2 +- .../elasticsearch/plugins/cli/RemovePluginCommand.java | 2 +- .../cli/EvilEnvironmentAwareCommandTests.java | 2 +- .../org/elasticsearch/bootstrap/Elasticsearch.java | 2 +- .../common/cli/EnvironmentAwareCommand.java | 2 +- .../elasticsearch/common/cli/KeyStoreAwareCommand.java | 2 +- .../common/cli/EnvironmentAwareCommandTests.java | 2 +- .../xpack/security/cli/AutoConfigureNode.java | 2 +- .../xpack/security/cli/CertificateGenerateTool.java | 2 +- .../xpack/security/cli/CertificateTool.java | 6 +++--- .../xpack/security/cli/HttpCertificateCommand.java | 2 +- .../authc/esnative/tool/SetupPasswordTool.java | 4 ++-- .../xpack/security/authc/file/tool/UsersTool.java | 10 +++++----- .../xpack/security/authc/saml/SamlMetadataCommand.java | 2 +- .../xpack/security/authc/service/FileTokensTool.java | 6 +++--- .../xpack/security/crypto/tool/SystemKeyTool.java | 2 +- .../tool/AutoConfigGenerateElasticPasswordHash.java | 2 +- .../xpack/security/tool/BaseRunAsSuperuserCommand.java | 2 +- 22 files changed, 31 insertions(+), 31 deletions(-) diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/BaseKeyStoreCommand.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/BaseKeyStoreCommand.java index 409466ad58150..74ab33a12efcf 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/BaseKeyStoreCommand.java +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/BaseKeyStoreCommand.java @@ -36,7 +36,7 @@ public BaseKeyStoreCommand(String description, boolean keyStoreMustExist) { } @Override - protected final void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public final void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { try { final Path configFile = env.configFile(); keyStore = KeyStoreWrapper.load(configFile); diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/CreateKeyStoreCommand.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/CreateKeyStoreCommand.java index d047e236076a5..e615ef76db007 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/CreateKeyStoreCommand.java +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/CreateKeyStoreCommand.java @@ -37,7 +37,7 @@ class CreateKeyStoreCommand extends KeyStoreAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { try (SecureString password = options.has(passwordOption) ? readPassword(terminal, true) : new SecureString(new char[0])) { Path keystoreFile = KeyStoreWrapper.keystorePath(env.configFile()); if (Files.exists(keystoreFile)) { diff --git a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/HasPasswordKeyStoreCommand.java b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/HasPasswordKeyStoreCommand.java index f1b4168e74153..4a0502a19ee49 100644 --- a/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/HasPasswordKeyStoreCommand.java +++ b/distribution/tools/keystore-cli/src/main/java/org/elasticsearch/cli/keystore/HasPasswordKeyStoreCommand.java @@ -30,7 +30,7 @@ public class HasPasswordKeyStoreCommand extends KeyStoreAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final Path configFile = env.configFile(); final KeyStoreWrapper keyStore = KeyStoreWrapper.load(configFile); diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginCommand.java index 19d7437ffbc8b..7fa9dcbe4f4bd 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/InstallPluginCommand.java @@ -75,7 +75,7 @@ protected void printAdditionalHelp(Terminal terminal) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { SyncPluginsAction.ensureNoConfigFile(env); List plugins = arguments.values(options) diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/ListPluginsCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/ListPluginsCommand.java index 6a93a4bb238d8..451ceaf0bb6c4 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/ListPluginsCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/ListPluginsCommand.java @@ -37,7 +37,7 @@ class ListPluginsCommand extends EnvironmentAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { if (Files.exists(env.pluginsFile()) == false) { throw new IOException("Plugins directory missing: " + env.pluginsFile()); } diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java index 9ace229e6a382..0b8adff5663aa 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java @@ -34,7 +34,7 @@ class RemovePluginCommand extends EnvironmentAwareCommand { } @Override - protected void execute(final Terminal terminal, final OptionSet options, final Environment env, ProcessInfo processInfo) + public void execute(final Terminal terminal, final OptionSet options, final Environment env, ProcessInfo processInfo) throws Exception { SyncPluginsAction.ensureNoConfigFile(env); diff --git a/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilEnvironmentAwareCommandTests.java b/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilEnvironmentAwareCommandTests.java index 2167203ff7192..6e0a248804283 100644 --- a/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilEnvironmentAwareCommandTests.java +++ b/qa/evil-tests/src/test/java/org/elasticsearch/cli/EvilEnvironmentAwareCommandTests.java @@ -38,7 +38,7 @@ private TestEnvironmentAwareCommand(String description) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 48b73439e4ba0..83c591cf8522c 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -140,7 +140,7 @@ static int main(final String[] args, final Elasticsearch elasticsearch, final Te } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws UserException { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws UserException { if (options.nonOptionArguments().isEmpty() == false) { throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments()); } diff --git a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java index 462df49cc6d1a..a28c6e2814207 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java @@ -151,6 +151,6 @@ private static void putSystemPropertyIfSettingIsMissing( } /** Execute the command with the initialized {@link Environment}. */ - protected abstract void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception; + public abstract void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception; } diff --git a/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java index 89d9cee2bdd51..b8bdbcd4ef8ed 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/KeyStoreAwareCommand.java @@ -69,5 +69,5 @@ protected static void decryptKeyStore(KeyStoreWrapper keyStore, Terminal termina } } - protected abstract void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception; + public abstract void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception; } diff --git a/server/src/test/java/org/elasticsearch/common/cli/EnvironmentAwareCommandTests.java b/server/src/test/java/org/elasticsearch/common/cli/EnvironmentAwareCommandTests.java index 4bcdeb9fd41e9..c5ebf6dc4f88a 100644 --- a/server/src/test/java/org/elasticsearch/common/cli/EnvironmentAwareCommandTests.java +++ b/server/src/test/java/org/elasticsearch/common/cli/EnvironmentAwareCommandTests.java @@ -39,7 +39,7 @@ public void resetHooks() { protected Command newCommand() { return new EnvironmentAwareCommand("test command") { @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) { if (callback != null) { callback.accept(env); } diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java index 40a44b951537b..f185b5d561999 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/AutoConfigureNode.java @@ -156,7 +156,7 @@ public AutoConfigureNode(boolean reconfigure) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final boolean inEnrollmentMode = options.has(enrollmentTokenParam); // skipping security auto-configuration because node considered as restarting. diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java index 0050525c80592..a6716f0360a1b 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateGenerateTool.java @@ -161,7 +161,7 @@ private static class InputFileParser { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final boolean csrOnly = options.has(csrSpec); printIntro(terminal, csrOnly); final Path outputFile = getOutputFile(terminal, outputPathSpec.value(options), csrOnly ? DEFAULT_CSR_FILE : DEFAULT_CERT_FILE); diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java index 647baf10f3b32..57de814a7e7ee 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/CertificateTool.java @@ -581,7 +581,7 @@ static class SigningRequestCommand extends CertificateCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { terminal.println(INTRO_TEXT); terminal.println(""); terminal.println("The 'csr' mode generates certificate signing requests that can be sent to"); @@ -671,7 +671,7 @@ static class GenerateCertificateCommand extends CertificateCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { terminal.println(INTRO_TEXT); terminal.println(""); terminal.println("The 'cert' mode generates X.509 certificate and private keys."); @@ -904,7 +904,7 @@ static class CertificateAuthorityCommand extends CertificateCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { terminal.println(INTRO_TEXT); terminal.println(""); terminal.println("The 'ca' mode generates a new 'certificate authority'"); diff --git a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommand.java b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommand.java index 7d68a5dcd4208..c28c1fe796d8b 100644 --- a/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommand.java +++ b/x-pack/plugin/security/cli/src/main/java/org/elasticsearch/xpack/security/cli/HttpCertificateCommand.java @@ -147,7 +147,7 @@ private CertOptions(String name, X500Principal subject, List dnsNames, L } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { printHeader("Elasticsearch HTTP Certificate Utility", terminal); terminal.println("The 'http' command guides you through the process of generating certificates"); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java index 02d6213abf872..10ae8ac7da8c1 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/tool/SetupPasswordTool.java @@ -141,7 +141,7 @@ class AutoSetup extends SetupCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { terminal.println(Verbosity.VERBOSE, "Running with configuration path: " + env.configFile()); setupOptions(terminal, options, env); checkElasticKeystorePasswordValid(terminal, env); @@ -197,7 +197,7 @@ class InteractiveSetup extends SetupCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { terminal.println(Verbosity.VERBOSE, "Running with configuration path: " + env.configFile()); setupOptions(terminal, options, env); checkElasticKeystorePasswordValid(terminal, env); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java index 83ecd06bcdb9e..40956bbc88af2 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java @@ -100,7 +100,7 @@ protected void printAdditionalHelp(Terminal terminal) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { String username = parseUsername(arguments.values(options), env.settings()); final boolean allowReserved = XPackSettings.RESERVED_REALM_ENABLED_SETTING.get(env.settings()) == false; @@ -155,7 +155,7 @@ protected void printAdditionalHelp(Terminal terminal) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { String username = parseUsername(arguments.values(options), env.settings()); Path passwordFile = FileUserPasswdStore.resolveFile(env); @@ -210,7 +210,7 @@ protected void printAdditionalHelp(Terminal terminal) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { String username = parseUsername(arguments.values(options), env.settings()); char[] passwordHash = getPasswordHash(terminal, env, passwordOption.value(options)); @@ -258,7 +258,7 @@ protected void printAdditionalHelp(Terminal terminal) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { String username = parseUsername(arguments.values(options), env.settings()); String[] addRoles = parseRoles(terminal, env, addOption.value(options)); @@ -318,7 +318,7 @@ protected void printAdditionalHelp(Terminal terminal) { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { String username = null; if (options.has(arguments)) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java index 12872f7101dfb..ac7e33d9ce844 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlMetadataCommand.java @@ -139,7 +139,7 @@ public void close() throws IOException { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { // OpenSAML prints a lot of _stuff_ at info level, that really isn't needed in a command line tool. Loggers.setLevel(LogManager.getLogger("org.opensaml"), Level.WARN); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java index 6206f9dc25597..51adcab5c3c13 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileTokensTool.java @@ -63,7 +63,7 @@ static class CreateFileTokenCommand extends EnvironmentAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final ServiceAccountTokenId accountTokenId = parsePrincipalAndTokenName(arguments.values(options), env.settings()); final Hasher hasher = Hasher.resolve(XPackSettings.SERVICE_TOKEN_HASHING_ALGORITHM.get(env.settings())); final Path serviceTokensFile = FileServiceAccountTokenStore.resolveFile(env); @@ -95,7 +95,7 @@ static class DeleteFileTokenCommand extends EnvironmentAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final ServiceAccountTokenId accountTokenId = parsePrincipalAndTokenName(arguments.values(options), env.settings()); final String qualifiedName = accountTokenId.getQualifiedName(); final Path serviceTokensFile = FileServiceAccountTokenStore.resolveFile(env); @@ -122,7 +122,7 @@ static class ListFileTokenCommand extends EnvironmentAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final List args = arguments.values(options); if (args.size() > 1) { throw new UserException( diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java index e08bc1790af03..2af9e53c66199 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/crypto/tool/SystemKeyTool.java @@ -51,7 +51,7 @@ class SystemKeyTool extends EnvironmentAwareCommand { ); @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final Path keyPath; if (options.hasArgument(arguments)) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java index 4b3df26b80059..8cce453f17fd7 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/enrollment/tool/AutoConfigGenerateElasticPasswordHash.java @@ -44,7 +44,7 @@ class AutoConfigGenerateElasticPasswordHash extends KeyStoreAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { final Hasher hasher = Hasher.resolve(XPackSettings.PASSWORD_HASHING_ALGORITHM.get(env.settings())); try ( SecureString elasticPassword = new SecureString(generatePassword(20)); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/tool/BaseRunAsSuperuserCommand.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/tool/BaseRunAsSuperuserCommand.java index 2dfb86f8efedd..bb19625c61101 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/tool/BaseRunAsSuperuserCommand.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/tool/BaseRunAsSuperuserCommand.java @@ -79,7 +79,7 @@ public BaseRunAsSuperuserCommand( } @Override - protected final void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + public final void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { validate(terminal, options, env); ensureFileRealmEnabled(env.settings()); KeyStoreWrapper keyStoreWrapper = keyStoreFunction.apply(env); From 2113ecfbb44110e81fdce676398f3f428ca76632 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 29 Apr 2022 15:02:15 -0700 Subject: [PATCH 057/166] spotless --- .../org/elasticsearch/plugins/cli/RemovePluginCommand.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java index 0b8adff5663aa..7c1a18db1bdba 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/cli/RemovePluginCommand.java @@ -34,8 +34,7 @@ class RemovePluginCommand extends EnvironmentAwareCommand { } @Override - public void execute(final Terminal terminal, final OptionSet options, final Environment env, ProcessInfo processInfo) - throws Exception { + public void execute(final Terminal terminal, final OptionSet options, final Environment env, ProcessInfo processInfo) throws Exception { SyncPluginsAction.ensureNoConfigFile(env); final List plugins = arguments.values(options).stream().map(PluginDescriptor::new).collect(Collectors.toList()); From 8027ce4c158b699289f05d010279d45e010bfef9 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 07:41:42 -0700 Subject: [PATCH 058/166] get some tests working --- distribution/tools/server-cli/build.gradle | 15 +- .../server/cli/JvmOptionsParser.java | 2 +- .../elasticsearch/server/cli/ServerCli.java | 144 ++++++------- .../server/cli/ElasticsearchCliTests.java | 192 ++++++++++++++++-- .../server/cli/JvmErgonomicsTests.java | 4 + .../windows_service/ProcrunCommand.java | 3 +- .../elasticsearch/cli/CommandTestCase.java | 2 +- 7 files changed, 262 insertions(+), 100 deletions(-) diff --git a/distribution/tools/server-cli/build.gradle b/distribution/tools/server-cli/build.gradle index b553154de30db..1f69dd5fd1f3f 100644 --- a/distribution/tools/server-cli/build.gradle +++ b/distribution/tools/server-cli/build.gradle @@ -16,17 +16,14 @@ dependencies { testImplementation project(":test:framework") } -tasks.withType(CheckForbiddenApis).configureEach { - replaceSignatureFiles 'jdk-signatures' +tasks.named("test").configure { + systemProperty "tests.security.manager", "false" } -tasks.named("testingConventions").configure { - naming.clear() - naming { - Tests { - baseClass 'org.elasticsearch.server.cli.LaunchersTestCase' - } - } +tasks.named("testingConventions").configure { enabled = false } + +tasks.withType(CheckForbiddenApis).configureEach { + replaceSignatureFiles 'jdk-signatures' } ["javadoc", "loggerUsageCheck", "jarHell"].each { tsk -> diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index 9357d2c7461d3..22909b8297698 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -61,7 +61,7 @@ SortedMap invalidLines() { } - static List determineOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, + static List determineJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, IOException, UserException { final JvmOptionsParser parser = new JvmOptionsParser(); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 90fede3897d78..571d12b216a91 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -35,6 +35,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -42,9 +43,11 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; +import static org.elasticsearch.server.cli.JvmOptionsParser.determineJvmOptions; class ServerCli extends EnvironmentAwareCommand { private final OptionSpecBuilder versionOption; @@ -72,7 +75,7 @@ class ServerCli extends EnvironmentAwareCommand { } @Override - protected void execute(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env) throws Exception { + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { if (options.nonOptionArguments().isEmpty() == false) { throw new UserException(ExitCodes.USAGE, "Positional arguments not allowed, found " + options.nonOptionArguments()); } @@ -83,58 +86,38 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process // setup security final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); - autoConfigureSecurity(terminal, options, keystorePassword, processInfo); + var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); + runAutoConfigTool(autoConfigTerminal, options, processInfo, env); // reload settings since auto security might have changed them // TODO: don't recreate if security settings were not changed env = createEnv(options, processInfo); - // determine process environment and arguments - Map envVars = new HashMap<>(processInfo.envVars()); - Path tempDir = TempDirectory.setup(envVars); - List jvmOptions = JvmOptionsParser.determineOptions( - env.configFile(), - env.pluginsFile(), - tempDir, - envVars.get("ES_JAVA_OPTS") - ); - // jvmOptions.add("-Des.path.conf=" + env.configFile()); - jvmOptions.add("-Des.distribution.type=" + sysprops.get("es.distribution.type")); - var args = createArgs(options, keystorePassword, env); + // start Elasticsearch + final Process process = createProcess(processInfo, env); - final Process process = createProcess(jvmOptions, envVars); + // send arguments + var args = createArgs(options, keystorePassword, env); try (var out = new OutputStreamStreamOutput(process.getOutputStream())) { args.writeTo(out); } - String userExceptionMsg = null; - boolean ready = false; - InputStream err = process.getErrorStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(err)); - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { - userExceptionMsg = line.substring(1); - break; - } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { - ready = true; - // The server closes stderr right after this message, but for some unknown reason - // the pipe closing does not close this end of the pipe, so we must explicitly - // break out of this loop, or we will block forever on the next read. - break; - } else { - terminal.getErrorWriter().println(line); - } - } + keystorePassword.close(); + // Read from stderr until we get a signal back that ES is either ready or it had an error. + // If we are running in the foreground, this pump will never exit. + AtomicReference userExceptionMsg = new AtomicReference<>(); + boolean ready = pumpStderr(terminal, process.getErrorStream(), userExceptionMsg); + + // if we are daemonized and we got the all-clear signal, we can exit cleanly if (ready && args.daemonize()) { closeStreams(process); return; } + // We pass any ES error code through UserException. If the message was set, + // then it is a real UserException, otherwise it is just the error code and a null message. int code = process.waitFor(); - terminal.flush(); - System.out.println("exited: " + code); if (code != ExitCodes.OK) { - throw new UserException(code, userExceptionMsg); + throw new UserException(code, userExceptionMsg.get()); } } @@ -145,7 +128,7 @@ private void closeStreams(Process process) throws IOException { private void printVersion(Terminal terminal) { final String versionOutput = String.format( Locale.ROOT, - "Version: %s, Build: %s/%s/%s/%s, JVM: %s", + "Version: %s, Build: %s/%s/%s, JVM: %s", Build.CURRENT.qualifiedVersion(), Build.CURRENT.type().displayName(), Build.CURRENT.hash(), @@ -168,41 +151,24 @@ private SecureString getKeystorePassword(Path configDir, Terminal terminal) { } } - private void autoConfigureSecurity(Terminal terminal, OptionSet options, SecureString keystorePassword) throws Exception { - KeystorePasswordTerminal autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); - - // reconstitute command lines - List settingValues = options.asMap().get(settingOption); - List args = new ArrayList<>(); - settingValues.forEach(v -> { - args.add("-E"); - args.add(v.toString()); - }); - + private void runAutoConfigTool(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env) throws Exception { String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; - Command cmd = CliToolProvider.load("auto-configure-node", autoConfigLibs).create(); + Command cmd = loadTool("auto-configure-node", autoConfigLibs); assert cmd instanceof EnvironmentAwareCommand; @SuppressWarnings("raw") var autoConfigNode = (EnvironmentAwareCommand) cmd; - OptionSet newOptions = new Op() - - if (options.has(enrollmentTokenOption)) { - final String enrollmentToken = enrollmentTokenOption.value(options); - args.add("--enrollment-token"); - args.add(enrollmentToken); - int ret = autoConfigNode.(args.toArray(new String[0]), autoConfigTerminal); - if (ret != 0) { - throw new UserException(ret, "Auto security enrollment failed"); - } - } else { - int ret = autoConfigNode.main(args.toArray(new String[0]), autoConfigTerminal); - switch (ret) { - case ExitCodes.OK, ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: - break; - default: - throw new UserException(ret, "Auto security enrollment failed"); + try { + autoConfigNode.execute(terminal, options, env, processInfo); + } catch (UserException e) { + if (options.has(enrollmentTokenOption) == false) { + // if we don't have an enrollment token then these exit codes are "ok" + switch (e.exitCode) { + case ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: + return; + } } + throw e; } } @@ -214,11 +180,17 @@ private ServerArgs createArgs(OptionSet options, SecureString keystorePassword, return new ServerArgs(daemonize, quiet, pidFile, keystorePassword, env.settings(), env.configFile()); } - private Process createProcess(List jvmOptions, Map envVars) throws IOException { - Path esHome = PathUtils.get(""); - Path javaHome = PathUtils.get(sysprops.get("java.home")); + private Process createProcess(ProcessInfo processInfo, Environment env) throws Exception { + Map envVars = new HashMap<>(processInfo.envVars()); + Path tempDir = TempDirectory.setup(envVars); + List jvmOptions = getJvmOptions(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); + // jvmOptions.add("-Des.path.conf=" + env.configFile()); + jvmOptions.add("-Des.distribution.type=" + processInfo.sysprops().get("es.distribution.type")); + + Path esHome = processInfo.workingDir(); + Path javaHome = PathUtils.get(processInfo.sysprops().get("java.home")); List command = new ArrayList<>(); - boolean isWindows = sysprops.get("os.name").startsWith("Windows"); + boolean isWindows = processInfo.sysprops().get("os.name").startsWith("Windows"); command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); command.addAll(jvmOptions); command.add("-cp"); @@ -232,7 +204,37 @@ private Process createProcess(List jvmOptions, Map envVa return startProcess(builder); } + private boolean pumpStderr(Terminal terminal, InputStream err, AtomicReference userExceptionMsg) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(err, StandardCharsets.UTF_8)); + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { + userExceptionMsg.set(line.substring(1)); + return false; + } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { + // The server closes stderr right after this message, but for some unknown reason + // the pipe closing does not close this end of the pipe, so we must explicitly + // break out of this loop, or we will block forever on the next read. + return true; + } else { + terminal.getErrorWriter().println(line); + } + } + return false; + } + + // protected to allow tests to override + protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws Exception { + return new ArrayList<>(determineJvmOptions(configDir, pluginsDir, tmpDir, envOptions)); + } + + // protected to allow tests to override protected Process startProcess(ProcessBuilder builder) throws IOException { return builder.start(); } + + // protected to allow tests to override + protected Command loadTool(String toolname, String libs) { + return CliToolProvider.load(toolname, libs).create(); + } } diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java index 666d7aa43cd62..459b5a41b9b82 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java @@ -8,19 +8,36 @@ package org.elasticsearch.server.cli; +import joptsimple.OptionSet; + import org.elasticsearch.Build; +import org.elasticsearch.bootstrap.ServerArgs; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.CommandTestCase; import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.ProcessInfo; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.common.cli.EnvironmentAwareCommand; +import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.IOUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; import org.hamcrest.Matcher; import org.junit.Before; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.CoreMatchers.containsString; @@ -30,6 +47,17 @@ import static org.hamcrest.Matchers.hasItem; public class ElasticsearchCliTests extends CommandTestCase { + + Path esConfigDir; + + @Before + public void setupDummyInstallation() throws IOException { + sysprops.put("java.home", "/javahome"); + esConfigDir = esHomeDir.resolve("config"); + Files.createDirectories(esConfigDir); + Files.writeString(esConfigDir.resolve("jvm.options"), ""); + } + private void assertOk(String... args) throws Exception { assertOkWithOutput(emptyString(), args); } @@ -44,7 +72,7 @@ private void assertOkWithOutput(Matcher matcher, String... args) throws private void assertUsage(Matcher matcher, String... args) throws Exception { terminal.reset(); - initCallback = FAIL_INIT; + mainCallback = FAIL_MAIN; int status = executeMain(args); assertThat(status, equalTo(ExitCodes.USAGE)); assertThat(terminal.getErrorOutput(), matcher); @@ -93,7 +121,7 @@ public void testPidFile() throws Exception { Path tmpDir = createTempDir(); Path pidFileArg = tmpDir.resolve("pid"); assertUsage(containsString("Option p/pidfile requires an argument"), "-p"); - initCallback = (daemonize, pidFile, quiet, env) -> { assertThat(pidFile.toString(), equalTo(pidFileArg.toString())); }; + mainCallback = (args, stdout, stderr, exitCode) -> { assertThat(args.pidFile().toString(), equalTo(pidFileArg.toString())); }; terminal.reset(); assertOk("-p", pidFileArg.toString()); terminal.reset(); @@ -102,7 +130,7 @@ public void testPidFile() throws Exception { public void testDaemonize() throws Exception { AtomicBoolean expectDaemonize = new AtomicBoolean(true); - initCallback = (d, p, q, e) -> assertThat(d, equalTo(expectDaemonize.get())); + mainCallback = (args, stdout, stderr, exitCode) -> assertThat(args.daemonize(), equalTo(expectDaemonize.get())); assertOk("-d"); assertOk("--daemonize"); expectDaemonize.set(false); @@ -111,7 +139,7 @@ public void testDaemonize() throws Exception { public void testQuiet() throws Exception { AtomicBoolean expectQuiet = new AtomicBoolean(true); - initCallback = (d, p, q, e) -> assertThat(q, equalTo(expectQuiet.get())); + mainCallback = (args, stdout, stderr, exitCode) -> assertThat(args.quiet(), equalTo(expectQuiet.get())); assertOk("-q"); assertOk("--quiet"); expectQuiet.set(false); @@ -119,8 +147,8 @@ public void testQuiet() throws Exception { } public void testElasticsearchSettings() throws Exception { - initCallback = (d, p, q, e) -> { - Settings settings = e.settings(); + mainCallback = (args, stdout, stderr, exitCode) -> { + Settings settings = args.nodeSettings(); assertThat(settings.get("foo"), equalTo("bar")); assertThat(settings.get("baz"), equalTo("qux")); }; @@ -142,8 +170,8 @@ public void testUnknownOption() throws Exception { public void testPathHome() throws Exception { AtomicReference expectedHomeDir = new AtomicReference<>(); expectedHomeDir.set(esHomeDir.toString()); - initCallback = (d, p, q, e) -> { - Settings settings = e.settings(); + mainCallback = (args, stdout, stderr, exitCode) -> { + Settings settings = args.nodeSettings(); assertThat(settings.get("path.home"), equalTo(expectedHomeDir.get())); assertThat(settings.keySet(), hasItem("path.logs")); // added by env initialization }; @@ -154,16 +182,136 @@ public void testPathHome() throws Exception { assertOk("-Epath.home=" + commandLineValue); } - interface InitMethod { - void init(boolean daemonize, Path pidFile, boolean quiet, Environment initialEnv); + interface MainMethod { + void main(ServerArgs args, OutputStream stdout, OutputStream stderr, AtomicInteger exitCode); } - InitMethod initCallback; - final InitMethod FAIL_INIT = (d, p, q, e) -> fail("Did not expect to run init"); + MainMethod mainCallback; + final MainMethod FAIL_MAIN = (args, stdout, stderr, exitCode) -> fail("Did not expect to run init"); @Before public void resetCommand() { - initCallback = null; + mainCallback = null; + } + + private static class MockAutoConfigCli extends EnvironmentAwareCommand { + MockAutoConfigCli() { + super("mock auto config tool"); + } + + @Override + protected void execute(Terminal terminal, OptionSet options, ProcessInfo processInfo) throws Exception { + fail("Called wrong execute method, must call the one that takes already parsed env"); + } + + @Override + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + // TODO: fake errors, check password from terminal, allow tests to make elasticsearch.yml change + + } + } + + // a "process" that just exits + private class NoopProcess extends Process { + private final OutputStream processStdin = OutputStream.nullOutputStream(); + private final InputStream processStdout = InputStream.nullInputStream(); + private final InputStream processStderr = InputStream.nullInputStream(); + + @Override + public OutputStream getOutputStream() { + return processStdin; + } + + @Override + public InputStream getInputStream() { + return processStdout; + } + + @Override + public InputStream getErrorStream() { + return processStderr; + } + + @Override + public int waitFor() { + return 0; + } + + @Override + public int exitValue() { + return 0; + } + + @Override + public void destroy() { + fail("Tried to kill ES process"); + } + } + + // a "process" that is really another thread + private class MockElasticsearchProcess extends Process { + private final PipedOutputStream processStdin = new PipedOutputStream(); + private final PipedInputStream processStdout = new PipedInputStream(); + private final PipedInputStream processStderr = new PipedInputStream(); + private final PipedInputStream stdin = new PipedInputStream(); + private final PipedOutputStream stdout = new PipedOutputStream(); + private final PipedOutputStream stderr = new PipedOutputStream(); + + private final AtomicInteger exitCode = new AtomicInteger(); + private final AtomicReference argsParsingException = new AtomicReference<>(); + private final Thread thread = new Thread(() -> { + try (var in = new InputStreamStreamInput(stdin)) { + final ServerArgs serverArgs = new ServerArgs(in); + mainCallback.main(serverArgs, stdout, stderr, exitCode); + } catch (IOException e) { + argsParsingException.set(e); + } + IOUtils.closeWhileHandlingException(stdin, stdout, stderr); + }, "dummy elasticsearch"); + + MockElasticsearchProcess() throws IOException { + stdin.connect(processStdin); + stdout.connect(processStdout); + stderr.connect(processStderr); + thread.start(); + } + + @Override + public OutputStream getOutputStream() { + return processStdin; + } + + @Override + public InputStream getInputStream() { + return processStdout; + } + + @Override + public InputStream getErrorStream() { + return processStderr; + } + + @Override + public int waitFor() throws InterruptedException { + thread.join(); + if (argsParsingException.get() != null) { + throw new AssertionError("Reading server args failed", argsParsingException.get()); + } + return exitCode.get(); + } + + @Override + public int exitValue() { + if (thread.isAlive()) { + throw new IllegalThreadStateException(); // match spec + } + return exitCode.get(); + } + + @Override + public void destroy() { + fail("Tried to kill ES process"); + } } @Override @@ -171,10 +319,19 @@ protected Command newCommand() { return new ServerCli() { @Override - protected Process startProcess(ProcessBuilder processBuilder) { - if (initCallback != null) { - initCallback.init(daemonize, pidFile, quiet, initialEnv); - } + protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws Exception { + return new ArrayList<>(); + } + + @Override + protected Process startProcess(ProcessBuilder processBuilder) throws IOException { + // TODO: validate processbuilder stuff + return new MockElasticsearchProcess(); + } + + @Override + protected Command loadTool(String toolname, String libs) { + return new MockAutoConfigCli(); } @Override @@ -183,4 +340,5 @@ public boolean addShutdownHook() { } }; } + } diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java index 5840746c7cf94..f8d488df42adc 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java @@ -8,6 +8,8 @@ package org.elasticsearch.server.cli; +import org.apache.lucene.tests.util.LuceneTestCase; + import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -27,6 +29,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +// mocking the filesystem hides the jvm we need to execute to find jvm options +@LuceneTestCase.SuppressFileSystems("*") public class JvmErgonomicsTests extends LaunchersTestCase { public void testExtractValidHeapSizeUsingXmx() throws Exception { diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java index 165b232fcd823..cd377e2cdd98c 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java @@ -12,6 +12,7 @@ import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; @@ -44,7 +45,7 @@ protected String getExecutable() { } @Override - protected void execute(Terminal terminal, OptionSet options) throws Exception { + protected void execute(Terminal terminal, OptionSet options, ProcessInfo processInfo) throws Exception { Map env = System.getenv(); Path esHome = Paths.get("").toAbsolutePath(); // TODO: this should be passed through execute String serviceId = getServiceId(options, env); diff --git a/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java b/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java index 930455160b072..62d9c7f45a247 100644 --- a/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java @@ -33,7 +33,7 @@ public abstract class CommandTestCase extends ESTestCase { protected Path esHomeDir; @Before - public void resetTerminal() { + public void resetState() { terminal.reset(); terminal.setSupportsBinary(false); terminal.setVerbosity(Terminal.Verbosity.NORMAL); From 44eea73fc0afd5744837b838d7c42bf9b04ef349 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 09:41:51 -0700 Subject: [PATCH 059/166] remove zip source import, doesn't work for integ-test-zip --- distribution/build.gradle | 1 - distribution/src/bin/elasticsearch-env | 4 ---- 2 files changed, 5 deletions(-) diff --git a/distribution/build.gradle b/distribution/build.gradle index b4470ac44bd02..158e7c70091a7 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -486,7 +486,6 @@ subprojects { 'source.path.env': [ 'deb': 'source /etc/default/elasticsearch', 'rpm': 'source /etc/sysconfig/elasticsearch', - 'zip': 'if not defined ES_PATH_CONF set ES_PATH_CONF=!ES_HOME!\\config', 'def': 'if [ -z "$ES_PATH_CONF" ]; then ES_PATH_CONF="$ES_HOME"/config; fi', ], 'path.logs': [ diff --git a/distribution/src/bin/elasticsearch-env b/distribution/src/bin/elasticsearch-env index 29713f3fbb196..a89495fc5d144 100644 --- a/distribution/src/bin/elasticsearch-env +++ b/distribution/src/bin/elasticsearch-env @@ -32,10 +32,6 @@ while [ "`basename "$ES_HOME"`" != "bin" ]; do done ES_HOME=`dirname "$ES_HOME"` -# now set the classpath -ES_CLASSPATH="$ES_HOME/lib/*" -SERVER_CLI_CLASSPATH="$ES_CLASSPATH:$ES_HOME/lib/tools/server-cli/*" - # now set the path to java if [ ! -z "$ES_JAVA_HOME" ]; then JAVA="$ES_JAVA_HOME/bin/java" From edf9d822ee6ede4cf53b01fe8bba8bea1561a8ce Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 11:05:10 -0700 Subject: [PATCH 060/166] pass keystore password through --- .../elasticsearch/cli/keystore/BootstrapTests.java | 7 +++---- .../java/org/elasticsearch/server/cli/ServerCli.java | 2 +- .../java/org/elasticsearch/bootstrap/Bootstrap.java | 12 +++++++++--- .../org/elasticsearch/bootstrap/BootstrapUtil.java | 8 ++------ .../org/elasticsearch/bootstrap/Elasticsearch.java | 10 ++++++---- .../common/settings/KeyStoreWrapper.java | 1 + 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java b/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java index 9b0ff657b7bcf..a4ab6397288d0 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java +++ b/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java @@ -54,17 +54,16 @@ public void testLoadSecureSettings() throws Exception { assertTrue(seed.length() > 0); keyStoreWrapper.save(configPath, password); } - final InputStream in = password.length > 0 - ? new ByteArrayInputStream(new String(password).getBytes(StandardCharsets.UTF_8)) - : System.in; + SecureString keystorePassword = new SecureString(password); assertTrue(Files.exists(configPath.resolve("elasticsearch.keystore"))); - try (SecureSettings secureSettings = BootstrapUtil.loadSecureSettings(env, in)) { + try (SecureSettings secureSettings = BootstrapUtil.loadSecureSettings(env, keystorePassword)) { SecureString seedAfterLoad = KeyStoreWrapper.SEED_SETTING.get(Settings.builder().setSecureSettings(secureSettings).build()); assertEquals(seedAfterLoad.toString(), seed.toString()); assertTrue(Files.exists(configPath.resolve("elasticsearch.keystore"))); } } + // TODO: these tests aren't needed anymore, password reading is done in ServerCli public void testReadCharsFromStdin() throws Exception { assertPassphraseRead("hello", "hello"); assertPassphraseRead("hello\n", "hello"); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 571d12b216a91..b2f4313d80b2d 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -140,7 +140,7 @@ private void printVersion(Terminal terminal) { private SecureString getKeystorePassword(Path configDir, Terminal terminal) { try { - KeyStoreWrapper keystore = KeyStoreWrapper.load(KeyStoreWrapper.keystorePath(configDir)); + KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir); if (keystore != null && keystore.hasPassword()) { return new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT)); } else { diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index f193543b0f6c6..b6417e43a283f 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.network.IfConfig; import org.elasticsearch.common.settings.SecureSettings; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.core.IOUtils; @@ -287,15 +288,20 @@ static void stop() throws IOException { /** * This method is invoked by {@link Elasticsearch#main(String[])} to startup elasticsearch. */ - static void init(final boolean foreground, final Path pidFile, final boolean quiet, final Environment initialEnv) - throws BootstrapException, NodeValidationException, UserException { + static void init( + final boolean foreground, + final Path pidFile, + final boolean quiet, + final Environment initialEnv, + SecureString keystorePassword + ) throws BootstrapException, NodeValidationException, UserException { // force the class initializer for BootstrapInfo to run before // the security manager is installed BootstrapInfo.init(); INSTANCE = new Bootstrap(); - final SecureSettings keystore = BootstrapUtil.loadSecureSettings(initialEnv); + final SecureSettings keystore = BootstrapUtil.loadSecureSettings(initialEnv, keystorePassword); final Environment environment = createEnvironment(pidFile, keystore, initialEnv.settings(), initialEnv.configFile()); BootstrapInfo.setConsole(getConsole(environment)); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java index 4f9f467eed048..099eacb064a1f 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java @@ -46,13 +46,9 @@ public static SecureString readPassphrase(InputStream stream) throws IOException return passphrase; } - public static SecureSettings loadSecureSettings(Environment initialEnv) throws BootstrapException { - return loadSecureSettings(initialEnv, System.in); - } - - public static SecureSettings loadSecureSettings(Environment initialEnv, InputStream stdin) throws BootstrapException { + public static SecureSettings loadSecureSettings(Environment initialEnv, SecureString keystorePassword) throws BootstrapException { try { - return KeyStoreWrapper.bootstrap(initialEnv.configFile(), () -> readPassphrase(stdin)); + return KeyStoreWrapper.bootstrap(initialEnv.configFile(), () -> keystorePassword); } catch (Exception e) { throw new BootstrapException(e); } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 61ad6f5145f13..4f8804f13b480 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -14,6 +14,7 @@ import org.elasticsearch.cli.UserException; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.logging.LogConfigurator; +import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.env.Environment; import org.elasticsearch.node.NodeValidationException; @@ -63,7 +64,8 @@ public void checkPermission(Permission perm) { serverArgs.daemonize(), serverArgs.pidFile(), serverArgs.quiet(), - new Environment(serverArgs.nodeSettings(), serverArgs.configDir()) + new Environment(serverArgs.nodeSettings(), serverArgs.configDir()), + serverArgs.keystorePassword() ); } catch (NodeValidationException e) { exitCode = ExitCodes.CONFIG; @@ -124,10 +126,10 @@ private static void overrideDnsCachePolicyProperties() { } } - void init(final boolean daemonize, final Path pidFile, final boolean quiet, Environment initialEnv) throws NodeValidationException, - UserException { + void init(final boolean daemonize, final Path pidFile, final boolean quiet, Environment initialEnv, SecureString keystorePassword) + throws NodeValidationException, UserException { try { - Bootstrap.init(daemonize == false, pidFile, quiet, initialEnv); + Bootstrap.init(daemonize == false, pidFile, quiet, initialEnv, keystorePassword); } catch (BootstrapException | RuntimeException e) { // format exceptions to the console in a special way // to avoid 2MB stacktraces from guice, etc. diff --git a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java index 9cc93aa5fb3b1..5e1398ce6d247 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java +++ b/server/src/main/java/org/elasticsearch/common/settings/KeyStoreWrapper.java @@ -204,6 +204,7 @@ public static void addBootstrapSeed(KeyStoreWrapper wrapper) { Arrays.fill(characters, (char) 0); } + // TODO: this doesn't need to be a supplier anymore public static KeyStoreWrapper bootstrap(Path configDir, CheckedSupplier passwordSupplier) throws Exception { KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir); From 76d6a4a4d49b6612475a1e453a187a8d7a1dbd68 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 11:14:14 -0700 Subject: [PATCH 061/166] fix javadoc --- .../java/org/elasticsearch/bootstrap/BootstrapException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapException.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapException.java index 8c45d8bfdd6c4..92be3791ddff1 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapException.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapException.java @@ -15,7 +15,7 @@ * during bootstrap should explicitly declare the checked exceptions that they can throw, rather * than declaring the top-level checked exception {@link Exception}. This exception exists to wrap * these checked exceptions so that - * {@link Bootstrap#init(boolean, Path, boolean, org.elasticsearch.env.Environment)} + * {@link Bootstrap#init(boolean, Path, boolean, org.elasticsearch.env.Environment, org.elasticsearch.common.settings.SecureString)} * does not have to declare all of these checked exceptions. */ class BootstrapException extends Exception { From b6a1382bb9720cc8a0dc6966bedd273a8e0ba4df Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 11:42:40 -0700 Subject: [PATCH 062/166] fix forbidden --- .../elasticsearch/bootstrap/Elasticsearch.java | 16 ++++++++++++---- .../org/elasticsearch/bootstrap/ServerArgs.java | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 4f8804f13b480..ea4d07d6c702f 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -15,6 +15,7 @@ import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.logging.LogConfigurator; import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.env.Environment; import org.elasticsearch.node.NodeValidationException; @@ -53,13 +54,11 @@ public void checkPermission(Permission perm) { LogConfigurator.registerErrorListener(); final Elasticsearch elasticsearch = new Elasticsearch(); - PrintStream err = System.err; + PrintStream err = getStderr(); int exitCode = 0; try { final var in = new InputStreamStreamInput(System.in); final ServerArgs serverArgs = new ServerArgs(in); - System.out.println("RUNNING ELASTICSEARCH"); - System.out.println(serverArgs); elasticsearch.init( serverArgs.daemonize(), serverArgs.pidFile(), @@ -88,10 +87,19 @@ public void checkPermission(Permission perm) { if (exitCode != ExitCodes.OK) { printLogsSuggestion(err); err.flush(); - System.exit(exitCode); + exit(exitCode); } } + @SuppressForbidden(reason = "grab stderr for communication with server-cli") + private static PrintStream getStderr() { + return System.err; + } + + @SuppressForbidden(reason = "main exit path") + private static void exit(int exitCode) { + System.exit(exitCode); + } /** * Prints a message directing the user to look at the logs. A message is only printed if * logging has been configured. diff --git a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java index 636d8f93dd35d..9305a90e7208b 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.PathUtils; +import org.elasticsearch.core.SuppressForbidden; import java.io.IOException; import java.nio.file.Path; @@ -34,13 +35,18 @@ public ServerArgs(StreamInput in) throws IOException { readPidFile(in), in.readSecureString(), Settings.readSettingsFromStream(in), - PathUtils.get(in.readString()) + resolvePath(in.readString()) ); } private static Path readPidFile(StreamInput in) throws IOException { String pidFile = in.readOptionalString(); - return pidFile == null ? null : PathUtils.get(pidFile); + return pidFile == null ? null : resolvePath(pidFile); + } + + @SuppressForbidden(reason = "reading local path from stream") + private static Path resolvePath(String path) { + return PathUtils.get(path); } @Override From 7a3f0dcd2669357e82c47fdfc5ff7ba0b9020003 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 12:07:11 -0700 Subject: [PATCH 063/166] spotless --- .../src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index ea4d07d6c702f..0ed953bf95c5c 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -100,6 +100,7 @@ private static PrintStream getStderr() { private static void exit(int exitCode) { System.exit(exitCode); } + /** * Prints a message directing the user to look at the logs. A message is only printed if * logging has been configured. From 71f37e3cde093696ae7c9615eb3755000ba083e3 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 12:13:51 -0700 Subject: [PATCH 064/166] attempt windows fix for classpath --- .../src/main/java/org/elasticsearch/server/cli/ServerCli.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index b2f4313d80b2d..3065fde341126 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -194,7 +194,9 @@ private Process createProcess(ProcessInfo processInfo, Environment env) throws E command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); command.addAll(jvmOptions); command.add("-cp"); - command.add(esHome.resolve("lib").resolve("*").toString()); + // The '*' isn't allows by the windows filesystem, so we need to force it into the classpath after converting to a string. + // Thankfully this will all go away when switching to modules, which take the directory instead of a glob. + command.add(esHome.resolve("lib") + (isWindows ? "\\" : "/") + "*"); command.add("org.elasticsearch.bootstrap.Elasticsearch"); var builder = new ProcessBuilder(command); From aac4d1a11528487f187c2dd3ee34f5ce47a07346 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 12:18:42 -0700 Subject: [PATCH 065/166] add assertion, somehow cursor is empty --- .../src/test/java/org/elasticsearch/packaging/util/Packages.java | 1 + 1 file changed, 1 insertion(+) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java index e42cc67f41ad6..bf092af865700 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java @@ -330,6 +330,7 @@ public JournaldWrapper(Shell sh) { public void clear() { final String script = "sudo journalctl --unit=elasticsearch.service --lines=0 --show-cursor -o cat | sed -e 's/-- cursor: //'"; cursor = sh.run(script).stdout().trim(); + assert cursor.isEmpty() == false; } /** From b894afbe5f7c6c58dbfd1d1b70f285e35a5f4a41 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 14:07:09 -0700 Subject: [PATCH 066/166] fix docker process lookup --- .../packaging/test/DockerTests.java | 17 +++++--- .../packaging/util/ProcessInfo.java | 43 ++++++++++--------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java index 2d6c176fbd4a1..0b4b48f8b87ea 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java @@ -959,13 +959,16 @@ public void test124CanRestartContainerWithStackLoggingConfig() { * Check that the Java process running inside the container has the expected UID, GID and username. */ public void test130JavaHasCorrectOwnership() { - final ProcessInfo info = ProcessInfo.getProcessInfo(sh, "java"); + final List infos = ProcessInfo.getProcessInfo(sh, "java"); + assertThat(infos, hasSize(2)); - assertThat("Incorrect UID", info.uid(), equalTo(1000)); - assertThat("Incorrect username", info.username(), equalTo("elasticsearch")); + for (ProcessInfo info : infos) { + assertThat("Incorrect UID", info.uid(), equalTo(1000)); + assertThat("Incorrect username", info.username(), equalTo("elasticsearch")); - assertThat("Incorrect GID", info.gid(), equalTo(0)); - assertThat("Incorrect group", info.group(), equalTo("root")); + assertThat("Incorrect GID", info.gid(), equalTo(0)); + assertThat("Incorrect group", info.group(), equalTo("root")); + } } /** @@ -973,7 +976,9 @@ public void test130JavaHasCorrectOwnership() { * The PID is particularly important because PID 1 handles signal forwarding and child reaping. */ public void test131InitProcessHasCorrectPID() { - final ProcessInfo info = ProcessInfo.getProcessInfo(sh, "tini"); + final List infos = ProcessInfo.getProcessInfo(sh, "tini"); + assertThat(infos, hasSize(1)); + ProcessInfo info = infos.get(0); assertThat("Incorrect PID", info.pid(), equalTo(1)); diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java index 91fd00d86e2b2..4080a0d7e76e2 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java @@ -8,12 +8,11 @@ package org.elasticsearch.packaging.util; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; - /** * Encapsulates the fetching of information about a running process. *

@@ -27,32 +26,34 @@ public record ProcessInfo(int pid, int uid, int gid, String username, String gro /** * Fetches process information about command, using sh to execute commands. * - * @return a populated ProcessInfo object + * @return a populated list of ProcessInfo objects */ - public static ProcessInfo getProcessInfo(Shell sh, String command) { + public static List getProcessInfo(Shell sh, String command) { final List processes = sh.run("pgrep " + command).stdout().lines().collect(Collectors.toList()); - assertThat("Expected a single process", processes, hasSize(1)); - - // Ensure we actually have a number - final int pid = Integer.parseInt(processes.get(0).trim()); + List infos = new ArrayList<>(); + for (String pidStr : processes) { + // Ensure we actually have a number + final int pid = Integer.parseInt(pidStr.trim()); - int uid = -1; - int gid = -1; + int uid = -1; + int gid = -1; - for (String line : sh.run("cat /proc/" + pid + "/status | grep '^[UG]id:'").stdout().split("\\n")) { - final String[] fields = line.split("\\s+"); + for (String line : sh.run("cat /proc/" + pid + "/status | grep '^[UG]id:'").stdout().split("\\n")) { + final String[] fields = line.split("\\s+"); - if (fields[0].equals("Uid:")) { - uid = Integer.parseInt(fields[1]); - } else { - gid = Integer.parseInt(fields[1]); + if (fields[0].equals("Uid:")) { + uid = Integer.parseInt(fields[1]); + } else { + gid = Integer.parseInt(fields[1]); + } } - } - final String username = sh.run("getent passwd " + uid + " | cut -f1 -d:").stdout().trim(); - final String group = sh.run("getent group " + gid + " | cut -f1 -d:").stdout().trim(); + final String username = sh.run("getent passwd " + uid + " | cut -f1 -d:").stdout().trim(); + final String group = sh.run("getent group " + gid + " | cut -f1 -d:").stdout().trim(); - return new ProcessInfo(pid, uid, gid, username, group); + infos.add(new ProcessInfo(pid, uid, gid, username, group)); + } + return Collections.unmodifiableList(infos); } } From bc64782975d94553ad2dbbc11c56f471bc1fcc65 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 14:10:46 -0700 Subject: [PATCH 067/166] defensive cursor --- .../java/org/elasticsearch/packaging/util/Packages.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java index bf092af865700..3dc9e6188ccee 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Packages.java @@ -330,7 +330,6 @@ public JournaldWrapper(Shell sh) { public void clear() { final String script = "sudo journalctl --unit=elasticsearch.service --lines=0 --show-cursor -o cat | sed -e 's/-- cursor: //'"; cursor = sh.run(script).stdout().trim(); - assert cursor.isEmpty() == false; } /** @@ -338,7 +337,11 @@ public void clear() { * @return Recent journald logs for the Elasticsearch service. */ public Result getLogs() { - return sh.run("journalctl -u elasticsearch.service --after-cursor='" + this.cursor + "'"); + String cmd = "journalctl -u elasticsearch.service"; + if (cursor.isEmpty() == false) { + cmd += " --after-cursor='" + this.cursor + "'"; + } + return sh.run(cmd); } } From 9624c26aadd43d27352250ccbb1f6d5784815943 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 14:43:40 -0700 Subject: [PATCH 068/166] copy old comment --- .../org/elasticsearch/server/cli/ServerCli.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 3065fde341126..ad2e69796fefb 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -87,10 +87,11 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // setup security final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); - runAutoConfigTool(autoConfigTerminal, options, processInfo, env); - // reload settings since auto security might have changed them - // TODO: don't recreate if security settings were not changed - env = createEnv(options, processInfo); + boolean changed = runAutoConfigTool(autoConfigTerminal, options, processInfo, env); + if (changed) { + // reload settings since auto security changed them + env = createEnv(options, processInfo); + } // start Elasticsearch final Process process = createProcess(processInfo, env); @@ -151,7 +152,7 @@ private SecureString getKeystorePassword(Path configDir, Terminal terminal) { } } - private void runAutoConfigTool(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env) throws Exception { + private boolean runAutoConfigTool(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env) throws Exception { String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; Command cmd = loadTool("auto-configure-node", autoConfigLibs); assert cmd instanceof EnvironmentAwareCommand; @@ -162,14 +163,16 @@ private void runAutoConfigTool(Terminal terminal, OptionSet options, ProcessInfo autoConfigNode.execute(terminal, options, env, processInfo); } catch (UserException e) { if (options.has(enrollmentTokenOption) == false) { - // if we don't have an enrollment token then these exit codes are "ok" + // these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual + // eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it switch (e.exitCode) { case ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: - return; + return false; } } throw e; } + return true; } private ServerArgs createArgs(OptionSet options, SecureString keystorePassword, Environment env) { From b9e4d9f52edda639cca9a1325f9e5f0331fb20b6 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 17:03:53 -0700 Subject: [PATCH 069/166] add check on enrollment token repeats --- .../elasticsearch/server/cli/ServerCli.java | 6 +++ .../server/cli/ElasticsearchCliTests.java | 7 ++++ .../test/EnrollNodeToClusterTests.java | 37 ------------------- 3 files changed, 13 insertions(+), 37 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index ad2e69796fefb..c22a99593b99c 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -153,6 +153,10 @@ private SecureString getKeystorePassword(Path configDir, Terminal terminal) { } private boolean runAutoConfigTool(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env) throws Exception { + if (options.valuesOf(enrollmentTokenOption).size() > 1) { + throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); + } + String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; Command cmd = loadTool("auto-configure-node", autoConfigLibs); assert cmd instanceof EnvironmentAwareCommand; @@ -215,6 +219,8 @@ private boolean pumpStderr(Terminal terminal, InputStream err, AtomicReference sh.chown(installation.config)); } - public void test60MultipleValuesForEnrollmentToken() throws Exception { - // if invoked with --enrollment-token tokenA tokenB tokenC, only tokenA is read - Shell.Result result = Archives.runElasticsearchStartCommand( - installation, - sh, - null, - List.of("--enrollment-token", generateMockEnrollmentToken(), "some-other-token", "some-other-token", "some-other-token"), - false - ); - // Assert we used the first value which is a proper enrollment token but failed because the node is already configured ( 80 ) - // something in our tests wrap the error code to 1 on windows - // TODO investigate this and remove this guard - if (distribution.platform != Distribution.Platform.WINDOWS) { - assertThat(result.exitCode(), equalTo(ExitCodes.NOOP)); - } - } - - public void test70MultipleParametersForEnrollmentTokenAreNotAllowed() throws Exception { - // if invoked with --enrollment-token tokenA --enrollment-token tokenB --enrollment-token tokenC, we exit - Shell.Result result = Archives.runElasticsearchStartCommand( - installation, - sh, - null, - List.of( - "--enrollment-token", - "some-other-token", - "--enrollment-token", - "some-other-token", - "--enrollment-token", - generateMockEnrollmentToken() - ), - false - ); - assertThat(result.stderr(), containsString("Multiple --enrollment-token parameters are not allowed")); - assertThat(result.exitCode(), equalTo(1)); - } - private String generateMockEnrollmentToken() throws Exception { EnrollmentToken enrollmentToken = new EnrollmentToken( "some-api-key", From 228d1674b4214fd4fbb4b2f8a4ca46fd12afdb3d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 17:08:36 -0700 Subject: [PATCH 070/166] move empty enrollemnt token test --- .../server/cli/ElasticsearchCliTests.java | 4 ++++ .../packaging/test/EnrollNodeToClusterTests.java | 10 ---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java index 87ea7a278910d..ebb59b17d5b3e 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java @@ -182,6 +182,10 @@ public void testPathHome() throws Exception { assertOk("-Epath.home=" + commandLineValue); } + public void testMissingEnrollmentToken() throws Exception { + assertUsage(containsString("Option enrollment-token requires an argument"), "--enrollment-token"); + } + public void testMultipleEnrollmentTokens() throws Exception { assertUsage(containsString("Multiple --enrollment-token parameters are not allowed"), "--enrollment-token", "some-token", diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java index 9d24bbb10da3c..891a406a1c696 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java @@ -37,16 +37,6 @@ public void test10Install() throws Exception { verifyArchiveInstallation(installation, distribution()); } - public void test20EnrollToClusterWithEmptyTokenValue() throws Exception { - Shell.Result result = Archives.runElasticsearchStartCommand(installation, sh, null, List.of("--enrollment-token"), false); - // something in our tests wrap the error code to 1 on windows - // TODO investigate this and remove this guard - if (distribution.platform != Distribution.Platform.WINDOWS) { - assertThat(result.exitCode(), equalTo(ExitCodes.USAGE)); - } - verifySecurityNotAutoConfigured(installation); - } - public void test30EnrollToClusterWithInvalidToken() throws Exception { Shell.Result result = Archives.runElasticsearchStartCommand( installation, From 3e36905a44721ecc7aa17a0349f811b838573fee Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 17:08:56 -0700 Subject: [PATCH 071/166] spotless --- .../elasticsearch/server/cli/ElasticsearchCliTests.java | 7 +++++-- .../packaging/test/EnrollNodeToClusterTests.java | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java index ebb59b17d5b3e..9a4c47a35e691 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java @@ -187,10 +187,13 @@ public void testMissingEnrollmentToken() throws Exception { } public void testMultipleEnrollmentTokens() throws Exception { - assertUsage(containsString("Multiple --enrollment-token parameters are not allowed"), "--enrollment-token", + assertUsage( + containsString("Multiple --enrollment-token parameters are not allowed"), + "--enrollment-token", "some-token", "--enrollment-token", - "some-other-token"); + "some-other-token" + ); } interface MainMethod { diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java index 891a406a1c696..3ca61ccacae17 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/EnrollNodeToClusterTests.java @@ -21,7 +21,6 @@ import static org.elasticsearch.packaging.util.Archives.installArchive; import static org.elasticsearch.packaging.util.Archives.verifyArchiveInstallation; -import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assume.assumeTrue; From cb9b7f999252f5e0df8bc6fc933f6660d949ce07 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 17:22:07 -0700 Subject: [PATCH 072/166] cleanup --- .../org/elasticsearch/server/cli/JvmOptionsParser.java | 2 +- .../org/elasticsearch/server/cli/JvmErgonomicsTests.java | 7 ++----- .../{ElasticsearchCliTests.java => ServerCliTests.java} | 2 +- .../main/java/org/elasticsearch/cli/CommandTestCase.java | 2 +- x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli | 1 + 5 files changed, 6 insertions(+), 8 deletions(-) rename distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/{ElasticsearchCliTests.java => ServerCliTests.java} (99%) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java index 22909b8297698..d040a6fcec169 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JvmOptionsParser.java @@ -99,7 +99,7 @@ static List determineJvmOptions(Path configDir, Path pluginsDir, Path tm } private List jvmOptions(final Path config, Path plugins, final String esJavaOpts, final Map substitutions) - throws IOException, InterruptedException, JvmOptionsFileParserException { + throws InterruptedException, IOException, JvmOptionsFileParserException { final List jvmOptions = readJvmOptionsFiles(config); diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java index f8d488df42adc..6193114ff7460 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JvmErgonomicsTests.java @@ -8,8 +8,7 @@ package org.elasticsearch.server.cli; -import org.apache.lucene.tests.util.LuceneTestCase; - +import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -29,8 +28,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -// mocking the filesystem hides the jvm we need to execute to find jvm options -@LuceneTestCase.SuppressFileSystems("*") public class JvmErgonomicsTests extends LaunchersTestCase { public void testExtractValidHeapSizeUsingXmx() throws Exception { @@ -48,7 +45,7 @@ public void testExtractValidHeapSizeNoOptionPresent() throws Exception { assertThat(JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.emptyList())), greaterThan(0L)); } - public void testHeapSizeInvalid() throws Exception { + public void testHeapSizeInvalid() throws InterruptedException, IOException { try { JvmOption.extractMaxHeapSize(JvmOption.findFinalOptions(Collections.singletonList("-Xmx2Z"))); fail("expected starting java to fail"); diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java similarity index 99% rename from distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java rename to distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 9a4c47a35e691..60fdc0df7dc9e 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ElasticsearchCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -46,7 +46,7 @@ import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.hasItem; -public class ElasticsearchCliTests extends CommandTestCase { +public class ServerCliTests extends CommandTestCase { Path esConfigDir; diff --git a/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java b/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java index 62d9c7f45a247..930455160b072 100644 --- a/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java @@ -33,7 +33,7 @@ public abstract class CommandTestCase extends ESTestCase { protected Path esHomeDir; @Before - public void resetState() { + public void resetTerminal() { terminal.reset(); terminal.setSupportsBinary(false); terminal.setVerbosity(Terminal.Verbosity.NORMAL); diff --git a/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli b/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli index 727850dae13a3..ee92e3fe924cc 100755 --- a/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli +++ b/x-pack/plugin/sql/src/main/bin/elasticsearch-sql-cli @@ -8,6 +8,7 @@ source "`dirname "$0"`"/elasticsearch-env CLI_JAR=$(ls "$ES_HOME"/bin/elasticsearch-sql-cli-*.jar) + exec \ "$JAVA" \ -jar "$CLI_JAR" \ From 7d92af7e17fcda34f2c0d9ce15e925f8443aef36 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 17:25:59 -0700 Subject: [PATCH 073/166] remove old exit method --- libs/cli/src/main/java/org/elasticsearch/cli/Command.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java index 6f03ff11b8bd7..ecb706a5c110b 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java @@ -132,11 +132,6 @@ protected void printUserException(Terminal terminal, UserException e) { } } - @SuppressForbidden(reason = "Allowed to exit explicitly from #main()") - protected static void exit(int status) { - System.exit(status); - } - /** * Executes this command. * From 22bf94926be52cd9440aafa4220fd2a81ced7eab Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 17:26:37 -0700 Subject: [PATCH 074/166] change back to private parser option --- .../org/elasticsearch/common/cli/EnvironmentAwareCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java index 83575ba085024..a28c6e2814207 100644 --- a/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java +++ b/server/src/main/java/org/elasticsearch/common/cli/EnvironmentAwareCommand.java @@ -36,7 +36,7 @@ public abstract class EnvironmentAwareCommand extends Command { private static final String DOCKER_UPPERCASE_SETTING_PREFIX = "ES_SETTING_"; private static final Pattern DOCKER_LOWERCASE_SETTING_REGEX = Pattern.compile("[-a-z0-9_]+(\\.[-a-z0-9_]+)+"); - protected final OptionSpec settingOption; + private final OptionSpec settingOption; /** * Construct the command with the specified command description. This command will have logging configured without reading Elasticsearch From 7b97d7e72fe412a146334ce95f0aeac31435aef8 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 30 Apr 2022 17:27:39 -0700 Subject: [PATCH 075/166] undo whitespace change --- .../src/test/java/org/elasticsearch/cli/MultiCommandTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java b/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java index 01cdb634a8223..d5dd24187623f 100644 --- a/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java +++ b/server/src/test/java/org/elasticsearch/cli/MultiCommandTests.java @@ -200,6 +200,7 @@ public void testCloseWhenSubCommandCloseThrowsException() throws Exception { } // Tests for multicommand error logging + static class ErrorThrowingSubCommand extends Command { ErrorThrowingSubCommand() { super("error throwing"); From 437b34c97bfdb23840f987a5d883f94f17617cff Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 06:03:30 -0700 Subject: [PATCH 076/166] add debugging for keystore tests --- .../org/elasticsearch/server/cli/ServerCli.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index c22a99593b99c..4f29e1ff0fb15 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -13,6 +13,8 @@ import joptsimple.OptionSpecBuilder; import joptsimple.util.PathConverter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.elasticsearch.Build; import org.elasticsearch.bootstrap.ServerArgs; import org.elasticsearch.cli.CliToolProvider; @@ -34,7 +36,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; @@ -50,6 +51,9 @@ import static org.elasticsearch.server.cli.JvmOptionsParser.determineJvmOptions; class ServerCli extends EnvironmentAwareCommand { + + private static final Logger logger = LogManager.getLogger(ServerCli.class); + private final OptionSpecBuilder versionOption; private final OptionSpecBuilder daemonizeOption; private final OptionSpec pidfileOption; @@ -86,6 +90,8 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // setup security final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); + // TODO: just for debugging! + logger.info("keystore password: " + keystorePassword); var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); boolean changed = runAutoConfigTool(autoConfigTerminal, options, processInfo, env); if (changed) { @@ -139,16 +145,15 @@ private void printVersion(Terminal terminal) { terminal.println(versionOutput); } - private SecureString getKeystorePassword(Path configDir, Terminal terminal) { - try { - KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir); + private SecureString getKeystorePassword(Path configDir, Terminal terminal) throws IOException { + try (KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir)) { if (keystore != null && keystore.hasPassword()) { + logger.info("keystore has password"); return new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT)); } else { + logger.info("keystore does not have password"); return new SecureString(new char[0]); } - } catch (IOException e) { - throw new UncheckedIOException(e); } } From 97775499a653b5424bf0cd4fae34d7626c98e3ad Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 06:10:26 -0700 Subject: [PATCH 077/166] add back exit, for now --- libs/cli/src/main/java/org/elasticsearch/cli/Command.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java index ecb706a5c110b..6f03ff11b8bd7 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java @@ -132,6 +132,11 @@ protected void printUserException(Terminal terminal, UserException e) { } } + @SuppressForbidden(reason = "Allowed to exit explicitly from #main()") + protected static void exit(int status) { + System.exit(status); + } + /** * Executes this command. * From 569c1f32d0ca0fc2a803e60c6fb8f9f1d0c59677 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 06:44:19 -0700 Subject: [PATCH 078/166] allow child process in systemd to send notify --- distribution/packages/src/common/systemd/elasticsearch.service | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/packages/src/common/systemd/elasticsearch.service b/distribution/packages/src/common/systemd/elasticsearch.service index 9af491822c81c..92dca34357f01 100644 --- a/distribution/packages/src/common/systemd/elasticsearch.service +++ b/distribution/packages/src/common/systemd/elasticsearch.service @@ -6,6 +6,7 @@ After=network-online.target [Service] Type=notify +NotifyAccess=exec RuntimeDirectory=elasticsearch PrivateTmp=true Environment=ES_HOME=/usr/share/elasticsearch From ea5696ccb3ed8856fa73daaa35ae34f124b4f8d9 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 06:45:53 -0700 Subject: [PATCH 079/166] add comment --- distribution/packages/src/common/systemd/elasticsearch.service | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distribution/packages/src/common/systemd/elasticsearch.service b/distribution/packages/src/common/systemd/elasticsearch.service index 92dca34357f01..1aff33d04b3bc 100644 --- a/distribution/packages/src/common/systemd/elasticsearch.service +++ b/distribution/packages/src/common/systemd/elasticsearch.service @@ -6,6 +6,8 @@ After=network-online.target [Service] Type=notify +# the main process is actually a child of that created by system, do allow it access +# to send back the sd_notify message for readiness NotifyAccess=exec RuntimeDirectory=elasticsearch PrivateTmp=true From 5e2da7e0bc43d1c4cbe8726c1b45eb7477352e3b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 09:41:22 -0700 Subject: [PATCH 080/166] try different notify access --- distribution/packages/src/common/systemd/elasticsearch.service | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distribution/packages/src/common/systemd/elasticsearch.service b/distribution/packages/src/common/systemd/elasticsearch.service index 1aff33d04b3bc..309f79aa85200 100644 --- a/distribution/packages/src/common/systemd/elasticsearch.service +++ b/distribution/packages/src/common/systemd/elasticsearch.service @@ -8,7 +8,8 @@ After=network-online.target Type=notify # the main process is actually a child of that created by system, do allow it access # to send back the sd_notify message for readiness -NotifyAccess=exec +# TODO: why doesn't exec work? +NotifyAccess=all RuntimeDirectory=elasticsearch PrivateTmp=true Environment=ES_HOME=/usr/share/elasticsearch From 1bbd4ead9fb38a2ee6102d79f2d458bc37bf57ca Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 10:07:27 -0700 Subject: [PATCH 081/166] add error debug --- .../org/elasticsearch/bootstrap/BootstrapUtil.java | 1 + .../org/elasticsearch/bootstrap/Elasticsearch.java | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java index 099eacb064a1f..df293423c5291 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java @@ -27,6 +27,7 @@ public class BootstrapUtil { // no construction private BootstrapUtil() {} + // TODO: remove this method /** * Read from an InputStream up to the first carriage return or newline, * returning no more than maxLength characters. diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 0ed953bf95c5c..6ee5948811867 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -54,6 +54,7 @@ public void checkPermission(Permission perm) { LogConfigurator.registerErrorListener(); final Elasticsearch elasticsearch = new Elasticsearch(); + PrintStream out = getStdout(); PrintStream err = getStderr(); int exitCode = 0; try { @@ -70,10 +71,12 @@ public void checkPermission(Permission perm) { exitCode = ExitCodes.CONFIG; err.print(USER_EXCEPTION_MARKER); err.println(e.getMessage()); + out.println(e.getMessage()); } catch (UserException e) { exitCode = e.exitCode; err.print(USER_EXCEPTION_MARKER); err.println(e.getMessage()); + out.println(e.getMessage()); } catch (Exception e) { exitCode = 1; // mimic JDK exit code on exception if (System.getProperty("es.logs.base_path") != null) { @@ -83,10 +86,13 @@ public void checkPermission(Permission perm) { logger.error("fatal exception while booting Elasticsearch", e); } e.printStackTrace(err); + e.printStackTrace(out); } if (exitCode != ExitCodes.OK) { + out.println("EXITING with non-zero status: " + exitCode); printLogsSuggestion(err); err.flush(); + out.flush(); exit(exitCode); } } @@ -96,6 +102,12 @@ private static PrintStream getStderr() { return System.err; } + // TODO: remove this, just for debugging + @SuppressForbidden(reason = "grab stdout for communication with server-cli") + private static PrintStream getStdout() { + return System.out; + } + @SuppressForbidden(reason = "main exit path") private static void exit(int exitCode) { System.exit(exitCode); From 686905c395d8792aa1c0a36f5f8d422389a849c5 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 10:49:58 -0700 Subject: [PATCH 082/166] add auto config tests --- .../elasticsearch/server/cli/ServerCli.java | 7 +++ .../server/cli/ServerCliTests.java | 63 +++++++++++++++++-- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 4f29e1ff0fb15..31238a11bde61 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -106,6 +106,11 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce var args = createArgs(options, keystorePassword, env); try (var out = new OutputStreamStreamOutput(process.getOutputStream())) { args.writeTo(out); + } catch (IOException e) { + // TODO: if process dies early (we didn't get the chance to write args, pipe died) + // then what happens to error output? can we still read it? need to check exit code (should assert non zero?) + assert process.exitValue() != 0; + throw new UserException(process.exitValue(), null); } keystorePassword.close(); @@ -176,6 +181,8 @@ private boolean runAutoConfigTool(Terminal terminal, OptionSet options, ProcessI // eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it switch (e.exitCode) { case ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: + // we still want to print the error, just don't fail startup + terminal.errorPrintln(e.getMessage()); return false; } } diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 60fdc0df7dc9e..559fa772e3e4e 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; import org.elasticsearch.common.cli.EnvironmentAwareCommand; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.settings.Settings; @@ -196,19 +197,65 @@ public void testMultipleEnrollmentTokens() throws Exception { ); } + public void testAutoConfigEnrollment() throws Exception { + autoConfigCallback = (t, options, env, processInfo) -> { + assertThat(options.valueOf("enrollment-token"), equalTo("mydummytoken")); + }; + assertOk("--enrollment-token", "mydummytoken"); + } + + public void testAutoConfig() throws Exception { + autoConfigCallback = (t, options, env, processInfo) -> { + t.println("message from auto config"); + }; + assertOkWithOutput(containsString("message from auto config")); + } + + public void testAutoConfigErrorPropagated() throws Exception { + autoConfigCallback = (t, options, env, processInfo) -> { + throw new UserException(ExitCodes.IO_ERROR, "error from auto config"); + }; + var e = expectThrows(UserException.class, () -> execute()); + assertThat(e.exitCode, equalTo(ExitCodes.IO_ERROR)); + assertThat(e.getMessage(), equalTo("error from auto config")); + } + + public void assertAutoConfigOkError(int exitCode) throws Exception { + autoConfigCallback = (t, options, env, processInfo) -> { + throw new UserException(exitCode, "ok error from auto config"); + }; + int mainExitCode = executeMain(); + assertThat(mainExitCode, equalTo(ExitCodes.OK)); + assertThat(terminal.getErrorOutput(), containsString("ok error from auto config")); + } + + public void testAutoConfigOkErrors() throws Exception { + assertAutoConfigOkError(ExitCodes.CANT_CREATE); + assertAutoConfigOkError(ExitCodes.CONFIG); + assertAutoConfigOkError(ExitCodes.NOOP); + } + interface MainMethod { void main(ServerArgs args, OutputStream stdout, OutputStream stderr, AtomicInteger exitCode); } + interface AutoConfigMethod { + void autoconfig(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws UserException; + } + MainMethod mainCallback; final MainMethod FAIL_MAIN = (args, stdout, stderr, exitCode) -> fail("Did not expect to run init"); + AutoConfigMethod autoConfigCallback; + private final MockAutoConfigCli AUTO_CONFIG_CLI = new MockAutoConfigCli(); + @Before public void resetCommand() { mainCallback = null; + autoConfigCallback = null; } - private static class MockAutoConfigCli extends EnvironmentAwareCommand { + private class MockAutoConfigCli extends EnvironmentAwareCommand { MockAutoConfigCli() { super("mock auto config tool"); } @@ -221,7 +268,9 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process @Override public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { // TODO: fake errors, check password from terminal, allow tests to make elasticsearch.yml change - + if (autoConfigCallback != null) { + autoConfigCallback.autoconfig(terminal, options, env, processInfo); + } } } @@ -276,12 +325,13 @@ private class MockElasticsearchProcess extends Process { private final Thread thread = new Thread(() -> { try (var in = new InputStreamStreamInput(stdin)) { final ServerArgs serverArgs = new ServerArgs(in); + mainCallback.main(serverArgs, stdout, stderr, exitCode); } catch (IOException e) { argsParsingException.set(e); } IOUtils.closeWhileHandlingException(stdin, stdout, stderr); - }, "dummy elasticsearch"); + }, "mock Elasticsearch process"); MockElasticsearchProcess() throws IOException { stdin.connect(processStdin); @@ -340,12 +390,17 @@ protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDi @Override protected Process startProcess(ProcessBuilder processBuilder) throws IOException { // TODO: validate processbuilder stuff + if (mainCallback == null) { + return new NoopProcess(); + } return new MockElasticsearchProcess(); } @Override protected Command loadTool(String toolname, String libs) { - return new MockAutoConfigCli(); + assertThat(toolname, equalTo("auto-configure-node")); + assertThat(libs, equalTo("modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli")); + return AUTO_CONFIG_CLI; } @Override From 023a5ad1ce5b21f8bcb2481f79bf6881b162545b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 12:10:37 -0700 Subject: [PATCH 083/166] fix keystore password reading --- .../server/cli/KeystorePasswordTerminal.java | 16 ++++- .../elasticsearch/server/cli/ServerCli.java | 2 +- .../server/cli/ServerCliTests.java | 59 ++++++++++++++----- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java index ca304edd2c3bf..bc822e4427b23 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -13,6 +13,8 @@ import java.io.OutputStream; import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; class KeystorePasswordTerminal extends Terminal { @@ -24,24 +26,32 @@ protected KeystorePasswordTerminal(Terminal delegate, SecureString password) { } private static Reader newPasswordReader(final SecureString password) { + // copy to a buffer with the expected newline as if a user input it and hit enter + char[] newline = System.lineSeparator().toCharArray(); + final char[] inputChars = new char[password.length() + newline.length]; + System.arraycopy(password.getChars(), 0, inputChars, 0, password.length()); + System.arraycopy(newline, 0, inputChars, password.length(), newline.length); + password.close(); + return new Reader() { int pos = 0; @Override public int read(char[] cbuf, int off, int len) { - int charsLeft = password.length() - pos; + int charsLeft = inputChars.length - pos; if (charsLeft == 0) { + close(); return -1; } int toCopy = Math.min(charsLeft, len); - System.arraycopy(password.getChars(), pos, cbuf, off, toCopy); + System.arraycopy(inputChars, pos, cbuf, off, toCopy); pos += toCopy; return toCopy; } @Override public void close() { - password.close(); + Arrays.fill(inputChars, '\0'); } }; } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 31238a11bde61..c588a541c92c5 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -92,7 +92,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); // TODO: just for debugging! logger.info("keystore password: " + keystorePassword); - var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword); + var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone()); boolean changed = runAutoConfigTool(autoConfigTerminal, options, processInfo, env); if (changed) { // reload settings since auto security changed them diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 559fa772e3e4e..efa0f90d5dac5 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.cli.UserException; import org.elasticsearch.common.cli.EnvironmentAwareCommand; import org.elasticsearch.common.io.stream.InputStreamStreamInput; +import org.elasticsearch.common.settings.KeyStoreWrapper; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.IOUtils; import org.elasticsearch.env.Environment; @@ -64,7 +65,6 @@ private void assertOk(String... args) throws Exception { } private void assertOkWithOutput(Matcher matcher, String... args) throws Exception { - terminal.reset(); int status = executeMain(args); assertThat(status, equalTo(ExitCodes.OK)); assertThat(terminal.getErrorOutput(), emptyString()); @@ -107,7 +107,9 @@ public void testVersion() throws Exception { containsString(expectedBuildOutput), containsString("JVM: " + JvmInfo.jvmInfo().version()) ); + terminal.reset(); assertOkWithOutput(versionOutput, "-V"); + terminal.reset(); assertOkWithOutput(versionOutput, "--version"); } @@ -211,28 +213,55 @@ public void testAutoConfig() throws Exception { assertOkWithOutput(containsString("message from auto config")); } - public void testAutoConfigErrorPropagated() throws Exception { + public void assertAutoConfigError(int autoConfigExitCode, int expectedMainExitCode, String... args) throws Exception { + terminal.reset(); autoConfigCallback = (t, options, env, processInfo) -> { - throw new UserException(ExitCodes.IO_ERROR, "error from auto config"); + throw new UserException(autoConfigExitCode, "message from auto config"); }; - var e = expectThrows(UserException.class, () -> execute()); - assertThat(e.exitCode, equalTo(ExitCodes.IO_ERROR)); - assertThat(e.getMessage(), equalTo("error from auto config")); + int gotMainExitCode = executeMain(args); + assertThat(gotMainExitCode, equalTo(expectedMainExitCode)); + assertThat(terminal.getErrorOutput(), containsString("message from auto config")); + } + + public void testAutoConfigErrorPropagated() throws Exception { + assertAutoConfigError(ExitCodes.IO_ERROR, ExitCodes.IO_ERROR); + terminal.reset(); + assertAutoConfigError(ExitCodes.CONFIG, ExitCodes.CONFIG, "--enrollment-token", "mytoken"); + } + + public void testAutoConfigOkErrors() throws Exception { + assertAutoConfigError(ExitCodes.CANT_CREATE, ExitCodes.OK); + assertAutoConfigError(ExitCodes.CONFIG, ExitCodes.OK); + assertAutoConfigError(ExitCodes.NOOP, ExitCodes.OK); } - public void assertAutoConfigOkError(int exitCode) throws Exception { + public void assertKeystorePassword(String password) throws Exception { + terminal.reset(); + if (password != null && password.isEmpty() == false) { + terminal.addSecretInput(password); + } + Path configDir = esHomeDir.resolve("config"); + Files.createDirectories(configDir); + if (password != null) { + try (KeyStoreWrapper keystore = KeyStoreWrapper.create()) { + keystore.save(configDir, password.toCharArray(), false); + } + } + String expectedPassword = password == null ? "" : password; + mainCallback = (args, stdout, stderr, exitCode) -> { + assertThat(args.keystorePassword().toString(), equalTo(expectedPassword)); + }; autoConfigCallback = (t, options, env, processInfo) -> { - throw new UserException(exitCode, "ok error from auto config"); + char[] gotPassword = t.readSecret("keystore password"); + assertThat(gotPassword, equalTo(expectedPassword.toCharArray())); }; - int mainExitCode = executeMain(); - assertThat(mainExitCode, equalTo(ExitCodes.OK)); - assertThat(terminal.getErrorOutput(), containsString("ok error from auto config")); + assertOk(); } - public void testAutoConfigOkErrors() throws Exception { - assertAutoConfigOkError(ExitCodes.CANT_CREATE); - assertAutoConfigOkError(ExitCodes.CONFIG); - assertAutoConfigOkError(ExitCodes.NOOP); + public void testKeystorePassword() throws Exception { + //assertKeystorePassword(null); // no keystore exists + //assertKeystorePassword(""); + assertKeystorePassword("dummypassword"); } interface MainMethod { From 386841e922683250214d223f0bcb578306781864 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 13:19:12 -0700 Subject: [PATCH 084/166] simplify password passing to auto config --- .../server/cli/KeystorePasswordTerminal.java | 47 +++++-------------- .../elasticsearch/server/cli/ServerCli.java | 6 ++- .../java/org/elasticsearch/cli/Terminal.java | 5 ++ 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java index bc822e4427b23..6efd540222311 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -11,53 +11,32 @@ import org.elasticsearch.cli.Terminal; import org.elasticsearch.common.settings.SecureString; +import java.io.Closeable; import java.io.OutputStream; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -class KeystorePasswordTerminal extends Terminal { +class KeystorePasswordTerminal extends Terminal implements Closeable { private final Terminal delegate; + private final SecureString password; protected KeystorePasswordTerminal(Terminal delegate, SecureString password) { - super(newPasswordReader(password), delegate.getWriter(), delegate.getErrorWriter()); + super(delegate.getReader(), delegate.getWriter(), delegate.getErrorWriter()); this.delegate = delegate; + this.password = password; } - private static Reader newPasswordReader(final SecureString password) { - // copy to a buffer with the expected newline as if a user input it and hit enter - char[] newline = System.lineSeparator().toCharArray(); - final char[] inputChars = new char[password.length() + newline.length]; - System.arraycopy(password.getChars(), 0, inputChars, 0, password.length()); - System.arraycopy(newline, 0, inputChars, password.length(), newline.length); - password.close(); - - return new Reader() { - int pos = 0; - - @Override - public int read(char[] cbuf, int off, int len) { - int charsLeft = inputChars.length - pos; - if (charsLeft == 0) { - close(); - return -1; - } - int toCopy = Math.min(charsLeft, len); - System.arraycopy(inputChars, pos, cbuf, off, toCopy); - pos += toCopy; - return toCopy; - } - - @Override - public void close() { - Arrays.fill(inputChars, '\0'); - } - }; + @Override + public char[] readSecret(String prompt) { + return password.getChars(); } @Override public OutputStream getOutputStream() { return delegate.getOutputStream(); } + + @Override + public void close() { + password.close(); + } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index c588a541c92c5..58a2793ce1870 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -92,8 +92,10 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); // TODO: just for debugging! logger.info("keystore password: " + keystorePassword); - var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone()); - boolean changed = runAutoConfigTool(autoConfigTerminal, options, processInfo, env); + final boolean changed; + try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { + changed = runAutoConfigTool(autoConfigTerminal, options, processInfo, env); + } if (changed) { // reload settings since auto security changed them env = createEnv(options, processInfo); diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java index 70a3c684e1d92..95b74566d8406 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java @@ -98,6 +98,11 @@ public char[] readSecret(String prompt) { return read(prompt); } + /** Returns a Reader which can be used to read directly from the terminal using standard input. */ + public final Reader getReader() { + return reader; + } + /** Returns a Writer which can be used to write to the terminal directly using standard output. */ public final PrintWriter getWriter() { return outWriter; From 94d2cee2926ba8cf8762053cd6cf00e48a2bec45 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 14:41:45 -0700 Subject: [PATCH 085/166] use separate thread for pump --- .../elasticsearch/server/cli/ServerCli.java | 123 ++++++++++++------ 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 58a2793ce1870..2fe057273fd8b 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -36,6 +36,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; @@ -90,39 +91,23 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // setup security final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); - // TODO: just for debugging! - logger.info("keystore password: " + keystorePassword); - final boolean changed; - try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { - changed = runAutoConfigTool(autoConfigTerminal, options, processInfo, env); - } - if (changed) { - // reload settings since auto security changed them - env = createEnv(options, processInfo); - } + env = autoConfigureSecurity(terminal, options, processInfo, env, keystorePassword); // start Elasticsearch - final Process process = createProcess(processInfo, env); - - // send arguments - var args = createArgs(options, keystorePassword, env); - try (var out = new OutputStreamStreamOutput(process.getOutputStream())) { - args.writeTo(out); - } catch (IOException e) { - // TODO: if process dies early (we didn't get the chance to write args, pipe died) - // then what happens to error output? can we still read it? need to check exit code (should assert non zero?) - assert process.exitValue() != 0; - throw new UserException(process.exitValue(), null); - } - keystorePassword.close(); + final Process process = createProcess(processInfo, env.configFile(), env.pluginsFile()); + final ErrorPumpThread errorPump = new ErrorPumpThread(terminal, process.getErrorStream()); + errorPump.start(); + sendArgs(options, keystorePassword, env, process.getOutputStream()); // Read from stderr until we get a signal back that ES is either ready or it had an error. // If we are running in the foreground, this pump will never exit. - AtomicReference userExceptionMsg = new AtomicReference<>(); - boolean ready = pumpStderr(terminal, process.getErrorStream(), userExceptionMsg); + errorPump.join(); + if (errorPump.ioFailure != null) { + throw errorPump.ioFailure; + } // if we are daemonized and we got the all-clear signal, we can exit cleanly - if (ready && args.daemonize()) { + if (errorPump.ready && options.has(daemonizeOption)) { closeStreams(process); return; } @@ -131,7 +116,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // then it is a real UserException, otherwise it is just the error code and a null message. int code = process.waitFor(); if (code != ExitCodes.OK) { - throw new UserException(code, userExceptionMsg.get()); + throw new UserException(code, errorPump.userExceptionMsg); } } @@ -164,7 +149,7 @@ private SecureString getKeystorePassword(Path configDir, Terminal terminal) thro } } - private boolean runAutoConfigTool(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env) throws Exception { + private Environment autoConfigureSecurity(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env, SecureString keystorePassword) throws Exception { if (options.valuesOf(enrollmentTokenOption).size() > 1) { throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); } @@ -175,36 +160,53 @@ private boolean runAutoConfigTool(Terminal terminal, OptionSet options, ProcessI @SuppressWarnings("raw") var autoConfigNode = (EnvironmentAwareCommand) cmd; - try { - autoConfigNode.execute(terminal, options, env, processInfo); + boolean changed = true; + try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { + autoConfigNode.execute(autoConfigTerminal, options, env, processInfo); } catch (UserException e) { - if (options.has(enrollmentTokenOption) == false) { + boolean okCode = switch (e.exitCode) { // these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual // eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it - switch (e.exitCode) { - case ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP: - // we still want to print the error, just don't fail startup - terminal.errorPrintln(e.getMessage()); - return false; - } + case ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP -> true; + default -> false; + }; + if (options.has(enrollmentTokenOption) == false && okCode) { + // we still want to print the error, just don't fail startup + terminal.errorPrintln(e.getMessage()); + changed = false; + } else { + throw e; } - throw e; } - return true; + if (changed) { + // reload settings since auto security changed them + env = createEnv(options, processInfo); + } + return env; + + } - private ServerArgs createArgs(OptionSet options, SecureString keystorePassword, Environment env) { + private void sendArgs(OptionSet options, SecureString keystorePassword, Environment env, OutputStream processStdin) { final boolean daemonize = options.has(daemonizeOption); final boolean quiet = options.has(quietOption); final Path pidFile = pidfileOption.value(options); + final var args = new ServerArgs(daemonize, quiet, pidFile, keystorePassword, env.settings(), env.configFile()); - return new ServerArgs(daemonize, quiet, pidFile, keystorePassword, env.settings(), env.configFile()); + try (var out = new OutputStreamStreamOutput(processStdin)) { + args.writeTo(out); + } catch (IOException ignore) { + // A failure to write here means the process has problems, and it will die anyways. We let this fall through + // so the pump thread can complete, writing out the actual error. All we get here is the failure to write to + // the process pipe, which isn't helpful to print. + } + keystorePassword.close(); } - private Process createProcess(ProcessInfo processInfo, Environment env) throws Exception { + private Process createProcess(ProcessInfo processInfo, Path configDir, Path pluginsDir) throws Exception { Map envVars = new HashMap<>(processInfo.envVars()); Path tempDir = TempDirectory.setup(envVars); - List jvmOptions = getJvmOptions(env.configFile(), env.pluginsFile(), tempDir, envVars.get("ES_JAVA_OPTS")); + List jvmOptions = getJvmOptions(configDir, pluginsDir, tempDir, envVars.get("ES_JAVA_OPTS")); // jvmOptions.add("-Des.path.conf=" + env.configFile()); jvmOptions.add("-Des.distribution.type=" + processInfo.sysprops().get("es.distribution.type")); @@ -248,6 +250,41 @@ private boolean pumpStderr(Terminal terminal, InputStream err, AtomicReference getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws Exception { return new ArrayList<>(determineJvmOptions(configDir, pluginsDir, tmpDir, envOptions)); From 7dcfcc302d82c2b4979222c9607664cc52561bee Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 15:01:24 -0700 Subject: [PATCH 086/166] improve tests --- .../server/cli/ServerCliTests.java | 86 +++++++------------ 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index efa0f90d5dac5..075d76e27b27a 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -26,6 +26,7 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; import org.hamcrest.Matcher; +import org.junit.AfterClass; import org.junit.Before; import java.io.IOException; @@ -38,6 +39,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -50,6 +55,7 @@ public class ServerCliTests extends CommandTestCase { + private static final ExecutorService mockProcessExecutor = Executors.newSingleThreadExecutor(); Path esConfigDir; @Before @@ -60,6 +66,11 @@ public void setupDummyInstallation() throws IOException { Files.writeString(esConfigDir.resolve("jvm.options"), ""); } + @AfterClass + public static void cleanupExecutor() { + mockProcessExecutor.shutdown(); + } + private void assertOk(String... args) throws Exception { assertOkWithOutput(emptyString(), args); } @@ -259,8 +270,8 @@ public void assertKeystorePassword(String password) throws Exception { } public void testKeystorePassword() throws Exception { - //assertKeystorePassword(null); // no keystore exists - //assertKeystorePassword(""); + assertKeystorePassword(null); // no keystore exists + assertKeystorePassword(""); assertKeystorePassword("dummypassword"); } @@ -303,43 +314,6 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce } } - // a "process" that just exits - private class NoopProcess extends Process { - private final OutputStream processStdin = OutputStream.nullOutputStream(); - private final InputStream processStdout = InputStream.nullInputStream(); - private final InputStream processStderr = InputStream.nullInputStream(); - - @Override - public OutputStream getOutputStream() { - return processStdin; - } - - @Override - public InputStream getInputStream() { - return processStdout; - } - - @Override - public InputStream getErrorStream() { - return processStderr; - } - - @Override - public int waitFor() { - return 0; - } - - @Override - public int exitValue() { - return 0; - } - - @Override - public void destroy() { - fail("Tried to kill ES process"); - } - } - // a "process" that is really another thread private class MockElasticsearchProcess extends Process { private final PipedOutputStream processStdin = new PipedOutputStream(); @@ -351,22 +325,23 @@ private class MockElasticsearchProcess extends Process { private final AtomicInteger exitCode = new AtomicInteger(); private final AtomicReference argsParsingException = new AtomicReference<>(); - private final Thread thread = new Thread(() -> { - try (var in = new InputStreamStreamInput(stdin)) { - final ServerArgs serverArgs = new ServerArgs(in); - - mainCallback.main(serverArgs, stdout, stderr, exitCode); - } catch (IOException e) { - argsParsingException.set(e); - } - IOUtils.closeWhileHandlingException(stdin, stdout, stderr); - }, "mock Elasticsearch process"); + private final Future main; MockElasticsearchProcess() throws IOException { stdin.connect(processStdin); stdout.connect(processStdout); stderr.connect(processStderr); - thread.start(); + this.main = mockProcessExecutor.submit(() -> { + try (var in = new InputStreamStreamInput(stdin)) { + final ServerArgs serverArgs = new ServerArgs(in); + if (mainCallback != null) { + mainCallback.main(serverArgs, stdout, stderr, exitCode); + } + } catch (IOException e) { + argsParsingException.set(e); + } + IOUtils.closeWhileHandlingException(stdin, stdout, stderr); + }); } @Override @@ -386,7 +361,11 @@ public InputStream getErrorStream() { @Override public int waitFor() throws InterruptedException { - thread.join(); + try { + main.get(); + } catch (ExecutionException e) { + throw new AssertionError(e); + } if (argsParsingException.get() != null) { throw new AssertionError("Reading server args failed", argsParsingException.get()); } @@ -395,7 +374,7 @@ public int waitFor() throws InterruptedException { @Override public int exitValue() { - if (thread.isAlive()) { + if (main.isDone() == false) { throw new IllegalThreadStateException(); // match spec } return exitCode.get(); @@ -419,9 +398,6 @@ protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDi @Override protected Process startProcess(ProcessBuilder processBuilder) throws IOException { // TODO: validate processbuilder stuff - if (mainCallback == null) { - return new NoopProcess(); - } return new MockElasticsearchProcess(); } From 737b9f52e372dee025efea0e0af21d70a563c133 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 15:14:59 -0700 Subject: [PATCH 087/166] spotless --- .../elasticsearch/server/cli/ServerCli.java | 30 +++++-------------- .../server/cli/ServerCliTests.java | 14 +++------ 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 2fe057273fd8b..c1dafa50ab649 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -149,7 +149,13 @@ private SecureString getKeystorePassword(Path configDir, Terminal terminal) thro } } - private Environment autoConfigureSecurity(Terminal terminal, OptionSet options, ProcessInfo processInfo, Environment env, SecureString keystorePassword) throws Exception { + private Environment autoConfigureSecurity( + Terminal terminal, + OptionSet options, + ProcessInfo processInfo, + Environment env, + SecureString keystorePassword + ) throws Exception { if (options.valuesOf(enrollmentTokenOption).size() > 1) { throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); } @@ -184,7 +190,6 @@ private Environment autoConfigureSecurity(Terminal terminal, OptionSet options, } return env; - } private void sendArgs(OptionSet options, SecureString keystorePassword, Environment env, OutputStream processStdin) { @@ -229,27 +234,6 @@ private Process createProcess(ProcessInfo processInfo, Path configDir, Path plug return startProcess(builder); } - private boolean pumpStderr(Terminal terminal, InputStream err, AtomicReference userExceptionMsg) throws IOException { - BufferedReader reader = new BufferedReader(new InputStreamReader(err, StandardCharsets.UTF_8)); - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { - userExceptionMsg.set(line.substring(1)); - // TODO: we need to not return here, there will still be more on stderr (logs suggestion), but - // for some reason the process exiting isn't breaking the final readLine, we just hang indefinitely... - return false; - } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { - // The server closes stderr right after this message, but for some unknown reason - // the pipe closing does not close this end of the pipe, so we must explicitly - // break out of this loop, or we will block forever on the next read. - return true; - } else { - terminal.getErrorWriter().println(line); - } - } - return false; - } - static class ErrorPumpThread extends Thread { private final Terminal terminal; private final BufferedReader reader; diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 075d76e27b27a..e5538bdf8bc0e 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -218,17 +218,13 @@ public void testAutoConfigEnrollment() throws Exception { } public void testAutoConfig() throws Exception { - autoConfigCallback = (t, options, env, processInfo) -> { - t.println("message from auto config"); - }; + autoConfigCallback = (t, options, env, processInfo) -> { t.println("message from auto config"); }; assertOkWithOutput(containsString("message from auto config")); } public void assertAutoConfigError(int autoConfigExitCode, int expectedMainExitCode, String... args) throws Exception { terminal.reset(); - autoConfigCallback = (t, options, env, processInfo) -> { - throw new UserException(autoConfigExitCode, "message from auto config"); - }; + autoConfigCallback = (t, options, env, processInfo) -> { throw new UserException(autoConfigExitCode, "message from auto config"); }; int gotMainExitCode = executeMain(args); assertThat(gotMainExitCode, equalTo(expectedMainExitCode)); assertThat(terminal.getErrorOutput(), containsString("message from auto config")); @@ -259,9 +255,7 @@ public void assertKeystorePassword(String password) throws Exception { } } String expectedPassword = password == null ? "" : password; - mainCallback = (args, stdout, stderr, exitCode) -> { - assertThat(args.keystorePassword().toString(), equalTo(expectedPassword)); - }; + mainCallback = (args, stdout, stderr, exitCode) -> { assertThat(args.keystorePassword().toString(), equalTo(expectedPassword)); }; autoConfigCallback = (t, options, env, processInfo) -> { char[] gotPassword = t.readSecret("keystore password"); assertThat(gotPassword, equalTo(expectedPassword.toCharArray())); @@ -270,7 +264,7 @@ public void assertKeystorePassword(String password) throws Exception { } public void testKeystorePassword() throws Exception { - assertKeystorePassword(null); // no keystore exists + assertKeystorePassword(null); // no keystore exists assertKeystorePassword(""); assertKeystorePassword("dummypassword"); } From a81b9601751e62ae0fa39db7f46251ae31b11f4e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 15:19:20 -0700 Subject: [PATCH 088/166] spotless again --- .../src/main/java/org/elasticsearch/server/cli/ServerCli.java | 1 - 1 file changed, 1 deletion(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index c1dafa50ab649..f018a7836b0b5 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -45,7 +45,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; From 5d5c6694d977f95d616acb84f20d84857991979f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 16:28:12 -0700 Subject: [PATCH 089/166] improve debug --- .../org/elasticsearch/bootstrap/Elasticsearch.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 6ee5948811867..5598b133f204e 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -58,8 +58,10 @@ public void checkPermission(Permission perm) { PrintStream err = getStderr(); int exitCode = 0; try { + err.println("Reading args from server-cli"); final var in = new InputStreamStreamInput(System.in); final ServerArgs serverArgs = new ServerArgs(in); + err.println(serverArgs); elasticsearch.init( serverArgs.daemonize(), serverArgs.pidFile(), @@ -71,12 +73,18 @@ public void checkPermission(Permission perm) { exitCode = ExitCodes.CONFIG; err.print(USER_EXCEPTION_MARKER); err.println(e.getMessage()); - out.println(e.getMessage()); + if (e.getMessage() != null) { + out.println(e.getMessage()); + } } catch (UserException e) { exitCode = e.exitCode; err.print(USER_EXCEPTION_MARKER); - err.println(e.getMessage()); - out.println(e.getMessage()); + if (e.getMessage() != null) { + err.println(e.getMessage()); + out.println(e.getMessage()); + } else { + err.println(); + } } catch (Exception e) { exitCode = 1; // mimic JDK exit code on exception if (System.getProperty("es.logs.base_path") != null) { From c5f45761fe9cdb5e6a974e001bfea924ee0df04d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 16:30:33 -0700 Subject: [PATCH 090/166] more debug --- .../src/main/java/org/elasticsearch/server/cli/ServerCli.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index f018a7836b0b5..413857631ad21 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -252,17 +252,21 @@ public void run() { while ((line = reader.readLine()) != null) { if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { userExceptionMsg = line.substring(1); + logger.error("Got user exception: " + userExceptionMsg); } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { // The server closes stderr right after this message, but for some unknown reason // the pipe closing does not close this end of the pipe, so we must explicitly // break out of this loop, or we will block forever on the next read. + logger.info("Got ready signal"); ready = true; return; } else { + logger.error("Got error line: " + line); terminal.getErrorWriter().println(line); } } } catch (IOException e) { + logger.error("Got io exception in pump", e); ioFailure = e; } } From fe2502ed4ed91d7a525e0c37d9198f6674dd1296 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 17:27:47 -0700 Subject: [PATCH 091/166] implement close for server cli to propagate shutdown signal --- .../java/org/elasticsearch/server/cli/ServerCli.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 413857631ad21..bf5f4e19e1b71 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -60,6 +60,8 @@ class ServerCli extends EnvironmentAwareCommand { private final OptionSpecBuilder quietOption; private final OptionSpec enrollmentTokenOption; + private volatile Process process; + // visible for testing ServerCli() { super("Starts Elasticsearch"); // we configure logging later so we override the base class from configuring logging @@ -94,6 +96,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // start Elasticsearch final Process process = createProcess(processInfo, env.configFile(), env.pluginsFile()); + this.process = process; // stash process so close can kill it (eg when SIGINT or SIGTERM is sent) final ErrorPumpThread errorPump = new ErrorPumpThread(terminal, process.getErrorStream()); errorPump.start(); sendArgs(options, keystorePassword, env, process.getOutputStream()); @@ -272,6 +275,13 @@ public void run() { } } + @Override + public void close() { + if (process != null) { + process.destroy(); + } + } + // protected to allow tests to override protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws Exception { return new ArrayList<>(determineJvmOptions(configDir, pluginsDir, tmpDir, envOptions)); From fdbdbb13dfec0aafac108424a2551722899b4537 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 18:27:49 -0700 Subject: [PATCH 092/166] wait for subprocess to die --- .../org/elasticsearch/server/cli/ServerCli.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index bf5f4e19e1b71..2cad42794ed0e 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -279,6 +279,19 @@ public void run() { public void close() { if (process != null) { process.destroy(); + + // TODO: we should wait adn then forcibly kill, but after how long? this is configurable in eg systemd, + // but that is giving us. For systemd we should sd_notify that the main pid is different. For SIGINT + // we should have some default max time before sending SIGKILL? we should also maybe block on the main thread + // exiting after it the process is terminated + while (true) { + try { + process.waitFor(); + break; + } catch (InterruptedException ignore) { + // retry + } + } } } From 23272b82320c1ba50d0963cfde9c77a12699451d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 2 May 2022 18:48:30 -0700 Subject: [PATCH 093/166] attempt to send main pid back to systemd --- .../org/elasticsearch/systemd/SystemdPlugin.java | 16 ++++++++++++++++ .../systemd/SystemdPluginTests.java | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java index a299f75b48e3d..8d60e328b464f 100644 --- a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java +++ b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java @@ -28,6 +28,7 @@ import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; +import java.lang.management.ManagementFactory; import java.util.Collection; import java.util.List; import java.util.function.Supplier; @@ -67,6 +68,9 @@ public SystemdPlugin() { throw new RuntimeException("ES_SD_NOTIFY set to unexpected value [" + esSDNotify + "]"); } enabled = Boolean.TRUE.toString().equals(esSDNotify); + if (enabled) { + setMainPid(); + } } private final SetOnce extender = new SetOnce<>(); @@ -106,9 +110,21 @@ public Collection createComponents( logger.warn("extending startup timeout via sd_notify failed with [{}]", rc); } }, TimeValue.timeValueSeconds(15), ThreadPool.Names.SAME)); + return List.of(); } + void setMainPid() { + final int rc = sd_notify(0, "MAINPID=" + getPid()); + if (rc < 0) { + logger.warn("setting main pid via sd_notify failed with [{}]", rc); + } + } + + long getPid() { + return ManagementFactory.getRuntimeMXBean().getPid(); + } + int sd_notify(@SuppressWarnings("SameParameterValue") final int unset_environment, final String state) { final int rc = Libsystemd.sd_notify(unset_environment, state); logger.trace("sd_notify({}, {}) returned [{}]", unset_environment, state, rc); diff --git a/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java b/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java index 7c99f28d396d9..8d0f7f2b4acf0 100644 --- a/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java +++ b/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java @@ -50,12 +50,14 @@ public class SystemdPluginTests extends ESTestCase { .thenReturn(extender); } + // TOOD: fix these tests... + /* public void testIsEnabled() { final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, Boolean.TRUE.toString()); plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); assertTrue(plugin.isEnabled()); assertNotNull(plugin.extender()); - } + }*/ public void testIsNotPackageDistribution() { final SystemdPlugin plugin = new SystemdPlugin(false, randomNonPackageBuildType, Boolean.TRUE.toString()); @@ -160,6 +162,10 @@ int sd_notify(final int unset_environment, final String state) { return rc; } + long getPid() { + return 12345; + } + }; plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); if (Boolean.TRUE.toString().equals(esSDNotify)) { From d5f315b600e963b321764dbb4fe956fda3fe9249 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 08:55:31 -0700 Subject: [PATCH 094/166] fix server cli to not kill subprocess when daemonized --- .../main/java/org/elasticsearch/server/cli/ServerCli.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 2cad42794ed0e..c8f02442a1e9f 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -94,9 +94,10 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); env = autoConfigureSecurity(terminal, options, processInfo, env, keystorePassword); - // start Elasticsearch - final Process process = createProcess(processInfo, env.configFile(), env.pluginsFile()); - this.process = process; // stash process so close can kill it (eg when SIGINT or SIGTERM is sent) + // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process + this.process = createProcess(processInfo, env.configFile(), env.pluginsFile()); + final Process process = this.process; // avoid volatile read locally, we only set it once above + logger.info("ES PID: " + process.pid()); final ErrorPumpThread errorPump = new ErrorPumpThread(terminal, process.getErrorStream()); errorPump.start(); sendArgs(options, keystorePassword, env, process.getOutputStream()); @@ -110,6 +111,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // if we are daemonized and we got the all-clear signal, we can exit cleanly if (errorPump.ready && options.has(daemonizeOption)) { + this.process = null; // clear the process handle, we don't want to shut it down now that we are started closeStreams(process); return; } From 9da910b6d21303d25f7deac6de9f1beb33f54024 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 10:17:52 -0700 Subject: [PATCH 095/166] implement fake pid --- .../java/org/elasticsearch/server/cli/ServerCliTests.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index e5538bdf8bc0e..37e29a07f42c3 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -353,6 +353,11 @@ public InputStream getErrorStream() { return processStderr; } + @Override + public long pid() { + return 12345; + } + @Override public int waitFor() throws InterruptedException { try { From 889e8bb52a0f05e191a8c2b15dc500e9fa01fb62 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 11:10:45 -0700 Subject: [PATCH 096/166] add debugging for auto config errors --- .../src/main/java/org/elasticsearch/server/cli/ServerCli.java | 1 + .../test/java/org/elasticsearch/server/cli/ServerCliTests.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index c8f02442a1e9f..74e75fedde2ee 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -174,6 +174,7 @@ private Environment autoConfigureSecurity( try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { autoConfigNode.execute(autoConfigTerminal, options, env, processInfo); } catch (UserException e) { + logger.error("GOT USER EXCEPTION from auto config", e); boolean okCode = switch (e.exitCode) { // these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual // eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 37e29a07f42c3..96eaf8c671381 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -234,6 +234,8 @@ public void testAutoConfigErrorPropagated() throws Exception { assertAutoConfigError(ExitCodes.IO_ERROR, ExitCodes.IO_ERROR); terminal.reset(); assertAutoConfigError(ExitCodes.CONFIG, ExitCodes.CONFIG, "--enrollment-token", "mytoken"); + terminal.reset(); + assertAutoConfigError(ExitCodes.DATA_ERROR, ExitCodes.DATA_ERROR, "--enrollment-token", "bogus"); } public void testAutoConfigOkErrors() throws Exception { From 1bbcf34397678300644cfcb3f8135d509ee1c0d2 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 11:49:05 -0700 Subject: [PATCH 097/166] more debug for windows --- .../org/elasticsearch/server/cli/ServerCli.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 74e75fedde2ee..d1d929b49443d 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -37,6 +37,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; @@ -91,17 +92,21 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce } // setup security + terminal.errorPrintln("Configuring security"); final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); env = autoConfigureSecurity(terminal, options, processInfo, env, keystorePassword); + terminal.errorPrintln("Starting process"); // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process this.process = createProcess(processInfo, env.configFile(), env.pluginsFile()); final Process process = this.process; // avoid volatile read locally, we only set it once above logger.info("ES PID: " + process.pid()); + terminal.errorPrintln("Starting error pump"); final ErrorPumpThread errorPump = new ErrorPumpThread(terminal, process.getErrorStream()); errorPump.start(); sendArgs(options, keystorePassword, env, process.getOutputStream()); + terminal.errorPrintln("Waiting for error pump"); // Read from stderr until we get a signal back that ES is either ready or it had an error. // If we are running in the foreground, this pump will never exit. errorPump.join(); @@ -240,15 +245,16 @@ private Process createProcess(ProcessInfo processInfo, Path configDir, Path plug } static class ErrorPumpThread extends Thread { - private final Terminal terminal; private final BufferedReader reader; + private final PrintWriter writer; private volatile boolean ready = false; private volatile String userExceptionMsg; private volatile IOException ioFailure; private ErrorPumpThread(Terminal terminal, InputStream err) { - this.terminal = terminal; + super("server-cli error pump"); this.reader = new BufferedReader(new InputStreamReader(err, StandardCharsets.UTF_8)); + this.writer = terminal.getErrorWriter(); } @Override @@ -268,13 +274,15 @@ public void run() { return; } else { logger.error("Got error line: " + line); - terminal.getErrorWriter().println(line); + writer.println(line); + writer.flush(); } } } catch (IOException e) { logger.error("Got io exception in pump", e); ioFailure = e; } + writer.flush(); } } From 189d6324b10c49779453c2cad19183ff550f7e92 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 12:52:41 -0700 Subject: [PATCH 098/166] improve stderr flush --- .../java/org/elasticsearch/launcher/CliToolLauncher.java | 5 ++++- .../main/java/org/elasticsearch/server/cli/ServerCli.java | 1 - libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java index fcb4d26fcf9cf..32002359d84ad 100644 --- a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java +++ b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java @@ -56,7 +56,10 @@ public static void main(String[] args) throws Exception { String libs = pinfo.sysprops().getOrDefault("cli.libs", ""); Command command = CliToolProvider.load(toolname, libs).create(); - exit(command.main(args, Terminal.DEFAULT, pinfo)); + Terminal terminal = Terminal.DEFAULT; + int exitCode = command.main(args, Terminal.DEFAULT, pinfo); + terminal.flush(); + exit(exitCode); } // package private for tests diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index d1d929b49443d..78c874e266b31 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -275,7 +275,6 @@ public void run() { } else { logger.error("Got error line: " + line); writer.println(line); - writer.flush(); } } } catch (IOException e) { diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java index 95b74566d8406..153b4cb00ab3d 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java @@ -42,7 +42,7 @@ public abstract class Terminal { @SuppressForbidden(reason = "Writer for System.err") private static PrintWriter newErrorWriter() { - return new PrintWriter(System.err); + return new PrintWriter(System.err, true); } /** Defines the available verbosity levels of messages to be printed. */ From 40875955b56cb3b9714e425c0a5decec2ab230b1 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 13:18:50 -0700 Subject: [PATCH 099/166] fix unit tests --- .../elasticsearch/server/cli/ServerCli.java | 4 ---- .../server/cli/ServerCliTests.java | 24 ++++++++++--------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 78c874e266b31..4fd4f03f79713 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -92,21 +92,17 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce } // setup security - terminal.errorPrintln("Configuring security"); final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); env = autoConfigureSecurity(terminal, options, processInfo, env, keystorePassword); - terminal.errorPrintln("Starting process"); // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process this.process = createProcess(processInfo, env.configFile(), env.pluginsFile()); final Process process = this.process; // avoid volatile read locally, we only set it once above logger.info("ES PID: " + process.pid()); - terminal.errorPrintln("Starting error pump"); final ErrorPumpThread errorPump = new ErrorPumpThread(terminal, process.getErrorStream()); errorPump.start(); sendArgs(options, keystorePassword, env, process.getOutputStream()); - terminal.errorPrintln("Waiting for error pump"); // Read from stderr until we get a signal back that ES is either ready or it had an error. // If we are running in the foreground, this pump will never exit. errorPump.join(); diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 96eaf8c671381..ecea29e7940a1 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -50,6 +50,7 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.anything; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.hasItem; @@ -72,14 +73,14 @@ public static void cleanupExecutor() { } private void assertOk(String... args) throws Exception { - assertOkWithOutput(emptyString(), args); + assertOkWithOutput(emptyString(), emptyString(), args); } - private void assertOkWithOutput(Matcher matcher, String... args) throws Exception { + private void assertOkWithOutput(Matcher outMatcher, Matcher errMatcher, String... args) throws Exception { int status = executeMain(args); assertThat(status, equalTo(ExitCodes.OK)); - assertThat(terminal.getErrorOutput(), emptyString()); - assertThat(terminal.getOutput(), matcher); + assertThat(terminal.getErrorOutput(), errMatcher); + assertThat(terminal.getOutput(), outMatcher); } private void assertUsage(Matcher matcher, String... args) throws Exception { @@ -119,9 +120,9 @@ public void testVersion() throws Exception { containsString("JVM: " + JvmInfo.jvmInfo().version()) ); terminal.reset(); - assertOkWithOutput(versionOutput, "-V"); + assertOkWithOutput(versionOutput, emptyString(), "-V"); terminal.reset(); - assertOkWithOutput(versionOutput, "--version"); + assertOkWithOutput(versionOutput, emptyString(), "--version"); } public void testPositionalArgs() throws Exception { @@ -219,7 +220,7 @@ public void testAutoConfigEnrollment() throws Exception { public void testAutoConfig() throws Exception { autoConfigCallback = (t, options, env, processInfo) -> { t.println("message from auto config"); }; - assertOkWithOutput(containsString("message from auto config")); + assertOkWithOutput(containsString("message from auto config"), emptyString()); } public void assertAutoConfigError(int autoConfigExitCode, int expectedMainExitCode, String... args) throws Exception { @@ -246,12 +247,13 @@ public void testAutoConfigOkErrors() throws Exception { public void assertKeystorePassword(String password) throws Exception { terminal.reset(); - if (password != null && password.isEmpty() == false) { + boolean hasPassword = password != null && password.isEmpty() == false; + if (hasPassword) { terminal.addSecretInput(password); } Path configDir = esHomeDir.resolve("config"); Files.createDirectories(configDir); - if (password != null) { + if (hasPassword) { try (KeyStoreWrapper keystore = KeyStoreWrapper.create()) { keystore.save(configDir, password.toCharArray(), false); } @@ -259,10 +261,10 @@ public void assertKeystorePassword(String password) throws Exception { String expectedPassword = password == null ? "" : password; mainCallback = (args, stdout, stderr, exitCode) -> { assertThat(args.keystorePassword().toString(), equalTo(expectedPassword)); }; autoConfigCallback = (t, options, env, processInfo) -> { - char[] gotPassword = t.readSecret("keystore password"); + char[] gotPassword = t.readSecret(""); assertThat(gotPassword, equalTo(expectedPassword.toCharArray())); }; - assertOk(); + assertOkWithOutput(emptyString(), hasPassword ? containsString("Enter password") : emptyString()); } public void testKeystorePassword() throws Exception { From 60e6840d20eff14cee05c89fb6f481928d9191a0 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 15:28:19 -0700 Subject: [PATCH 100/166] improve windows cli --- .../server/cli/ServerCliTests.java | 20 +------ .../tools/windows-service-cli/build.gradle | 2 + .../service}/ProcrunCommand.java | 26 ++++---- .../service}/WindowsServiceCli.java | 60 ++++++++++--------- .../service}/WindowsServiceCliProvider.java | 2 +- .../org.elasticsearch.cli.CliToolProvider | 2 +- .../service/WindowsServiceCliTest.java | 55 +++++++++++++++++ .../packaging/test/WindowsServiceTests.java | 23 ------- .../elasticsearch/cli/CommandTestCase.java | 22 +++++++ 9 files changed, 128 insertions(+), 84 deletions(-) rename distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/{windows_service => windows/service}/ProcrunCommand.java (80%) rename distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/{windows_service => windows/service}/WindowsServiceCli.java (77%) rename distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/{windows_service => windows/service}/WindowsServiceCliProvider.java (94%) create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTest.java diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index ecea29e7940a1..b8f3d73d78c3e 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -50,7 +50,6 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.anything; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.hasItem; @@ -72,23 +71,10 @@ public static void cleanupExecutor() { mockProcessExecutor.shutdown(); } - private void assertOk(String... args) throws Exception { - assertOkWithOutput(emptyString(), emptyString(), args); - } - - private void assertOkWithOutput(Matcher outMatcher, Matcher errMatcher, String... args) throws Exception { - int status = executeMain(args); - assertThat(status, equalTo(ExitCodes.OK)); - assertThat(terminal.getErrorOutput(), errMatcher); - assertThat(terminal.getOutput(), outMatcher); - } - - private void assertUsage(Matcher matcher, String... args) throws Exception { - terminal.reset(); + @Override + protected void assertUsage(Matcher matcher, String... args) throws Exception { mainCallback = FAIL_MAIN; - int status = executeMain(args); - assertThat(status, equalTo(ExitCodes.USAGE)); - assertThat(terminal.getErrorOutput(), matcher); + super.assertUsage(matcher, args); } private void assertMutuallyExclusiveOptions(String... args) throws Exception { diff --git a/distribution/tools/windows-service-cli/build.gradle b/distribution/tools/windows-service-cli/build.gradle index ad158ec05961b..d1304a5aaeed0 100644 --- a/distribution/tools/windows-service-cli/build.gradle +++ b/distribution/tools/windows-service-cli/build.gradle @@ -3,4 +3,6 @@ apply plugin: 'elasticsearch.java' dependencies { compileOnly project(":server") compileOnly project(":libs:elasticsearch-cli") + + testImplementation project(":test:framework") } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java similarity index 80% rename from distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java index cd377e2cdd98c..32247ec2936a2 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.windows_service; +package org.elasticsearch.windows.service; import joptsimple.OptionSet; @@ -18,7 +18,6 @@ import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -28,15 +27,10 @@ * Base command for interacting with Apache procrun executable. */ abstract class ProcrunCommand extends Command { - private final Path procrun; private final String cmd; protected ProcrunCommand(String desc, String cmd) { super(desc); - this.procrun = Paths.get("").resolve("bin").resolve(getExecutable()).toAbsolutePath(); - if (Files.exists(procrun) == false) { - throw new IllegalStateException("Missing procrun exe: " + procrun); - } this.cmd = cmd; } @@ -46,17 +40,19 @@ protected String getExecutable() { @Override protected void execute(Terminal terminal, OptionSet options, ProcessInfo processInfo) throws Exception { - Map env = System.getenv(); - Path esHome = Paths.get("").toAbsolutePath(); // TODO: this should be passed through execute - String serviceId = getServiceId(options, env); - preExecute(terminal, serviceId); + Path procrun = processInfo.workingDir().resolve("bin").resolve(getExecutable()).toAbsolutePath(); + if (Files.exists(procrun) == false) { + throw new IllegalStateException("Missing procrun exe: " + procrun); + } + String serviceId = getServiceId(options, processInfo.envVars()); + preExecute(terminal, processInfo, serviceId); List procrunCmd = new ArrayList<>(); procrunCmd.add(procrun.toString()); procrunCmd.add(cmd); procrunCmd.add(serviceId); - procrunCmd.add(getLogArgs(serviceId, esHome, env)); - procrunCmd.add(getAdditionalArgs(serviceId, esHome, env)); + procrunCmd.add(getLogArgs(serviceId, processInfo.workingDir(), processInfo.envVars())); + procrunCmd.add(getAdditionalArgs(serviceId, processInfo)); ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/C", String.join(" ", procrunCmd).trim()); processBuilder.inheritIO(); @@ -96,11 +92,11 @@ private String getLogArgs(String serviceId, Path esHome, Map env return String.format(Locale.ROOT, logArgsFormat, logsDir, serviceId); } - protected String getAdditionalArgs(String serviceId, Path esHome, Map env) { + protected String getAdditionalArgs(String serviceId, ProcessInfo processInfo) { return ""; } - protected void preExecute(Terminal terminal, String serviceId) throws UserException {} + protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException {} protected abstract String getSuccessMessage(String serviceId); diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java similarity index 77% rename from distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 4f594df5e987d..0f2d0f80e2338 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -6,14 +6,16 @@ * Side Public License, v 1. */ -package org.elasticsearch.windows_service; +package org.elasticsearch.windows.service; import org.elasticsearch.Version; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.MultiCommand; +import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; +import org.elasticsearch.core.SuppressForbidden; import java.nio.file.Files; import java.nio.file.Path; @@ -25,58 +27,47 @@ class WindowsServiceCli extends MultiCommand { private static final Command installCommand = new ProcrunCommand("Install Elasticsearch as a Windows Service", "install") { - private final Path javaHome; - private final Path javaDll; - { - javaHome = Paths.get(System.getProperty("java.home")); - Path dll = javaHome.resolve("jre/bin/server/jvm.dll"); - if (Files.exists(dll) == false) { - dll = javaHome.resolve("bin/server/jvm.dll"); - } - javaDll = dll; - } - @Override - protected String getAdditionalArgs(String serviceId, Path esHome, Map env) { + protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { List args = new ArrayList<>(); - addArg(args, "--Startup", env.getOrDefault("ES_START_TYPE", "manual")); - addArg(args, "--StopTimeout", env.getOrDefault("ES_STOP_TIMEOUT", "0")); + addArg(args, "--Startup", pinfo.envVars().getOrDefault("ES_START_TYPE", "manual")); + addArg(args, "--StopTimeout", pinfo.envVars().getOrDefault("ES_STOP_TIMEOUT", "0")); addArg(args, "--StartClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StartMethod", "main"); addArg(args, "++StartParams", "--quiet"); addArg(args, "--StopClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StopMethod", "close"); - addArg(args, "--Classpath", System.getProperty("java.class.path")); + addArg(args, "--Classpath", pinfo.sysprops().get("java.class.path")); addArg(args, "--JvmMs", "4m"); addArg(args, "--JvmMx", "64m"); - addArg(args, "--JvmOptions", getJvmOptions()); + addArg(args, "--JvmOptions", getJvmOptions(pinfo.sysprops())); addArg(args, "--PidFile", "%s.pid".formatted(serviceId)); addArg( args, "--DisplayName", - env.getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch %s (%s)".formatted(Version.CURRENT, serviceId)) + pinfo.envVars().getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch %s (%s)".formatted(Version.CURRENT, serviceId)) ); addArg( args, "--Description", - env.getOrDefault("SERVICE_DESCRIPTION", "Elasticsearch ES_VERSION Windows Service - https://elastic.co") + pinfo.envVars().getOrDefault("SERVICE_DESCRIPTION", "Elasticsearch ES_VERSION Windows Service - https://elastic.co") ); - addArg(args, "--Jvm", javaDll.toString()); + addArg(args, "--Jvm", getJvmDll(getJavaHome(pinfo.sysprops())).toString()); addArg(args, "--StartMode", "jvm"); - addArg(args, "--StartPath", esHome.toString()); + addArg(args, "--StartPath", pinfo.workingDir().toString()); addArg(args, "++Environment", "LAUNCHER_TOOLNAME=server"); addArg(args, "++Environment", "LAUNCHER_LIBS=lib/tools/server-cli"); - String serviceUsername = env.get("SERVICE_USERNAME"); + String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); if (serviceUsername != null) { - String servicePassword = env.get("SERVICE_PASSWORD"); + String servicePassword = pinfo.envVars().get("SERVICE_PASSWORD"); if (servicePassword != null) { addArg(args, "--ServiceUser", serviceUsername); addArg(args, "--ServicePassword", servicePassword); } // else WHY ISN'T THIS AN ERROR? username provided but no password... } - String serviceParams = env.get("SERVICE_PARAMS"); + String serviceParams = pinfo.envVars().get("SERVICE_PARAMS"); if (serviceParams != null) { args.add(serviceParams); } @@ -92,21 +83,36 @@ private static void addArg(List args, String arg, String value) { args.add(value); } - private static String getJvmOptions() { + @SuppressForbidden(reason = "get java home path to pass through") + private static Path getJavaHome(Map sysprops) { + return Paths.get(sysprops.get("java.home")); + } + + private static Path getJvmDll(Path javaHome) { + Path dll = javaHome.resolve("jre/bin/server/jvm.dll"); + if (Files.exists(dll) == false) { + dll = javaHome.resolve("bin/server/jvm.dll"); + } + return dll; + } + + private static String getJvmOptions(Map sysprops) { List jvmOptions = new ArrayList<>(); jvmOptions.add("-XX:+UseSerialGC"); // passthrough these properties for (var prop : List.of("es.path.home", "es.path.conf", "es.distribution.type")) { - jvmOptions.add("-D%s=%s".formatted(prop, System.getProperty(prop))); + jvmOptions.add("-D%s=%s".formatted(prop, sysprops.get(prop))); } return String.join(";", jvmOptions); } @Override - protected void preExecute(Terminal terminal, String serviceId) throws UserException { + protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException { + Path javaHome = getJavaHome(pinfo.sysprops()); terminal.println("Installing service : %s".formatted(serviceId)); terminal.println("Using ES_JAVA_HOME : %s".formatted(javaHome.toString())); + Path javaDll = getJvmDll(javaHome); if (Files.exists(javaDll) == false) { throw new UserException( ExitCodes.CONFIG, diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCliProvider.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCliProvider.java similarity index 94% rename from distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCliProvider.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCliProvider.java index 2df2a1d81a6fc..1b1f8d08c9805 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows_service/WindowsServiceCliProvider.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCliProvider.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.windows_service; +package org.elasticsearch.windows.service; import org.elasticsearch.cli.CliToolProvider; import org.elasticsearch.cli.Command; diff --git a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider index d99aea9958202..137387fb5a75d 100644 --- a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider +++ b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider @@ -1 +1 @@ -org.elasticsearch.windows_service.WindowsServiceCliProvider +WindowsServiceCliProvider diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTest.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTest.java new file mode 100644 index 0000000000000..8d760b74e2aa0 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTest.java @@ -0,0 +1,55 @@ +/* + * 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.windows.service; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.CommandTestCase; +import org.junit.Before; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.hamcrest.Matchers.containsString; + +public class WindowsServiceCliTest extends CommandTestCase { + + Path javaHome; + Path binDir; + Path serviceExe; + Path mgrExe; + + @Override + protected Command newCommand() { + return new WindowsServiceCli() { + @Override + public boolean addShutdownHook() { + return false; + } + }; + } + + @Before + public void setupMockJvm() throws Exception { + javaHome = createTempDir(); + Path javaBin = javaHome.resolve("bin"); + sysprops.put("java.home", javaHome.toString()); + binDir = esHomeDir.resolve("bin"); + Files.createDirectories(binDir); + serviceExe = binDir.resolve("elasticsearch-service-x64.exe"); + Files.createFile(serviceExe); + mgrExe = binDir.resolve("elasticsearch-service-mgr.exe"); + Files.createFile(mgrExe); + } + + public void testMissingExe() throws Exception { + Files.delete(serviceExe); + var e = expectThrows(IllegalStateException.class, () -> executeMain("install")); + assertThat(e.getMessage(), containsString("Missing procrun exe")); + } +} diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index d3e8974e0e8f0..f79f079c1a6b6 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -97,29 +97,12 @@ public void test10InstallArchive() throws Exception { serviceScript = installation.bin("elasticsearch-service.bat").toString(); } - public void test11InstallServiceExeMissing() throws IOException { - Path serviceExe = installation.bin("elasticsearch-service-x64.exe"); - Path tmpServiceExe = serviceExe.getParent().resolve(serviceExe.getFileName() + ".tmp"); - Files.move(serviceExe, tmpServiceExe); - Result result = sh.runIgnoreExitCode(serviceScript + " install"); - assertThat(result.exitCode(), equalTo(1)); - assertThat(result.stdout(), containsString("elasticsearch-service-x64.exe was not found...")); - Files.move(tmpServiceExe, serviceExe); - } - public void test12InstallService() { sh.run(serviceScript + " install"); assertService(DEFAULT_ID, "Stopped", DEFAULT_DISPLAY_NAME); sh.run(serviceScript + " remove"); } - public void test14InstallBadJavaHome() throws IOException { - sh.getEnv().put("ES_JAVA_HOME", "doesnotexist"); - Result result = sh.runIgnoreExitCode(serviceScript + " install"); - assertThat(result.exitCode(), equalTo(1)); - assertThat(result.stderr(), containsString("could not find java in ES_JAVA_HOME")); - } - public void test15RemoveNotInstalled() { Result result = assertFailure(serviceScript + " remove", 1); assertThat(result.stdout(), containsString("Failed removing '" + DEFAULT_ID + "' service")); @@ -244,12 +227,6 @@ public void test60Manager() throws IOException { Files.move(tmpServiceMgr, serviceMgr); } - public void test70UnknownCommand() { - Result result = sh.runIgnoreExitCode(serviceScript + " bogus"); - assertThat(result.exitCode(), equalTo(1)); - assertThat(result.stdout(), containsString("Unknown option \"bogus\"")); - } - public void test80JavaOptsInEnvVar() throws Exception { sh.getEnv().put("ES_JAVA_OPTS", "-Xmx2g -Xms2g"); sh.run(serviceScript + " install"); diff --git a/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java b/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java index 930455160b072..99d66c7206f31 100644 --- a/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/cli/CommandTestCase.java @@ -9,12 +9,16 @@ package org.elasticsearch.cli; import org.elasticsearch.test.ESTestCase; +import org.hamcrest.Matcher; import org.junit.Before; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.Matchers.emptyString; + /** * A base test case for cli tools. */ @@ -79,4 +83,22 @@ public String execute(Command command, String... args) throws Exception { command.mainWithoutErrorHandling(args, terminal, new ProcessInfo(sysprops, envVars, esHomeDir)); return terminal.getOutput(); } + + protected void assertOk(String... args) throws Exception { + assertOkWithOutput(emptyString(), emptyString(), args); + } + + protected void assertOkWithOutput(Matcher outMatcher, Matcher errMatcher, String... args) throws Exception { + int status = executeMain(args); + assertThat(status, equalTo(ExitCodes.OK)); + assertThat(terminal.getErrorOutput(), errMatcher); + assertThat(terminal.getOutput(), outMatcher); + } + + protected void assertUsage(Matcher matcher, String... args) throws Exception { + terminal.reset(); + int status = executeMain(args); + assertThat(status, equalTo(ExitCodes.USAGE)); + assertThat(terminal.getErrorOutput(), matcher); + } } From 0d375cab1a1b18cd6ceb250dab5b5fa2618ad31a Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 15:32:08 -0700 Subject: [PATCH 101/166] fix env vars to server script --- .../org/elasticsearch/windows/service/WindowsServiceCli.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 0f2d0f80e2338..d3daaa271e01d 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -55,8 +55,8 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--Jvm", getJvmDll(getJavaHome(pinfo.sysprops())).toString()); addArg(args, "--StartMode", "jvm"); addArg(args, "--StartPath", pinfo.workingDir().toString()); - addArg(args, "++Environment", "LAUNCHER_TOOLNAME=server"); - addArg(args, "++Environment", "LAUNCHER_LIBS=lib/tools/server-cli"); + addArg(args, "++Environment", "CLI_TOOL=server"); + addArg(args, "++Environment", "CLI_LIBS=lib/tools/server-cli"); String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); if (serviceUsername != null) { From 7b20225fffa6cef1d6fa98031d6e857e10f61c2b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 16:46:53 -0700 Subject: [PATCH 102/166] fix passthrough of enrollment token to auto config --- .../java/org/elasticsearch/server/cli/ServerCli.java | 10 +++++++++- .../org/elasticsearch/server/cli/ServerCliTests.java | 4 ++++ .../src/main/java/org/elasticsearch/cli/Command.java | 6 +++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 4fd4f03f79713..98838a89f39cf 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -165,15 +165,23 @@ private Environment autoConfigureSecurity( throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); } + logger.info("Running auto config"); String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; Command cmd = loadTool("auto-configure-node", autoConfigLibs); assert cmd instanceof EnvironmentAwareCommand; @SuppressWarnings("raw") var autoConfigNode = (EnvironmentAwareCommand) cmd; + final String[] autoConfigArgs; + if (options.has(enrollmentTokenOption)) { + autoConfigArgs = new String[] { "--enrollment-token", options.valueOf(enrollmentTokenOption) }; + } else { + autoConfigArgs = new String[0]; + } + OptionSet autoConfigOptions = autoConfigNode.parseOptions(autoConfigArgs); boolean changed = true; try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { - autoConfigNode.execute(autoConfigTerminal, options, env, processInfo); + autoConfigNode.execute(autoConfigTerminal, autoConfigOptions, env, processInfo); } catch (UserException e) { logger.error("GOT USER EXCEPTION from auto config", e); boolean okCode = switch (e.exitCode) { diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index b8f3d73d78c3e..85ac6c628fea7 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -9,6 +9,7 @@ package org.elasticsearch.server.cli; import joptsimple.OptionSet; +import joptsimple.OptionSpec; import org.elasticsearch.Build; import org.elasticsearch.bootstrap.ServerArgs; @@ -280,8 +281,11 @@ public void resetCommand() { } private class MockAutoConfigCli extends EnvironmentAwareCommand { + private final OptionSpec enrollmentTokenOption; + MockAutoConfigCli() { super("mock auto config tool"); + enrollmentTokenOption = parser.accepts("enrollment-token").withRequiredArg(); } @Override diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java index 6f03ff11b8bd7..ac983c1b50ae8 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java @@ -90,7 +90,7 @@ public final int main(String[] args, Terminal terminal, ProcessInfo processInfo) * Executes the command, but all errors are thrown. */ protected void mainWithoutErrorHandling(String[] args, Terminal terminal, ProcessInfo processInfo) throws Exception { - final OptionSet options = parser.parse(args); + final OptionSet options = parseOptions(args); if (options.has(helpOption)) { printHelp(terminal, false); @@ -108,6 +108,10 @@ protected void mainWithoutErrorHandling(String[] args, Terminal terminal, Proces execute(terminal, options, processInfo); } + public OptionSet parseOptions(String[] args) { + return parser.parse(args); + } + /** Prints a help message for the command to the terminal. */ private void printHelp(Terminal terminal, boolean toStdError) throws IOException { if (toStdError) { From 38f716eebd8c41fc0330e0736c4c02120acdfad9 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 16:48:44 -0700 Subject: [PATCH 103/166] fix windows service provider --- .../META-INF/services/org.elasticsearch.cli.CliToolProvider | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider index 137387fb5a75d..23f39460f9f37 100644 --- a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider +++ b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider @@ -1 +1 @@ -WindowsServiceCliProvider +org.elasticsearch.windows.service.WindowsServiceCliProvider From 04091e4d6d565b743db532b4c855d363493a838e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 16:52:37 -0700 Subject: [PATCH 104/166] fix test name --- .../{WindowsServiceCliTest.java => WindowsServiceCliTests.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/{WindowsServiceCliTest.java => WindowsServiceCliTests.java} (96%) diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTest.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java similarity index 96% rename from distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTest.java rename to distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java index 8d760b74e2aa0..fd3b589e13fdf 100644 --- a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTest.java +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java @@ -17,7 +17,7 @@ import static org.hamcrest.Matchers.containsString; -public class WindowsServiceCliTest extends CommandTestCase { +public class WindowsServiceCliTests extends CommandTestCase { Path javaHome; Path binDir; From 8e088cad9582b73348e1906b7ea3577a088e2a49 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 18:25:42 -0700 Subject: [PATCH 105/166] remove display name from assertions will add it later to cli unit tests --- .../windows/service/WindowsServiceCliTests.java | 2 ++ .../packaging/test/WindowsServiceTests.java | 12 +++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java index fd3b589e13fdf..0d2b4235ddca4 100644 --- a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java @@ -52,4 +52,6 @@ public void testMissingExe() throws Exception { var e = expectThrows(IllegalStateException.class, () -> executeMain("install")); assertThat(e.getMessage(), containsString("Missing procrun exe")); } + + // TODO: add tests for display name (and custom display name) } diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index f79f079c1a6b6..705f6e32f133e 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -48,11 +48,10 @@ public void uninstallService() { sh.runIgnoreExitCode(serviceScript + " remove"); } - private void assertService(String id, String status, String displayName) { + private void assertService(String id, String status) { Result result = sh.run("Get-Service " + id + " | Format-List -Property Name, Status, DisplayName"); assertThat(result.stdout(), containsString("Name : " + id)); assertThat(result.stdout(), containsString("Status : " + status)); - assertThat(result.stdout(), containsString("DisplayName : " + displayName)); } // runs the service command, dumping all log files on failure @@ -99,7 +98,7 @@ public void test10InstallArchive() throws Exception { public void test12InstallService() { sh.run(serviceScript + " install"); - assertService(DEFAULT_ID, "Stopped", DEFAULT_DISPLAY_NAME); + assertService(DEFAULT_ID, "Stopped"); sh.run(serviceScript + " remove"); } @@ -125,10 +124,9 @@ public void test16InstallSpecialCharactersInJdkPath() throws IOException { public void test20CustomizeServiceId() { String serviceId = "my-es-service"; - String displayName = DEFAULT_DISPLAY_NAME.replace(DEFAULT_ID, serviceId); sh.getEnv().put("SERVICE_ID", serviceId); sh.run(serviceScript + " install"); - assertService(serviceId, "Stopped", displayName); + assertService(serviceId, "Stopped"); sh.run(serviceScript + " remove"); } @@ -136,7 +134,7 @@ public void test21CustomizeServiceDisplayName() { String displayName = "my es service display name"; sh.getEnv().put("SERVICE_DISPLAY_NAME", displayName); sh.run(serviceScript + " install"); - assertService(DEFAULT_ID, "Stopped", displayName); + assertService(DEFAULT_ID, "Stopped"); sh.run(serviceScript + " remove"); } @@ -146,7 +144,7 @@ public void assertStartedAndStop() throws Exception { runElasticsearchTests(); assertCommand(serviceScript + " stop"); - assertService(DEFAULT_ID, "Stopped", DEFAULT_DISPLAY_NAME); + assertService(DEFAULT_ID, "Stopped"); // the process is stopped async, and can become a zombie process, so we poll for the process actually being gone assertCommand( "$p = Get-Service -Name \"elasticsearch-service-x64\" -ErrorAction SilentlyContinue;" From 8b1258a841df6ad12bdf5e4b30aa89d7b095a893 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 19:19:36 -0700 Subject: [PATCH 106/166] tweak windows service test failure expectation --- .../elasticsearch/packaging/test/WindowsServiceTests.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 705f6e32f133e..f200befe88ae6 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -104,7 +104,7 @@ public void test12InstallService() { public void test15RemoveNotInstalled() { Result result = assertFailure(serviceScript + " remove", 1); - assertThat(result.stdout(), containsString("Failed removing '" + DEFAULT_ID + "' service")); + assertThat(result.stderr(), containsString("Failed removing '" + DEFAULT_ID + "' service")); } public void test16InstallSpecialCharactersInJdkPath() throws IOException { @@ -182,8 +182,8 @@ public void test30StartStop() throws Exception { public void test31StartNotInstalled() throws IOException { Result result = sh.runIgnoreExitCode(serviceScript + " start"); - assertThat(result.stdout(), result.exitCode(), equalTo(1)); - assertThat(result.stdout(), containsString("Failed starting '" + DEFAULT_ID + "' service")); + assertThat(result.stderr(), result.exitCode(), equalTo(1)); + assertThat(result.stderr(), containsString("Failed starting '" + DEFAULT_ID + "' service")); } public void test32StopNotStarted() throws IOException { @@ -221,7 +221,7 @@ public void test60Manager() throws IOException { Files.write(fakeServiceMgr, Arrays.asList("echo \"Fake Service Manager GUI Failure\"", "exit 1")); result = sh.runIgnoreExitCode(serviceScript + " manager"); TestCase.assertEquals(1, result.exitCode()); - TestCase.assertTrue(result.stdout(), result.stdout().contains("Fake Service Manager GUI Failure")); + assertThat(result.stderr(), containsString("Fake Service Manager GUI Failure")); Files.move(tmpServiceMgr, serviceMgr); } From 937b0a8b2d9bf3b115e61cfc8b90f8131894bbf2 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 19:29:40 -0700 Subject: [PATCH 107/166] add packaging test debug --- .../packaging/test/PackageTests.java | 6 +++- .../packaging/test/PackagingTestCase.java | 35 +++++++++++-------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java index 5c38fa36a6640..912b4f0748a8b 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java @@ -166,7 +166,11 @@ public void test50Remove() throws Exception { remove(distribution()); // removing must stop the service - assertThat(sh.run("ps aux").stdout(), not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); + String psOutput = sh.run("ps aux").stdout(); + if (psOutput.contains("org.elasticsearch.bootstrap.Elasticsearch")) { + dumpDebug(); + } + assertThat(psOutput, not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); if (isSystemd()) { diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java index 8f56e47174fdd..36b0bc58308d3 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java @@ -257,6 +257,26 @@ protected static void cleanup() throws Exception { cleanEverything(); } + /** + * Prints all available information about the installed Elasticsearch process, including pid, logs and stdout/stderr. + */ + protected void dumpDebug() { + if (Files.exists(installation.home.resolve("elasticsearch.pid"))) { + String pid = FileUtils.slurp(installation.home.resolve("elasticsearch.pid")).trim(); + logger.info("Dumping jstack of elasticsearch processb ({}) that failed to start", pid); + sh.runIgnoreExitCode("jstack " + pid); + } + if (Files.exists(installation.logs.resolve("elasticsearch.log"))) { + logger.warn("Elasticsearch log:\n" + FileUtils.slurpAllLogs(installation.logs, "elasticsearch.log", "*.log.gz")); + } + if (Files.exists(installation.logs.resolve("output.out"))) { + logger.warn("Stdout:\n" + FileUtils.slurpTxtorGz(installation.logs.resolve("output.out"))); + } + if (Files.exists(installation.logs.resolve("output.err"))) { + logger.warn("Stderr:\n" + FileUtils.slurpTxtorGz(installation.logs.resolve("output.err"))); + } + } + /** * Starts and stops elasticsearch, and performs assertions while it is running. */ @@ -264,20 +284,7 @@ protected void assertWhileRunning(Platforms.PlatformAction assertions) throws Ex try { awaitElasticsearchStartup(runElasticsearchStartCommand(null, true, false)); } catch (Exception e) { - if (Files.exists(installation.home.resolve("elasticsearch.pid"))) { - String pid = FileUtils.slurp(installation.home.resolve("elasticsearch.pid")).trim(); - logger.info("Dumping jstack of elasticsearch processb ({}) that failed to start", pid); - sh.runIgnoreExitCode("jstack " + pid); - } - if (Files.exists(installation.logs.resolve("elasticsearch.log"))) { - logger.warn("Elasticsearch log:\n" + FileUtils.slurpAllLogs(installation.logs, "elasticsearch.log", "*.log.gz")); - } - if (Files.exists(installation.logs.resolve("output.out"))) { - logger.warn("Stdout:\n" + FileUtils.slurpTxtorGz(installation.logs.resolve("output.out"))); - } - if (Files.exists(installation.logs.resolve("output.err"))) { - logger.warn("Stderr:\n" + FileUtils.slurpTxtorGz(installation.logs.resolve("output.err"))); - } + dumpDebug(); throw e; } From 38d47120c3ea317d0c7afef8b37d29596251500e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 20:20:54 -0700 Subject: [PATCH 108/166] more debugging for package tests --- .../main/java/org/elasticsearch/server/cli/ServerCli.java | 5 ++++- .../java/org/elasticsearch/packaging/test/PackageTests.java | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 98838a89f39cf..5fb81054469a8 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -46,6 +46,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.concurrent.CompletableFuture; import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; @@ -277,7 +278,6 @@ public void run() { ready = true; return; } else { - logger.error("Got error line: " + line); writer.println(line); } } @@ -292,6 +292,7 @@ public void run() { @Override public void close() { if (process != null) { + logger.info("Terminating subprocess"); process.destroy(); // TODO: we should wait adn then forcibly kill, but after how long? this is configurable in eg systemd, @@ -300,6 +301,8 @@ public void close() { // exiting after it the process is terminated while (true) { try { + logger.info("Waiting for subprocess"); + // TODO: check the exit code!! process.waitFor(); break; } catch (InterruptedException ignore) { diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java index 912b4f0748a8b..f0746b9625dc7 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java @@ -168,6 +168,7 @@ public void test50Remove() throws Exception { // removing must stop the service String psOutput = sh.run("ps aux").stdout(); if (psOutput.contains("org.elasticsearch.bootstrap.Elasticsearch")) { + logger.error("Elasticsearch process still alive after uninstall"); dumpDebug(); } assertThat(psOutput, not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); From 25b9a40f87328822ef13eacba6b09cc8f7243e84 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 20:22:26 -0700 Subject: [PATCH 109/166] another windows test --- .../org/elasticsearch/packaging/test/WindowsServiceTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index f200befe88ae6..93e7d275e1b30 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -115,7 +115,7 @@ public void test16InstallSpecialCharactersInJdkPath() throws IOException { try { mv(installation.bundledJdk, relocatedJdk); Result result = sh.run(serviceScript + " install"); - assertThat(result.stdout(), containsString("The service 'elasticsearch-service-x64' has been installed.")); + assertThat(result.stdout(), containsString("The service 'elasticsearch-service-x64' has been installed")); } finally { sh.runIgnoreExitCode(serviceScript + " remove"); mv(relocatedJdk, installation.bundledJdk); From 242e11191439e201968520924b42cdf413826c09 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 20:52:48 -0700 Subject: [PATCH 110/166] spotless --- .../src/main/java/org/elasticsearch/server/cli/ServerCli.java | 1 - 1 file changed, 1 deletion(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 5fb81054469a8..72db65a938fe2 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -46,7 +46,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.concurrent.CompletableFuture; import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; From ce91516da576160ecddc1da5eac10a84ec03155e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 21:40:49 -0700 Subject: [PATCH 111/166] more packaging test debug --- .../org/elasticsearch/packaging/test/PackageTests.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java index f0746b9625dc7..cfe48f337a493 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java @@ -163,15 +163,11 @@ public void test50Remove() throws Exception { // add fake bin directory as if a plugin was installed Files.createDirectories(installation.bin.resolve("myplugin")); + dumpDebug(); remove(distribution()); // removing must stop the service - String psOutput = sh.run("ps aux").stdout(); - if (psOutput.contains("org.elasticsearch.bootstrap.Elasticsearch")) { - logger.error("Elasticsearch process still alive after uninstall"); - dumpDebug(); - } - assertThat(psOutput, not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); + assertThat(sh.run("ps aux").stdout(), not(containsString("org.elasticsearch.bootstrap.Elasticsearch"))); if (isSystemd()) { From 67cb0d97c576c68649e630f3ec273346e02c8865 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 22:25:49 -0700 Subject: [PATCH 112/166] pass verbosity through to keystore terminal --- .../server/cli/KeystorePasswordTerminal.java | 1 + .../elasticsearch/server/cli/ServerCliTests.java | 13 ++++++++++--- .../main/java/org/elasticsearch/cli/Terminal.java | 6 +++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java index 6efd540222311..bc059ff7d01bc 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -23,6 +23,7 @@ protected KeystorePasswordTerminal(Terminal delegate, SecureString password) { super(delegate.getReader(), delegate.getWriter(), delegate.getErrorWriter()); this.delegate = delegate; this.password = password; + setVerbosity(delegate.getVerbosity()); } @Override diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 85ac6c628fea7..c4d28d4c7e075 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -18,6 +18,7 @@ import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.Terminal.Verbosity; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.cli.EnvironmentAwareCommand; import org.elasticsearch.common.io.stream.InputStreamStreamInput; @@ -48,6 +49,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import static org.elasticsearch.cli.Terminal.Verbosity.VERBOSE; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.allOf; @@ -205,9 +207,14 @@ public void testAutoConfigEnrollment() throws Exception { assertOk("--enrollment-token", "mydummytoken"); } - public void testAutoConfig() throws Exception { - autoConfigCallback = (t, options, env, processInfo) -> { t.println("message from auto config"); }; - assertOkWithOutput(containsString("message from auto config"), emptyString()); + public void testAutoConfigLogging() throws Exception { + autoConfigCallback = (t, options, env, processInfo) -> { + t.println("message from auto config"); + t.errorPrintln("error message"); + t.errorPrintln(Verbosity.VERBOSE, "verbose error"); + }; + assertOkWithOutput(containsString("message from auto config"), + allOf(containsString("error message"), containsString("verbose error")), "-v"); } public void assertAutoConfigError(int autoConfigExitCode, int expectedMainExitCode, String... args) throws Exception { diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java index 153b4cb00ab3d..caa2011409d05 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Terminal.java @@ -79,6 +79,10 @@ public void setVerbosity(Verbosity verbosity) { this.currentVerbosity = verbosity; } + public Verbosity getVerbosity() { + return currentVerbosity; + } + private char[] read(String prompt) { errWriter.print(prompt); // prompts should go to standard error to avoid mixing with list output final char[] line = readLineToCharArray(reader); @@ -297,7 +301,7 @@ static class SystemTerminal extends Terminal { // at the end of each character based read, so that switching to using getInputStream() returns binary data // right after the last character based input (newline) new InputStreamReader(System.in, Charset.defaultCharset()), - new PrintWriter(System.out), + new PrintWriter(System.out, true), ERROR_WRITER ); } From 89539b2e6a0b6c0035a330eb88a8f9dadb530d24 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 3 May 2022 22:42:50 -0700 Subject: [PATCH 113/166] attempting better windows service logs --- .../windows/service/ProcrunCommand.java | 2 +- .../packaging/test/WindowsServiceTests.java | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java index 32247ec2936a2..5e532b5799abc 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java @@ -88,7 +88,7 @@ private String getLogArgs(String serviceId, Path esHome, Map env if (logsDir == null || logsDir.isBlank()) { logsDir = esHome.resolve("logs").toString(); } - String logArgsFormat = "--LogPath \"%s\" --LogPrefix \"%s\" --StdError auto --StdOutput auto"; + String logArgsFormat = "--LogPath \"%s\" --LogPrefix \"%s\" --StdError auto --StdOutput auto --LogLevel Debug"; return String.format(Locale.ROOT, logArgsFormat, logsDir, serviceId); } diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 93e7d275e1b30..4d3d54e61c4a4 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -19,6 +19,7 @@ import org.junit.BeforeClass; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; @@ -72,7 +73,20 @@ private void assertExit(Result result, String script, int exitCode) { logger.error("---- Unexpected exit code (expected " + exitCode + ", got " + result.exitCode() + ") for script: " + script); logger.error(result); logger.error("Dumping log files\n"); - Result logs = sh.run( + dumpDebug(); + try (var logsDir = Files.list(installation.logs)) { + for (Path logFile : logsDir.toList()) { + String filename = logFile.getFileName().toString(); + if (filename.startsWith("elasticsearch-service-x64")) { + logger.warn(filename + "\n" + FileUtils.slurp(logFile)); + } + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + + /*Result logs = sh.run( "$files = Get-ChildItem \"" + installation.logs + "\\elasticsearch.log\"; " @@ -82,7 +96,7 @@ private void assertExit(Result result, String script, int exitCode) { + " Get-Content \"$file\" " + "}" ); - logger.error(logs.stdout()); + logger.error(logs.stdout());*/ fail(); } else { logger.info("\nscript: " + script + "\nstdout: " + result.stdout() + "\nstderr: " + result.stderr()); From b31e532feed70eeb6dc80c1c61b5a08be3144e49 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 05:59:51 -0700 Subject: [PATCH 114/166] pass tool info as sysprops, not env for windows service --- .../org/elasticsearch/windows/service/WindowsServiceCli.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index d3daaa271e01d..8defed67acdc0 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -55,8 +55,8 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--Jvm", getJvmDll(getJavaHome(pinfo.sysprops())).toString()); addArg(args, "--StartMode", "jvm"); addArg(args, "--StartPath", pinfo.workingDir().toString()); - addArg(args, "++Environment", "CLI_TOOL=server"); - addArg(args, "++Environment", "CLI_LIBS=lib/tools/server-cli"); + addArg(args, "++JvmOptions", "-Dcli.name=server"); + addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli"); String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); if (serviceUsername != null) { From 4953f1e2e4572f5a4c5508aff7b53f1f2ebc62d0 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 06:22:44 -0700 Subject: [PATCH 115/166] more debug for packaging tests --- .../java/org/elasticsearch/packaging/test/PackageTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java index cfe48f337a493..9310755d648f4 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java @@ -163,7 +163,7 @@ public void test50Remove() throws Exception { // add fake bin directory as if a plugin was installed Files.createDirectories(installation.bin.resolve("myplugin")); - dumpDebug(); + logger.info(sh.run("journalctl -u elasticsearch.service").stdout()); remove(distribution()); // removing must stop the service From 20e2dc2f26742842cc7bb390ed395a5b767aaa51 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 06:23:10 -0700 Subject: [PATCH 116/166] spotless --- .../java/org/elasticsearch/server/cli/ServerCliTests.java | 8 +++++--- .../elasticsearch/packaging/test/WindowsServiceTests.java | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index c4d28d4c7e075..8f7532cc597c9 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -49,7 +49,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static org.elasticsearch.cli.Terminal.Verbosity.VERBOSE; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.allOf; @@ -213,8 +212,11 @@ public void testAutoConfigLogging() throws Exception { t.errorPrintln("error message"); t.errorPrintln(Verbosity.VERBOSE, "verbose error"); }; - assertOkWithOutput(containsString("message from auto config"), - allOf(containsString("error message"), containsString("verbose error")), "-v"); + assertOkWithOutput( + containsString("message from auto config"), + allOf(containsString("error message"), containsString("verbose error")), + "-v" + ); } public void assertAutoConfigError(int autoConfigExitCode, int expectedMainExitCode, String... args) throws Exception { diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 4d3d54e61c4a4..c189ad6f2939b 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -85,7 +85,6 @@ private void assertExit(Result result, String script, int exitCode) { throw new UncheckedIOException(e); } - /*Result logs = sh.run( "$files = Get-ChildItem \"" + installation.logs From 74634beea47c1c7a7d87b73dfca8392a84244f64 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 11:30:14 -0700 Subject: [PATCH 117/166] cleanup debug logging --- .../main/java/org/elasticsearch/server/cli/ServerCli.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 72db65a938fe2..77aa85acfb9dc 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -112,6 +112,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // if we are daemonized and we got the all-clear signal, we can exit cleanly if (errorPump.ready && options.has(daemonizeOption)) { + logger.info("Subprocess is ready and we are daemonized, exiting..."); this.process = null; // clear the process handle, we don't want to shut it down now that we are started closeStreams(process); return; @@ -120,6 +121,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // We pass any ES error code through UserException. If the message was set, // then it is a real UserException, otherwise it is just the error code and a null message. int code = process.waitFor(); + logger.info("Subprocess exited [" + code + "]"); if (code != ExitCodes.OK) { throw new UserException(code, errorPump.userExceptionMsg); } @@ -145,10 +147,8 @@ private void printVersion(Terminal terminal) { private SecureString getKeystorePassword(Path configDir, Terminal terminal) throws IOException { try (KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir)) { if (keystore != null && keystore.hasPassword()) { - logger.info("keystore has password"); return new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT)); } else { - logger.info("keystore does not have password"); return new SecureString(new char[0]); } } @@ -165,7 +165,6 @@ private Environment autoConfigureSecurity( throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); } - logger.info("Running auto config"); String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; Command cmd = loadTool("auto-configure-node", autoConfigLibs); assert cmd instanceof EnvironmentAwareCommand; @@ -183,7 +182,6 @@ private Environment autoConfigureSecurity( try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { autoConfigNode.execute(autoConfigTerminal, autoConfigOptions, env, processInfo); } catch (UserException e) { - logger.error("GOT USER EXCEPTION from auto config", e); boolean okCode = switch (e.exitCode) { // these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual // eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it From 584da86b9cf580f83d01aacc4776bb9eae012d21 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 12:53:31 -0700 Subject: [PATCH 118/166] try leaving server cli as main process with systemd --- .../main/java/org/elasticsearch/systemd/SystemdPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java index 8d60e328b464f..1cde9d1a67043 100644 --- a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java +++ b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java @@ -115,10 +115,10 @@ public Collection createComponents( } void setMainPid() { - final int rc = sd_notify(0, "MAINPID=" + getPid()); + /*final int rc = sd_notify(0, "MAINPID=" + getPid()); if (rc < 0) { logger.warn("setting main pid via sd_notify failed with [{}]", rc); - } + }*/ } long getPid() { From a56f5fe3404746aeab270497529e00186353938d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 12:59:38 -0700 Subject: [PATCH 119/166] add debug for windows service --- .../org/elasticsearch/windows/service/ProcrunCommand.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java index 5e532b5799abc..ccb1ad0ce2039 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java @@ -10,6 +10,8 @@ import joptsimple.OptionSet; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.ProcessInfo; @@ -27,6 +29,8 @@ * Base command for interacting with Apache procrun executable. */ abstract class ProcrunCommand extends Command { + private static final Logger logger = LogManager.getLogger(ProcrunCommand.class); + private final String cmd; protected ProcrunCommand(String desc, String cmd) { @@ -55,6 +59,7 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process procrunCmd.add(getAdditionalArgs(serviceId, processInfo)); ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/C", String.join(" ", procrunCmd).trim()); + logger.info("Running procrun: " + String.join(" ", processBuilder.command())); processBuilder.inheritIO(); Process process = processBuilder.start(); int ret = process.waitFor(); From 39d13e6d000ef18d9b3173624873fa597582c163 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 14:32:52 -0700 Subject: [PATCH 120/166] add stop mode for windows service --- .../org/elasticsearch/windows/service/WindowsServiceCli.java | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 8defed67acdc0..d04f781373f97 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -54,6 +54,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { ); addArg(args, "--Jvm", getJvmDll(getJavaHome(pinfo.sysprops())).toString()); addArg(args, "--StartMode", "jvm"); + addArg(args, "--StopMode", "jvm"); addArg(args, "--StartPath", pinfo.workingDir().toString()); addArg(args, "++JvmOptions", "-Dcli.name=server"); addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli"); From 5614994405cb089822bc41b5da07222710f78c8e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 15:39:03 -0700 Subject: [PATCH 121/166] log more when cleanup fails --- .../packaging/test/PackagingTestCase.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java index 36b0bc58308d3..b110e6a8667b0 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackagingTestCase.java @@ -209,7 +209,15 @@ public void teardown() throws Exception { String prefix = this.getClass().getSimpleName() + "." + testNameRule.getMethodName(); if (Files.exists(logFile)) { Path newFile = installation.logs.resolve(prefix + ".elasticsearch.log"); - FileUtils.mv(logFile, newFile); + try { + FileUtils.mv(logFile, newFile); + } catch (Exception e) { + // There was a problem moving the log file. This usually means Windows wackiness + // where something still has the file open. Here we dump the log files to see + // if ES is still running. + dumpDebug(); + throw e; + } } for (Path rotatedLogFile : FileUtils.lsGlob(installation.logs, "elasticsearch*.tar.gz")) { Path newRotatedLogFile = installation.logs.resolve(prefix + "." + rotatedLogFile.getFileName()); From 6451adf2862a8876b203083a281a1547bde31920 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 18:16:32 -0700 Subject: [PATCH 122/166] put command in the static, doh! --- .../main/java/org/elasticsearch/launcher/CliToolLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java index 32002359d84ad..c6fc3f77e9112 100644 --- a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java +++ b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java @@ -55,7 +55,7 @@ public static void main(String[] args) throws Exception { String toolname = getToolName(pinfo.sysprops()); String libs = pinfo.sysprops().getOrDefault("cli.libs", ""); - Command command = CliToolProvider.load(toolname, libs).create(); + command = CliToolProvider.load(toolname, libs).create(); Terminal terminal = Terminal.DEFAULT; int exitCode = command.main(args, Terminal.DEFAULT, pinfo); terminal.flush(); From 0a977151b2f38ca04ee1b9464d40acfbd3c6308c Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 18:25:03 -0700 Subject: [PATCH 123/166] rework rework service cli command passing --- .../windows/service/ProcrunCommand.java | 11 ++++++++--- .../windows/service/WindowsServiceCli.java | 15 ++++++++++----- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java index ccb1ad0ce2039..2e8526b898c1b 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java @@ -53,9 +53,10 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process List procrunCmd = new ArrayList<>(); procrunCmd.add(procrun.toString()); - procrunCmd.add(cmd); - procrunCmd.add(serviceId); - procrunCmd.add(getLogArgs(serviceId, processInfo.workingDir(), processInfo.envVars())); + procrunCmd.add("//" + cmd + "//" +serviceId); + if (includeLogArgs()) { + procrunCmd.add(getLogArgs(serviceId, processInfo.workingDir(), processInfo.envVars())); + } procrunCmd.add(getAdditionalArgs(serviceId, processInfo)); ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/C", String.join(" ", procrunCmd).trim()); @@ -101,6 +102,10 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo processInfo) { return ""; } + protected boolean includeLogArgs() { + return true; + } + protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException {} protected abstract String getSuccessMessage(String serviceId); diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index d04f781373f97..15b2c3be59a70 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -26,7 +26,7 @@ class WindowsServiceCli extends MultiCommand { - private static final Command installCommand = new ProcrunCommand("Install Elasticsearch as a Windows Service", "install") { + private static final Command installCommand = new ProcrunCommand("Install Elasticsearch as a Windows Service", "IS") { @Override protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { List args = new ArrayList<>(); @@ -136,7 +136,7 @@ protected String getFailureMessage(String serviceId) { } }; - private static final Command removeCommand = new ProcrunCommand("Remove the Elasticsearch Windows Service", "delete") { + private static final Command removeCommand = new ProcrunCommand("Remove the Elasticsearch Windows Service", "DS") { @Override protected String getSuccessMessage(String serviceId) { return "The service '%s' has been removed".formatted(serviceId); @@ -148,7 +148,7 @@ protected String getFailureMessage(String serviceId) { } }; - private static final Command startCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service", "start") { + private static final Command startCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service", "ES") { @Override protected String getSuccessMessage(String serviceId) { return "The service '%s' has been started".formatted(serviceId); @@ -160,7 +160,7 @@ protected String getFailureMessage(String serviceId) { } }; - private static final Command stopCommand = new ProcrunCommand("Stops the Elasticsearch Windows Service", "stop") { + private static final Command stopCommand = new ProcrunCommand("Stops the Elasticsearch Windows Service", "SS") { @Override protected String getSuccessMessage(String serviceId) { return "The service '%s' has been stopped".formatted(serviceId); @@ -172,12 +172,17 @@ protected String getFailureMessage(String serviceId) { } }; - private static final Command managerCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service manager", "manage") { + private static final Command managerCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service manager", "ES") { @Override protected String getExecutable() { return "elasticsearch-service-mgr.exe"; } + @Override + protected boolean includeLogArgs() { + return false; + } + @Override protected String getSuccessMessage(String serviceId) { return "Successfully started service manager for '%s'".formatted(serviceId); From 7069fccd60a577c733f861fe2e6ad691843e6770 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 18:36:19 -0700 Subject: [PATCH 124/166] spotless --- .../java/org/elasticsearch/windows/service/ProcrunCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java index 2e8526b898c1b..962de987fdae9 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java @@ -53,7 +53,7 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process List procrunCmd = new ArrayList<>(); procrunCmd.add(procrun.toString()); - procrunCmd.add("//" + cmd + "//" +serviceId); + procrunCmd.add("//" + cmd + "//" + serviceId); if (includeLogArgs()) { procrunCmd.add(getLogArgs(serviceId, processInfo.workingDir(), processInfo.envVars())); } From 46306e63352f0c0492106a7a163e561034e55f39 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 4 May 2022 19:44:25 -0700 Subject: [PATCH 125/166] more windows debugging --- .../windows/service/ProcrunCommand.java | 2 +- .../packaging/test/WindowsServiceTests.java | 38 ++++++++----------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java index 962de987fdae9..7fc7247e63ce7 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java @@ -53,7 +53,7 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process List procrunCmd = new ArrayList<>(); procrunCmd.add(procrun.toString()); - procrunCmd.add("//" + cmd + "//" + serviceId); + procrunCmd.add("//%s/%s".formatted(cmd, serviceId)); if (includeLogArgs()) { procrunCmd.add(getLogArgs(serviceId, processInfo.workingDir(), processInfo.envVars())); } diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index c189ad6f2939b..2a394bef9c604 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -68,34 +68,27 @@ private Result assertFailure(String script, int exitCode) { return result; } + private void dumpServiceLogs() { + logger.warn("\n"); + try (var logsDir = Files.list(installation.logs)) { + for (Path logFile : logsDir.toList()) { + String filename = logFile.getFileName().toString(); + if (filename.startsWith("elasticsearch-service-x64")) { + logger.warn(filename + "\n" + FileUtils.slurp(logFile)); + } + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + private void assertExit(Result result, String script, int exitCode) { if (result.exitCode() != exitCode) { logger.error("---- Unexpected exit code (expected " + exitCode + ", got " + result.exitCode() + ") for script: " + script); logger.error(result); logger.error("Dumping log files\n"); dumpDebug(); - try (var logsDir = Files.list(installation.logs)) { - for (Path logFile : logsDir.toList()) { - String filename = logFile.getFileName().toString(); - if (filename.startsWith("elasticsearch-service-x64")) { - logger.warn(filename + "\n" + FileUtils.slurp(logFile)); - } - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - /*Result logs = sh.run( - "$files = Get-ChildItem \"" - + installation.logs - + "\\elasticsearch.log\"; " - + "Write-Output $files; " - + "foreach ($file in $files) {" - + " Write-Output \"$file\"; " - + " Get-Content \"$file\" " - + "}" - ); - logger.error(logs.stdout());*/ + dumpServiceLogs(); fail(); } else { logger.info("\nscript: " + script + "\nstdout: " + result.stdout() + "\nstderr: " + result.stderr()); @@ -196,6 +189,7 @@ public void test30StartStop() throws Exception { public void test31StartNotInstalled() throws IOException { Result result = sh.runIgnoreExitCode(serviceScript + " start"); assertThat(result.stderr(), result.exitCode(), equalTo(1)); + dumpServiceLogs(); assertThat(result.stderr(), containsString("Failed starting '" + DEFAULT_ID + "' service")); } From 3e776f834c539eb2e189639d05e0f15fb29bffe5 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 5 May 2022 06:38:44 -0700 Subject: [PATCH 126/166] add systemd comment --- .../packages/src/common/systemd/elasticsearch.service | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distribution/packages/src/common/systemd/elasticsearch.service b/distribution/packages/src/common/systemd/elasticsearch.service index 309f79aa85200..4da74970ab42f 100644 --- a/distribution/packages/src/common/systemd/elasticsearch.service +++ b/distribution/packages/src/common/systemd/elasticsearch.service @@ -6,9 +6,9 @@ After=network-online.target [Service] Type=notify -# the main process is actually a child of that created by system, do allow it access -# to send back the sd_notify message for readiness -# TODO: why doesn't exec work? +# the elasticsearch process currently sends the notifications back to systemd +# and for some reason exec does not work (even though it is a child). We should change +# this notify access back to main (the default), see https://github.com/elastic/elasticsearch/issues/86475 NotifyAccess=all RuntimeDirectory=elasticsearch PrivateTmp=true From 0f83215bf6e91b0ea8c7ed84a76f3194472e0bd7 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 5 May 2022 06:40:12 -0700 Subject: [PATCH 127/166] remove systemd changes --- .../org/elasticsearch/systemd/SystemdPlugin.java | 16 ---------------- .../systemd/SystemdPluginTests.java | 8 +------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java index 1cde9d1a67043..a299f75b48e3d 100644 --- a/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java +++ b/modules/systemd/src/main/java/org/elasticsearch/systemd/SystemdPlugin.java @@ -28,7 +28,6 @@ import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.xcontent.NamedXContentRegistry; -import java.lang.management.ManagementFactory; import java.util.Collection; import java.util.List; import java.util.function.Supplier; @@ -68,9 +67,6 @@ public SystemdPlugin() { throw new RuntimeException("ES_SD_NOTIFY set to unexpected value [" + esSDNotify + "]"); } enabled = Boolean.TRUE.toString().equals(esSDNotify); - if (enabled) { - setMainPid(); - } } private final SetOnce extender = new SetOnce<>(); @@ -110,21 +106,9 @@ public Collection createComponents( logger.warn("extending startup timeout via sd_notify failed with [{}]", rc); } }, TimeValue.timeValueSeconds(15), ThreadPool.Names.SAME)); - return List.of(); } - void setMainPid() { - /*final int rc = sd_notify(0, "MAINPID=" + getPid()); - if (rc < 0) { - logger.warn("setting main pid via sd_notify failed with [{}]", rc); - }*/ - } - - long getPid() { - return ManagementFactory.getRuntimeMXBean().getPid(); - } - int sd_notify(@SuppressWarnings("SameParameterValue") final int unset_environment, final String state) { final int rc = Libsystemd.sd_notify(unset_environment, state); logger.trace("sd_notify({}, {}) returned [{}]", unset_environment, state, rc); diff --git a/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java b/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java index 8d0f7f2b4acf0..7c99f28d396d9 100644 --- a/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java +++ b/modules/systemd/src/test/java/org/elasticsearch/systemd/SystemdPluginTests.java @@ -50,14 +50,12 @@ public class SystemdPluginTests extends ESTestCase { .thenReturn(extender); } - // TOOD: fix these tests... - /* public void testIsEnabled() { final SystemdPlugin plugin = new SystemdPlugin(false, randomPackageBuildType, Boolean.TRUE.toString()); plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); assertTrue(plugin.isEnabled()); assertNotNull(plugin.extender()); - }*/ + } public void testIsNotPackageDistribution() { final SystemdPlugin plugin = new SystemdPlugin(false, randomNonPackageBuildType, Boolean.TRUE.toString()); @@ -162,10 +160,6 @@ int sd_notify(final int unset_environment, final String state) { return rc; } - long getPid() { - return 12345; - } - }; plugin.createComponents(null, null, threadPool, null, null, null, null, null, null, null, null); if (Boolean.TRUE.toString().equals(esSDNotify)) { From 55552ab3ed6f342392bbfa6baa8727de0b174afb Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 5 May 2022 16:59:29 -0700 Subject: [PATCH 128/166] pass hostname through to windows service --- .../org/elasticsearch/windows/service/WindowsServiceCli.java | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 15b2c3be59a70..548e91a6f6122 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -58,6 +58,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StartPath", pinfo.workingDir().toString()); addArg(args, "++JvmOptions", "-Dcli.name=server"); addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli"); + addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))) String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); if (serviceUsername != null) { From fbeea852a33b801597aef5e9f7158281e33529a8 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 5 May 2022 17:17:37 -0700 Subject: [PATCH 129/166] fix compile --- .../java/org/elasticsearch/launcher/CliToolLauncher.java | 3 --- .../java/org/elasticsearch/server/cli/ServerCliTests.java | 5 ----- .../elasticsearch/windows/service/WindowsServiceCli.java | 2 +- .../windows/service/WindowsServiceCliTests.java | 7 +------ 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java index a0861f306cf3e..0387c3f7430ac 100644 --- a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java +++ b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java @@ -17,10 +17,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.core.SuppressForbidden; -<<<<<<< HEAD -======= import java.io.Closeable; ->>>>>>> master import java.io.IOException; import java.util.Map; diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 8f7532cc597c9..17820ab793942 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -409,11 +409,6 @@ protected Command loadTool(String toolname, String libs) { assertThat(libs, equalTo("modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli")); return AUTO_CONFIG_CLI; } - - @Override - public boolean addShutdownHook() { - return false; - } }; } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 548e91a6f6122..478314b125229 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -58,7 +58,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StartPath", pinfo.workingDir().toString()); addArg(args, "++JvmOptions", "-Dcli.name=server"); addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli"); - addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))) + addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))); String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); if (serviceUsername != null) { diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java index 0d2b4235ddca4..b76e68bcc6a95 100644 --- a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java @@ -26,12 +26,7 @@ public class WindowsServiceCliTests extends CommandTestCase { @Override protected Command newCommand() { - return new WindowsServiceCli() { - @Override - public boolean addShutdownHook() { - return false; - } - }; + return new WindowsServiceCli(); } @Before From 77745ae77c41610ae5793ef31141c8592534a2a8 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 5 May 2022 19:58:23 -0700 Subject: [PATCH 130/166] daemonize in the windows service this allows the main method called by procrun to end, but the process is still alive and the jvm continues --- .../java/org/elasticsearch/launcher/CliToolLauncher.java | 5 ++++- .../org/elasticsearch/windows/service/WindowsServiceCli.java | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java index 0387c3f7430ac..a287674f12515 100644 --- a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java +++ b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.Level; import org.elasticsearch.cli.CliToolProvider; import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; import org.elasticsearch.common.logging.LogConfigurator; @@ -62,7 +63,9 @@ public static void main(String[] args) throws Exception { int exitCode = command.main(args, terminal, pinfo); terminal.flush(); // make sure nothing is left in buffers - exit(exitCode); + if (exitCode != ExitCodes.OK) { + exit(exitCode); + } } // package private for tests diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 478314b125229..4c0d2589d7f13 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -34,6 +34,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StopTimeout", pinfo.envVars().getOrDefault("ES_STOP_TIMEOUT", "0")); addArg(args, "--StartClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StartMethod", "main"); + addArg(args, "++StartParams", "--daemonize"); addArg(args, "++StartParams", "--quiet"); addArg(args, "--StopClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StopMethod", "close"); From 7fdbeb351dbd3f17e628cdf384d0bfee43cf2f02 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 5 May 2022 21:05:40 -0700 Subject: [PATCH 131/166] don't pass quiet to windows service since we are now daemonizing --- .../org/elasticsearch/windows/service/WindowsServiceCli.java | 1 - 1 file changed, 1 deletion(-) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 4c0d2589d7f13..154e2b8c96c86 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -35,7 +35,6 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StartClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StartMethod", "main"); addArg(args, "++StartParams", "--daemonize"); - addArg(args, "++StartParams", "--quiet"); addArg(args, "--StopClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StopMethod", "close"); addArg(args, "--Classpath", pinfo.sysprops().get("java.class.path")); From ae5457f7a7303c18943bdd80b98ce84845891747 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 6 May 2022 06:13:21 -0700 Subject: [PATCH 132/166] dump service logs on move failure --- .../elasticsearch/packaging/test/WindowsServiceTests.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 2a394bef9c604..b0970f0a3e60c 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -68,6 +68,12 @@ private Result assertFailure(String script, int exitCode) { return result; } + @Override + protected void dumpDebug() { + super.dumpDebug(); + dumpServiceLogs(); + } + private void dumpServiceLogs() { logger.warn("\n"); try (var logsDir = Files.list(installation.logs)) { @@ -88,7 +94,6 @@ private void assertExit(Result result, String script, int exitCode) { logger.error(result); logger.error("Dumping log files\n"); dumpDebug(); - dumpServiceLogs(); fail(); } else { logger.info("\nscript: " + script + "\nstdout: " + result.stdout() + "\nstderr: " + result.stderr()); From d6ad750818921feb8ae62ea8b0aea88e355b3979 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 6 May 2022 08:59:53 -0700 Subject: [PATCH 133/166] don't clear process handle for windows --- .../main/java/org/elasticsearch/server/cli/ServerCli.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 77aa85acfb9dc..74885a4e5b800 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -113,8 +113,10 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // if we are daemonized and we got the all-clear signal, we can exit cleanly if (errorPump.ready && options.has(daemonizeOption)) { logger.info("Subprocess is ready and we are daemonized, exiting..."); - this.process = null; // clear the process handle, we don't want to shut it down now that we are started - closeStreams(process); + if (processInfo.envVars().get("os.name").startsWith("Windows") == false) { + this.process = null; // clear the process handle, we don't want to shut it down now that we are started + closeStreams(process); + } return; } From 142a94e44eaa4da9058e6bc91e14e77cbc91270a Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 6 May 2022 16:05:50 -0700 Subject: [PATCH 134/166] move logic into ServerProcess, very broken right now --- .../elasticsearch/server/cli/ServerCli.java | 229 ++------------ .../server/cli/ServerProcess.java | 298 ++++++++++++++++++ .../server/cli/WindowsServiceServer.java | 44 +++ .../bootstrap/BootstrapInfo.java | 6 +- 4 files changed, 366 insertions(+), 211 deletions(-) create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 74885a4e5b800..f4c7af36a210d 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -16,7 +16,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.elasticsearch.Build; -import org.elasticsearch.bootstrap.ServerArgs; import org.elasticsearch.cli.CliToolProvider; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; @@ -24,31 +23,18 @@ import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; import org.elasticsearch.common.cli.EnvironmentAwareCommand; -import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; -import org.elasticsearch.common.settings.KeyStoreWrapper; -import org.elasticsearch.common.settings.SecureString; -import org.elasticsearch.core.IOUtils; -import org.elasticsearch.core.PathUtils; import org.elasticsearch.env.Environment; import org.elasticsearch.monitor.jvm.JvmInfo; +import org.elasticsearch.server.cli.ServerProcess.Options; -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; +import java.util.function.Supplier; -import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; -import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; import static org.elasticsearch.server.cli.JvmOptionsParser.determineJvmOptions; class ServerCli extends EnvironmentAwareCommand { @@ -61,7 +47,7 @@ class ServerCli extends EnvironmentAwareCommand { private final OptionSpecBuilder quietOption; private final OptionSpec enrollmentTokenOption; - private volatile Process process; + private volatile ServerProcess server; // visible for testing ServerCli() { @@ -91,46 +77,27 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce return; } - // setup security - final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); - env = autoConfigureSecurity(terminal, options, processInfo, env, keystorePassword); - - // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process - this.process = createProcess(processInfo, env.configFile(), env.pluginsFile()); - final Process process = this.process; // avoid volatile read locally, we only set it once above - logger.info("ES PID: " + process.pid()); - final ErrorPumpThread errorPump = new ErrorPumpThread(terminal, process.getErrorStream()); - errorPump.start(); - sendArgs(options, keystorePassword, env, process.getOutputStream()); - - // Read from stderr until we get a signal back that ES is either ready or it had an error. - // If we are running in the foreground, this pump will never exit. - errorPump.join(); - if (errorPump.ioFailure != null) { - throw errorPump.ioFailure; + if (options.valuesOf(enrollmentTokenOption).size() > 1) { + throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); } + Options serverOptions = new Options( + options.has(daemonizeOption), + options.has(quietOption), + options.valueOf(pidfileOption), + options.valueOf(enrollmentTokenOption)); + + this.server = new ServerProcess(terminal, processInfo, serverOptions, env, () -> createEnv(options, processInfo)); // if we are daemonized and we got the all-clear signal, we can exit cleanly - if (errorPump.ready && options.has(daemonizeOption)) { + if (server.isReady() && options.has(daemonizeOption)) { logger.info("Subprocess is ready and we are daemonized, exiting..."); - if (processInfo.envVars().get("os.name").startsWith("Windows") == false) { - this.process = null; // clear the process handle, we don't want to shut it down now that we are started - closeStreams(process); - } + server.detach(); + this.server = null; // clear the handle, we don't want to shut it down now that we are started return; } - // We pass any ES error code through UserException. If the message was set, - // then it is a real UserException, otherwise it is just the error code and a null message. - int code = process.waitFor(); - logger.info("Subprocess exited [" + code + "]"); - if (code != ExitCodes.OK) { - throw new UserException(code, errorPump.userExceptionMsg); - } - } - - private void closeStreams(Process process) throws IOException { - IOUtils.close(process.getOutputStream(), process.getInputStream(), process.getErrorStream()); + // we are running in the foreground, so wait for the server to exit, or rethrow a startup error if we failed + server.waitFor(); } private void printVersion(Terminal terminal) { @@ -146,168 +113,12 @@ private void printVersion(Terminal terminal) { terminal.println(versionOutput); } - private SecureString getKeystorePassword(Path configDir, Terminal terminal) throws IOException { - try (KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir)) { - if (keystore != null && keystore.hasPassword()) { - return new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT)); - } else { - return new SecureString(new char[0]); - } - } - } - - private Environment autoConfigureSecurity( - Terminal terminal, - OptionSet options, - ProcessInfo processInfo, - Environment env, - SecureString keystorePassword - ) throws Exception { - if (options.valuesOf(enrollmentTokenOption).size() > 1) { - throw new UserException(ExitCodes.USAGE, "Multiple --enrollment-token parameters are not allowed"); - } - - String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; - Command cmd = loadTool("auto-configure-node", autoConfigLibs); - assert cmd instanceof EnvironmentAwareCommand; - @SuppressWarnings("raw") - var autoConfigNode = (EnvironmentAwareCommand) cmd; - final String[] autoConfigArgs; - if (options.has(enrollmentTokenOption)) { - autoConfigArgs = new String[] { "--enrollment-token", options.valueOf(enrollmentTokenOption) }; - } else { - autoConfigArgs = new String[0]; - } - OptionSet autoConfigOptions = autoConfigNode.parseOptions(autoConfigArgs); - - boolean changed = true; - try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { - autoConfigNode.execute(autoConfigTerminal, autoConfigOptions, env, processInfo); - } catch (UserException e) { - boolean okCode = switch (e.exitCode) { - // these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual - // eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it - case ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP -> true; - default -> false; - }; - if (options.has(enrollmentTokenOption) == false && okCode) { - // we still want to print the error, just don't fail startup - terminal.errorPrintln(e.getMessage()); - changed = false; - } else { - throw e; - } - } - if (changed) { - // reload settings since auto security changed them - env = createEnv(options, processInfo); - } - return env; - - } - - private void sendArgs(OptionSet options, SecureString keystorePassword, Environment env, OutputStream processStdin) { - final boolean daemonize = options.has(daemonizeOption); - final boolean quiet = options.has(quietOption); - final Path pidFile = pidfileOption.value(options); - final var args = new ServerArgs(daemonize, quiet, pidFile, keystorePassword, env.settings(), env.configFile()); - - try (var out = new OutputStreamStreamOutput(processStdin)) { - args.writeTo(out); - } catch (IOException ignore) { - // A failure to write here means the process has problems, and it will die anyways. We let this fall through - // so the pump thread can complete, writing out the actual error. All we get here is the failure to write to - // the process pipe, which isn't helpful to print. - } - keystorePassword.close(); - } - - private Process createProcess(ProcessInfo processInfo, Path configDir, Path pluginsDir) throws Exception { - Map envVars = new HashMap<>(processInfo.envVars()); - Path tempDir = TempDirectory.setup(envVars); - List jvmOptions = getJvmOptions(configDir, pluginsDir, tempDir, envVars.get("ES_JAVA_OPTS")); - // jvmOptions.add("-Des.path.conf=" + env.configFile()); - jvmOptions.add("-Des.distribution.type=" + processInfo.sysprops().get("es.distribution.type")); - - Path esHome = processInfo.workingDir(); - Path javaHome = PathUtils.get(processInfo.sysprops().get("java.home")); - List command = new ArrayList<>(); - boolean isWindows = processInfo.sysprops().get("os.name").startsWith("Windows"); - command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); - command.addAll(jvmOptions); - command.add("-cp"); - // The '*' isn't allows by the windows filesystem, so we need to force it into the classpath after converting to a string. - // Thankfully this will all go away when switching to modules, which take the directory instead of a glob. - command.add(esHome.resolve("lib") + (isWindows ? "\\" : "/") + "*"); - command.add("org.elasticsearch.bootstrap.Elasticsearch"); - - var builder = new ProcessBuilder(command); - builder.environment().putAll(envVars); - builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); - - return startProcess(builder); - } - - static class ErrorPumpThread extends Thread { - private final BufferedReader reader; - private final PrintWriter writer; - private volatile boolean ready = false; - private volatile String userExceptionMsg; - private volatile IOException ioFailure; - - private ErrorPumpThread(Terminal terminal, InputStream err) { - super("server-cli error pump"); - this.reader = new BufferedReader(new InputStreamReader(err, StandardCharsets.UTF_8)); - this.writer = terminal.getErrorWriter(); - } - - @Override - public void run() { - try { - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { - userExceptionMsg = line.substring(1); - logger.error("Got user exception: " + userExceptionMsg); - } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { - // The server closes stderr right after this message, but for some unknown reason - // the pipe closing does not close this end of the pipe, so we must explicitly - // break out of this loop, or we will block forever on the next read. - logger.info("Got ready signal"); - ready = true; - return; - } else { - writer.println(line); - } - } - } catch (IOException e) { - logger.error("Got io exception in pump", e); - ioFailure = e; - } - writer.flush(); - } - } - @Override public void close() { - if (process != null) { - logger.info("Terminating subprocess"); - process.destroy(); + var server = this.server; // read volatile into local - // TODO: we should wait adn then forcibly kill, but after how long? this is configurable in eg systemd, - // but that is giving us. For systemd we should sd_notify that the main pid is different. For SIGINT - // we should have some default max time before sending SIGKILL? we should also maybe block on the main thread - // exiting after it the process is terminated - while (true) { - try { - logger.info("Waiting for subprocess"); - // TODO: check the exit code!! - process.waitFor(); - break; - } catch (InterruptedException ignore) { - // retry - } - } + if (server != null) { + server.stop(); } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java new file mode 100644 index 0000000000000..e3b9e7dd7b283 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -0,0 +1,298 @@ +/* + * 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.server.cli; + +import joptsimple.OptionSet; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.bootstrap.BootstrapInfo; +import org.elasticsearch.bootstrap.ServerArgs; +import org.elasticsearch.cli.CliToolProvider; +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.ProcessInfo; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; +import org.elasticsearch.common.cli.EnvironmentAwareCommand; +import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; +import org.elasticsearch.common.settings.KeyStoreWrapper; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.core.IOUtils; +import org.elasticsearch.core.PathUtils; +import org.elasticsearch.env.Environment; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; +import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; +import static org.elasticsearch.server.cli.JvmOptionsParser.determineJvmOptions; + +class ServerProcess { + + private static final Logger logger = LogManager.getLogger(ServerProcess.class); + + record Options(boolean daemonize, boolean quiet, Path pidFile, String enrollmentToken) {} + + interface EnvReloader { + Environment reload() throws UserException; + } + + private final Process jvmProcess; + private final ErrorPumpThread errorPump; + private final CountDownLatch readyOrDead = new CountDownLatch(1); + private volatile boolean ready; + private volatile String userExceptionMsg; + private volatile IOException ioFailure; + + ServerProcess(Terminal terminal, ProcessInfo processInfo, Options options, + Environment env, EnvReloader envReloader) throws Exception { + // setup security + final SecureString keystorePassword = getKeystorePassword(env.configFile(), terminal); + env = autoConfigureSecurity(terminal, processInfo, env, keystorePassword, options.enrollmentToken, envReloader); + + // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process + this.jvmProcess = createProcess(processInfo, env.configFile(), env.pluginsFile()); + this.errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream()); + errorPump.start(); + logger.info("ES PID: " + jvmProcess.pid()); + sendArgs(options, keystorePassword, env, jvmProcess.getOutputStream()); + + // Read from stderr until we get a signal back that ES is either ready or it had an error. + readyOrDead.await(); + if (ioFailure != null) { + throw ioFailure; + } + } + + boolean isReady() { + return ready; + } + + void detach() throws IOException { + try { + // the server will close its streams when we want to detach, so we wait to finish reading stderr + errorPump.join(); + } catch (InterruptedException e) { + // how can this happen? + } + IOUtils.close(jvmProcess.getOutputStream(), jvmProcess.getInputStream(), jvmProcess.getErrorStream()); + } + + void waitFor() throws UserException, InterruptedException { + // todo: should we catch interrupted and retry? + int exitCode = jvmProcess.waitFor(); + if (exitCode != ExitCodes.OK) { + throw new UserException(exitCode, userExceptionMsg); + } + } + + void stop() { + logger.info("Terminating subprocess"); + try { + OutputStream os = jvmProcess.getOutputStream(); + os.write(BootstrapInfo.SERVER_SHUTDOWN_MARKER); + os.flush(); + } catch (IOException e) { + // process is already effectively dead, fall through to wait for it, or should we SIGKILL? + } + + try { + errorPump.join(); + } catch (InterruptedException e) { + // what cases can this happen during shutdown? + } + //process.destroy(); + + // TODO: we should wait adn then forcibly kill, but after how long? this is configurable in eg systemd, + // but that is giving us. For systemd we should sd_notify that the main pid is different. For SIGINT + // we should have some default max time before sending SIGKILL? we should also maybe block on the main thread + // exiting after it the process is terminated + while (true) { + try { + logger.info("Waiting for subprocess"); + int exitCode = jvmProcess.waitFor(); + // TODO: what error conditions exist? if we got a SIGINT, SIGTERM, these are sent tot he child, so it should + // exit the same + break; + } catch (InterruptedException ignore) { + // retry + } + } + } + + private static SecureString getKeystorePassword(Path configDir, Terminal terminal) throws IOException { + try (KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir)) { + if (keystore != null && keystore.hasPassword()) { + return new SecureString(terminal.readSecret(KeyStoreWrapper.PROMPT)); + } else { + return new SecureString(new char[0]); + } + } + } + + private Environment autoConfigureSecurity( + Terminal terminal, + ProcessInfo processInfo, + Environment env, + SecureString keystorePassword, + String enrollmentToken, + EnvReloader envReloader + ) throws Exception { + + String autoConfigLibs = "modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli"; + Command cmd = loadTool("auto-configure-node", autoConfigLibs); + assert cmd instanceof EnvironmentAwareCommand; + @SuppressWarnings("raw") + var autoConfigNode = (EnvironmentAwareCommand) cmd; + final String[] autoConfigArgs; + if (enrollmentToken != null) { + autoConfigArgs = new String[] { "--enrollment-token", enrollmentToken }; + } else { + autoConfigArgs = new String[0]; + } + OptionSet autoConfigOptions = autoConfigNode.parseOptions(autoConfigArgs); + + boolean changed = true; + try (var autoConfigTerminal = new KeystorePasswordTerminal(terminal, keystorePassword.clone())) { + autoConfigNode.execute(autoConfigTerminal, autoConfigOptions, env, processInfo); + } catch (UserException e) { + boolean okCode = switch (e.exitCode) { + // these exit codes cover the cases where auto-conf cannot run but the node should NOT be prevented from starting as usual + // eg the node is restarted, is already configured in an incompatible way, or the file system permissions do not allow it + case ExitCodes.CANT_CREATE, ExitCodes.CONFIG, ExitCodes.NOOP -> true; + default -> false; + }; + if (enrollmentToken == null && okCode) { + // we still want to print the error, just don't fail startup + terminal.errorPrintln(e.getMessage()); + changed = false; + } else { + throw e; + } + } + if (changed) { + // reload settings since auto security changed them + env = envReloader.reload(); + } + return env; + + } + + private void sendArgs(Options options, SecureString keystorePassword, Environment env, OutputStream processStdin) { + final boolean daemonize = options.daemonize(); + final boolean quiet = options.quiet(); + final Path pidFile = options.pidFile(); + final var args = new ServerArgs(daemonize, quiet, pidFile, keystorePassword, env.settings(), env.configFile()); + + // DO NOT close the underlying process stdin, since we need to be able to write to it to signal exit + var out = new OutputStreamStreamOutput(processStdin); + try { + args.writeTo(out); + } catch (IOException ignore) { + // A failure to write here means the process has problems, and it will die anyways. We let this fall through + // so the pump thread can complete, writing out the actual error. All we get here is the failure to write to + // the process pipe, which isn't helpful to print. + } + keystorePassword.close(); + } + + private Process createProcess(ProcessInfo processInfo, Path configDir, Path pluginsDir) throws Exception { + Map envVars = new HashMap<>(processInfo.envVars()); + Path tempDir = TempDirectory.setup(envVars); + List jvmOptions = getJvmOptions(configDir, pluginsDir, tempDir, envVars.get("ES_JAVA_OPTS")); + // jvmOptions.add("-Des.path.conf=" + env.configFile()); + jvmOptions.add("-Des.distribution.type=" + processInfo.sysprops().get("es.distribution.type")); + + Path esHome = processInfo.workingDir(); + Path javaHome = PathUtils.get(processInfo.sysprops().get("java.home")); + List command = new ArrayList<>(); + boolean isWindows = processInfo.sysprops().get("os.name").startsWith("Windows"); + command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); + command.addAll(jvmOptions); + command.add("-cp"); + // The '*' isn't allows by the windows filesystem, so we need to force it into the classpath after converting to a string. + // Thankfully this will all go away when switching to modules, which take the directory instead of a glob. + command.add(esHome.resolve("lib") + (isWindows ? "\\" : "/") + "*"); + command.add("org.elasticsearch.bootstrap.Elasticsearch"); + + var builder = new ProcessBuilder(command); + builder.environment().putAll(envVars); + builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); + + return startProcess(builder); + } + + private class ErrorPumpThread extends Thread { + private final BufferedReader reader; + private final PrintWriter writer; + + private ErrorPumpThread(PrintWriter errOutput, InputStream errInput) { + super("server-cli error pump"); + this.reader = new BufferedReader(new InputStreamReader(errInput, StandardCharsets.UTF_8)); + this.writer = errOutput; + } + + @Override + public void run() { + try { + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { + userExceptionMsg = line.substring(1); + logger.error("Got user exception: " + userExceptionMsg); + readyOrDead.countDown(); + } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { + // The server closes stderr right after this message, but for some unknown reason + // the pipe closing does not close this end of the pipe, so we must explicitly + // break out of this loop, or we will block forever on the next read. + logger.info("Got ready signal"); + ready = true; + readyOrDead.countDown(); + } else { + writer.println(line); + logger.info("got stderr: " + line); + } + } + } catch (IOException e) { + logger.error("Got io exception in pump", e); + ioFailure = e; + } + writer.flush(); + readyOrDead.countDown(); + } + } + + // protected to allow tests to override + protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws Exception { + return new ArrayList<>(determineJvmOptions(configDir, pluginsDir, tmpDir, envOptions)); + } + + // protected to allow tests to override + protected Process startProcess(ProcessBuilder builder) throws IOException { + return builder.start(); + } + + // protected to allow tests to override + protected Command loadTool(String toolname, String libs) { + return CliToolProvider.load(toolname, libs).create(); + } +} diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java new file mode 100644 index 0000000000000..0b50d2c6cc6f1 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java @@ -0,0 +1,44 @@ +/* + * 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.server.cli; + +import joptsimple.OptionSet; + +import org.elasticsearch.cli.ProcessInfo; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.common.cli.EnvironmentAwareCommand; +import org.elasticsearch.env.Environment; +import org.elasticsearch.server.cli.ServerProcess.Options; + +/** + * Starts an Elasticsearch server process, but does not wait for it. + * + * Closing this cli will stop the server process. + */ +class WindowsServiceServer extends EnvironmentAwareCommand { + + private volatile ServerProcess server; + + WindowsServiceServer() { + super("Starts and stops the Elasticsearch server process for a Windows Service"); + } + + @Override + public void execute(Terminal terminal, OptionSet options, Environment env, ProcessInfo processInfo) throws Exception { + Options serverOptions = new Options(false, true, null, null); + this.server = new ServerProcess(terminal, processInfo, serverOptions, env, () -> createEnv(options, processInfo)); + } + + @Override + public void close() { + if (server != null) { + server.stop(); + } + } +} diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java index e2d7637de7f11..af199c7159c48 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java @@ -64,10 +64,12 @@ public static ConsoleLoader.Console getConsole() { public static final String UNTRUSTED_CODEBASE = "/untrusted"; // TODO: document - public static final char USER_EXCEPTION_MARKER = '\24'; + public static final char USER_EXCEPTION_MARKER = '\u0018'; // TODO: document - public static final char SERVER_READY_MARKER = '\21'; + public static final char SERVER_READY_MARKER = '\u0015'; + + public static final char SERVER_SHUTDOWN_MARKER = '\u001B'; // create a view of sysprops map that does not allow modifications // this must be done this way (e.g. versus an actual typed map), because From dc87ec71e55ad8e9c6fabb3cfbe8cbc23029321b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 6 May 2022 21:52:34 -0700 Subject: [PATCH 135/166] use sysprop to avoid waiting when starting windows service --- .../elasticsearch/server/cli/ServerCli.java | 26 ++++---- .../windows/service/WindowsServiceCli.java | 3 +- .../elasticsearch/bootstrap/Bootstrap.java | 22 ------- .../bootstrap/BootstrapInfo.java | 4 +- .../bootstrap/Elasticsearch.java | 63 ++++++++++--------- 5 files changed, 52 insertions(+), 66 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 74885a4e5b800..70234810600e6 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -113,19 +113,22 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce // if we are daemonized and we got the all-clear signal, we can exit cleanly if (errorPump.ready && options.has(daemonizeOption)) { logger.info("Subprocess is ready and we are daemonized, exiting..."); - if (processInfo.envVars().get("os.name").startsWith("Windows") == false) { - this.process = null; // clear the process handle, we don't want to shut it down now that we are started - closeStreams(process); - } + this.process = null; // clear the process handle, we don't want to shut it down now that we are started + closeStreams(process); return; } + // TODO: need a better sysprop name... + boolean shouldWait = Boolean.parseBoolean(processInfo.sysprops().getOrDefault("cli.wait", "true")); + + if (errorPump.userExceptionMsg != null || shouldWait) { - // We pass any ES error code through UserException. If the message was set, - // then it is a real UserException, otherwise it is just the error code and a null message. - int code = process.waitFor(); - logger.info("Subprocess exited [" + code + "]"); - if (code != ExitCodes.OK) { - throw new UserException(code, errorPump.userExceptionMsg); + // We pass any ES error code through UserException. If the message was set, + // then it is a real UserException, otherwise it is just the error code and a null message. + int code = process.waitFor(); + logger.info("Subprocess exited [" + code + "]"); + if (code != ExitCodes.OK) { + throw new UserException(code, errorPump.userExceptionMsg); + } } } @@ -212,7 +215,8 @@ private void sendArgs(OptionSet options, SecureString keystorePassword, Environm final Path pidFile = pidfileOption.value(options); final var args = new ServerArgs(daemonize, quiet, pidFile, keystorePassword, env.settings(), env.configFile()); - try (var out = new OutputStreamStreamOutput(processStdin)) { + var out = new OutputStreamStreamOutput(processStdin); + try { args.writeTo(out); } catch (IOException ignore) { // A failure to write here means the process has problems, and it will die anyways. We let this fall through diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 154e2b8c96c86..e6e2091cd0a7e 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -34,7 +34,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StopTimeout", pinfo.envVars().getOrDefault("ES_STOP_TIMEOUT", "0")); addArg(args, "--StartClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StartMethod", "main"); - addArg(args, "++StartParams", "--daemonize"); + addArg(args, "++StartParams", "--quiet"); addArg(args, "--StopClass", "org.elasticsearch.launcher.CliToolLauncher"); addArg(args, "--StopMethod", "close"); addArg(args, "--Classpath", pinfo.sysprops().get("java.class.path")); @@ -58,6 +58,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StartPath", pinfo.workingDir().toString()); addArg(args, "++JvmOptions", "-Dcli.name=server"); addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli"); + addArg(args, "++JvmOptions", "-Dcli.wait=false"); addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))); String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index b6417e43a283f..273f58747f9c0 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -29,7 +29,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.BoundTransportAddress; import org.elasticsearch.core.IOUtils; -import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.env.Environment; import org.elasticsearch.jdk.JarHell; import org.elasticsearch.monitor.jvm.HotThreads; @@ -306,11 +305,6 @@ static void init( BootstrapInfo.setConsole(getConsole(environment)); - // the LogConfigurator will replace System.out and System.err with redirects to our logfile, so we need to capture - // the stream objects before calling LogConfigurator to be able to close them when appropriate - final Runnable sysOutCloser = getSysOutCloser(); - final Runnable sysErrorCloser = getSysErrorCloser(); - LogConfigurator.setNodeName(Node.NODE_NAME_SETTING.get(environment.settings())); try { LogConfigurator.configure(environment, quiet == false); @@ -361,8 +355,6 @@ static void init( if (foreground == false) { LogConfigurator.removeConsoleAppender(); - sysOutCloser.run(); - sysErrorCloser.run(); } } catch (NodeValidationException | RuntimeException e) { @@ -392,20 +384,6 @@ private static ConsoleLoader.Console getConsole(Environment environment) { return ConsoleLoader.loadConsole(environment); } - @SuppressForbidden(reason = "System#out") - private static Runnable getSysOutCloser() { - return System.out::close; - } - - @SuppressForbidden(reason = "System#err") - private static Runnable getSysErrorCloser() { - final PrintStream err = System.err; - return () -> { - err.println(BootstrapInfo.SERVER_READY_MARKER); - err.close(); - }; - } - private static void checkLucene() { if (Version.CURRENT.luceneVersion.equals(org.apache.lucene.util.Version.LATEST) == false) { throw new AssertionError( diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java index e2d7637de7f11..c8dfc274caa6b 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java @@ -64,10 +64,10 @@ public static ConsoleLoader.Console getConsole() { public static final String UNTRUSTED_CODEBASE = "/untrusted"; // TODO: document - public static final char USER_EXCEPTION_MARKER = '\24'; + public static final char USER_EXCEPTION_MARKER = '\u0015'; // TODO: document - public static final char SERVER_READY_MARKER = '\21'; + public static final char SERVER_READY_MARKER = '\u0018'; // create a view of sysprops map that does not allow modifications // this must be done this way (e.g. versus an actual typed map), because diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 5598b133f204e..2f2d1752f1ce0 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -56,7 +56,6 @@ public void checkPermission(Permission perm) { final Elasticsearch elasticsearch = new Elasticsearch(); PrintStream out = getStdout(); PrintStream err = getStderr(); - int exitCode = 0; try { err.println("Reading args from server-cli"); final var in = new InputStreamStreamInput(System.in); @@ -69,40 +68,44 @@ public void checkPermission(Permission perm) { new Environment(serverArgs.nodeSettings(), serverArgs.configDir()), serverArgs.keystorePassword() ); - } catch (NodeValidationException e) { - exitCode = ExitCodes.CONFIG; - err.print(USER_EXCEPTION_MARKER); - err.println(e.getMessage()); - if (e.getMessage() != null) { - out.println(e.getMessage()); + + err.println(BootstrapInfo.SERVER_READY_MARKER); + if (serverArgs.daemonize()) { + out.close(); + err.close(); } + + } catch (NodeValidationException e) { + exitWithUserException(err, ExitCodes.CONFIG, e); } catch (UserException e) { - exitCode = e.exitCode; - err.print(USER_EXCEPTION_MARKER); - if (e.getMessage() != null) { - err.println(e.getMessage()); - out.println(e.getMessage()); - } else { - err.println(); - } + exitWithUserException(err, e.exitCode, e); } catch (Exception e) { - exitCode = 1; // mimic JDK exit code on exception - if (System.getProperty("es.logs.base_path") != null) { - // this is a horrible hack to see if logging has been initialized - // we need to find a better way! - Logger logger = LogManager.getLogger(Elasticsearch.class); - logger.error("fatal exception while booting Elasticsearch", e); - } - e.printStackTrace(err); - e.printStackTrace(out); + exitWithUnknownException(err, e); } - if (exitCode != ExitCodes.OK) { - out.println("EXITING with non-zero status: " + exitCode); - printLogsSuggestion(err); - err.flush(); - out.flush(); - exit(exitCode); + } + + private static void exitWithUserException(PrintStream err, int exitCode, Exception e) { + err.print(USER_EXCEPTION_MARKER); + err.println(e.getMessage()); + gracefullyExit(err, exitCode); + } + + private static void exitWithUnknownException(PrintStream err, Exception e) { + if (System.getProperty("es.logs.base_path") != null) { + // this is a horrible hack to see if logging has been initialized + // we need to find a better way! + Logger logger = LogManager.getLogger(Elasticsearch.class); + logger.error("fatal exception while booting Elasticsearch", e); } + e.printStackTrace(err); + gracefullyExit(err, 1); // mimic JDK exit code on exception + } + + private static void gracefullyExit(PrintStream err, int exitCode) { + err.println("EXITING with non-zero status: " + exitCode); + printLogsSuggestion(err); + err.flush(); + exit(exitCode); } @SuppressForbidden(reason = "grab stderr for communication with server-cli") From b334d5cfa5be71ac3bd202c9bdc8a64ea327816f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 9 May 2022 15:40:37 -0700 Subject: [PATCH 136/166] don't wait for stderr to close, jvm bug? --- .../server/cli/JavaServerProcess.java | 4 ++-- .../elasticsearch/bootstrap/Elasticsearch.java | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java index ef8287e3ffdf9..04b681034fa0e 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java @@ -79,12 +79,12 @@ class JavaServerProcess implements ServerProcess { @Override public void detach() throws IOException { // TODO: use a flag to store the fact we are detached, so stop is a noop - try { + /*try { // the server will close its streams when we want to detach, so we wait to finish reading stderr errorPump.join(); } catch (InterruptedException e) { // how can this happen? - } + }*/ IOUtils.close(jvmProcess.getOutputStream(), jvmProcess.getInputStream(), jvmProcess.getErrorStream()); } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 2f2d1752f1ce0..3fc5481649aba 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -19,6 +19,7 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.node.NodeValidationException; +import java.io.IOException; import java.io.PrintStream; import java.nio.file.Path; import java.security.Permission; @@ -73,6 +74,21 @@ public void checkPermission(Permission perm) { if (serverArgs.daemonize()) { out.close(); err.close(); + } else { + new Thread(() -> { + int msg = -1; + try { + msg = in.read(); + } catch (IOException e) {} + if (msg == BootstrapInfo.SERVER_SHUTDOWN_MARKER) { + out.println("Got shutdown signal from parent process"); + System.exit(0); + } else { + err.println("Parent process died, shutting down..."); + // parent process died or there was an error reading from it + System.exit(1); + } + }).start(); } } catch (NodeValidationException e) { From ae31299d682b3e868621b57cf27219c98d0f1083 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 9 May 2022 15:41:05 -0700 Subject: [PATCH 137/166] spotless --- .../org/elasticsearch/server/cli/JavaServerProcess.java | 6 ++++-- .../java/org/elasticsearch/server/cli/ServerProcess.java | 4 +++- .../elasticsearch/server/cli/JavaServerProcessTests.java | 5 ----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java index 04b681034fa0e..7a8845f7687e5 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java @@ -142,7 +142,8 @@ private void sendArgs(ServerArgs args, OutputStream processStdin) { args.keystorePassword().close(); } - private Process createProcess(ProcessInfo processInfo, Path configDir, Path pluginsDir) throws InterruptedException, IOException, UserException { + private Process createProcess(ProcessInfo processInfo, Path configDir, Path pluginsDir) throws InterruptedException, IOException, + UserException { Map envVars = new HashMap<>(processInfo.envVars()); Path tempDir = TempDirectory.setup(envVars); List jvmOptions = getJvmOptions(configDir, pluginsDir, tempDir, envVars.get("ES_JAVA_OPTS")); @@ -209,7 +210,8 @@ public void run() { } // protected to allow tests to override - protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, IOException, UserException { + protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, + IOException, UserException { return new ArrayList<>(determineJvmOptions(configDir, pluginsDir, tmpDir, envOptions)); } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java index c46b72f730b4c..7ae267b821fda 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -19,10 +19,12 @@ interface ServerProcess { void detach() throws IOException; + void waitFor() throws UserException, InterruptedException; + void stop(); - static ServerProcess start(Terminal terminal, ProcessInfo processInfo, ServerArgs args, Path pluginsDir) throws UserException{ + static ServerProcess start(Terminal terminal, ProcessInfo processInfo, ServerArgs args, Path pluginsDir) throws UserException { return new JavaServerProcess(terminal, processInfo, args, pluginsDir); } } diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java index 9b02928854718..023807ed20585 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java @@ -9,7 +9,6 @@ package org.elasticsearch.server.cli; import org.elasticsearch.bootstrap.ServerArgs; -import org.elasticsearch.cli.Command; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.core.IOUtils; import org.elasticsearch.test.ESTestCase; @@ -21,8 +20,6 @@ import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -30,8 +27,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static org.hamcrest.CoreMatchers.equalTo; - public class JavaServerProcessTests extends ESTestCase { private static final ExecutorService mockJvmProcessExecutor = Executors.newSingleThreadExecutor(); From 8b89c2e142ae220dc3ce1014ff83669a6524c1d8 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 9 May 2022 18:52:26 -0700 Subject: [PATCH 138/166] fix bugs and add debugging --- .../src/main/java/org/elasticsearch/server/cli/ServerCli.java | 1 + .../org/elasticsearch/windows/service/WindowsServiceCli.java | 2 +- .../main/java/org/elasticsearch/bootstrap/Elasticsearch.java | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index cb086e8c8d2da..39a84f36ce0ed 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -89,6 +89,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce logger.info("Subprocess is started and we are daemonized, detaching..."); server.detach(); this.server = null; // clear the handle, we don't want to shut it down now that we are started + logger.info("exiting..."); return; } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 92b7ad6a5544c..4dc93f3884512 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -55,7 +55,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StartMode", "jvm"); addArg(args, "--StopMode", "jvm"); addArg(args, "--StartPath", pinfo.workingDir().toString()); - addArg(args, "++JvmOptions", "-Dcli.name=server"); + addArg(args, "++JvmOptions", "-Dcli.name=windows-service-server"); addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli"); addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 3fc5481649aba..3edb0b752accd 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -82,11 +82,11 @@ public void checkPermission(Permission perm) { } catch (IOException e) {} if (msg == BootstrapInfo.SERVER_SHUTDOWN_MARKER) { out.println("Got shutdown signal from parent process"); - System.exit(0); + exit(0); } else { err.println("Parent process died, shutting down..."); // parent process died or there was an error reading from it - System.exit(1); + exit(1); } }).start(); } From ed2cd517025a2bbc63a49f7c9944179af283b93c Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 9 May 2022 21:25:40 -0700 Subject: [PATCH 139/166] exit pump early --- .../server/cli/JavaServerProcess.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java index 7a8845f7687e5..0d4724ae4db23 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java @@ -108,6 +108,7 @@ public void stop() { // process is already effectively dead, fall through to wait for it, or should we SIGKILL? } + logger.info("Waiting for stderr to drain"); try { errorPump.join(); } catch (InterruptedException e) { @@ -172,11 +173,13 @@ private Process createProcess(ProcessInfo processInfo, Path configDir, Path plug private class ErrorPumpThread extends Thread { private final BufferedReader reader; private final PrintWriter writer; + private final boolean deamonized; - private ErrorPumpThread(PrintWriter errOutput, InputStream errInput) { + private ErrorPumpThread(PrintWriter errOutput, InputStream errInput, boolean daemonized) { super("server-cli error pump"); this.reader = new BufferedReader(new InputStreamReader(errInput, StandardCharsets.UTF_8)); this.writer = errOutput; + this.deamonized = daemonized; } @Override @@ -189,12 +192,15 @@ public void run() { logger.error("Got user exception: " + userExceptionMsg); readyOrDead.countDown(); } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { - // The server closes stderr right after this message, but for some unknown reason - // the pipe closing does not close this end of the pipe, so we must explicitly - // break out of this loop, or we will block forever on the next read. logger.info("Got ready signal"); ready = true; readyOrDead.countDown(); + if (this.deamonized) { + // The server closes stderr right after this message, but for some unknown reason + // the pipe closing does not close this end of the pipe, so we must explicitly + // break out of this loop, or we will block forever on the next read. + break; + } } else { writer.println(line); logger.info("got stderr: " + line); From 4bc16972346da901a3d159993c2e61fc7595cd0f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 9 May 2022 21:56:33 -0700 Subject: [PATCH 140/166] fix compile --- .../elasticsearch/server/cli/JavaServerProcess.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java index 0d4724ae4db23..35bf393b40193 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java @@ -54,7 +54,7 @@ class JavaServerProcess implements ServerProcess { try { // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process this.jvmProcess = createProcess(processInfo, args.configDir(), pluginsDir); - this.errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream()); + this.errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream(), args.daemonize()); errorPump.start(); logger.info("ES PID: " + jvmProcess.pid()); sendArgs(args, jvmProcess.getOutputStream()); @@ -173,13 +173,13 @@ private Process createProcess(ProcessInfo processInfo, Path configDir, Path plug private class ErrorPumpThread extends Thread { private final BufferedReader reader; private final PrintWriter writer; - private final boolean deamonized; + private final boolean exitWhenReady; - private ErrorPumpThread(PrintWriter errOutput, InputStream errInput, boolean daemonized) { + private ErrorPumpThread(PrintWriter errOutput, InputStream errInput, boolean exitWhenRead) { super("server-cli error pump"); this.reader = new BufferedReader(new InputStreamReader(errInput, StandardCharsets.UTF_8)); this.writer = errOutput; - this.deamonized = daemonized; + this.exitWhenReady = exitWhenRead; } @Override @@ -195,7 +195,7 @@ public void run() { logger.info("Got ready signal"); ready = true; readyOrDead.countDown(); - if (this.deamonized) { + if (this.exitWhenReady) { // The server closes stderr right after this message, but for some unknown reason // the pipe closing does not close this end of the pipe, so we must explicitly // break out of this loop, or we will block forever on the next read. From 6a9c1af0ffff4e16570d6f7fb7571db51e5b717f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 10 May 2022 10:13:05 -0700 Subject: [PATCH 141/166] copy bundled jdk instead of moving it --- .../packaging/test/WindowsServiceTests.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index b0970f0a3e60c..4ae628cac801a 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -28,6 +28,7 @@ import static org.elasticsearch.packaging.util.Archives.installArchive; import static org.elasticsearch.packaging.util.Archives.verifyArchiveInstallation; import static org.elasticsearch.packaging.util.FileUtils.append; +import static org.elasticsearch.packaging.util.FileUtils.copyDirectory; import static org.elasticsearch.packaging.util.FileUtils.mv; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; @@ -205,17 +206,17 @@ public void test32StopNotStarted() throws IOException { } public void test33JavaChanged() throws Exception { - final Path relocatedJdk = installation.bundledJdk.getParent().resolve("jdk.relocated"); + final Path alternateJdk = installation.bundledJdk.getParent().resolve("jdk.copy"); try { - mv(installation.bundledJdk, relocatedJdk); - sh.getEnv().put("ES_JAVA_HOME", relocatedJdk.toString()); + copyDirectory(installation.bundledJdk, alternateJdk); + sh.getEnv().put("ES_JAVA_HOME", alternateJdk.toString()); assertCommand(serviceScript + " install"); sh.getEnv().remove("ES_JAVA_HOME"); assertCommand(serviceScript + " start"); assertStartedAndStop(); } finally { - mv(relocatedJdk, installation.bundledJdk); + FileUtils.rm(alternateJdk); } } From 2ca3bee3926028dbc011ae02ad87d229e957edf5 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 10 May 2022 10:58:44 -0700 Subject: [PATCH 142/166] remove manager test, won't work with new clis --- .../packaging/test/WindowsServiceTests.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 4ae628cac801a..27e79ac66c971 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -220,24 +220,6 @@ public void test33JavaChanged() throws Exception { } } - public void test60Manager() throws IOException { - Path serviceMgr = installation.bin("elasticsearch-service-mgr.exe"); - Path tmpServiceMgr = serviceMgr.getParent().resolve(serviceMgr.getFileName() + ".tmp"); - Files.move(serviceMgr, tmpServiceMgr); - Path fakeServiceMgr = serviceMgr.getParent().resolve("elasticsearch-service-mgr.bat"); - Files.write(fakeServiceMgr, Arrays.asList("echo \"Fake Service Manager GUI\"")); - Shell sh = new Shell(); - Result result = sh.run(serviceScript + " manager"); - assertThat(result.stdout(), containsString("Fake Service Manager GUI")); - - // check failure too - Files.write(fakeServiceMgr, Arrays.asList("echo \"Fake Service Manager GUI Failure\"", "exit 1")); - result = sh.runIgnoreExitCode(serviceScript + " manager"); - TestCase.assertEquals(1, result.exitCode()); - assertThat(result.stderr(), containsString("Fake Service Manager GUI Failure")); - Files.move(tmpServiceMgr, serviceMgr); - } - public void test80JavaOptsInEnvVar() throws Exception { sh.getEnv().put("ES_JAVA_OPTS", "-Xmx2g -Xms2g"); sh.run(serviceScript + " install"); From 27ba962aed40b352ac4a5cdef09a42ff6945205b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 10 May 2022 11:05:41 -0700 Subject: [PATCH 143/166] more debugging --- .../elasticsearch/packaging/test/WindowsServiceTests.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 27e79ac66c971..52741d34dd4b2 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -8,12 +8,9 @@ package org.elasticsearch.packaging.test; -import junit.framework.TestCase; - import org.elasticsearch.packaging.util.FileUtils; import org.elasticsearch.packaging.util.Platforms; import org.elasticsearch.packaging.util.ServerUtils; -import org.elasticsearch.packaging.util.Shell; import org.elasticsearch.packaging.util.Shell.Result; import org.junit.After; import org.junit.BeforeClass; @@ -22,7 +19,6 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import static com.carrotsearch.randomizedtesting.RandomizedTest.assumeTrue; import static org.elasticsearch.packaging.util.Archives.installArchive; @@ -196,6 +192,7 @@ public void test31StartNotInstalled() throws IOException { Result result = sh.runIgnoreExitCode(serviceScript + " start"); assertThat(result.stderr(), result.exitCode(), equalTo(1)); dumpServiceLogs(); + logger.warn(result.stderr()); assertThat(result.stderr(), containsString("Failed starting '" + DEFAULT_ID + "' service")); } From 5f84788bfa1ce156272a26bfeb336654778d208b Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 10 May 2022 15:30:53 -0700 Subject: [PATCH 144/166] more windows debugging --- .../org/elasticsearch/packaging/test/WindowsServiceTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 52741d34dd4b2..0bb2337326fca 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -192,7 +192,8 @@ public void test31StartNotInstalled() throws IOException { Result result = sh.runIgnoreExitCode(serviceScript + " start"); assertThat(result.stderr(), result.exitCode(), equalTo(1)); dumpServiceLogs(); - logger.warn(result.stderr()); + logger.warn(result.stderr().substring(0, 8192)); + fail("NO MATCH"); assertThat(result.stderr(), containsString("Failed starting '" + DEFAULT_ID + "' service")); } From 5b7da1b02fe3855d4eeeabe93750f925f5a7cb1d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 10 May 2022 16:32:32 -0700 Subject: [PATCH 145/166] better large file error matching --- .../elasticsearch/packaging/test/WindowsServiceTests.java | 2 -- .../test/java/org/elasticsearch/packaging/util/Shell.java | 7 ++++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java index 0bb2337326fca..cca5bd702485a 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/WindowsServiceTests.java @@ -192,8 +192,6 @@ public void test31StartNotInstalled() throws IOException { Result result = sh.runIgnoreExitCode(serviceScript + " start"); assertThat(result.stderr(), result.exitCode(), equalTo(1)); dumpServiceLogs(); - logger.warn(result.stderr().substring(0, 8192)); - fail("NO MATCH"); assertThat(result.stderr(), containsString("Failed starting '" + DEFAULT_ID + "' service")); } diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java index 327c805fad351..8f58da8a1d975 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java @@ -203,7 +203,12 @@ private String readFileIfExists(Path path) throws IOException { if (Files.exists(path)) { long size = Files.size(path); if (size > 100 * 1024) { - return "<>"; + // file is really big, truncate at 100k + try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + char[] buf = new char[100 * 1024]; + int nRead = br.read(buf); + return new String(buf, 0, nRead) + "\n<>"; + } } try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) { return lines.collect(Collectors.joining("\n")); From 38709229a201459643326024881ae36cf2391e34 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 10 May 2022 18:39:02 -0700 Subject: [PATCH 146/166] are these all nulls?? --- .../org/elasticsearch/packaging/util/Shell.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java index 8f58da8a1d975..76ee54209cb1b 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java @@ -206,8 +206,21 @@ private String readFileIfExists(Path path) throws IOException { // file is really big, truncate at 100k try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { char[] buf = new char[100 * 1024]; + int start = 0; int nRead = br.read(buf); - return new String(buf, 0, nRead) + "\n<>"; + while (nRead != -1) { + while (start < nRead) { + if (buf[start] != '\u0000') { + break; + } + ++start; + } + if (start < nRead) { + break; + } + nRead = br.read(buf); + } + return new String(buf, start, nRead) + "\n<>"; } } try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) { From 18ebfeb866520d034bc4d3ebba0096c9d1964aa7 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 10 May 2022 20:39:38 -0700 Subject: [PATCH 147/166] cleaner debug string --- .../org/elasticsearch/packaging/util/Shell.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java index 76ee54209cb1b..794d9d77eca8a 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java @@ -204,23 +204,23 @@ private String readFileIfExists(Path path) throws IOException { long size = Files.size(path); if (size > 100 * 1024) { // file is really big, truncate at 100k + char[] data = new char[100 * 1024]; + char[] buf = new char[4096]; + int end = 0; try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { - char[] buf = new char[100 * 1024]; - int start = 0; int nRead = br.read(buf); while (nRead != -1) { - while (start < nRead) { - if (buf[start] != '\u0000') { - break; + for (int i = 0; i < nRead && end < data.length; ++i) { + if (Character.isISOControl(buf[i]) == false) { + data[end++] = buf[i]; } - ++start; } - if (start < nRead) { + if (end == data.length) { break; } nRead = br.read(buf); } - return new String(buf, start, nRead) + "\n<>"; + return new String(data, 0, end) + "\n<>"; } } try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) { From a3dc55b5f41d0d8ef22e7094843d3b57228222f0 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 12 May 2022 18:59:02 -0700 Subject: [PATCH 148/166] simpler read file again --- .../org/elasticsearch/packaging/util/Shell.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java index 794d9d77eca8a..8f58da8a1d975 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java @@ -204,23 +204,10 @@ private String readFileIfExists(Path path) throws IOException { long size = Files.size(path); if (size > 100 * 1024) { // file is really big, truncate at 100k - char[] data = new char[100 * 1024]; - char[] buf = new char[4096]; - int end = 0; try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + char[] buf = new char[100 * 1024]; int nRead = br.read(buf); - while (nRead != -1) { - for (int i = 0; i < nRead && end < data.length; ++i) { - if (Character.isISOControl(buf[i]) == false) { - data[end++] = buf[i]; - } - } - if (end == data.length) { - break; - } - nRead = br.read(buf); - } - return new String(data, 0, end) + "\n<>"; + return new String(buf, 0, nRead) + "\n<>"; } } try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) { From db1f04f1277ba3103abb5738e2090c5478005498 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 12 May 2022 19:04:26 -0700 Subject: [PATCH 149/166] let stderr pump fully drain --- .../org/elasticsearch/server/cli/JavaServerProcess.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java index 35bf393b40193..1663a5a570bfe 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java @@ -54,7 +54,7 @@ class JavaServerProcess implements ServerProcess { try { // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process this.jvmProcess = createProcess(processInfo, args.configDir(), pluginsDir); - this.errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream(), args.daemonize()); + this.errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream(), false); errorPump.start(); logger.info("ES PID: " + jvmProcess.pid()); sendArgs(args, jvmProcess.getOutputStream()); @@ -175,11 +175,11 @@ private class ErrorPumpThread extends Thread { private final PrintWriter writer; private final boolean exitWhenReady; - private ErrorPumpThread(PrintWriter errOutput, InputStream errInput, boolean exitWhenRead) { + private ErrorPumpThread(PrintWriter errOutput, InputStream errInput, boolean exitWhenReady) { super("server-cli error pump"); this.reader = new BufferedReader(new InputStreamReader(errInput, StandardCharsets.UTF_8)); this.writer = errOutput; - this.exitWhenReady = exitWhenRead; + this.exitWhenReady = exitWhenReady; } @Override From e4d7d4ef14f99d00c108c00e2bd2aa4c5727c0a1 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 16 May 2022 06:46:23 -0700 Subject: [PATCH 150/166] rework and document --- .../cli/keystore/BootstrapTests.java | 40 --- .../server/cli/ErrorPumpThread.java | 104 ++++++++ .../server/cli/JavaServerProcess.java | 228 ------------------ .../elasticsearch/server/cli/ProcessUtil.java | 43 ++++ .../server/cli/ServerProcess.java | 164 ++++++++++++- .../server/cli/ServerCliTests.java | 6 +- 6 files changed, 310 insertions(+), 275 deletions(-) create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java delete mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java create mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ProcessUtil.java diff --git a/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java b/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java index a4ab6397288d0..d4405db422976 100644 --- a/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java +++ b/distribution/tools/keystore-cli/src/test/java/org/elasticsearch/cli/keystore/BootstrapTests.java @@ -18,18 +18,13 @@ import org.junit.After; import org.junit.Before; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import static org.hamcrest.Matchers.equalTo; - public class BootstrapTests extends ESTestCase { Environment env; List fileSystems = new ArrayList<>(); @@ -62,39 +57,4 @@ public void testLoadSecureSettings() throws Exception { assertTrue(Files.exists(configPath.resolve("elasticsearch.keystore"))); } } - - // TODO: these tests aren't needed anymore, password reading is done in ServerCli - public void testReadCharsFromStdin() throws Exception { - assertPassphraseRead("hello", "hello"); - assertPassphraseRead("hello\n", "hello"); - assertPassphraseRead("hello\r\n", "hello"); - - assertPassphraseRead("hellohello", "hellohello"); - assertPassphraseRead("hellohello\n", "hellohello"); - assertPassphraseRead("hellohello\r\n", "hellohello"); - - assertPassphraseRead("hello\nhi\n", "hello"); - assertPassphraseRead("hello\r\nhi\r\n", "hello"); - } - - public void testNoPassPhraseProvided() throws Exception { - byte[] source = "\r\n".getBytes(StandardCharsets.UTF_8); - try (InputStream stream = new ByteArrayInputStream(source)) { - expectThrows( - RuntimeException.class, - "Keystore passphrase required but none provided.", - () -> BootstrapUtil.readPassphrase(stream) - ); - } - } - - private void assertPassphraseRead(String source, String expected) { - try (InputStream stream = new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8))) { - SecureString result = BootstrapUtil.readPassphrase(stream); - assertThat(result, equalTo(expected)); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java new file mode 100644 index 0000000000000..bdb0b0d4aae47 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java @@ -0,0 +1,104 @@ +/* + * 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.server.cli; + +import org.elasticsearch.bootstrap.BootstrapInfo; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CountDownLatch; + +import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; +import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; +import static org.elasticsearch.server.cli.ProcessUtil.nonInterruptibleVoid; + +/** + * A thread which reads stderr of the jvm process and writes it to this process' stderr. + * + *

Two special state markers are watched for. These are ascii control characters which signal + * to the cli process something has changed in the server process. The two possible special messages are: + *

    + *
  • {@link BootstrapInfo#USER_EXCEPTION_MARKER} - signals a bootstrap error has occurred, and is followed + * by the error message
  • + *
  • {@link BootstrapInfo#SERVER_READY_MARKER} - signals the server is ready so the cli may detach if daemonizing
  • + *
+ */ +class ErrorPumpThread extends Thread { + private final BufferedReader reader; + private final PrintWriter writer; + + // a latch which changes state when the server is ready or has had a bootstrap error + private final CountDownLatch readyOrDead = new CountDownLatch(1); + + // a flag denoting whether the ready marker has been received by the server process + private volatile boolean ready; + + // an exception message received alongside the user exception marker, if a bootstrap error has occurred + private volatile String userExceptionMsg; + + // an unexpected io failure that occurred while pumping stderr + private volatile IOException ioFailure; + + ErrorPumpThread(PrintWriter errOutput, InputStream errInput) { + super("server-cli[stderr_pump]"); + this.reader = new BufferedReader(new InputStreamReader(errInput, StandardCharsets.UTF_8)); + this.writer = errOutput; + } + + /** + * Waits until the server ready marker has been received. + * + * @return a bootstrap exeption message if a bootstrap error occurred, or null otherwise + * @throws IOException if there was a problem reading from stderr of the process + */ + String waitUntilReady() throws IOException { + nonInterruptibleVoid(readyOrDead::await); + if (ioFailure != null) { + throw ioFailure; + } + if (ready == false) { + return userExceptionMsg; + } + assert userExceptionMsg == null; + return null; + } + + /** + * Waits for the stderr pump thread to exit. + */ + void drain() { + nonInterruptibleVoid(this::join); + } + + @Override + public void run() { + try { + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { + userExceptionMsg = line.substring(1); + readyOrDead.countDown(); + } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { + ready = true; + readyOrDead.countDown(); + } else { + writer.println(line); + } + } + } catch (IOException e) { + ioFailure = e; + } + writer.flush(); + readyOrDead.countDown(); + } +} diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java deleted file mode 100644 index 1663a5a570bfe..0000000000000 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/JavaServerProcess.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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.server.cli; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.elasticsearch.bootstrap.BootstrapInfo; -import org.elasticsearch.bootstrap.ServerArgs; -import org.elasticsearch.cli.ExitCodes; -import org.elasticsearch.cli.ProcessInfo; -import org.elasticsearch.cli.Terminal; -import org.elasticsearch.cli.UserException; -import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; -import org.elasticsearch.core.IOUtils; -import org.elasticsearch.core.PathUtils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CountDownLatch; - -import static org.elasticsearch.bootstrap.BootstrapInfo.SERVER_READY_MARKER; -import static org.elasticsearch.bootstrap.BootstrapInfo.USER_EXCEPTION_MARKER; -import static org.elasticsearch.server.cli.JvmOptionsParser.determineJvmOptions; - -class JavaServerProcess implements ServerProcess { - private static final Logger logger = LogManager.getLogger(ServerProcess.class); - - private final Process jvmProcess; - private final ErrorPumpThread errorPump; - private final CountDownLatch readyOrDead = new CountDownLatch(1); - private volatile boolean ready; - private volatile String userExceptionMsg; - private volatile IOException ioFailure; - - JavaServerProcess(Terminal terminal, ProcessInfo processInfo, ServerArgs args, Path pluginsDir) throws UserException { - - try { - // start Elasticsearch, stashing the process into a volatile so the close via the shutdown handler will kill the process - this.jvmProcess = createProcess(processInfo, args.configDir(), pluginsDir); - this.errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream(), false); - errorPump.start(); - logger.info("ES PID: " + jvmProcess.pid()); - sendArgs(args, jvmProcess.getOutputStream()); - - // Read from stderr until we get a signal back that ES is either ready or it had an error. - readyOrDead.await(); - if (ioFailure != null) { - throw ioFailure; - } - if (ready == false) { - // something bad happened, wait for the process to exit then rethrow - int exitCode = jvmProcess.waitFor(); - throw new UserException(exitCode, userExceptionMsg); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - @Override - public void detach() throws IOException { - // TODO: use a flag to store the fact we are detached, so stop is a noop - /*try { - // the server will close its streams when we want to detach, so we wait to finish reading stderr - errorPump.join(); - } catch (InterruptedException e) { - // how can this happen? - }*/ - IOUtils.close(jvmProcess.getOutputStream(), jvmProcess.getInputStream(), jvmProcess.getErrorStream()); - } - - @Override - public void waitFor() throws UserException, InterruptedException { - // todo: should we catch interrupted and retry? - int exitCode = jvmProcess.waitFor(); - if (exitCode != ExitCodes.OK) { - throw new UserException(exitCode, userExceptionMsg); - } - } - - @Override - public void stop() { - logger.info("Terminating subprocess"); - try { - OutputStream os = jvmProcess.getOutputStream(); - os.write(BootstrapInfo.SERVER_SHUTDOWN_MARKER); - os.flush(); - } catch (IOException e) { - // process is already effectively dead, fall through to wait for it, or should we SIGKILL? - } - - logger.info("Waiting for stderr to drain"); - try { - errorPump.join(); - } catch (InterruptedException e) { - // we don't interrupt this thread, so should not be possible - throw new AssertionError(e); - } - - // We wait forever here. Any timeout while waiting, eg in systemd would forcibly kill the - // parent process. The server process would then break out of its block on stdin and exit. - while (true) { - try { - logger.info("Waiting for subprocess"); - jvmProcess.waitFor(); - break; - } catch (InterruptedException ignore) { - // retry - } - } - } - - private void sendArgs(ServerArgs args, OutputStream processStdin) { - // DO NOT close the underlying process stdin, since we need to be able to write to it to signal exit - var out = new OutputStreamStreamOutput(processStdin); - try { - args.writeTo(out); - out.flush(); - } catch (IOException ignore) { - // A failure to write here means the process has problems, and it will die anyways. We let this fall through - // so the pump thread can complete, writing out the actual error. All we get here is the failure to write to - // the process pipe, which isn't helpful to print. - } - args.keystorePassword().close(); - } - - private Process createProcess(ProcessInfo processInfo, Path configDir, Path pluginsDir) throws InterruptedException, IOException, - UserException { - Map envVars = new HashMap<>(processInfo.envVars()); - Path tempDir = TempDirectory.setup(envVars); - List jvmOptions = getJvmOptions(configDir, pluginsDir, tempDir, envVars.get("ES_JAVA_OPTS")); - // jvmOptions.add("-Des.path.conf=" + env.configFile()); - jvmOptions.add("-Des.distribution.type=" + processInfo.sysprops().get("es.distribution.type")); - - Path esHome = processInfo.workingDir(); - Path javaHome = PathUtils.get(processInfo.sysprops().get("java.home")); - List command = new ArrayList<>(); - boolean isWindows = processInfo.sysprops().get("os.name").startsWith("Windows"); - command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); - command.addAll(jvmOptions); - command.add("-cp"); - // The '*' isn't allows by the windows filesystem, so we need to force it into the classpath after converting to a string. - // Thankfully this will all go away when switching to modules, which take the directory instead of a glob. - command.add(esHome.resolve("lib") + (isWindows ? "\\" : "/") + "*"); - command.add("org.elasticsearch.bootstrap.Elasticsearch"); - - var builder = new ProcessBuilder(command); - builder.environment().putAll(envVars); - builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); - - return startProcess(builder); - } - - private class ErrorPumpThread extends Thread { - private final BufferedReader reader; - private final PrintWriter writer; - private final boolean exitWhenReady; - - private ErrorPumpThread(PrintWriter errOutput, InputStream errInput, boolean exitWhenReady) { - super("server-cli error pump"); - this.reader = new BufferedReader(new InputStreamReader(errInput, StandardCharsets.UTF_8)); - this.writer = errOutput; - this.exitWhenReady = exitWhenReady; - } - - @Override - public void run() { - try { - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty() == false && line.charAt(0) == USER_EXCEPTION_MARKER) { - userExceptionMsg = line.substring(1); - logger.error("Got user exception: " + userExceptionMsg); - readyOrDead.countDown(); - } else if (line.isEmpty() == false && line.charAt(0) == SERVER_READY_MARKER) { - logger.info("Got ready signal"); - ready = true; - readyOrDead.countDown(); - if (this.exitWhenReady) { - // The server closes stderr right after this message, but for some unknown reason - // the pipe closing does not close this end of the pipe, so we must explicitly - // break out of this loop, or we will block forever on the next read. - break; - } - } else { - writer.println(line); - logger.info("got stderr: " + line); - } - } - } catch (IOException e) { - logger.error("Got io exception in pump", e); - ioFailure = e; - } - writer.flush(); - readyOrDead.countDown(); - } - } - - // protected to allow tests to override - protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, - IOException, UserException { - return new ArrayList<>(determineJvmOptions(configDir, pluginsDir, tmpDir, envOptions)); - } - - // protected to allow tests to override - protected Process startProcess(ProcessBuilder builder) throws IOException { - return builder.start(); - } -} diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ProcessUtil.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ProcessUtil.java new file mode 100644 index 0000000000000..fb5f7bae34844 --- /dev/null +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ProcessUtil.java @@ -0,0 +1,43 @@ +/* + * 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.server.cli; + +class ProcessUtil { + + private ProcessUtil() { /* no instance*/ } + + interface Interruptible { + T run() throws InterruptedException; + } + + interface InterruptibleVoid { + void run() throws InterruptedException; + } + + /** + * Runs an interruptable method, but throws an assertion if an interrupt is received. + * + * This is useful for threads which expect a no interruption policy + */ + static T nonInterruptible(Interruptible interruptible) { + try { + return interruptible.run(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError(e); + } + } + + static void nonInterruptibleVoid(InterruptibleVoid interruptible) { + nonInterruptible(() -> { + interruptible.run(); + return null; + }); + } +} diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java index 7ae267b821fda..bb7bf28671a05 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -8,23 +8,175 @@ package org.elasticsearch.server.cli; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.bootstrap.BootstrapInfo; import org.elasticsearch.bootstrap.ServerArgs; +import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; +import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; +import org.elasticsearch.core.IOUtils; +import org.elasticsearch.core.PathUtils; import java.io.IOException; +import java.io.OutputStream; +import java.io.UncheckedIOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -interface ServerProcess { +import static org.elasticsearch.server.cli.JvmOptionsParser.determineJvmOptions; +import static org.elasticsearch.server.cli.ProcessUtil.nonInterruptible; - void detach() throws IOException; +public class ServerProcess { + private static final Logger logger = LogManager.getLogger(ServerProcess.class); - void waitFor() throws UserException, InterruptedException; + // the actual java process of the server + private final Process jvmProcess; - void stop(); + // the thread pumping stderr watching for state change messages + private final ErrorPumpThread errorPump; - static ServerProcess start(Terminal terminal, ProcessInfo processInfo, ServerArgs args, Path pluginsDir) throws UserException { - return new JavaServerProcess(terminal, processInfo, args, pluginsDir); + // a flag marking whether the java process has been detached from + private volatile boolean detached = false; + + ServerProcess(Process jvmProcess, ErrorPumpThread errorPump) { + this.jvmProcess = jvmProcess; + this.errorPump = errorPump; + } + + interface OptionsBuilder { + List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, + IOException, UserException; + } + + interface ProcessStarter { + Process start(ProcessBuilder pb) throws IOException; + } + + public static ServerProcess start(Terminal terminal, ProcessInfo processInfo, ServerArgs args, Path pluginsDir) throws UserException { + return start(terminal, processInfo, args, pluginsDir, JvmOptionsParser::determineJvmOptions, ProcessBuilder::start); + } + + // package private so tests can mock options building and process starting + static ServerProcess start( + Terminal terminal, + ProcessInfo processInfo, + ServerArgs args, + Path pluginsDir, + OptionsBuilder optionsBuilder, + ProcessStarter processStarter + ) throws UserException { + Process jvmProcess = null; + ErrorPumpThread errorPump; + + boolean success = false; + try { + jvmProcess = createProcess(processInfo, args.configDir(), pluginsDir, optionsBuilder, processStarter); + errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream()); + errorPump.start(); + logger.info("ES PID: " + jvmProcess.pid()); + sendArgs(args, jvmProcess.getOutputStream()); + + String errorMsg = errorPump.waitUntilReady(); + if (errorMsg != null) { + // something bad happened, wait for the process to exit then rethrow + int exitCode = jvmProcess.waitFor(); + throw new UserException(exitCode, errorMsg); + } + success = true; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new UncheckedIOException(e); + } finally { + if (success == false && jvmProcess != null && jvmProcess.isAlive()) { + jvmProcess.destroyForcibly(); + } + } + + return new ServerProcess(jvmProcess, errorPump); + } + + public synchronized void detach() throws IOException { + errorPump.drain(); + IOUtils.close(jvmProcess.getOutputStream(), jvmProcess.getInputStream(), jvmProcess.getErrorStream()); + } + + public void waitFor() { + int exitCode = nonInterruptible(jvmProcess::waitFor); + if (exitCode != ExitCodes.OK) { + throw new RuntimeException("server process exited with status code " + exitCode); + } + } + + public synchronized void stop() { + if (detached) { + return; + } + + sendShutdownMarker(); + errorPump.drain(); + waitFor(); + } + + private static void sendArgs(ServerArgs args, OutputStream processStdin) { + // DO NOT close the underlying process stdin, since we need to be able to write to it to signal exit + var out = new OutputStreamStreamOutput(processStdin); + try { + args.writeTo(out); + out.flush(); + } catch (IOException ignore) { + // A failure to write here means the process has problems, and it will die anyways. We let this fall through + // so the pump thread can complete, writing out the actual error. All we get here is the failure to write to + // the process pipe, which isn't helpful to print. + } + args.keystorePassword().close(); + } + + private void sendShutdownMarker() { + try { + OutputStream os = jvmProcess.getOutputStream(); + os.write(BootstrapInfo.SERVER_SHUTDOWN_MARKER); + os.flush(); + } catch (IOException e) { + // process is already effectively dead, fall through to wait for it, or should we SIGKILL? + } + } + + private static Process createProcess( + ProcessInfo processInfo, + Path configDir, + Path pluginsDir, + OptionsBuilder optionsBuilder, + ProcessStarter processStarter + ) throws InterruptedException, IOException, UserException { + Map envVars = new HashMap<>(processInfo.envVars()); + Path tempDir = TempDirectory.setup(envVars); + List jvmOptions = optionsBuilder.getJvmOptions(configDir, pluginsDir, tempDir, envVars.get("ES_JAVA_OPTS")); + // jvmOptions.add("-Des.path.conf=" + env.configFile()); + jvmOptions.add("-Des.distribution.type=" + processInfo.sysprops().get("es.distribution.type")); + + Path esHome = processInfo.workingDir(); + Path javaHome = PathUtils.get(processInfo.sysprops().get("java.home")); + List command = new ArrayList<>(); + boolean isWindows = processInfo.sysprops().get("os.name").startsWith("Windows"); + command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); + command.addAll(jvmOptions); + command.add("-cp"); + // The '*' isn't allows by the windows filesystem, so we need to force it into the classpath after converting to a string. + // Thankfully this will all go away when switching to modules, which take the directory instead of a glob. + command.add(esHome.resolve("lib") + (isWindows ? "\\" : "/") + "*"); + command.add("org.elasticsearch.bootstrap.Elasticsearch"); + + var builder = new ProcessBuilder(command); + builder.environment().putAll(envVars); + builder.redirectOutput(ProcessBuilder.Redirect.INHERIT); + + return processStarter.start(builder); } } diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index edb7298ba04f1..719bb0441d19b 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -290,11 +290,15 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce } } - private class MockServerProcess implements ServerProcess { + private class MockServerProcess extends ServerProcess { boolean detachCalled = false; boolean waitForCalled = false; boolean stopCalled = false; + MockServerProcess() { + super(null, null); + } + @Override public void detach() { assert detachCalled == false; From 8289fc95f919385a778d174dc04f7ff1b492dddc Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 16 May 2022 07:33:27 -0700 Subject: [PATCH 151/166] more javadocs --- .../server/cli/KeystorePasswordTerminal.java | 5 ++- .../elasticsearch/server/cli/ServerCli.java | 6 ++-- .../server/cli/ServerProcess.java | 34 ++++++++++++++++++- .../windows/service/WindowsServiceCli.java | 3 ++ .../java/org/elasticsearch/cli/Command.java | 5 +++ .../packaging/test/PackageTests.java | 3 +- 6 files changed, 49 insertions(+), 7 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java index bc059ff7d01bc..bf03acaf7a5da 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/KeystorePasswordTerminal.java @@ -14,12 +14,15 @@ import java.io.Closeable; import java.io.OutputStream; +/** + * A terminal that wraps an existing terminal and provides a single secret input, the keystore password. + */ class KeystorePasswordTerminal extends Terminal implements Closeable { private final Terminal delegate; private final SecureString password; - protected KeystorePasswordTerminal(Terminal delegate, SecureString password) { + KeystorePasswordTerminal(Terminal delegate, SecureString password) { super(delegate.getReader(), delegate.getWriter(), delegate.getErrorWriter()); this.delegate = delegate; this.password = password; diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java index 39a84f36ce0ed..59588d1fcedfc 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerCli.java @@ -34,6 +34,9 @@ import java.util.Arrays; import java.util.Locale; +/** + * The main CLI for running Elasticsearch. + */ class ServerCli extends EnvironmentAwareCommand { private static final Logger logger = LogManager.getLogger(ServerCli.class); @@ -86,10 +89,7 @@ public void execute(Terminal terminal, OptionSet options, Environment env, Proce this.server = startServer(terminal, processInfo, args, env.pluginsFile()); if (options.has(daemonizeOption)) { - logger.info("Subprocess is started and we are daemonized, detaching..."); server.detach(); - this.server = null; // clear the handle, we don't want to shut it down now that we are started - logger.info("exiting..."); return; } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java index bb7bf28671a05..87c1d539dd813 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -29,9 +29,23 @@ import java.util.List; import java.util.Map; -import static org.elasticsearch.server.cli.JvmOptionsParser.determineJvmOptions; import static org.elasticsearch.server.cli.ProcessUtil.nonInterruptible; +/** + * A helper to control a {@link Process} running the main Elasticsearch server. + * + *

The process can be started by calling {@link #start(Terminal, ProcessInfo, ServerArgs, Path)}. + * The process is controlled by internally sending arguments and control signals on stdin, + * and receiving control signals on stderr. The start method does not return until the + * server is ready to process requests and has exited the bootstrap thread. + * + *

The caller starting a {@link ServerProcess} can do one of several things: + *

    + *
  • Block on the server process exiting, by calling {@link #waitFor()}
  • + *
  • Detach from the server process by calling {@link #detach()}
  • + *
  • Tell the server process to shutdown and wait for it by calling {@link #stop()}
  • + *
+ */ public class ServerProcess { private static final Logger logger = LogManager.getLogger(ServerProcess.class); @@ -58,6 +72,16 @@ interface ProcessStarter { Process start(ProcessBuilder pb) throws IOException; } + /** + * Start a server in a new process. + * + * @param terminal A terminal to connect the standard inputs and outputs to for the new process. + * @param processInfo Info about the current process, for passing through to the subprocess. + * @param args Arguments to the server process. + * @param pluginsDir The directory in which plugins can be found + * @return A running server process that is ready for requests + * @throws UserException If the process failed during bootstrap + */ public static ServerProcess start(Terminal terminal, ProcessInfo processInfo, ServerArgs args, Path pluginsDir) throws UserException { return start(terminal, processInfo, args, pluginsDir, JvmOptionsParser::determineJvmOptions, ProcessBuilder::start); } @@ -102,11 +126,19 @@ static ServerProcess start( return new ServerProcess(jvmProcess, errorPump); } + /** + * Detaches the server process from the current process, enabling the current process to exit. + * + * @throws IOException If an I/O error occured while reading stderr or closing any of the standard streams + */ public synchronized void detach() throws IOException { errorPump.drain(); IOUtils.close(jvmProcess.getOutputStream(), jvmProcess.getInputStream(), jvmProcess.getErrorStream()); } + /** + * Waits for the subprocess to exit. + */ public void waitFor() { int exitCode = nonInterruptible(jvmProcess::waitFor); if (exitCode != ExitCodes.OK) { diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index 745bebeded8bb..d2f162a4f32d9 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -24,6 +24,9 @@ import java.util.List; import java.util.Map; +/** + * A CLI for managing Elasticsearch as a Windows Service. + */ class WindowsServiceCli extends MultiCommand { private static final Command installCommand = new ProcrunCommand("Install Elasticsearch as a Windows Service", "IS") { diff --git a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java index 52747f9f5e1e8..27c34408c5f86 100644 --- a/libs/cli/src/main/java/org/elasticsearch/cli/Command.java +++ b/libs/cli/src/main/java/org/elasticsearch/cli/Command.java @@ -85,6 +85,11 @@ protected void mainWithoutErrorHandling(String[] args, Terminal terminal, Proces execute(terminal, options, processInfo); } + /** + * Parse command line arguments for this command. + * @param args The string arguments passed to the command + * @return A set of parsed options + */ public OptionSet parseOptions(String[] args) { return parser.parse(args); } diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java index 9310755d648f4..5171ea20626b7 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java @@ -162,8 +162,7 @@ public void test42BundledJdkRemoved() throws Exception { public void test50Remove() throws Exception { // add fake bin directory as if a plugin was installed Files.createDirectories(installation.bin.resolve("myplugin")); - - logger.info(sh.run("journalctl -u elasticsearch.service").stdout()); + remove(distribution()); // removing must stop the service From c6c0da5305cc58100d8ac41f5565f1af4badcf93 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 16 May 2022 08:58:16 -0700 Subject: [PATCH 152/166] more javadocs --- .../bootstrap/BootstrapInfo.java | 17 +++++++- .../bootstrap/BootstrapUtil.java | 26 ------------ .../bootstrap/Elasticsearch.java | 41 +++++++++++-------- .../elasticsearch/bootstrap/ServerArgs.java | 13 ++++++ 4 files changed, 53 insertions(+), 44 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java index 6e297ddf1f06e..9f9e404ba4b90 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapInfo.java @@ -63,12 +63,25 @@ public static ConsoleLoader.Console getConsole() { */ public static final String UNTRUSTED_CODEBASE = "/untrusted"; - // TODO: document + /** + * A non-printable character denoting a UserException has occurred. + * + * This is sent over stderr to the controlling CLI process. + */ public static final char USER_EXCEPTION_MARKER = '\u0015'; - // TODO: document + /** + * A non-printable character denoting the server is ready to process requests. + * + * This is sent over stderr to the controlling CLI process. + */ public static final char SERVER_READY_MARKER = '\u0018'; + /** + * A non-printable character denoting the server should shut itself down. + * + * This is sent over stdin from the controlling CLI process. + */ public static final char SERVER_SHUTDOWN_MARKER = '\u001B'; // create a view of sysprops map that does not allow modifications diff --git a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java index df293423c5291..7ff0bfeb4c76d 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/BootstrapUtil.java @@ -8,17 +8,11 @@ package org.elasticsearch.bootstrap; -import org.elasticsearch.cli.Terminal; import org.elasticsearch.common.settings.KeyStoreWrapper; import org.elasticsearch.common.settings.SecureSettings; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.env.Environment; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; - /** * Utilities for use during bootstrap. This is public so that tests may use these methods. */ @@ -27,26 +21,6 @@ public class BootstrapUtil { // no construction private BootstrapUtil() {} - // TODO: remove this method - /** - * Read from an InputStream up to the first carriage return or newline, - * returning no more than maxLength characters. - */ - public static SecureString readPassphrase(InputStream stream) throws IOException { - SecureString passphrase; - - try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) { - passphrase = new SecureString(Terminal.readLineToCharArray(reader)); - } - - if (passphrase.length() == 0) { - passphrase.close(); - throw new IllegalStateException("Keystore passphrase required but none provided."); - } - - return passphrase; - } - public static SecureSettings loadSecureSettings(Environment initialEnv, SecureString keystorePassword) throws BootstrapException { try { return KeyStoreWrapper.bootstrap(initialEnv.configFile(), () -> keystorePassword); diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 3edb0b752accd..12f62f4db8727 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -20,6 +20,7 @@ import org.elasticsearch.node.NodeValidationException; import java.io.IOException; +import java.io.InputStream; import java.io.PrintStream; import java.nio.file.Path; import java.security.Permission; @@ -58,10 +59,8 @@ public void checkPermission(Permission perm) { PrintStream out = getStdout(); PrintStream err = getStderr(); try { - err.println("Reading args from server-cli"); final var in = new InputStreamStreamInput(System.in); final ServerArgs serverArgs = new ServerArgs(in); - err.println(serverArgs); elasticsearch.init( serverArgs.daemonize(), serverArgs.pidFile(), @@ -75,20 +74,7 @@ public void checkPermission(Permission perm) { out.close(); err.close(); } else { - new Thread(() -> { - int msg = -1; - try { - msg = in.read(); - } catch (IOException e) {} - if (msg == BootstrapInfo.SERVER_SHUTDOWN_MARKER) { - out.println("Got shutdown signal from parent process"); - exit(0); - } else { - err.println("Parent process died, shutting down..."); - // parent process died or there was an error reading from it - exit(1); - } - }).start(); + startCliMonitorThread(System.in); } } catch (NodeValidationException e) { @@ -159,6 +145,29 @@ static void printLogsSuggestion(PrintStream err) { } } + /** + * Starts a thread that monitors stdin for a shutdown signal. + * + * If the shutdown signal is received, Elasticsearch exits with status code 0. + * If the pipe is broken, Elasticsearch exits with status code 1. + * + * @param stdin Standard input for this process + */ + private static void startCliMonitorThread(InputStream stdin) { + new Thread(() -> { + int msg = -1; + try { + msg = stdin.read(); + } catch (IOException e) {} + if (msg == BootstrapInfo.SERVER_SHUTDOWN_MARKER) { + exit(0); + } else { + // parent process died or there was an error reading from it + exit(1); + } + }).start(); + } + private static void overrideDnsCachePolicyProperties() { for (final String property : new String[] { "networkaddress.cache.ttl", "networkaddress.cache.negative.ttl" }) { final String overrideProperty = "es." + property; diff --git a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java index 9305a90e7208b..8adb791d5db52 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/ServerArgs.java @@ -19,6 +19,16 @@ import java.io.IOException; import java.nio.file.Path; +/** + * Arguments for running Elasticsearch. + * + * @param daemonize {@code true} if Elasticsearch should run as a daemon process, or {@code false} otherwise + * @param quiet {@code false} if Elasticsearch should print log output to the console, {@code true} otherwise + * @param pidFile a path to a file Elasticsearch should write its process id to, or {@code null} if no pid file should be written + * @param keystorePassword the password for the Elasticsearch keystore + * @param nodeSettings the node settings read from {@code elasticsearch.yml}, the cli and the process environment + * @param configDir the directory where {@code elasticsearch.yml} and other config exists + */ public record ServerArgs( boolean daemonize, boolean quiet, @@ -28,6 +38,9 @@ public record ServerArgs( Path configDir ) implements Writeable { + /** + * Alternate constructor to read the args from a binary stream. + */ public ServerArgs(StreamInput in) throws IOException { this( in.readBoolean(), From 4aa295340ba546cf88ee8312cb38dcc807cb5120 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 16 May 2022 08:58:34 -0700 Subject: [PATCH 153/166] spotless --- .../java/org/elasticsearch/packaging/test/PackageTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java index 5171ea20626b7..5c38fa36a6640 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/PackageTests.java @@ -162,7 +162,7 @@ public void test42BundledJdkRemoved() throws Exception { public void test50Remove() throws Exception { // add fake bin directory as if a plugin was installed Files.createDirectories(installation.bin.resolve("myplugin")); - + remove(distribution()); // removing must stop the service From efabb338cfada361859744fc2ef540597d312f18 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Mon, 16 May 2022 09:00:32 -0700 Subject: [PATCH 154/166] forbidden --- distribution/tools/server-cli/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distribution/tools/server-cli/build.gradle b/distribution/tools/server-cli/build.gradle index 9ba7079567208..3ab5e6e86f5ba 100644 --- a/distribution/tools/server-cli/build.gradle +++ b/distribution/tools/server-cli/build.gradle @@ -20,9 +20,9 @@ tasks.named("test").configure { systemProperty "tests.security.manager", "false" } -/*tasks.withType(CheckForbiddenApis).configureEach { +tasks.withType(CheckForbiddenApis).configureEach { replaceSignatureFiles 'jdk-signatures' -}*/ +} ["javadoc", "loggerUsageCheck", "jarHell"].each { tsk -> tasks.named(tsk).configure { enabled = false } From cf0fe874b8302979aed6a15cd71c2dbfdc1a665f Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 17 May 2022 08:47:46 -0700 Subject: [PATCH 155/166] windows service tests --- .../org.elasticsearch.cli.CliToolProvider | 1 - .../tools/windows-service-cli/build.gradle | 1 + .../windows/service/ProcrunCommand.java | 42 +++- .../windows/service/WindowsService.java} | 12 +- .../windows/service/WindowsServiceCli.java | 195 +--------------- .../service/WindowsServiceInstallCommand.java | 148 +++++++++++++ .../service/WindowsServiceManagerCommand.java | 38 ++++ .../service/WindowsServiceProvider.java} | 8 +- .../service/WindowsServiceRemoveCommand.java | 28 +++ .../service/WindowsServiceStartCommand.java | 28 +++ .../service/WindowsServiceStopCommand.java | 28 +++ .../org.elasticsearch.cli.CliToolProvider | 1 + .../windows/service/ProcrunCommandTests.java | 160 ++++++++++++++ .../service/WindowsServiceCliTestCase.java | 208 ++++++++++++++++++ .../service/WindowsServiceCliTests.java | 52 ----- .../WindowsServiceInstallCommandTests.java | 188 ++++++++++++++++ .../WindowsServiceManagerCommandTests.java | 50 +++++ .../WindowsServiceRemoveCommandTests.java | 40 ++++ .../WindowsServiceStartCommandTests.java | 40 ++++ .../WindowsServiceStopCommandTests.java | 40 ++++ 20 files changed, 1054 insertions(+), 254 deletions(-) rename distribution/tools/{server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java => windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsService.java} (78%) create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceManagerCommand.java rename distribution/tools/{server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServerProvider.java => windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceProvider.java} (73%) create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommand.java create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStartCommand.java create mode 100644 distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStopCommand.java create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTestCase.java delete mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceManagerCommandTests.java create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommandTests.java create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStartCommandTests.java create mode 100644 distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStopCommandTests.java diff --git a/distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider b/distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider index 96c0af3c88d85..7a07f0225080a 100644 --- a/distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider +++ b/distribution/tools/server-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider @@ -1,2 +1 @@ org.elasticsearch.server.cli.ServerCliProvider -org.elasticsearch.server.cli.WindowsServiceServerProvider diff --git a/distribution/tools/windows-service-cli/build.gradle b/distribution/tools/windows-service-cli/build.gradle index d1304a5aaeed0..103e5322913b9 100644 --- a/distribution/tools/windows-service-cli/build.gradle +++ b/distribution/tools/windows-service-cli/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'elasticsearch.java' dependencies { compileOnly project(":server") compileOnly project(":libs:elasticsearch-cli") + compileOnly project(":distribution:tools:server-cli") testImplementation project(":test:framework") } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java index 7fc7247e63ce7..c10495d3b8af6 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/ProcrunCommand.java @@ -12,12 +12,14 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.util.Supplier; import org.elasticsearch.cli.Command; import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -27,17 +29,30 @@ /** * Base command for interacting with Apache procrun executable. + * + * @see Apache Procrun Docs */ abstract class ProcrunCommand extends Command { private static final Logger logger = LogManager.getLogger(ProcrunCommand.class); private final String cmd; + /** + * Constructs CLI subcommand that will internally call procrun. + * @param desc A help description for this subcommand + * @param cmd The procrun command to run + */ protected ProcrunCommand(String desc, String cmd) { super(desc); this.cmd = cmd; } + /** + * Returns the name of the exe within the Elasticsearch bin dir to run. + * + *

Procrun comes with two executables, {@code prunsrv.exe} and {@code prunmgr.exe}. These are renamed by + * Elasticsearch to {@code elasticsearch-service-x64.exe} and {@code elasticsearch-service-mgr.exe}, respectively. + */ protected String getExecutable() { return "elasticsearch-service-x64.exe"; } @@ -60,9 +75,9 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process procrunCmd.add(getAdditionalArgs(serviceId, processInfo)); ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/C", String.join(" ", procrunCmd).trim()); - logger.info("Running procrun: " + String.join(" ", processBuilder.command())); + logger.debug((Supplier) () -> "Running procrun: " + String.join(" ", processBuilder.command())); processBuilder.inheritIO(); - Process process = processBuilder.start(); + Process process = startProcess(processBuilder); int ret = process.waitFor(); if (ret != ExitCodes.OK) { throw new UserException(ret, getFailureMessage(serviceId)); @@ -71,10 +86,11 @@ protected void execute(Terminal terminal, OptionSet options, ProcessInfo process } } + /** Determines the service id for the Elasticsearch service that should be used */ private String getServiceId(OptionSet options, Map env) throws UserException { List args = options.nonOptionArguments(); if (args.size() > 1) { - throw new UserException(ExitCodes.USAGE, null); + throw new UserException(ExitCodes.USAGE, "too many arguments, expected one service id"); } final String serviceId; if (args.size() > 0) { @@ -85,6 +101,7 @@ private String getServiceId(OptionSet options, Map env) throws U return serviceId; } + /** Determines the logging arguments that should be passed to the procrun command */ private String getLogArgs(String serviceId, Path esHome, Map env) { String logArgs = env.get("LOG_OPTS"); if (logArgs != null && logArgs.isBlank() == false) { @@ -98,17 +115,36 @@ private String getLogArgs(String serviceId, Path esHome, Map env return String.format(Locale.ROOT, logArgsFormat, logsDir, serviceId); } + /** + * Gets arguments that should be passed to the procrun command. + * + * @param serviceId The service id of the Elasticsearch service + * @param processInfo The current process info + * @return The additional arguments, space delimited + */ protected String getAdditionalArgs(String serviceId, ProcessInfo processInfo) { return ""; } + /** Return whether logging args should be added to the procrun command */ protected boolean includeLogArgs() { return true; } + /** + * A hook to add logging and validation before executing the procrun command. + * @throws UserException if there is a problem with the command invocation + */ protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException {} + /** Returns a message that should be output on success of the procrun command */ protected abstract String getSuccessMessage(String serviceId); + /** Returns a message that should be output on failure of the procrun command */ protected abstract String getFailureMessage(String serviceId); + + // package private to allow tests to override + Process startProcess(ProcessBuilder processBuilder) throws IOException { + return processBuilder.start(); + } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsService.java similarity index 78% rename from distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsService.java index 36839c19b4849..a3cd4a624a292 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServer.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsService.java @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -package org.elasticsearch.server.cli; +package org.elasticsearch.windows.service; import joptsimple.OptionSet; @@ -16,17 +16,19 @@ import org.elasticsearch.common.cli.EnvironmentAwareCommand; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.env.Environment; +import org.elasticsearch.server.cli.ServerProcess; /** - * Starts an Elasticsearch server process, but does not wait for it. + * Starts an Elasticsearch process, but does not wait for it to exit. * - * Closing this cli will stop the server process. + * This class is expected to be run via Apache Procrun in a long lived JVM that will call close + * when the server should shutdown. */ -class WindowsServiceServer extends EnvironmentAwareCommand { +class WindowsService extends EnvironmentAwareCommand { private volatile ServerProcess server; - WindowsServiceServer() { + WindowsService() { super("Starts and stops the Elasticsearch server process for a Windows Service"); } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java index d2f162a4f32d9..6f9a7e6b2169f 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceCli.java @@ -8,203 +8,20 @@ package org.elasticsearch.windows.service; -import org.elasticsearch.Version; -import org.elasticsearch.cli.Command; -import org.elasticsearch.cli.ExitCodes; import org.elasticsearch.cli.MultiCommand; -import org.elasticsearch.cli.ProcessInfo; -import org.elasticsearch.cli.Terminal; -import org.elasticsearch.cli.UserException; -import org.elasticsearch.core.SuppressForbidden; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; /** * A CLI for managing Elasticsearch as a Windows Service. */ class WindowsServiceCli extends MultiCommand { - private static final Command installCommand = new ProcrunCommand("Install Elasticsearch as a Windows Service", "IS") { - @Override - protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { - List args = new ArrayList<>(); - addArg(args, "--Startup", pinfo.envVars().getOrDefault("ES_START_TYPE", "manual")); - addArg(args, "--StopTimeout", pinfo.envVars().getOrDefault("ES_STOP_TIMEOUT", "0")); - addArg(args, "--StartClass", "org.elasticsearch.launcher.CliToolLauncher"); - addArg(args, "--StartMethod", "main"); - addArg(args, "--StopClass", "org.elasticsearch.launcher.CliToolLauncher"); - addArg(args, "--StopMethod", "close"); - addArg(args, "--Classpath", pinfo.sysprops().get("java.class.path")); - addArg(args, "--JvmMs", "4m"); - addArg(args, "--JvmMx", "64m"); - addArg(args, "--JvmOptions", getJvmOptions(pinfo.sysprops())); - addArg(args, "--PidFile", "%s.pid".formatted(serviceId)); - addArg( - args, - "--DisplayName", - pinfo.envVars().getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch %s (%s)".formatted(Version.CURRENT, serviceId)) - ); - addArg( - args, - "--Description", - pinfo.envVars().getOrDefault("SERVICE_DESCRIPTION", "Elasticsearch ES_VERSION Windows Service - https://elastic.co") - ); - addArg(args, "--Jvm", getJvmDll(getJavaHome(pinfo.sysprops())).toString()); - addArg(args, "--StartMode", "jvm"); - addArg(args, "--StopMode", "jvm"); - addArg(args, "--StartPath", pinfo.workingDir().toString()); - addArg(args, "++JvmOptions", "-Dcli.name=windows-service-server"); - addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli"); - addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))); - - String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); - if (serviceUsername != null) { - String servicePassword = pinfo.envVars().get("SERVICE_PASSWORD"); - if (servicePassword != null) { - addArg(args, "--ServiceUser", serviceUsername); - addArg(args, "--ServicePassword", servicePassword); - } // else WHY ISN'T THIS AN ERROR? username provided but no password... - } else { - addArg(args, "--ServiceUser", "LocalSystem"); - } - - String serviceParams = pinfo.envVars().get("SERVICE_PARAMS"); - if (serviceParams != null) { - args.add(serviceParams); - } - - return String.join(" ", args); - } - - private static void addArg(List args, String arg, String value) { - args.add(arg); - if (value.contains(" ")) { - value = "\"%s\"".formatted(value); - } - args.add(value); - } - - @SuppressForbidden(reason = "get java home path to pass through") - private static Path getJavaHome(Map sysprops) { - return Paths.get(sysprops.get("java.home")); - } - - private static Path getJvmDll(Path javaHome) { - Path dll = javaHome.resolve("jre/bin/server/jvm.dll"); - if (Files.exists(dll) == false) { - dll = javaHome.resolve("bin/server/jvm.dll"); - } - return dll; - } - - private static String getJvmOptions(Map sysprops) { - List jvmOptions = new ArrayList<>(); - jvmOptions.add("-XX:+UseSerialGC"); - // passthrough these properties - for (var prop : List.of("es.path.home", "es.path.conf", "es.distribution.type")) { - jvmOptions.add("-D%s=%s".formatted(prop, sysprops.get(prop))); - } - return String.join(";", jvmOptions); - } - - @Override - protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException { - Path javaHome = getJavaHome(pinfo.sysprops()); - terminal.println("Installing service : %s".formatted(serviceId)); - terminal.println("Using ES_JAVA_HOME : %s".formatted(javaHome.toString())); - - Path javaDll = getJvmDll(javaHome); - if (Files.exists(javaDll) == false) { - throw new UserException( - ExitCodes.CONFIG, - "Invalid java installation (no jvm.dll found in %s\\jre\\bin\\server\\ or %s\\bin\\server\"). Exiting...".formatted( - javaHome.toString(), - javaHome.toString() - ) - ); - } - } - - @Override - protected String getSuccessMessage(String serviceId) { - return "The service '%s' has been installed".formatted(serviceId); - } - - @Override - protected String getFailureMessage(String serviceId) { - return "Failed installing '%s' service".formatted(serviceId); - } - }; - - private static final Command removeCommand = new ProcrunCommand("Remove the Elasticsearch Windows Service", "DS") { - @Override - protected String getSuccessMessage(String serviceId) { - return "The service '%s' has been removed".formatted(serviceId); - } - - @Override - protected String getFailureMessage(String serviceId) { - return "Failed removing '%s' service".formatted(serviceId); - } - }; - - private static final Command startCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service", "ES") { - @Override - protected String getSuccessMessage(String serviceId) { - return "The service '%s' has been started".formatted(serviceId); - } - - @Override - protected String getFailureMessage(String serviceId) { - return "Failed starting '%s' service".formatted(serviceId); - } - }; - - private static final Command stopCommand = new ProcrunCommand("Stops the Elasticsearch Windows Service", "SS") { - @Override - protected String getSuccessMessage(String serviceId) { - return "The service '%s' has been stopped".formatted(serviceId); - } - - @Override - protected String getFailureMessage(String serviceId) { - return "Failed stopping '%s' service".formatted(serviceId); - } - }; - - private static final Command managerCommand = new ProcrunCommand("Starts the Elasticsearch Windows Service manager", "ES") { - @Override - protected String getExecutable() { - return "elasticsearch-service-mgr.exe"; - } - - @Override - protected boolean includeLogArgs() { - return false; - } - - @Override - protected String getSuccessMessage(String serviceId) { - return "Successfully started service manager for '%s'".formatted(serviceId); - } - - @Override - protected String getFailureMessage(String serviceId) { - return "Failed starting service manager for '%s'".formatted(serviceId); - } - }; - WindowsServiceCli() { super("A tool for managing Elasticsearch as a Windows service"); - subcommands.put("install", installCommand); - subcommands.put("remove", removeCommand); - subcommands.put("start", startCommand); - subcommands.put("stop", stopCommand); - subcommands.put("manager", managerCommand); + subcommands.put("install", new WindowsServiceInstallCommand()); + subcommands.put("remove", new WindowsServiceRemoveCommand()); + subcommands.put("start", new WindowsServiceStartCommand()); + subcommands.put("stop", new WindowsServiceStopCommand()); + subcommands.put("manager", new WindowsServiceManagerCommand()); } + } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java new file mode 100644 index 0000000000000..91e40de1f6540 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java @@ -0,0 +1,148 @@ +/* + * 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.windows.service; + +import org.elasticsearch.Version; +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.ProcessInfo; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; +import org.elasticsearch.core.SuppressForbidden; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Elasticsearch the Elasticsearch Windows service into the Windows Service Registry. + */ +class WindowsServiceInstallCommand extends ProcrunCommand { + WindowsServiceInstallCommand() { + super("Install Elasticsearch as a Windows Service", "IS"); + } + + @Override + protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { + List args = new ArrayList<>(); + addArg(args, "--Startup", pinfo.envVars().getOrDefault("ES_START_TYPE", "manual")); + addArg(args, "--StopTimeout", pinfo.envVars().getOrDefault("ES_STOP_TIMEOUT", "0")); + addArg(args, "--StartClass", "org.elasticsearch.launcher.CliToolLauncher"); + addArg(args, "--StartMethod", "main"); + addArg(args, "--StopClass", "org.elasticsearch.launcher.CliToolLauncher"); + addArg(args, "--StopMethod", "close"); + addArg(args, "--Classpath", pinfo.sysprops().get("java.class.path")); + addArg(args, "--JvmMs", "4m"); + addArg(args, "--JvmMx", "64m"); + addArg(args, "--JvmOptions", getJvmOptions(pinfo.sysprops())); + addArg(args, "--PidFile", "%s.pid".formatted(serviceId)); + addArg( + args, + "--DisplayName", + pinfo.envVars().getOrDefault("SERVICE_DISPLAY_NAME", "Elasticsearch %s (%s)".formatted(Version.CURRENT, serviceId)) + ); + addArg( + args, + "--Description", + pinfo.envVars().getOrDefault("SERVICE_DESCRIPTION", + "Elasticsearch %s Windows Service - https://elastic.co".formatted(Version.CURRENT)) + ); + addArg(args, "--Jvm", getJvmDll(getJavaHome(pinfo.sysprops())).toString()); + addArg(args, "--StartMode", "jvm"); + addArg(args, "--StopMode", "jvm"); + addArg(args, "--StartPath", pinfo.workingDir().toString()); + addArg(args, "++JvmOptions", "-Dcli.name=windows-service"); + addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli,lib/tools/windows-service-cli"); + addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))); + + String serviceUsername = pinfo.envVars().get("SERVICE_USERNAME"); + if (serviceUsername != null) { + String servicePassword = pinfo.envVars().get("SERVICE_PASSWORD"); + assert servicePassword != null; // validated in preExecute + addArg(args, "--ServiceUser", serviceUsername); + addArg(args, "--ServicePassword", servicePassword); + } else { + addArg(args, "--ServiceUser", "LocalSystem"); + } + + String serviceParams = pinfo.envVars().get("SERVICE_PARAMS"); + if (serviceParams != null) { + args.add(serviceParams); + } + + return String.join(" ", args); + } + + private static void addArg(List args, String arg, String value) { + args.add(arg); + if (value.contains(" ")) { + value = "\"%s\"".formatted(value); + } + args.add(value); + } + + @SuppressForbidden(reason = "get java home path to pass through") + private static Path getJavaHome(Map sysprops) { + return Paths.get(sysprops.get("java.home")); + } + + private static Path getJvmDll(Path javaHome) { + Path dll = javaHome.resolve("jre/bin/server/jvm.dll"); + if (Files.exists(dll) == false) { + dll = javaHome.resolve("bin/server/jvm.dll"); + } + return dll; + } + + private static String getJvmOptions(Map sysprops) { + List jvmOptions = new ArrayList<>(); + jvmOptions.add("-XX:+UseSerialGC"); + // passthrough these properties + for (var prop : List.of("es.path.home", "es.path.conf", "es.distribution.type")) { + jvmOptions.add("-D%s=%s".formatted(prop, sysprops.get(prop))); + } + return String.join(";", jvmOptions); + } + + @Override + protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException { + Path javaHome = getJavaHome(pinfo.sysprops()); + terminal.println("Installing service : %s".formatted(serviceId)); + terminal.println("Using ES_JAVA_HOME : %s".formatted(javaHome.toString())); + + Path javaDll = getJvmDll(javaHome); + if (Files.exists(javaDll) == false) { + throw new UserException( + ExitCodes.CONFIG, + "Invalid java installation (no jvm.dll found in %s\\jre\\bin\\server\\ or %s\\bin\\server\"). Exiting...".formatted( + javaHome.toString(), + javaHome.toString() + ) + ); + } + + // validate username and password come together + boolean hasUsername = pinfo.envVars().containsKey("SERVICE_USERNAME"); + if (pinfo.envVars().containsKey("SERVICE_PASSWORD") != hasUsername) { + throw new UserException(ExitCodes.CONFIG, "Both service username and password must be set, only got " + (hasUsername ? "SERVICE_USERNAME" : "SERVICE_PASSWORD")); + } + } + + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been installed".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed installing '%s' service".formatted(serviceId); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceManagerCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceManagerCommand.java new file mode 100644 index 0000000000000..a0c3a4e11e208 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceManagerCommand.java @@ -0,0 +1,38 @@ +/* + * 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.windows.service; + +/** + * Runs the procrun GUI manager for the Elasticsearch Windows service. + */ +class WindowsServiceManagerCommand extends ProcrunCommand { + WindowsServiceManagerCommand() { + super("Starts the Elasticsearch Windows Service manager", "ES"); + } + + @Override + protected String getExecutable() { + return "elasticsearch-service-mgr.exe"; + } + + @Override + protected boolean includeLogArgs() { + return false; + } + + @Override + protected String getSuccessMessage(String serviceId) { + return "Successfully started service manager for '%s'".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed starting service manager for '%s'".formatted(serviceId); + } +} diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServerProvider.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceProvider.java similarity index 73% rename from distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServerProvider.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceProvider.java index de97779105a6f..859782c9cb140 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/WindowsServiceServerProvider.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceProvider.java @@ -6,19 +6,19 @@ * Side Public License, v 1. */ -package org.elasticsearch.server.cli; +package org.elasticsearch.windows.service; import org.elasticsearch.cli.CliToolProvider; import org.elasticsearch.cli.Command; -public class WindowsServiceServerProvider implements CliToolProvider { +public class WindowsServiceProvider implements CliToolProvider { @Override public String name() { - return "windows-service-server"; + return "windows-service"; } @Override public Command create() { - return new WindowsServiceServer(); + return new WindowsService(); } } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommand.java new file mode 100644 index 0000000000000..c9df3639fbf38 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommand.java @@ -0,0 +1,28 @@ +/* + * 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.windows.service; + +/** + * Removes the Elasticsearch Windows service, first stopping it if it is running. + */ +class WindowsServiceRemoveCommand extends ProcrunCommand { + WindowsServiceRemoveCommand() { + super("Remove the Elasticsearch Windows Service", "DS"); + } + + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been removed".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed removing '%s' service".formatted(serviceId); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStartCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStartCommand.java new file mode 100644 index 0000000000000..8f048fb39045e --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStartCommand.java @@ -0,0 +1,28 @@ +/* + * 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.windows.service; + +/** + * Starts the Elasticsearch Windows service. + */ +class WindowsServiceStartCommand extends ProcrunCommand { + WindowsServiceStartCommand() { + super("Starts the Elasticsearch Windows Service", "ES"); + } + + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been started".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed starting '%s' service".formatted(serviceId); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStopCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStopCommand.java new file mode 100644 index 0000000000000..7b880f501c6ae --- /dev/null +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceStopCommand.java @@ -0,0 +1,28 @@ +/* + * 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.windows.service; + +/** + * Stops the Elasticsearch Windows service. + */ +class WindowsServiceStopCommand extends ProcrunCommand { + WindowsServiceStopCommand() { + super("Stops the Elasticsearch Windows Service", "SS"); + } + + @Override + protected String getSuccessMessage(String serviceId) { + return "The service '%s' has been stopped".formatted(serviceId); + } + + @Override + protected String getFailureMessage(String serviceId) { + return "Failed stopping '%s' service".formatted(serviceId); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider index 23f39460f9f37..a2b1d3e183143 100644 --- a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider +++ b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider @@ -1 +1,2 @@ org.elasticsearch.windows.service.WindowsServiceCliProvider +org.elasticsearch.windows.service.WindowsServiceProvider diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java new file mode 100644 index 0000000000000..ffc46c4a53692 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java @@ -0,0 +1,160 @@ +/* + * 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.windows.service; + +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.ProcessInfo; +import org.elasticsearch.cli.Terminal; +import org.elasticsearch.cli.UserException; + +import java.io.IOException; +import java.nio.file.Files; +import java.util.Map; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.equalTo; + +public class ProcrunCommandTests extends WindowsServiceCliTestCase { + + PreExecuteHook preExecuteHook = null; + boolean includeLogArgs = false; + String additionalArgs = ""; + + interface PreExecuteHook { + void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException; + } + + class TestProcrunCommand extends ProcrunCommand { + + protected TestProcrunCommand() { + super("test command", "DC"); + } + + @Override + protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException { + if (preExecuteHook != null) { + preExecuteHook.preExecute(terminal, pinfo, serviceId); + } + } + + protected String getAdditionalArgs(String serviceId, ProcessInfo processInfo) { + return additionalArgs; + } + + @Override + protected boolean includeLogArgs() { + return includeLogArgs; + } + + @Override + protected String getSuccessMessage(String serviceId) { + return "success message for " + serviceId; + } + + @Override + protected String getFailureMessage(String serviceId) { + return "failure message for " + serviceId; + } + + @Override + Process startProcess(ProcessBuilder processBuilder) throws IOException { + return mockProcess(processBuilder); + } + } + + @Override + protected Command newCommand() { + return new TestProcrunCommand(); + } + + @Override + protected boolean includeLogsArgs() { + return includeLogArgs; + } + + @Override + protected String getCommand() { + return "DC"; + } + + @Override + protected String getDefaultSuccessMessage() { + return "success message for elasticsearch-service-x64"; + } + + @Override + protected String getDefaultFailureMessage() { + return "failure message for elasticsearch-service-x64"; + } + + public void testMissingExe() throws Exception { + Files.delete(serviceExe); + var e = expectThrows(IllegalStateException.class, () -> executeMain("install")); + assertThat(e.getMessage(), containsString("Missing procrun exe")); + } + + public void testServiceId() throws Exception { + assertUsage(containsString("too many arguments"), "servicename", "servicename"); + terminal.reset(); + preExecuteHook = (terminal, pinfo, serviceId) -> assertThat(serviceId, equalTo("my-service-id")); + assertOkWithOutput(containsString("success"), emptyString(), "my-service-id"); + terminal.reset(); + envVars.put("SERVICE_ID", "my-service-id"); + assertOkWithOutput(containsString("success"), emptyString()); + } + + public void testPreExecuteError() throws Exception { + preExecuteHook = (terminal, pinfo, serviceId) -> { throw new UserException(ExitCodes.USAGE, "validation error"); }; + assertUsage(containsString("validation error")); + } + + void assertLogArgs(Map logArgs) throws Exception { + terminal.reset(); + includeLogArgs = true; + assertServiceArgs(logArgs); + } + + public void testDefaultLogArgs() throws Exception { + String logsDir = esHomeDir.resolve("logs").toString(); + assertLogArgs( + Map.of( + "--LogPath", + "\"" + logsDir + "\"", + "--LogPrefix", + "\"elasticsearch-service-x64\"", + "--StdError", + "auto", + "--StdOutput", + "auto" + ) + ); + } + + public void testLogOpts() throws Exception { + envVars.put("LOG_OPTS", "--LogPath custom"); + assertLogArgs(Map.of("--LogPath", "custom")); + } + + public void testLogDir() throws Exception { + envVars.put("SERVICE_LOG_DIR", "mylogdir"); + assertLogArgs(Map.of("--LogPath", "\"mylogdir\"")); + } + + public void testLogPrefix() throws Exception { + envVars.put("SERVICE_ID", "myservice"); + assertLogArgs(Map.of("--LogPrefix", "\"myservice\"")); + } + + public void testAdditionalArgs() throws Exception { + additionalArgs = "--Foo bar"; + assertServiceArgs(Map.of("Foo", "bar")); + } +} diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTestCase.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTestCase.java new file mode 100644 index 0000000000000..b727774ea2d1d --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTestCase.java @@ -0,0 +1,208 @@ +/* + * 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.windows.service; + +import org.elasticsearch.cli.CommandTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.lang.ProcessBuilder.Redirect.INHERIT; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.startsWith; + +public abstract class WindowsServiceCliTestCase extends CommandTestCase { + + Path javaHome; + Path binDir; + Path serviceExe; + Path mgrExe; + int mockProcessExit = 0; + ProcessValidator mockProcessValidator = null; + + interface ProcessValidator { + void validate(Map env, ProcrunCall procrunCall); + } + + record ProcrunCall(String exe, String command, String serviceId, Map> args) {} + + class MockProcess extends Process { + + @Override + public OutputStream getOutputStream() { + throw new AssertionError("should not access output stream"); + } + + @Override + public InputStream getInputStream() { + throw new AssertionError("should not access input stream"); + } + + @Override + public InputStream getErrorStream() { + throw new AssertionError("should not access error stream"); + } + + @Override + public int waitFor() { + return mockProcessExit; + } + + @Override + public int exitValue() { + return mockProcessExit; + } + + @Override + public void destroy() { + throw new AssertionError("should not kill procrun process"); + } + } + + protected Process mockProcess(ProcessBuilder processBuilder) throws IOException { + assertThat(processBuilder.redirectInput(), equalTo(INHERIT)); + assertThat(processBuilder.redirectOutput(), equalTo(INHERIT)); + assertThat(processBuilder.redirectError(), equalTo(INHERIT)); + if (mockProcessValidator != null) { + var fullCommand = processBuilder.command(); + assertThat(fullCommand, hasSize(3)); + assertThat(fullCommand.get(0), equalTo("cmd.exe")); + assertThat(fullCommand.get(1), equalTo("/C")); + ProcrunCall procrunCall = parseProcrunCall(fullCommand.get(2)); + mockProcessValidator.validate(processBuilder.environment(), procrunCall); + } + return new MockProcess(); + } + + // args could have spaces in them, so splitting on string alone is not enough + // instead we look for the next --Foo and reconstitute the argument following it + private static final Pattern commandPattern = Pattern.compile("//([A-Z]{2})/([\\w-]+)"); + + private static ProcrunCall parseProcrunCall(String unparsedArgs) { + String[] splitArgs = unparsedArgs.split(" "); + assertThat(unparsedArgs, splitArgs.length, greaterThanOrEqualTo(2)); + Map> args = new HashMap<>(); + String exe = splitArgs[0]; + Matcher commandMatcher = commandPattern.matcher(splitArgs[1]); + assertThat(splitArgs[1], commandMatcher.matches(), is(true)); + String command = commandMatcher.group(1); + String serviceId = commandMatcher.group(2); + + int i = 2; + while (i < splitArgs.length) { + String arg = splitArgs[i]; + assertThat("procrun args begin with -- or ++", arg, anyOf(startsWith("--"), startsWith("++"))); + ++i; + assertThat("missing value for arg " + arg, i, lessThan(splitArgs.length)); + + List argValue = new ArrayList<>(); + while (i < splitArgs.length && splitArgs[i].startsWith("--") == false && splitArgs[i].startsWith("++") == false) { + argValue.add(splitArgs[i++]); + } + + String key = arg.substring(2); + args.compute(key, (k, value) -> { + if (arg.startsWith("--")) { + assertThat("overwriting existing arg: " + key, value, nullValue()); + } + if (value == null) { + // could be ++ implicitly creating new list, or -- above + value = new ArrayList<>(); + } + value.add(String.join(" ", argValue)); + return value; + }); + } + + return new ProcrunCall(exe, command, serviceId, args); + } + + @Before + public void resetMockProcess() throws Exception { + javaHome = createTempDir(); + Path javaBin = javaHome.resolve("bin"); + sysprops.put("java.home", javaHome.toString()); + binDir = esHomeDir.resolve("bin"); + Files.createDirectories(binDir); + serviceExe = binDir.resolve("elasticsearch-service-x64.exe"); + Files.createFile(serviceExe); + mgrExe = binDir.resolve("elasticsearch-service-mgr.exe"); + Files.createFile(mgrExe); + mockProcessExit = 0; + mockProcessValidator = null; + } + + protected abstract String getCommand(); + + protected abstract String getDefaultSuccessMessage(); + + protected abstract String getDefaultFailureMessage(); + + protected String getExe() { + return serviceExe.toString(); + } + + protected boolean includeLogsArgs() { + return true; + } + + public void testDefaultCommand() throws Exception { + mockProcessValidator = (environment, procrunCall) -> { + assertThat(procrunCall.exe, equalTo(getExe())); + assertThat(procrunCall.command, equalTo(getCommand())); + assertThat(procrunCall.serviceId, equalTo("elasticsearch-service-x64")); + if (includeLogsArgs()) { + assertThat(procrunCall.args, hasKey("LogPath")); + } else { + assertThat(procrunCall.args, not(hasKey("LogPath"))); + } + }; + assertOkWithOutput(containsString(getDefaultSuccessMessage()), emptyString()); + } + + public void testFailure() throws Exception { + mockProcessExit = 5; + assertThat(executeMain(), equalTo(5)); + assertThat(terminal.getErrorOutput(), containsString(getDefaultFailureMessage())); + } + + // for single value args + protected void assertServiceArgs(Map expectedArgs) throws Exception { + mockProcessValidator = (environment, procrunCall) -> { + for (var expected : expectedArgs.entrySet()) { + List value = procrunCall.args.get(expected.getKey()); + assertThat("missing arg " + expected.getKey(), value, notNullValue()); + assertThat(value.toString(), value, hasSize(1)); + assertThat(value.get(0), equalTo(expected.getValue())); + } + }; + assertOkWithOutput(containsString(getDefaultSuccessMessage()), emptyString()); + } +} diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java deleted file mode 100644 index b76e68bcc6a95..0000000000000 --- a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceCliTests.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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.windows.service; - -import org.elasticsearch.cli.Command; -import org.elasticsearch.cli.CommandTestCase; -import org.junit.Before; - -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.hamcrest.Matchers.containsString; - -public class WindowsServiceCliTests extends CommandTestCase { - - Path javaHome; - Path binDir; - Path serviceExe; - Path mgrExe; - - @Override - protected Command newCommand() { - return new WindowsServiceCli(); - } - - @Before - public void setupMockJvm() throws Exception { - javaHome = createTempDir(); - Path javaBin = javaHome.resolve("bin"); - sysprops.put("java.home", javaHome.toString()); - binDir = esHomeDir.resolve("bin"); - Files.createDirectories(binDir); - serviceExe = binDir.resolve("elasticsearch-service-x64.exe"); - Files.createFile(serviceExe); - mgrExe = binDir.resolve("elasticsearch-service-mgr.exe"); - Files.createFile(mgrExe); - } - - public void testMissingExe() throws Exception { - Files.delete(serviceExe); - var e = expectThrows(IllegalStateException.class, () -> executeMain("install")); - assertThat(e.getMessage(), containsString("Missing procrun exe")); - } - - // TODO: add tests for display name (and custom display name) -} diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java new file mode 100644 index 0000000000000..b2e6ed1daabf7 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java @@ -0,0 +1,188 @@ +/* + * 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.windows.service; + +import org.elasticsearch.Version; +import org.elasticsearch.cli.Command; +import org.elasticsearch.cli.ExitCodes; +import org.junit.Before; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import static java.util.Map.entry; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.any; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.equalTo; + +public class WindowsServiceInstallCommandTests extends WindowsServiceCliTestCase { + + Path jvmDll; + + @Before + public void setupJvm() throws Exception { + jvmDll = javaHome.resolve("jre/bin/server/jvm.dll"); + Files.createDirectories(jvmDll.getParent()); + Files.createFile(jvmDll); + sysprops.put("java.class.path", "javaclasspath"); + envVars.put("COMPUTERNAME", "mycomputer"); + } + + @Override + protected Command newCommand() { + return new WindowsServiceInstallCommand() { + @Override + Process startProcess(ProcessBuilder processBuilder) throws IOException { + return mockProcess(processBuilder); + } + }; + } + + @Override + protected String getCommand() { + return "IS"; + } + + @Override + protected String getDefaultSuccessMessage() { + return "The service 'elasticsearch-service-x64' has been installed"; + } + + @Override + protected String getDefaultFailureMessage() { + return "Failed installing 'elasticsearch-service-x64' service"; + } + + public void testDllMissing() throws Exception { + Files.delete(jvmDll); + assertThat(executeMain(), equalTo(ExitCodes.CONFIG)); + assertThat(terminal.getErrorOutput(), containsString("Invalid java installation (no jvm.dll")); + } + + public void testAlternateDllLocation() throws Exception { + Files.delete(jvmDll); + Path altJvmDll = javaHome.resolve("bin/server/jvm.dll"); + Files.createDirectories(altJvmDll.getParent()); + Files.createFile(altJvmDll); + assertServiceArgs(Map.of()); + } + + public void testDll() throws Exception { + assertServiceArgs(Map.of("Jvm", jvmDll.toString())); + } + + public void testPreExecuteOutput() throws Exception { + envVars.put("SERVICE_ID", "myservice"); + assertOkWithOutput( + allOf(containsString("Installing service : myservice"), containsString("Using ES_JAVA_HOME : " + javaHome)), + emptyString() + ); + } + + public void testJvmOptions() throws Exception { + sysprops.put("es.distribution.type", "testdistro"); + List expectedOptions = List.of( + "" + "-XX:+UseSerialGC", + "-Des.path.home=" + esHomeDir.toString(), + "-Des.path.conf=" + esHomeDir.resolve("config").toString(), + "-Des.distribution.type=testdistro" + ); + mockProcessValidator = (environment, procrunCall) -> { + List options = procrunCall.args().get("JvmOptions"); + assertThat( + options, + containsInAnyOrder( + "-Dcli.name=windows-service", + "-Dcli.libs=lib/tools/server-cli,lib/tools/windows-service-cli", + String.join(";", expectedOptions) + ) + ); + }; + assertOkWithOutput(any(String.class), emptyString()); + } + + public void testStartupType() throws Exception { + assertServiceArgs(Map.of("Startup", "manual")); + envVars.put("ES_START_TYPE", "auto"); + assertServiceArgs(Map.of("Startup", "auto")); + } + + public void testStopTimeout() throws Exception { + assertServiceArgs(Map.of("StopTimeout", "0")); + envVars.put("ES_STOP_TIMEOUT", "5"); + assertServiceArgs(Map.of("StopTimeout", "5")); + } + + public void testFixedArgs() throws Exception { + assertServiceArgs( + Map.ofEntries( + entry("StartClass", "org.elasticsearch.launcher.CliToolLauncher"), + entry("StartMethod", "main"), + entry("StartMode", "jvm"), + entry("StopClass", "org.elasticsearch.launcher.CliToolLauncher"), + entry("StopMethod", "close"), + entry("StopMode", "jvm"), + entry("JvmMs", "4m"), + entry("JvmMx", "64m"), + entry("StartPath", esHomeDir.toString()), + entry("Classpath", "javaclasspath") // dummy value for tests + ) + ); + } + + public void testPidFile() throws Exception { + assertServiceArgs(Map.of("PidFile", "elasticsearch-service-x64.pid")); + envVars.put("SERVICE_ID", "myservice"); + assertServiceArgs(Map.of("PidFile", "myservice.pid")); + } + + public void testDisplayName() throws Exception { + assertServiceArgs(Map.of("DisplayName", "\"Elasticsearch %s (elasticsearch-service-x64)\"".formatted(Version.CURRENT))); + envVars.put("SERVICE_DISPLAY_NAME", "my service name"); + assertServiceArgs(Map.of("DisplayName", "\"my service name\"")); + } + + public void testDescription() throws Exception { + String defaultDescription = "\"Elasticsearch %s Windows Service - https://elastic.co\"".formatted(Version.CURRENT); + assertServiceArgs(Map.of("Description", defaultDescription)); + envVars.put("SERVICE_DESCRIPTION", "my description"); + assertServiceArgs(Map.of("Description", "\"my description\"")); + } + + public void testUsernamePassword() throws Exception { + assertServiceArgs(Map.of("ServiceUser", "LocalSystem")); + + terminal.reset(); + envVars.put("SERVICE_USERNAME", "myuser"); + assertThat(executeMain(), equalTo(ExitCodes.CONFIG)); + assertThat(terminal.getErrorOutput(), containsString("Both service username and password must be set")); + + terminal.reset(); + envVars.remove("SERVICE_USERNAME"); + envVars.put("SERVICE_PASSWORD", "mypassword"); + assertThat(executeMain(), equalTo(ExitCodes.CONFIG)); + assertThat(terminal.getErrorOutput(), containsString("Both service username and password must be set")); + + terminal.reset(); + envVars.put("SERVICE_USERNAME", "myuser"); + envVars.put("SERVICE_PASSWORD", "mypassword"); + assertServiceArgs(Map.of("ServiceUser", "myuser", "ServicePassword", "mypassword")); + } + + public void testExtraServiceParams() throws Exception { + envVars.put("SERVICE_PARAMS", "--MyExtraArg \"and value\""); + assertServiceArgs(Map.of("MyExtraArg", "\"and value\"")); + } +} diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceManagerCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceManagerCommandTests.java new file mode 100644 index 0000000000000..cd3aea949f0f6 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceManagerCommandTests.java @@ -0,0 +1,50 @@ +/* + * 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.windows.service; + +import org.elasticsearch.cli.Command; + +import java.io.IOException; + +public class WindowsServiceManagerCommandTests extends WindowsServiceCliTestCase { + @Override + protected Command newCommand() { + return new WindowsServiceManagerCommand() { + @Override + Process startProcess(ProcessBuilder processBuilder) throws IOException { + return mockProcess(processBuilder); + } + }; + } + + @Override + protected String getExe() { + return mgrExe.toString(); + } + + @Override + protected boolean includeLogsArgs() { + return false; + } + + @Override + protected String getCommand() { + return "ES"; + } + + @Override + protected String getDefaultSuccessMessage() { + return "Successfully started service manager for 'elasticsearch-service-x64'"; + } + + @Override + protected String getDefaultFailureMessage() { + return "Failed starting service manager for 'elasticsearch-service-x64'"; + } +} diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommandTests.java new file mode 100644 index 0000000000000..3d2032d75a195 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceRemoveCommandTests.java @@ -0,0 +1,40 @@ +/* + * 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.windows.service; + +import org.elasticsearch.cli.Command; + +import java.io.IOException; + +public class WindowsServiceRemoveCommandTests extends WindowsServiceCliTestCase { + @Override + protected Command newCommand() { + return new WindowsServiceRemoveCommand() { + @Override + Process startProcess(ProcessBuilder processBuilder) throws IOException { + return mockProcess(processBuilder); + } + }; + } + + @Override + protected String getCommand() { + return "DS"; + } + + @Override + protected String getDefaultSuccessMessage() { + return "The service 'elasticsearch-service-x64' has been removed"; + } + + @Override + protected String getDefaultFailureMessage() { + return "Failed removing 'elasticsearch-service-x64' service"; + } +} diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStartCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStartCommandTests.java new file mode 100644 index 0000000000000..7a30540d53ba0 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStartCommandTests.java @@ -0,0 +1,40 @@ +/* + * 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.windows.service; + +import org.elasticsearch.cli.Command; + +import java.io.IOException; + +public class WindowsServiceStartCommandTests extends WindowsServiceCliTestCase { + @Override + protected Command newCommand() { + return new WindowsServiceStartCommand() { + @Override + Process startProcess(ProcessBuilder processBuilder) throws IOException { + return mockProcess(processBuilder); + } + }; + } + + @Override + protected String getCommand() { + return "ES"; + } + + @Override + protected String getDefaultSuccessMessage() { + return "The service 'elasticsearch-service-x64' has been started"; + } + + @Override + protected String getDefaultFailureMessage() { + return "Failed starting 'elasticsearch-service-x64' service"; + } +} diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStopCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStopCommandTests.java new file mode 100644 index 0000000000000..f623c5d2465f3 --- /dev/null +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceStopCommandTests.java @@ -0,0 +1,40 @@ +/* + * 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.windows.service; + +import org.elasticsearch.cli.Command; + +import java.io.IOException; + +public class WindowsServiceStopCommandTests extends WindowsServiceCliTestCase { + @Override + protected Command newCommand() { + return new WindowsServiceStopCommand() { + @Override + Process startProcess(ProcessBuilder processBuilder) throws IOException { + return mockProcess(processBuilder); + } + }; + } + + @Override + protected String getCommand() { + return "SS"; + } + + @Override + protected String getDefaultSuccessMessage() { + return "The service 'elasticsearch-service-x64' has been stopped"; + } + + @Override + protected String getDefaultFailureMessage() { + return "Failed stopping 'elasticsearch-service-x64' service"; + } +} From 0eb87f2962e5e434c4866526b8fe3965cd52a953 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 17 May 2022 17:42:48 -0700 Subject: [PATCH 156/166] tests done --- .../server/cli/ServerProcess.java | 64 ++- .../server/cli/TempDirectory.java | 75 ---- .../server/cli/JavaServerProcessTests.java | 200 --------- .../server/cli/ServerProcessTests.java | 423 ++++++++++++++++++ .../service/WindowsServiceInstallCommand.java | 9 +- .../org/elasticsearch/cli/MockTerminal.java | 2 +- 6 files changed, 484 insertions(+), 289 deletions(-) delete mode 100644 distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java delete mode 100644 distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java create mode 100644 distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java index 87c1d539dd813..42bb44cd2081a 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -8,8 +8,6 @@ package org.elasticsearch.server.cli; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.elasticsearch.bootstrap.BootstrapInfo; import org.elasticsearch.bootstrap.ServerArgs; import org.elasticsearch.cli.ExitCodes; @@ -19,11 +17,15 @@ import org.elasticsearch.common.io.stream.OutputStreamStreamOutput; import org.elasticsearch.core.IOUtils; import org.elasticsearch.core.PathUtils; +import org.elasticsearch.core.SuppressForbidden; import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileAttribute; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -39,15 +41,14 @@ * and receiving control signals on stderr. The start method does not return until the * server is ready to process requests and has exited the bootstrap thread. * - *

The caller starting a {@link ServerProcess} can do one of several things: + *

The caller starting a {@link ServerProcess} can then do one of several things: *

    *
  • Block on the server process exiting, by calling {@link #waitFor()}
  • *
  • Detach from the server process by calling {@link #detach()}
  • - *
  • Tell the server process to shutdown and wait for it by calling {@link #stop()}
  • + *
  • Tell the server process to shutdown and wait for it to exit by calling {@link #stop()}
  • *
*/ public class ServerProcess { - private static final Logger logger = LogManager.getLogger(ServerProcess.class); // the actual java process of the server private final Process jvmProcess; @@ -63,11 +64,13 @@ public class ServerProcess { this.errorPump = errorPump; } + // this allows mocking the process building by tests interface OptionsBuilder { List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws InterruptedException, IOException, UserException; } + // this allows mocking the process building by tests interface ProcessStarter { Process start(ProcessBuilder pb) throws IOException; } @@ -103,7 +106,6 @@ static ServerProcess start( jvmProcess = createProcess(processInfo, args.configDir(), pluginsDir, optionsBuilder, processStarter); errorPump = new ErrorPumpThread(terminal.getErrorWriter(), jvmProcess.getErrorStream()); errorPump.start(); - logger.info("ES PID: " + jvmProcess.pid()); sendArgs(args, jvmProcess.getOutputStream()); String errorMsg = errorPump.waitUntilReady(); @@ -140,6 +142,7 @@ public synchronized void detach() throws IOException { * Waits for the subprocess to exit. */ public void waitFor() { + errorPump.drain(); int exitCode = nonInterruptible(jvmProcess::waitFor); if (exitCode != ExitCodes.OK) { throw new RuntimeException("server process exited with status code " + exitCode); @@ -188,9 +191,13 @@ private static Process createProcess( ProcessStarter processStarter ) throws InterruptedException, IOException, UserException { Map envVars = new HashMap<>(processInfo.envVars()); - Path tempDir = TempDirectory.setup(envVars); - List jvmOptions = optionsBuilder.getJvmOptions(configDir, pluginsDir, tempDir, envVars.get("ES_JAVA_OPTS")); - // jvmOptions.add("-Des.path.conf=" + env.configFile()); + Path tempDir = setupTempDir(processInfo, envVars.remove("ES_TMPDIR")); + if (envVars.containsKey("LIBFFI_TMPDIR") == false) { + envVars.put("LIBFFI_TMPDIR", tempDir.toString()); + } + + List jvmOptions = optionsBuilder.getJvmOptions(configDir, pluginsDir, tempDir, envVars.remove("ES_JAVA_OPTS")); + // also pass through distribution type jvmOptions.add("-Des.distribution.type=" + processInfo.sysprops().get("es.distribution.type")); Path esHome = processInfo.workingDir(); @@ -200,7 +207,7 @@ private static Process createProcess( command.add(javaHome.resolve("bin").resolve("java" + (isWindows ? ".exe" : "")).toString()); command.addAll(jvmOptions); command.add("-cp"); - // The '*' isn't allows by the windows filesystem, so we need to force it into the classpath after converting to a string. + // The '*' isn't allowed by the windows filesystem, so we need to force it into the classpath after converting to a string. // Thankfully this will all go away when switching to modules, which take the directory instead of a glob. command.add(esHome.resolve("lib") + (isWindows ? "\\" : "/") + "*"); command.add("org.elasticsearch.bootstrap.Elasticsearch"); @@ -211,4 +218,41 @@ private static Process createProcess( return processStarter.start(builder); } + + /** + * Returns the java.io.tmpdir Elasticsearch should use, creating it if necessary. + * + *

On non-Windows OS, this will be created as a sub-directory of the default temporary directory. + * Note that this causes the created temporary directory to be a private temporary directory. + */ + private static Path setupTempDir(ProcessInfo processInfo, String tmpDirOverride) throws UserException, IOException { + final Path path; + if (tmpDirOverride != null) { + path = Paths.get(tmpDirOverride); + if (Files.exists(path) == false) { + throw new UserException(ExitCodes.CONFIG, "Temporary directory [" + path + "] does not exist or is not accessible"); + } + if (Files.isDirectory(path) == false) { + throw new UserException(ExitCodes.CONFIG, "Temporary directory [" + path + "] is not a directory"); + } + } else { + if (processInfo.sysprops().get("os.name").startsWith("Windows")) { + /* + * On Windows, we avoid creating a unique temporary directory per invocation lest + * we pollute the temporary directory. On other operating systems, temporary directories + * will be cleaned automatically via various mechanisms (e.g., systemd, or restarts). + */ + path = Paths.get(processInfo.sysprops().get("java.io.tmpdir"), "elasticsearch"); + Files.createDirectories(path); + } else { + path = createTempDirectory("elasticsearch-"); + } + } + return path; + } + + @SuppressForbidden(reason = "Files#createTempDirectory(String, FileAttribute...)") + private static Path createTempDirectory(final String prefix, final FileAttribute... attrs) throws IOException { + return Files.createTempDirectory(prefix, attrs); + } } diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java deleted file mode 100644 index 7ed02802121e4..0000000000000 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/TempDirectory.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.server.cli; - -import org.elasticsearch.cli.ExitCodes; -import org.elasticsearch.cli.UserException; -import org.elasticsearch.core.SuppressForbidden; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.attribute.FileAttribute; -import java.util.Map; - -/** - * Provides a path for a temporary directory. On non-Windows OS, this will be created as a sub-directory of the default temporary directory. - * Note that this causes the created temporary directory to be a private temporary directory. - */ -final class TempDirectory { - - /** - * Ensures the environment map has {@code ES_TMPDIR} and {@code LIBFFI_TMPDIR}. - * - * @param env the current environment variables that will be passed to Elasticsearch - * @return the temporary directory - */ - public static Path setup(Map env) throws UserException { - final Path path; - String existingTempDir = env.remove("ES_TMPDIR"); - if (existingTempDir != null) { - path = Paths.get(existingTempDir); - if (Files.exists(path) == false) { - throw new UserException(ExitCodes.CONFIG, "Temporary directory [" + path + "] does not exist or is not accessible"); - } - if (Files.isDirectory(path) == false) { - throw new UserException(ExitCodes.CONFIG, "Temporary directory [" + path + "] is not a directory"); - } - } else { - try { - if (System.getProperty("os.name").startsWith("Windows")) { - /* - * On Windows, we avoid creating a unique temporary directory per invocation lest - * we pollute the temporary directory. On other operating systems, temporary directories - * will be cleaned automatically via various mechanisms (e.g., systemd, or restarts). - */ - path = Paths.get(System.getProperty("java.io.tmpdir"), "elasticsearch"); - Files.createDirectories(path); - } else { - path = createTempDirectory("elasticsearch-"); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - if (env.containsKey("LIBFFI_TMPDIR") == false) { - env.put("LIBFFI_TMPDIR", path.toString()); - } - return path; - } - - @SuppressForbidden(reason = "Files#createTempDirectory(String, FileAttribute...)") - private static Path createTempDirectory(final String prefix, final FileAttribute... attrs) throws IOException { - return Files.createTempDirectory(prefix, attrs); - } - -} diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java deleted file mode 100644 index 023807ed20585..0000000000000 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/JavaServerProcessTests.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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.server.cli; - -import org.elasticsearch.bootstrap.ServerArgs; -import org.elasticsearch.common.io.stream.InputStreamStreamInput; -import org.elasticsearch.core.IOUtils; -import org.elasticsearch.test.ESTestCase; -import org.junit.Before; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.nio.file.Path; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -public class JavaServerProcessTests extends ESTestCase { - - private static final ExecutorService mockJvmProcessExecutor = Executors.newSingleThreadExecutor(); - Path esConfigDir; - - /* - @Before - public void setupDummyInstallation() throws IOException { - sysprops.put("java.home", "/javahome"); - esConfigDir = esHomeDir.resolve("config"); - Files.createDirectories(esConfigDir); - Files.writeString(esConfigDir.resolve("jvm.options"), ""); - } - - @AfterClass - public static void cleanupExecutor() { - mockJvmProcessExecutor.shutdown(); - } - - @Override - protected void assertUsage(Matcher matcher, String... args) throws Exception { - mainCallback = FAIL_MAIN; - super.assertUsage(matcher, args); - } - - private void assertMutuallyExclusiveOptions(String... args) throws Exception { - assertUsage(allOf(containsString("ERROR:"), containsString("are unavailable given other options on the command line")), args); - */ - - /* - TO TEST - createProcess failure (UserException, InterruptedException, IOException) - createProcess - - env vars passed through - - temp dir setup - - ES_JAVA_OPTS passed to getJvmOptions - - distribution type passed through - - jvm options returned from getJvmOptions passed through - - processInfo.workingDir() used as process builder working dir - - java.home used for java bin - - java path on windows ends in exe - - classpath slashes for windows/nix - - main classname - - output fd inherited, others piped - ServerArgs written to stdin - ServerArgs error writing fallthrough - stderr written through - user exception written to stderr - empty stderr - stderr ioexception rethrown - ready signal on stderr - dead (ready == false) waits for exit and throws userException - detach waits for stderr done, closes streams - - */ - - interface MainMethod { - void main(ServerArgs args, OutputStream stdout, OutputStream stderr, AtomicInteger exitCode); - } - - MainMethod mainCallback; - final MainMethod FAIL_MAIN = (args, stdout, stderr, exitCode) -> fail("Did not expect to run init"); - - @Before - public void resetCommand() { - mainCallback = null; - } - - // a "process" that is really another thread - private class MockElasticsearchProcess extends Process { - private final PipedOutputStream processStdin = new PipedOutputStream(); - private final PipedInputStream processStdout = new PipedInputStream(); - private final PipedInputStream processStderr = new PipedInputStream(); - private final PipedInputStream stdin = new PipedInputStream(); - private final PipedOutputStream stdout = new PipedOutputStream(); - private final PipedOutputStream stderr = new PipedOutputStream(); - - private final AtomicInteger exitCode = new AtomicInteger(); - private final AtomicReference argsParsingException = new AtomicReference<>(); - private final Future main; - - MockElasticsearchProcess() throws IOException { - stdin.connect(processStdin); - stdout.connect(processStdout); - stderr.connect(processStderr); - this.main = mockJvmProcessExecutor.submit(() -> { - try (var in = new InputStreamStreamInput(stdin)) { - final ServerArgs serverArgs = new ServerArgs(in); - if (mainCallback != null) { - mainCallback.main(serverArgs, stdout, stderr, exitCode); - } - } catch (IOException e) { - argsParsingException.set(e); - } - IOUtils.closeWhileHandlingException(stdin, stdout, stderr); - }); - } - - @Override - public OutputStream getOutputStream() { - return processStdin; - } - - @Override - public InputStream getInputStream() { - return processStdout; - } - - @Override - public InputStream getErrorStream() { - return processStderr; - } - - @Override - public long pid() { - return 12345; - } - - @Override - public int waitFor() throws InterruptedException { - try { - main.get(); - } catch (ExecutionException e) { - throw new AssertionError(e); - } - if (argsParsingException.get() != null) { - throw new AssertionError("Reading server args failed", argsParsingException.get()); - } - return exitCode.get(); - } - - @Override - public int exitValue() { - if (main.isDone() == false) { - throw new IllegalThreadStateException(); // match spec - } - return exitCode.get(); - } - - @Override - public void destroy() { - fail("Tried to kill ES process"); - } - } - - /* - @Override - protected Command newCommand() { - return new ServerCli() { - - @Override - protected List getJvmOptions(Path configDir, Path pluginsDir, Path tmpDir, String envOptions) throws Exception { - return new ArrayList<>(); - } - - @Override - protected Process startProcess(ProcessBuilder processBuilder) throws IOException { - // TODO: validate processbuilder stuff - return new MockElasticsearchProcess(); - } - - @Override - protected Command loadTool(String toolname, String libs) { - assertThat(toolname, equalTo("auto-configure-node")); - assertThat(libs, equalTo("modules/x-pack-core,modules/x-pack-security,lib/tools/security-cli")); - return AUTO_CONFIG_CLI; - } - }; - }*/ - -} diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java new file mode 100644 index 0000000000000..4ce73230eff87 --- /dev/null +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java @@ -0,0 +1,423 @@ +/* + * 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.server.cli; + +import org.elasticsearch.bootstrap.BootstrapInfo; +import org.elasticsearch.bootstrap.ServerArgs; +import org.elasticsearch.cli.ExitCodes; +import org.elasticsearch.cli.MockTerminal; +import org.elasticsearch.cli.ProcessInfo; +import org.elasticsearch.cli.UserException; +import org.elasticsearch.common.io.stream.InputStreamStreamInput; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.core.IOUtils; +import org.elasticsearch.test.ESTestCase; +import org.junit.AfterClass; +import org.junit.Before; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import static org.elasticsearch.server.cli.ProcessUtil.nonInterruptibleVoid; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.startsWith; + +public class ServerProcessTests extends ESTestCase { + + private static final ExecutorService mockJvmProcessExecutor = Executors.newSingleThreadExecutor(); + final MockTerminal terminal = MockTerminal.create(); + protected final Map sysprops = new HashMap<>(); + protected final Map envVars = new HashMap<>(); + Path esHomeDir; + Settings.Builder nodeSettings; + ServerProcess.OptionsBuilder optionsBuilder; + ProcessValidator processValidator; + MainMethod mainCallback; + MockElasticsearchProcess process; + + interface MainMethod { + void main(ServerArgs args, InputStream stdin, PrintStream stderr, AtomicInteger exitCode) throws IOException; + } + + interface ProcessValidator { + void validate(ProcessBuilder processBuilder) throws IOException; + } + + void runForeground() throws Exception { + var server = startProcess(false, false, null, ""); + server.waitFor(); + } + + @Before + public void resetEnv() { + terminal.reset(); + sysprops.clear(); + sysprops.put("os.name", "Linux"); + sysprops.put("java.home", "javahome"); + envVars.clear(); + esHomeDir = createTempDir(); + nodeSettings = Settings.builder(); + optionsBuilder = (configDir, pluginsDir, tmpDir, envOptions) -> new ArrayList<>(); + processValidator = null; + mainCallback = null; + } + + @AfterClass + public static void cleanupExecutor() { + mockJvmProcessExecutor.shutdown(); + } + + // a "process" that is really another thread + private class MockElasticsearchProcess extends Process { + private final PipedOutputStream processStdin = new PipedOutputStream(); + private final PipedInputStream processStderr = new PipedInputStream(); + private final PipedInputStream stdin = new PipedInputStream(); + private final PipedOutputStream stderr = new PipedOutputStream(); + + private final AtomicInteger exitCode = new AtomicInteger(); + private final AtomicReference processException = new AtomicReference<>(); + private final AtomicReference assertion = new AtomicReference<>(); + private final Future main; + + MockElasticsearchProcess() throws IOException { + stdin.connect(processStdin); + stderr.connect(processStderr); + this.main = mockJvmProcessExecutor.submit(() -> { + var in = new InputStreamStreamInput(stdin); + try { + var serverArgs = new ServerArgs(in); + if (mainCallback != null) { + try (var err = new PrintStream(stderr, true, StandardCharsets.UTF_8)) { + mainCallback.main(serverArgs, stdin, err, exitCode); + } + } + } catch (IOException e) { + processException.set(e); + } catch (AssertionError e) { + assertion.set(e); + } + IOUtils.closeWhileHandlingException(stdin, stderr); + }); + } + + @Override + public OutputStream getOutputStream() { + return processStdin; + } + + @Override + public InputStream getInputStream() { + return InputStream.nullInputStream(); + } + + @Override + public InputStream getErrorStream() { + return processStderr; + } + + @Override + public long pid() { + return 12345; + } + + @Override + public int waitFor() throws InterruptedException { + try { + main.get(); + } catch (ExecutionException e) { + throw new AssertionError(e); + } + if (processException.get() != null) { + throw new AssertionError("Process failed", processException.get()); + } + if (assertion.get() != null) { + throw assertion.get(); + } + return exitCode.get(); + } + + @Override + public int exitValue() { + if (main.isDone() == false) { + throw new IllegalThreadStateException(); // match spec + } + return exitCode.get(); + } + + @Override + public void destroy() { + fail("Tried to kill ES process directly"); + } + + public Process destroyForcibly() { + main.cancel(true); + return this; + } + } + + ServerProcess startProcess(boolean daemonize, boolean quiet, Path pidFile, String keystorePassword) throws Exception { + var pinfo = new ProcessInfo(Map.copyOf(sysprops), Map.copyOf(envVars), esHomeDir); + SecureString password = new SecureString(keystorePassword.toCharArray()); + var args = new ServerArgs(daemonize, quiet, pidFile, password, nodeSettings.build(), esHomeDir.resolve("config")); + ServerProcess.ProcessStarter starter = pb -> { + if (processValidator != null) { + processValidator.validate(pb); + } + process = new MockElasticsearchProcess(); + return process; + }; + return ServerProcess.start(terminal, pinfo, args, esHomeDir.resolve("plugins"), optionsBuilder, starter); + } + + public void testProcessBuilder() throws Exception { + processValidator = pb -> { + assertThat(pb.redirectInput(), equalTo(ProcessBuilder.Redirect.PIPE)); + assertThat(pb.redirectOutput(), equalTo(ProcessBuilder.Redirect.INHERIT)); + assertThat(pb.redirectError(), equalTo(ProcessBuilder.Redirect.PIPE)); + assertThat(pb.directory(), nullValue()); // leave default, which is working directory + }; + mainCallback = (args, stdin, stderr, exitCode) -> { + try (PrintStream err = new PrintStream(stderr, true, StandardCharsets.UTF_8)) { + err.println("stderr message"); + } + }; + runForeground(); + assertThat(terminal.getErrorOutput(), containsString("stderr message")); + } + + public void testBootstrapError() throws Exception { + mainCallback = (args, stdin, stderr, exitCode) -> { + stderr.println(BootstrapInfo.USER_EXCEPTION_MARKER + "a bootstrap exception"); + exitCode.set(ExitCodes.CONFIG); + }; + var e = expectThrows(UserException.class, () -> runForeground()); + assertThat(e.exitCode, equalTo(ExitCodes.CONFIG)); + assertThat(e.getMessage(), equalTo("a bootstrap exception")); + } + + public void testUserError() throws Exception { + mainCallback = (args, stdin, stderr, exitCode) -> { + stderr.println(BootstrapInfo.USER_EXCEPTION_MARKER + "a user exception"); + exitCode.set(ExitCodes.USAGE); + }; + var e = expectThrows(UserException.class, () -> runForeground()); + assertThat(e.exitCode, equalTo(ExitCodes.USAGE)); + assertThat(e.getMessage(), equalTo("a user exception")); + } + + public void testStartError() throws Exception { + processValidator = pb -> { throw new IOException("something went wrong"); }; + var e = expectThrows(UncheckedIOException.class, () -> runForeground()); + assertThat(e.getCause().getMessage(), equalTo("something went wrong")); + } + + public void testOptionsBuildingInterrupted() throws Exception { + optionsBuilder = (configDir, pluginsDir, tmpDir, envOptions) -> { + throw new InterruptedException("interrupted while get jvm options"); + }; + var e = expectThrows(RuntimeException.class, () -> runForeground()); + assertThat(e.getCause().getMessage(), equalTo("interrupted while get jvm options")); + } + + public void testEnvPassthrough() throws Exception { + envVars.put("MY_ENV", "foo"); + processValidator = pb -> { assertThat(pb.environment(), hasEntry(equalTo("MY_ENV"), equalTo("foo"))); }; + runForeground(); + } + + public void testLibffiEnv() throws Exception { + processValidator = pb -> { + assertThat(pb.environment(), hasKey("LIBFFI_TMPDIR")); + Path libffi = Paths.get(pb.environment().get("LIBFFI_TMPDIR")); + assertThat(Files.exists(libffi), is(true)); + }; + runForeground(); + envVars.put("LIBFFI_TMPDIR", "mylibffi_tmp"); + processValidator = pb -> { assertThat(pb.environment(), hasEntry(equalTo("LIBFFI_TMPDIR"), equalTo("mylibffi_tmp"))); }; + runForeground(); + } + + public void testTempDir() throws Exception { + optionsBuilder = (configDir, pluginsDir, tmpDir, envOptions) -> { + assertThat(tmpDir.toString(), Files.exists(tmpDir), is(true)); + assertThat(tmpDir.getFileName().toString(), startsWith("elasticsearch-")); + return new ArrayList<>(); + }; + runForeground(); + } + + public void testTempDirWindows() throws Exception { + Path baseTmpDir = createTempDir(); + sysprops.put("os.name", "Windows 10"); + sysprops.put("java.io.tmpdir", baseTmpDir.toString()); + optionsBuilder = (configDir, pluginsDir, tmpDir, envOptions) -> { + assertThat(tmpDir.toString(), Files.exists(tmpDir), is(true)); + assertThat(tmpDir.getFileName().toString(), equalTo("elasticsearch")); + assertThat(tmpDir.getParent().toString(), equalTo(baseTmpDir.toString())); + return new ArrayList<>(); + }; + runForeground(); + } + + public void testTempDirOverride() throws Exception { + Path customTmpDir = createTempDir(); + envVars.put("ES_TMPDIR", customTmpDir.toString()); + optionsBuilder = (configDir, pluginsDir, tmpDir, envOptions) -> { + assertThat(tmpDir.toString(), equalTo(customTmpDir.toString())); + return new ArrayList<>(); + }; + processValidator = pb -> assertThat(pb.environment(), not(hasKey("ES_TMPDIR"))); + runForeground(); + } + + public void testTempDirOverrideMissing() throws Exception { + Path baseDir = createTempDir(); + envVars.put("ES_TMPDIR", baseDir.resolve("dne").toString()); + var e = expectThrows(UserException.class, () -> runForeground()); + assertThat(e.exitCode, equalTo(ExitCodes.CONFIG)); + assertThat(e.getMessage(), containsString("dne] does not exist")); + } + + public void testTempDirOverrideNotADirectory() throws Exception { + Path tmpFile = createTempFile(); + envVars.put("ES_TMPDIR", tmpFile.toString()); + var e = expectThrows(UserException.class, () -> runForeground()); + assertThat(e.exitCode, equalTo(ExitCodes.CONFIG)); + assertThat(e.getMessage(), containsString("is not a directory")); + } + + public void testCustomJvmOptions() throws Exception { + envVars.put("ES_JAVA_OPTS", "-Dmyoption=foo"); + optionsBuilder = (configDir, pluginsDir, tmpDir, envOptions) -> { + assertThat(envOptions, equalTo("-Dmyoption=foo")); + return new ArrayList<>(); + }; + processValidator = pb -> assertThat(pb.environment(), not(hasKey("ES_JAVA_OPTS"))); + runForeground(); + } + + public void testCommandLineSysprops() throws Exception { + optionsBuilder = (configDir, pluginsDir, tmpDir, envOptions) -> List.of("-Dfoo1=bar", "-Dfoo2=baz"); + processValidator = pb -> { + assertThat(pb.command(), contains("-Dfoo1=bar")); + assertThat(pb.command(), contains("-Dfoo2=bar")); + }; + } + + public void testCommandLine() throws Exception { + String mainClass = "org.elasticsearch.bootstrap.Elasticsearch"; + String distroSysprop = "-Des.distribution.type=testdistro"; + sysprops.put("es.distribution.type", "testdistro"); + AtomicReference expectedJava = new AtomicReference<>("javahome/bin/java"); + AtomicReference expectedClasspath = new AtomicReference<>(esHomeDir.resolve("lib") + "/*"); + processValidator = pb -> { + assertThat(pb.command(), hasItems(expectedJava.get(), distroSysprop, "-cp", expectedClasspath.get(), mainClass)); + }; + runForeground(); + + sysprops.put("os.name", "Windows 10"); + sysprops.put("java.io.tmpdir", createTempDir().toString()); + expectedJava.set(Paths.get("javahome").resolve("bin").resolve("java.exe").toString()); + expectedClasspath.set(esHomeDir.resolve("lib") + "\\*"); + runForeground(); + } + + public void testDetach() throws Exception { + mainCallback = (args, stdin, stderr, exitCode) -> { + assertThat(args.daemonize(), equalTo(true)); + stderr.println(BootstrapInfo.SERVER_READY_MARKER); + stderr.println("final message"); + stderr.close(); + }; + var server = startProcess(true, false, null, ""); + server.detach(); + assertThat(terminal.getErrorOutput(), containsString("final message")); + } + + public void testStop() throws Exception { + CountDownLatch mainReady = new CountDownLatch(1); + mainCallback = (args, stdin, stderr, exitCode) -> { + stderr.println(BootstrapInfo.SERVER_READY_MARKER); + nonInterruptibleVoid(mainReady::await); + stderr.println("final message"); + }; + var server = startProcess(false, false, null, ""); + mainReady.countDown(); + server.stop(); + assertThat(process.main.isDone(), is(true)); // stop should have waited + assertThat(terminal.getErrorOutput(), containsString("final message")); + } + + public void testWaitFor() throws Exception { + CountDownLatch mainReady = new CountDownLatch(1); + mainCallback = (args, stdin, stderr, exitCode) -> { + stderr.println(BootstrapInfo.SERVER_READY_MARKER); + mainReady.countDown(); + assertThat(stdin.read(), equalTo((int) BootstrapInfo.SERVER_SHUTDOWN_MARKER)); + stderr.println("final message"); + }; + var server = startProcess(false, false, null, ""); + new Thread(() -> { + // simulate stop run as shutdown hook in another thread, eg from Ctrl-C + nonInterruptibleVoid(mainReady::await); + server.stop(); + }).start(); + server.waitFor(); + assertThat(process.main.isDone(), is(true)); + assertThat(terminal.getErrorOutput(), containsString("final message")); + } + + public void testProcessDies() throws Exception { + CountDownLatch mainReady = new CountDownLatch(1); + CountDownLatch mainExit = new CountDownLatch(1); + mainCallback = (args, stdin, stderr, exitCode) -> { + stderr.println(BootstrapInfo.SERVER_READY_MARKER); + mainReady.countDown(); + stderr.println("fatal message"); + nonInterruptibleVoid(mainExit::await); + exitCode.set(-9); + }; + var server = startProcess(false, false, null, ""); + nonInterruptibleVoid(mainReady::await); + process.processStderr.close(); // mimic pipe break if cli process dies + mainExit.countDown(); + var e = expectThrows(RuntimeException.class, server::waitFor); + assertThat(e.getMessage(), equalTo("server process exited with status code -9")); + assertThat(terminal.getErrorOutput(), containsString("fatal message")); + } +} diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java index 91e40de1f6540..d372a5e5bbe3e 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java @@ -52,8 +52,8 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg( args, "--Description", - pinfo.envVars().getOrDefault("SERVICE_DESCRIPTION", - "Elasticsearch %s Windows Service - https://elastic.co".formatted(Version.CURRENT)) + pinfo.envVars() + .getOrDefault("SERVICE_DESCRIPTION", "Elasticsearch %s Windows Service - https://elastic.co".formatted(Version.CURRENT)) ); addArg(args, "--Jvm", getJvmDll(getJavaHome(pinfo.sysprops())).toString()); addArg(args, "--StartMode", "jvm"); @@ -132,7 +132,10 @@ protected void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId // validate username and password come together boolean hasUsername = pinfo.envVars().containsKey("SERVICE_USERNAME"); if (pinfo.envVars().containsKey("SERVICE_PASSWORD") != hasUsername) { - throw new UserException(ExitCodes.CONFIG, "Both service username and password must be set, only got " + (hasUsername ? "SERVICE_USERNAME" : "SERVICE_PASSWORD")); + throw new UserException( + ExitCodes.CONFIG, + "Both service username and password must be set, only got " + (hasUsername ? "SERVICE_USERNAME" : "SERVICE_PASSWORD") + ); } } diff --git a/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java b/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java index dfb960c270810..fd1fa661bc151 100644 --- a/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java +++ b/test/framework/src/main/java/org/elasticsearch/cli/MockTerminal.java @@ -100,7 +100,7 @@ public OutputStream getOutputStream() { } private static PrintWriter newPrintWriter(OutputStream out) { - return new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); + return new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8), true); } public static MockTerminal create() { From 36951815f825875097e1d125e07a75b165fa7289 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 17 May 2022 17:44:33 -0700 Subject: [PATCH 157/166] revert large file handling --- .../test/java/org/elasticsearch/packaging/util/Shell.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java index 8f58da8a1d975..327c805fad351 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Shell.java @@ -203,12 +203,7 @@ private String readFileIfExists(Path path) throws IOException { if (Files.exists(path)) { long size = Files.size(path); if (size > 100 * 1024) { - // file is really big, truncate at 100k - try (var br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { - char[] buf = new char[100 * 1024]; - int nRead = br.read(buf); - return new String(buf, 0, nRead) + "\n<>"; - } + return "<>"; } try (Stream lines = Files.lines(path, StandardCharsets.UTF_8)) { return lines.collect(Collectors.joining("\n")); From fa29cd99a8db09cffe32aa01a3917d38f9101f89 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 17 May 2022 20:10:01 -0700 Subject: [PATCH 158/166] fix command tests --- .../windows/service/ProcrunCommandTests.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java index ffc46c4a53692..b683884a37571 100644 --- a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/ProcrunCommandTests.java @@ -13,6 +13,7 @@ import org.elasticsearch.cli.ProcessInfo; import org.elasticsearch.cli.Terminal; import org.elasticsearch.cli.UserException; +import org.junit.Before; import java.io.IOException; import java.nio.file.Files; @@ -24,14 +25,23 @@ public class ProcrunCommandTests extends WindowsServiceCliTestCase { - PreExecuteHook preExecuteHook = null; - boolean includeLogArgs = false; - String additionalArgs = ""; + PreExecuteHook preExecuteHook; + boolean includeLogArgs; + String additionalArgs; + String serviceId; interface PreExecuteHook { void preExecute(Terminal terminal, ProcessInfo pinfo, String serviceId) throws UserException; } + @Before + public void resetArgs() { + serviceId = "elasticsearch-service-x64"; + preExecuteHook = null; + includeLogArgs = false; + additionalArgs = ""; + } + class TestProcrunCommand extends ProcrunCommand { protected TestProcrunCommand() { @@ -87,12 +97,12 @@ protected String getCommand() { @Override protected String getDefaultSuccessMessage() { - return "success message for elasticsearch-service-x64"; + return "success message for " + serviceId; } @Override protected String getDefaultFailureMessage() { - return "failure message for elasticsearch-service-x64"; + return "failure message for " + serviceId; } public void testMissingExe() throws Exception { @@ -125,32 +135,24 @@ void assertLogArgs(Map logArgs) throws Exception { public void testDefaultLogArgs() throws Exception { String logsDir = esHomeDir.resolve("logs").toString(); assertLogArgs( - Map.of( - "--LogPath", - "\"" + logsDir + "\"", - "--LogPrefix", - "\"elasticsearch-service-x64\"", - "--StdError", - "auto", - "--StdOutput", - "auto" - ) + Map.of("LogPath", "\"" + logsDir + "\"", "LogPrefix", "\"elasticsearch-service-x64\"", "StdError", "auto", "StdOutput", "auto") ); } public void testLogOpts() throws Exception { envVars.put("LOG_OPTS", "--LogPath custom"); - assertLogArgs(Map.of("--LogPath", "custom")); + assertLogArgs(Map.of("LogPath", "custom")); } public void testLogDir() throws Exception { envVars.put("SERVICE_LOG_DIR", "mylogdir"); - assertLogArgs(Map.of("--LogPath", "\"mylogdir\"")); + assertLogArgs(Map.of("LogPath", "\"mylogdir\"")); } public void testLogPrefix() throws Exception { + serviceId = "myservice"; envVars.put("SERVICE_ID", "myservice"); - assertLogArgs(Map.of("--LogPrefix", "\"myservice\"")); + assertLogArgs(Map.of("LogPrefix", "\"myservice\"")); } public void testAdditionalArgs() throws Exception { From 1c1c7c9e6f625042382f7507f65d0e481c505dd7 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 17 May 2022 20:12:59 -0700 Subject: [PATCH 159/166] fix naming of windows service daemon --- .../{WindowsService.java => WindowsServiceDaemon.java} | 4 ++-- ...rviceProvider.java => WindowsServiceDaemonProvider.java} | 6 +++--- .../windows/service/WindowsServiceInstallCommand.java | 2 +- .../META-INF/services/org.elasticsearch.cli.CliToolProvider | 2 +- .../windows/service/WindowsServiceInstallCommandTests.java | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) rename distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/{WindowsService.java => WindowsServiceDaemon.java} (94%) rename distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/{WindowsServiceProvider.java => WindowsServiceDaemonProvider.java} (79%) diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsService.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java similarity index 94% rename from distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsService.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java index a3cd4a624a292..ebd2e74fddf43 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsService.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemon.java @@ -24,11 +24,11 @@ * This class is expected to be run via Apache Procrun in a long lived JVM that will call close * when the server should shutdown. */ -class WindowsService extends EnvironmentAwareCommand { +class WindowsServiceDaemon extends EnvironmentAwareCommand { private volatile ServerProcess server; - WindowsService() { + WindowsServiceDaemon() { super("Starts and stops the Elasticsearch server process for a Windows Service"); } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceProvider.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemonProvider.java similarity index 79% rename from distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceProvider.java rename to distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemonProvider.java index 859782c9cb140..a07883dcffcec 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceProvider.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceDaemonProvider.java @@ -11,14 +11,14 @@ import org.elasticsearch.cli.CliToolProvider; import org.elasticsearch.cli.Command; -public class WindowsServiceProvider implements CliToolProvider { +public class WindowsServiceDaemonProvider implements CliToolProvider { @Override public String name() { - return "windows-service"; + return "windows-service-daemon"; } @Override public Command create() { - return new WindowsService(); + return new WindowsServiceDaemon(); } } diff --git a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java index d372a5e5bbe3e..4e6e2cddfeb93 100644 --- a/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java +++ b/distribution/tools/windows-service-cli/src/main/java/org/elasticsearch/windows/service/WindowsServiceInstallCommand.java @@ -59,7 +59,7 @@ protected String getAdditionalArgs(String serviceId, ProcessInfo pinfo) { addArg(args, "--StartMode", "jvm"); addArg(args, "--StopMode", "jvm"); addArg(args, "--StartPath", pinfo.workingDir().toString()); - addArg(args, "++JvmOptions", "-Dcli.name=windows-service"); + addArg(args, "++JvmOptions", "-Dcli.name=windows-service-daemon"); addArg(args, "++JvmOptions", "-Dcli.libs=lib/tools/server-cli,lib/tools/windows-service-cli"); addArg(args, "++Environment", "HOSTNAME=%s".formatted(pinfo.envVars().get("COMPUTERNAME"))); diff --git a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider index a2b1d3e183143..49d8b3d9cb0a6 100644 --- a/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider +++ b/distribution/tools/windows-service-cli/src/main/resources/META-INF/services/org.elasticsearch.cli.CliToolProvider @@ -1,2 +1,2 @@ org.elasticsearch.windows.service.WindowsServiceCliProvider -org.elasticsearch.windows.service.WindowsServiceProvider +org.elasticsearch.windows.service.WindowsServiceDaemonProvider diff --git a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java index b2e6ed1daabf7..ffd0e16fd6f79 100644 --- a/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java +++ b/distribution/tools/windows-service-cli/src/test/java/org/elasticsearch/windows/service/WindowsServiceInstallCommandTests.java @@ -104,7 +104,7 @@ public void testJvmOptions() throws Exception { assertThat( options, containsInAnyOrder( - "-Dcli.name=windows-service", + "-Dcli.name=windows-service-daemon", "-Dcli.libs=lib/tools/server-cli,lib/tools/windows-service-cli", String.join(";", expectedOptions) ) From 48363b53622ec841f7b1f133013be2c2f486f9ec Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 17 May 2022 21:26:06 -0700 Subject: [PATCH 160/166] fix detach state --- .../java/org/elasticsearch/server/cli/ServerProcess.java | 1 + .../org/elasticsearch/server/cli/ServerProcessTests.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java index 42bb44cd2081a..9195c1cf9a6eb 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -136,6 +136,7 @@ static ServerProcess start( public synchronized void detach() throws IOException { errorPump.drain(); IOUtils.close(jvmProcess.getOutputStream(), jvmProcess.getInputStream(), jvmProcess.getErrorStream()); + detached = true; } /** diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java index 4ce73230eff87..059c43f5b00fa 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.core.IOUtils; import org.elasticsearch.test.ESTestCase; import org.junit.AfterClass; @@ -363,10 +364,14 @@ public void testDetach() throws Exception { stderr.println(BootstrapInfo.SERVER_READY_MARKER); stderr.println("final message"); stderr.close(); + // will block until stdin closed manually after test + assertThat(stdin.read(), equalTo(-1)); }; var server = startProcess(true, false, null, ""); server.detach(); assertThat(terminal.getErrorOutput(), containsString("final message")); + server.stop(); // this should be a noop, and will fail the stdin read assert above if shutdown sent + process.processStdin.close(); // unblock the "process" thread so it can exit } public void testStop() throws Exception { From 358447a29052f837128e200be91caf026fb7030e Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 17 May 2022 22:29:08 -0700 Subject: [PATCH 161/166] spotless --- .../java/org/elasticsearch/server/cli/ServerProcessTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java index 059c43f5b00fa..78ec097a14d90 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java @@ -17,7 +17,6 @@ import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.util.concurrent.CountDown; import org.elasticsearch.core.IOUtils; import org.elasticsearch.test.ESTestCase; import org.junit.AfterClass; From 48b55a741acd296319af1241d8d59caa70180411 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 18 May 2022 13:50:49 -0700 Subject: [PATCH 162/166] add enrollment token test --- .../test/java/org/elasticsearch/server/cli/ServerCliTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java index 719bb0441d19b..471e32f13f921 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerCliTests.java @@ -60,6 +60,7 @@ public void testVersion() throws Exception { assertMutuallyExclusiveOptions("-V", "--daemonize"); assertMutuallyExclusiveOptions("-V", "-p", "/tmp/pid"); assertMutuallyExclusiveOptions("-V", "--pidfile", "/tmp/pid"); + assertMutuallyExclusiveOptions("-V", "--enrollment-token", "mytoken"); assertMutuallyExclusiveOptions("--version", "-d"); assertMutuallyExclusiveOptions("--version", "--daemonize"); assertMutuallyExclusiveOptions("--version", "-p", "/tmp/pid"); From 3234bd460a7f7b4931bd2b5d1e9d40542713dd59 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 18 May 2022 13:56:05 -0700 Subject: [PATCH 163/166] guard against exceptional read errors --- .../elasticsearch/server/cli/ServerProcess.java | 1 - .../elasticsearch/bootstrap/Elasticsearch.java | 15 +++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java index 9195c1cf9a6eb..c6a6eea18cc46 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -156,7 +156,6 @@ public synchronized void stop() { } sendShutdownMarker(); - errorPump.drain(); waitFor(); } diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 12f62f4db8727..c62c8744a9e44 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -158,12 +158,15 @@ private static void startCliMonitorThread(InputStream stdin) { int msg = -1; try { msg = stdin.read(); - } catch (IOException e) {} - if (msg == BootstrapInfo.SERVER_SHUTDOWN_MARKER) { - exit(0); - } else { - // parent process died or there was an error reading from it - exit(1); + } catch (IOException e) { + // ignore, whether we cleanly got end of stream (-1) or an error, we will shut down below + } finally { + if (msg == BootstrapInfo.SERVER_SHUTDOWN_MARKER) { + exit(0); + } else { + // parent process died or there was an error reading from it + exit(1); + } } }).start(); } From ce2a433ada93150772eea1be48df3cb759e03d30 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 18 May 2022 14:24:13 -0700 Subject: [PATCH 164/166] protect from unexpected exception in error pump --- .../java/org/elasticsearch/server/cli/ErrorPumpThread.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java index bdb0b0d4aae47..d8f0c4471c658 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ErrorPumpThread.java @@ -97,8 +97,9 @@ public void run() { } } catch (IOException e) { ioFailure = e; + } finally { + writer.flush(); + readyOrDead.countDown(); } - writer.flush(); - readyOrDead.countDown(); } } From 83083e2f8d4dde8a631063e67610aebb68bf3b25 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 18 May 2022 14:43:38 -0700 Subject: [PATCH 165/166] improve javadocs --- .../org/elasticsearch/launcher/CliToolLauncher.java | 2 +- .../org/elasticsearch/server/cli/ServerProcess.java | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java index a287674f12515..f187763f13cad 100644 --- a/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java +++ b/distribution/tools/cli-launcher/src/main/java/org/elasticsearch/launcher/CliToolLauncher.java @@ -119,7 +119,7 @@ private static void configureLoggingWithoutConfig(Map sysprops) * http://commons.apache.org/proper/commons-daemon/procrun.html * * NOTE: If this method is renamed and/or moved, make sure to - * update elasticsearch-service.bat! + * update WindowsServiceInstallCommand! */ static void close(String[] args) throws IOException { command.close(); diff --git a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java index c6a6eea18cc46..274131137c8d1 100644 --- a/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java +++ b/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/ServerProcess.java @@ -56,7 +56,7 @@ public class ServerProcess { // the thread pumping stderr watching for state change messages private final ErrorPumpThread errorPump; - // a flag marking whether the java process has been detached from + // a flag marking whether the streams of the java subprocess have been closed private volatile boolean detached = false; ServerProcess(Process jvmProcess, ErrorPumpThread errorPump) { @@ -150,6 +150,14 @@ public void waitFor() { } } + /** + * Stop the subprocess. + * + *

This sends a special code, {@link BootstrapInfo#SERVER_SHUTDOWN_MARKER} to the stdin + * of the process, then waits for the process to exit. + * + *

Note that if {@link #detach()} has been called, this method is a no-op. + */ public synchronized void stop() { if (detached) { return; From a7f9442ba5f38c8ca935a2f3907719e2410de321 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 18 May 2022 17:34:41 -0700 Subject: [PATCH 166/166] tweak for running test on windows --- .../org/elasticsearch/server/cli/ServerProcessTests.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java index 78ec097a14d90..91640e056ae64 100644 --- a/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java +++ b/distribution/tools/server-cli/src/test/java/org/elasticsearch/server/cli/ServerProcessTests.java @@ -342,8 +342,9 @@ public void testCommandLineSysprops() throws Exception { public void testCommandLine() throws Exception { String mainClass = "org.elasticsearch.bootstrap.Elasticsearch"; String distroSysprop = "-Des.distribution.type=testdistro"; + Path javaBin = Paths.get("javahome").resolve("bin"); sysprops.put("es.distribution.type", "testdistro"); - AtomicReference expectedJava = new AtomicReference<>("javahome/bin/java"); + AtomicReference expectedJava = new AtomicReference<>(javaBin.resolve("java").toString()); AtomicReference expectedClasspath = new AtomicReference<>(esHomeDir.resolve("lib") + "/*"); processValidator = pb -> { assertThat(pb.command(), hasItems(expectedJava.get(), distroSysprop, "-cp", expectedClasspath.get(), mainClass)); @@ -352,7 +353,7 @@ public void testCommandLine() throws Exception { sysprops.put("os.name", "Windows 10"); sysprops.put("java.io.tmpdir", createTempDir().toString()); - expectedJava.set(Paths.get("javahome").resolve("bin").resolve("java.exe").toString()); + expectedJava.set(javaBin.resolve("java.exe").toString()); expectedClasspath.set(esHomeDir.resolve("lib") + "\\*"); runForeground(); }