Skip to content

Commit 32b2caa

Browse files
authored
Correct handling of default and array settings
In Elasticsearch 5.3.0 a bug was introduced in the merging of default settings when the target setting existed as an array. This arose due to the fact that when a target setting is an array, the setting key is broken into key.0, key.1, ..., key.n, one for each element of the array. When settings are replaced by default.key, we are looking for the target key but not the target key.0. This leads to key, and key.0, ..., key.n being present in the constructed settings object. This commit addresses two issues here. The first is that we fix the merging of the keys so that when we try to merge default.key, we also check for the presence of the flattened keys. The second is that when we try to get a setting value as an array from a settings object, we check whether or not the backing map contains the top-level key as well as the flattened keys. This latter check would have caught the first bug. For kicks, we add some tests. Relates #24074
1 parent fb3a281 commit 32b2caa

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

core/src/main/java/org/elasticsearch/common/settings/Settings.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import java.util.Iterator;
5858
import java.util.LinkedHashMap;
5959
import java.util.List;
60+
import java.util.Locale;
6061
import java.util.Map;
6162
import java.util.NoSuchElementException;
6263
import java.util.Objects;
@@ -442,6 +443,20 @@ public String[] getAsArray(String settingPrefix, String[] defaultArray) throws S
442443
public String[] getAsArray(String settingPrefix, String[] defaultArray, Boolean commaDelimited) throws SettingsException {
443444
List<String> result = new ArrayList<>();
444445

446+
final String valueFromPrefix = get(settingPrefix);
447+
final String valueFromPreifx0 = get(settingPrefix + ".0");
448+
449+
if (valueFromPrefix != null && valueFromPreifx0 != null) {
450+
final String message = String.format(
451+
Locale.ROOT,
452+
"settings object contains values for [%s=%s] and [%s=%s]",
453+
settingPrefix,
454+
valueFromPrefix,
455+
settingPrefix + ".0",
456+
valueFromPreifx0);
457+
throw new IllegalStateException(message);
458+
}
459+
445460
if (get(settingPrefix) != null) {
446461
if (commaDelimited) {
447462
String[] strings = Strings.splitStringByCommaToArray(get(settingPrefix));

core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,21 @@ public static Environment prepareEnvironment(Settings input, Terminal terminal,
125125
}
126126

127127
/**
128-
* Initializes the builder with the given input settings, and loads system properties settings if allowed.
129-
* If loadDefaults is true, system property default settings are loaded.
128+
* Initializes the builder with the given input settings, and applies settings and default settings from the specified map (these
129+
* settings typically come from the command line). The default settings are applied only if the setting does not exist in the specified
130+
* output.
131+
*
132+
* @param output the settings builder to apply the input and default settings to
133+
* @param input the input settings
134+
* @param esSettings a map from which to apply settings and default settings
130135
*/
131-
private static void initializeSettings(Settings.Builder output, Settings input, Map<String, String> esSettings) {
136+
static void initializeSettings(final Settings.Builder output, final Settings input, final Map<String, String> esSettings) {
132137
output.put(input);
133138
output.putProperties(esSettings,
134-
PROPERTY_DEFAULTS_PREDICATE.and(key -> output.get(STRIP_PROPERTY_DEFAULTS_PREFIX.apply(key)) == null),
135-
STRIP_PROPERTY_DEFAULTS_PREFIX);
139+
PROPERTY_DEFAULTS_PREDICATE
140+
.and(key -> output.get(STRIP_PROPERTY_DEFAULTS_PREFIX.apply(key)) == null)
141+
.and(key -> output.get(STRIP_PROPERTY_DEFAULTS_PREFIX.apply(key) + ".0") == null),
142+
STRIP_PROPERTY_DEFAULTS_PREFIX);
136143
output.putProperties(esSettings, PROPERTY_DEFAULTS_PREDICATE.negate(), Function.identity());
137144
output.replacePropertyPlaceholders();
138145
}

core/src/test/java/org/elasticsearch/common/settings/SettingsTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,4 +562,16 @@ public void testSecureSettingConflict() {
562562
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> setting.get(settings));
563563
assertTrue(e.getMessage().contains("must be stored inside the Elasticsearch keystore"));
564564
}
565+
566+
public void testGetAsArrayFailsOnDuplicates() {
567+
final Settings settings =
568+
Settings.builder()
569+
.put("foobar.0", "bar")
570+
.put("foobar.1", "baz")
571+
.put("foobar", "foo")
572+
.build();
573+
final IllegalStateException e = expectThrows(IllegalStateException.class, () -> settings.getAsArray("foobar"));
574+
assertThat(e, hasToString(containsString("settings object contains values for [foobar=foo] and [foobar.0=bar]")));
575+
}
576+
565577
}

core/src/test/java/org/elasticsearch/node/internal/InternalSettingsPreparerTests.java renamed to core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* under the License.
1818
*/
1919

20-
package org.elasticsearch.node.internal;
20+
package org.elasticsearch.node;
2121

2222
import org.elasticsearch.cli.MockTerminal;
2323
import org.elasticsearch.cluster.ClusterName;
@@ -196,4 +196,15 @@ public void testDefaultPropertiesOverride() throws Exception {
196196
Environment env = InternalSettingsPreparer.prepareEnvironment(baseEnvSettings, null, props);
197197
assertEquals("bar", env.settings().get("setting"));
198198
}
199+
200+
public void testDefaultWithArray() {
201+
final Settings.Builder output = Settings.builder().put("foobar.0", "bar").put("foobar.1", "baz");
202+
final Map<String, String> esSettings = Collections.singletonMap("default.foobar", "foo");
203+
InternalSettingsPreparer.initializeSettings(output, Settings.EMPTY, esSettings);
204+
final Settings settings = output.build();
205+
assertThat(settings.get("foobar.0"), equalTo("bar"));
206+
assertThat(settings.get("foobar.1"), equalTo("baz"));
207+
assertNull(settings.get("foobar"));
208+
}
209+
199210
}

0 commit comments

Comments
 (0)