/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.node.internal; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.common.cli.CliToolTestCase; import org.elasticsearch.common.cli.Terminal; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsException; import org.elasticsearch.env.Environment; import org.elasticsearch.test.ESTestCase; import org.junit.After; import org.junit.Before; import java.io.IOException; import java.io.InputStream; 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.concurrent.atomic.AtomicInteger; import static org.elasticsearch.common.settings.Settings.settingsBuilder; import static org.hamcrest.Matchers.*; public class InternalSettingsPreparerTests extends ESTestCase { Map<String, String> savedProperties = new HashMap<>(); Settings baseEnvSettings; @Before public void saveSettingsSystemProperties() { // clear out any properties the settings preparer may look for savedProperties.clear(); for (Object propObj : System.getProperties().keySet()) { String property = (String)propObj; // NOTE: these prefixes are prefixes of the defaults, so both are handled here for (String prefix : InternalSettingsPreparer.PROPERTY_PREFIXES) { if (property.startsWith(prefix)) { savedProperties.put(property, System.getProperty(property)); } } } String name = System.getProperty("name"); if (name != null) { savedProperties.put("name", name); } for (String property : savedProperties.keySet()) { System.clearProperty(property); } } @After public void restoreSettingsSystemProperties() { for (Map.Entry<String, String> property : savedProperties.entrySet()) { System.setProperty(property.getKey(), property.getValue()); } } @Before public void createBaseEnvSettings() { baseEnvSettings = settingsBuilder() .put("path.home", createTempDir()) .build(); } @After public void clearBaseEnvSettings() { baseEnvSettings = null; } public void testEmptySettings() { Settings settings = InternalSettingsPreparer.prepareSettings(Settings.EMPTY); assertNotNull(settings.get("name")); // a name was set assertNotNull(settings.get(ClusterName.SETTING)); // a cluster name was set assertEquals(settings.toString(), 2, settings.names().size()); Environment env = InternalSettingsPreparer.prepareEnvironment(baseEnvSettings, null); settings = env.settings(); assertNotNull(settings.get("name")); // a name was set assertNotNull(settings.get(ClusterName.SETTING)); // a cluster name was set assertEquals(settings.toString(), 3 /* path.home is in the base settings */, settings.names().size()); String home = baseEnvSettings.get("path.home"); String configDir = env.configFile().toString(); assertTrue(configDir, configDir.startsWith(home)); } public void testClusterNameDefault() { Settings settings = InternalSettingsPreparer.prepareSettings(Settings.EMPTY); assertEquals(ClusterName.DEFAULT.value(), settings.get(ClusterName.SETTING)); settings = InternalSettingsPreparer.prepareEnvironment(baseEnvSettings, null).settings(); assertEquals(ClusterName.DEFAULT.value(), settings.get(ClusterName.SETTING)); } public void testIgnoreSystemProperties() { try { System.setProperty("es.node.zone", "foo"); Settings settings = settingsBuilder() .put("node.zone", "bar") .put(baseEnvSettings) .build(); Environment env = InternalSettingsPreparer.prepareEnvironment(settings, null); // Should use setting from the system property assertThat(env.settings().get("node.zone"), equalTo("foo")); settings = settingsBuilder() .put(InternalSettingsPreparer.IGNORE_SYSTEM_PROPERTIES_SETTING, true) .put("node.zone", "bar") .put(baseEnvSettings) .build(); env = InternalSettingsPreparer.prepareEnvironment(settings, null); // Should use setting from the system property assertThat(env.settings().get("node.zone"), equalTo("bar")); } finally { System.clearProperty("es.node.zone"); } } public void testReplacePromptPlaceholders() { final List<String> replacedSecretProperties = new ArrayList<>(); final List<String> replacedTextProperties = new ArrayList<>(); final Terminal terminal = new CliToolTestCase.MockTerminal() { @Override public char[] readSecret(String message, Object... args) { for (Object arg : args) { replacedSecretProperties.add((String) arg); } return "replaced".toCharArray(); } @Override public String readText(String message, Object... args) { for (Object arg : args) { replacedTextProperties.add((String) arg); } return "text"; } }; Settings.Builder builder = settingsBuilder() .put(baseEnvSettings) .put("password.replace", InternalSettingsPreparer.SECRET_PROMPT_VALUE) .put("dont.replace", "prompt:secret") .put("dont.replace2", "_prompt:secret_") .put("dont.replace3", "_prompt:text__") .put("dont.replace4", "__prompt:text_") .put("dont.replace5", "prompt:secret__") .put("replace_me", InternalSettingsPreparer.TEXT_PROMPT_VALUE); Settings settings = InternalSettingsPreparer.prepareEnvironment(builder.build(), terminal).settings(); assertThat(replacedSecretProperties.size(), is(1)); assertThat(replacedTextProperties.size(), is(1)); assertThat(settings.get("password.replace"), equalTo("replaced")); assertThat(settings.get("replace_me"), equalTo("text")); // verify other values unchanged assertThat(settings.get("dont.replace"), equalTo("prompt:secret")); assertThat(settings.get("dont.replace2"), equalTo("_prompt:secret_")); assertThat(settings.get("dont.replace3"), equalTo("_prompt:text__")); assertThat(settings.get("dont.replace4"), equalTo("__prompt:text_")); assertThat(settings.get("dont.replace5"), equalTo("prompt:secret__")); } public void testReplaceSecretPromptPlaceholderWithNullTerminal() { Settings.Builder builder = settingsBuilder() .put(baseEnvSettings) .put("replace_me1", InternalSettingsPreparer.SECRET_PROMPT_VALUE); try { InternalSettingsPreparer.prepareEnvironment(builder.build(), null); fail("an exception should have been thrown since no terminal was provided!"); } catch (UnsupportedOperationException e) { assertThat(e.getMessage(), containsString("with value [" + InternalSettingsPreparer.SECRET_PROMPT_VALUE + "]")); } } public void testReplaceTextPromptPlaceholderWithNullTerminal() { Settings.Builder builder = settingsBuilder() .put(baseEnvSettings) .put("replace_me1", InternalSettingsPreparer.TEXT_PROMPT_VALUE); try { InternalSettingsPreparer.prepareEnvironment(builder.build(), null); fail("an exception should have been thrown since no terminal was provided!"); } catch (UnsupportedOperationException e) { assertThat(e.getMessage(), containsString("with value [" + InternalSettingsPreparer.TEXT_PROMPT_VALUE + "]")); } } public void testNameSettingsPreference() { try { System.setProperty("name", "sys-prop-name"); // Test system property overrides node.name Settings settings = settingsBuilder() .put("node.name", "node-name") .put(baseEnvSettings) .build(); Environment env = InternalSettingsPreparer.prepareEnvironment(settings, null); assertThat(env.settings().get("name"), equalTo("sys-prop-name")); // test name in settings overrides sys prop and node.name settings = settingsBuilder() .put("name", "name-in-settings") .put("node.name", "node-name") .put(baseEnvSettings) .build(); env = InternalSettingsPreparer.prepareEnvironment(settings, null); assertThat(env.settings().get("name"), equalTo("name-in-settings")); // test only node.name in settings System.clearProperty("name"); settings = settingsBuilder() .put("node.name", "node-name") .put(baseEnvSettings) .build(); env = InternalSettingsPreparer.prepareEnvironment(settings, null); assertThat(env.settings().get("name"), equalTo("node-name")); // test no name at all results in name being set env = InternalSettingsPreparer.prepareEnvironment(baseEnvSettings, null); assertThat(env.settings().get("name"), not("name-in-settings")); assertThat(env.settings().get("name"), not("sys-prop-name")); assertThat(env.settings().get("name"), not("node-name")); assertThat(env.settings().get("name"), notNullValue()); } finally { System.clearProperty("name"); } } public void testPromptForNodeNameOnlyPromptsOnce() { final AtomicInteger counter = new AtomicInteger(); final Terminal terminal = new CliToolTestCase.MockTerminal() { @Override public char[] readSecret(String message, Object... args) { fail("readSecret should never be called by this test"); return null; } @Override public String readText(String message, Object... args) { int count = counter.getAndIncrement(); return "prompted name " + count; } }; System.clearProperty("name"); Settings settings = Settings.builder() .put(baseEnvSettings) .put("node.name", InternalSettingsPreparer.TEXT_PROMPT_VALUE) .build(); Environment env = InternalSettingsPreparer.prepareEnvironment(settings, terminal); settings = env.settings(); assertThat(counter.intValue(), is(1)); assertThat(settings.get("name"), is("prompted name 0")); assertThat(settings.get("node.name"), is("prompted name 0")); } public void testGarbageIsNotSwallowed() throws IOException { try { InputStream garbage = getClass().getResourceAsStream("/config/garbage/garbage.yml"); Path home = createTempDir(); Path config = home.resolve("config"); Files.createDirectory(config); Files.copy(garbage, config.resolve("elasticsearch.yml")); InternalSettingsPreparer.prepareEnvironment(settingsBuilder() .put("config.ignore_system_properties", true) .put(baseEnvSettings) .build(), null); } catch (SettingsException e) { assertEquals("Failed to load settings from [elasticsearch.yml]", e.getMessage()); } } public void testMultipleSettingsFileNotAllowed() throws IOException { InputStream yaml = getClass().getResourceAsStream("/config/elasticsearch.yaml"); InputStream properties = getClass().getResourceAsStream("/config/elasticsearch.properties"); Path home = createTempDir(); Path config = home.resolve("config"); Files.createDirectory(config); Files.copy(yaml, config.resolve("elasticsearch.yaml")); Files.copy(properties, config.resolve("elasticsearch.properties")); try { InternalSettingsPreparer.prepareEnvironment(settingsBuilder() .put("config.ignore_system_properties", true) .put(baseEnvSettings) .build(), null); } catch (SettingsException e) { assertTrue(e.getMessage(), e.getMessage().contains("multiple settings files found with suffixes")); assertTrue(e.getMessage(), e.getMessage().contains(".yaml")); assertTrue(e.getMessage(), e.getMessage().contains(".properties")); } } }