Browse Source

Accept settings in snake case in Docker image (#74327)

Closes #74036. Since some orchestration platforms forbid periods in
environment variable names, allow Docker users to pass settings to ES
using an alternative name scheme. For example:

    bootstrap.memory_lock

...becomes:

    ES_BOOTSTRAP_MEMORY__LOCK

The setting name is uppercased, prefixed, all underscores are converted
to double underscores, and all periods are converted to underscores.
Rory Hunter 4 năm trước cách đây
mục cha
commit
d08b851a5b

+ 28 - 4
distribution/src/bin/elasticsearch-env

@@ -111,7 +111,7 @@ if [[ "$ES_DISTRIBUTION_TYPE" == "docker" ]]; then
 
   # Parse Docker env vars to customize Elasticsearch
   #
-  # e.g. Setting the env var cluster.name=testcluster
+  # e.g. Setting the env var cluster.name=testcluster or ES_CLUSTER_NAME=testcluster
   #
   # will cause Elasticsearch to be invoked with -Ecluster.name=testcluster
   #
@@ -122,11 +122,35 @@ if [[ "$ES_DISTRIBUTION_TYPE" == "docker" ]]; then
   while IFS='=' read -r envvar_key envvar_value
   do
     # Elasticsearch settings need to have at least two dot separated lowercase
-    # words, e.g. `cluster.name`
-    if [[ "$envvar_key" =~ ^[a-z0-9_]+\.[a-z0-9_]+ ]]; then
-      if [[ ! -z $envvar_value ]]; then
+    # words, e.g. `cluster.name`, or uppercased with underscore separators and
+    # prefixed with `ES_`, e.g. `ES_CLUSTER_NAME`. Underscores in setting names
+    # are escaped by writing them as a double-underscore e.g. "__"
+    if [[ ! -z "$envvar_value" ]]; then
+      if [[ "$envvar_key" =~ ^[a-z0-9_]+\.[a-z0-9_]+ ]]; then
         es_opt="-E${envvar_key}=${envvar_value}"
         es_arg_array+=("${es_opt}")
+      elif [[ "$envvar_key" =~ ^ES(_{1,2}[A-Z]+)+$ ]]; then
+        case "$envvar_key" in
+          # Do nothing for these. Not all of these are actually exported into the environment by our scripts,
+          # and so don't appear in the output of the `env` command, but it's better to be safe than sorry
+          # in case a user exported them in their own shell for some reason.
+          ES_DISTRIBUTION_FLAVOR) ;;
+          ES_DISTRIBUTION_TYPE) ;;
+          ES_HOME) ;;
+          ES_JAVA_HOME) ;;
+          ES_JAVA_OPTS) ;;
+          ES_KEYSTORE_PASSPHRASE_FILE) ;;
+          ES_LOG_STYLE) ;;
+          ES_PATH_CONF) ;;
+          ES_SD_NOTIFY) ;;
+          ES_TMPDIR) ;;
+          *)
+            # The long-hand sed `y` command works in any sed variant.
+            envvar_key="$(echo "$envvar_key" | sed -e 's/^ES_//; s/_/./g ; s/\.\./_/g; y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' )"
+            es_opt="-E${envvar_key}=${envvar_value}"
+            es_arg_array+=("${es_opt}")
+            ;;
+        esac
       fi
     fi
   done <<< "$(env)"

+ 13 - 1
docs/reference/setup/install/docker.asciidoc

@@ -339,7 +339,19 @@ over the configuration files in the image.
 
 You can set individual {es} configuration parameters using Docker environment variables.
 The <<docker-compose-file, sample compose file>> and the
-<<docker-cli-run-dev-mode, single-node example>> use this method.
+<<docker-cli-run-dev-mode, single-node example>> use this method. You can
+use the setting name directly as the environment variable name. If
+you cannot do this, for example because your orchestration platform forbids
+periods in environment variable names, then you can use an alternative
+style by converting the setting name as follows.
+
+. Change the setting name to uppercase
+. Prefix it with `ES_`
+. Escape any underscores (`_`) by duplicating them
+. Convert all periods (`.`) to underscores (`_`)
+
+For example, `-e bootstrap.memory_lock=true` becomes
+`-e ES_BOOTSTRAP_MEMORY__LOCK=true`.
 
 To use the contents of a file to set an environment variable, suffix the environment
 variable name with `_FILE`. This is useful for passing secrets such as passwords to {es}

+ 44 - 0
qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java

@@ -486,6 +486,50 @@ public class DockerTests extends PackagingTestCase {
         assertThat(result.stdout, containsString("java.net.UnknownHostException: this.is.not.valid"));
     }
 
+    /**
+     * Check that settings are applied when they are supplied as environment variables with names that are:
+     * <ul>
+     *     <li>Prefixed with {@code ES_}</li>
+     *     <li>All uppercase</li>
+     *     <li>Dots (periods) are converted to underscores</li>
+     *     <li>Underscores in setting names are escaped by doubling them</li>
+     * </ul>
+     */
+    public void test086EnvironmentVariablesInSnakeCaseAreTranslated() {
+        // Note the double-underscore in the var name here, which retains the underscore in translation
+        installation = runContainer(distribution(), builder().envVars(Map.of("ES_XPACK_SECURITY_FIPS__MODE_ENABLED", "false")));
+
+        final Optional<String> commandLine = sh.run("bash -c 'COLUMNS=2000 ps ax'").stdout.lines()
+            .filter(line -> line.contains("org.elasticsearch.bootstrap.Elasticsearch"))
+            .findFirst();
+
+        assertThat(commandLine.isPresent(), equalTo(true));
+
+        assertThat(commandLine.get(), containsString("-Expack.security.fips_mode.enabled=false"));
+    }
+
+    /**
+     * Check that environment variables that do not match the criteria for translation to settings are ignored.
+     */
+    public void test087EnvironmentVariablesInIncorrectFormatAreIgnored() {
+        final Map<String, String> envVars = new HashMap<>();
+        // No ES_ prefix
+        envVars.put("XPACK_SECURITY_FIPS__MODE_ENABLED", "false");
+        // Not underscore-separated
+        envVars.put("ES.XPACK.SECURITY.FIPS_MODE.ENABLED", "false");
+        // Not uppercase
+        envVars.put("es_xpack_security_fips__mode_enabled", "false");
+        installation = runContainer(distribution(), builder().envVars(envVars));
+
+        final Optional<String> commandLine = sh.run("bash -c 'COLUMNS=2000 ps ax'").stdout.lines()
+            .filter(line -> line.contains("org.elasticsearch.bootstrap.Elasticsearch"))
+            .findFirst();
+
+        assertThat(commandLine.isPresent(), equalTo(true));
+
+        assertThat(commandLine.get(), not(containsString("-Expack.security.fips_mode.enabled=false")));
+    }
+
     /**
      * Check whether the elasticsearch-certutil tool has been shipped correctly,
      * and if present then it can execute.