/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ /* * 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. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.common.settings; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LogEvent; import org.opensearch.cluster.metadata.IndexMetadata; import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.core.common.io.stream.BytesStreamInput; import org.opensearch.common.io.stream.BytesStreamOutput; import org.opensearch.common.settings.AbstractScopedSettings.SettingUpdater; import org.opensearch.common.settings.Setting.ByteSizeValueParser; import org.opensearch.common.settings.Setting.DoubleParser; import org.opensearch.common.settings.Setting.FloatParser; import org.opensearch.common.settings.Setting.IntegerParser; import org.opensearch.common.settings.Setting.LongParser; import org.opensearch.common.settings.Setting.MemorySizeValueParser; import org.opensearch.common.settings.Setting.MinMaxTimeValueParser; import org.opensearch.common.settings.Setting.MinTimeValueParser; import org.opensearch.common.settings.Setting.Property; import org.opensearch.common.settings.Setting.RegexValidator; import org.opensearch.core.common.unit.ByteSizeUnit; import org.opensearch.core.common.unit.ByteSizeValue; import org.opensearch.common.unit.TimeValue; import org.opensearch.index.IndexSettings; import org.opensearch.monitor.jvm.JvmInfo; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.MockLogAppender; import org.opensearch.test.junit.annotations.TestLogging; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.opensearch.index.IndexSettingsTests.newIndexMeta; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; public class SettingTests extends OpenSearchTestCase { public void testGet() { Setting booleanSetting = Setting.boolSetting("foo.bar", false, Property.Dynamic, Property.NodeScope); assertFalse(booleanSetting.get(Settings.EMPTY)); assertFalse(booleanSetting.get(Settings.builder().put("foo.bar", false).build())); assertTrue(booleanSetting.get(Settings.builder().put("foo.bar", true).build())); } public void testByteSizeSetting() { final Setting byteSizeValueSetting = Setting.byteSizeSetting( "a.byte.size", new ByteSizeValue(1024), Property.Dynamic, Property.NodeScope ); assertFalse(byteSizeValueSetting.isGroupSetting()); final ByteSizeValue byteSizeValue = byteSizeValueSetting.get(Settings.EMPTY); assertThat(byteSizeValue.getBytes(), equalTo(1024L)); } public void testByteSizeSettingMinValue() { final Setting byteSizeValueSetting = Setting.byteSizeSetting( "a.byte.size", new ByteSizeValue(100, ByteSizeUnit.MB), new ByteSizeValue(20_000_000, ByteSizeUnit.BYTES), new ByteSizeValue(Integer.MAX_VALUE, ByteSizeUnit.BYTES) ); final long value = 20_000_000 - randomIntBetween(1, 1024); final Settings settings = Settings.builder().put("a.byte.size", value + "b").build(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> byteSizeValueSetting.get(settings)); final String expectedMessage = "failed to parse value [" + value + "b] for setting [a.byte.size], must be >= [20000000b]"; assertThat(e, hasToString(containsString(expectedMessage))); } public void testByteSizeSettingMaxValue() { final Setting byteSizeValueSetting = Setting.byteSizeSetting( "a.byte.size", new ByteSizeValue(100, ByteSizeUnit.MB), new ByteSizeValue(16, ByteSizeUnit.MB), new ByteSizeValue(Integer.MAX_VALUE, ByteSizeUnit.BYTES) ); final long value = (1L << 31) - 1 + randomIntBetween(1, 1024); final Settings settings = Settings.builder().put("a.byte.size", value + "b").build(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> byteSizeValueSetting.get(settings)); final String expectedMessage = "failed to parse value [" + value + "b] for setting [a.byte.size], must be <= [2147483647b]"; assertThat(e, hasToString(containsString(expectedMessage))); } public void testByteSizeSettingValidation() { final Setting byteSizeValueSetting = Setting.byteSizeSetting( "a.byte.size", s -> "2048b", Property.Dynamic, Property.NodeScope ); final ByteSizeValue byteSizeValue = byteSizeValueSetting.get(Settings.EMPTY); assertThat(byteSizeValue.getBytes(), equalTo(2048L)); AtomicReference value = new AtomicReference<>(null); ClusterSettings.SettingUpdater settingUpdater = byteSizeValueSetting.newUpdater(value::set, logger); final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> settingUpdater.apply(Settings.builder().put("a.byte.size", 12).build(), Settings.EMPTY) ); assertThat(e, hasToString(containsString("illegal value can't update [a.byte.size] from [2048b] to [12]"))); assertNotNull(e.getCause()); assertThat(e.getCause(), instanceOf(IllegalArgumentException.class)); final IllegalArgumentException cause = (IllegalArgumentException) e.getCause(); final String expected = "failed to parse setting [a.byte.size] with value [12] as a size in bytes: unit is missing or unrecognized"; assertThat(cause, hasToString(containsString(expected))); assertTrue(settingUpdater.apply(Settings.builder().put("a.byte.size", "12b").build(), Settings.EMPTY)); assertThat(value.get(), equalTo(new ByteSizeValue(12))); } public void testMemorySize() { Setting memorySizeValueSetting = Setting.memorySizeSetting( "a.byte.size", new ByteSizeValue(1024), Property.Dynamic, Property.NodeScope ); assertFalse(memorySizeValueSetting.isGroupSetting()); ByteSizeValue memorySizeValue = memorySizeValueSetting.get(Settings.EMPTY); assertEquals(memorySizeValue.getBytes(), 1024); memorySizeValueSetting = Setting.memorySizeSetting("a.byte.size", s -> "2048b", Property.Dynamic, Property.NodeScope); memorySizeValue = memorySizeValueSetting.get(Settings.EMPTY); assertEquals(memorySizeValue.getBytes(), 2048); memorySizeValueSetting = Setting.memorySizeSetting("a.byte.size", "50%", Property.Dynamic, Property.NodeScope); assertFalse(memorySizeValueSetting.isGroupSetting()); memorySizeValue = memorySizeValueSetting.get(Settings.EMPTY); assertEquals(memorySizeValue.getBytes(), JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.5, 1.0); memorySizeValueSetting = Setting.memorySizeSetting("a.byte.size", s -> "25%", Property.Dynamic, Property.NodeScope); memorySizeValue = memorySizeValueSetting.get(Settings.EMPTY); assertEquals(memorySizeValue.getBytes(), JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.25, 1.0); AtomicReference value = new AtomicReference<>(null); ClusterSettings.SettingUpdater settingUpdater = memorySizeValueSetting.newUpdater(value::set, logger); try { settingUpdater.apply(Settings.builder().put("a.byte.size", 12).build(), Settings.EMPTY); fail("no unit"); } catch (IllegalArgumentException ex) { assertThat(ex, hasToString(containsString("illegal value can't update [a.byte.size] from [25%] to [12]"))); assertNotNull(ex.getCause()); assertThat(ex.getCause(), instanceOf(IllegalArgumentException.class)); final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause(); final String expected = "failed to parse setting [a.byte.size] with value [12] as a size in bytes: unit is missing or unrecognized"; assertThat(cause, hasToString(containsString(expected))); } assertTrue(settingUpdater.apply(Settings.builder().put("a.byte.size", "12b").build(), Settings.EMPTY)); assertEquals(new ByteSizeValue(12), value.get()); assertTrue(settingUpdater.apply(Settings.builder().put("a.byte.size", "20%").build(), Settings.EMPTY)); assertEquals(new ByteSizeValue((int) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.2)), value.get()); } public void testMemorySizeWithFallbackValue() { Setting fallbackSetting = Setting.memorySizeSetting("a.byte.size", "20%", Property.Dynamic, Property.NodeScope); Setting memorySizeSetting = Setting.memorySizeSetting( "b.byte.size", fallbackSetting, Property.Dynamic, Property.NodeScope ); AtomicReference value = new AtomicReference<>(null); ClusterSettings.SettingUpdater settingUpdater = memorySizeSetting.newUpdater(value::set, logger); ByteSizeValue memorySizeValue = memorySizeSetting.get(Settings.EMPTY); assertEquals(memorySizeValue.getBytes(), JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.2, 1.0); assertTrue(settingUpdater.apply(Settings.builder().put("a.byte.size", "30%").build(), Settings.EMPTY)); assertEquals(new ByteSizeValue((int) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.3)), value.get()); assertTrue(settingUpdater.apply(Settings.builder().put("b.byte.size", "40%").build(), Settings.EMPTY)); assertEquals(new ByteSizeValue((int) (JvmInfo.jvmInfo().getMem().getHeapMax().getBytes() * 0.4)), value.get()); } public void testSimpleUpdate() { Setting booleanSetting = Setting.boolSetting("foo.bar", false, Property.Dynamic, Property.NodeScope); AtomicReference atomicBoolean = new AtomicReference<>(null); ClusterSettings.SettingUpdater settingUpdater = booleanSetting.newUpdater(atomicBoolean::set, logger); Settings build = Settings.builder().put("foo.bar", false).build(); settingUpdater.apply(build, Settings.EMPTY); assertNull(atomicBoolean.get()); build = Settings.builder().put("foo.bar", true).build(); settingUpdater.apply(build, Settings.EMPTY); assertTrue(atomicBoolean.get()); // try update bogus value build = Settings.builder().put("foo.bar", "I am not a boolean").build(); try { settingUpdater.apply(build, Settings.EMPTY); fail("not a boolean"); } catch (IllegalArgumentException ex) { assertThat(ex, hasToString(containsString("illegal value can't update [foo.bar] from [false] to [I am not a boolean]"))); assertNotNull(ex.getCause()); assertThat(ex.getCause(), instanceOf(IllegalArgumentException.class)); final IllegalArgumentException cause = (IllegalArgumentException) ex.getCause(); assertThat( cause, hasToString(containsString("Failed to parse value [I am not a boolean] as only [true] or [false] are allowed.")) ); } } public void testSimpleUpdateOfFilteredSetting() { Setting booleanSetting = Setting.boolSetting("foo.bar", false, Property.Dynamic, Property.Filtered); AtomicReference atomicBoolean = new AtomicReference<>(null); ClusterSettings.SettingUpdater settingUpdater = booleanSetting.newUpdater(atomicBoolean::set, logger); // try update bogus value Settings build = Settings.builder().put("foo.bar", "I am not a boolean").build(); IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> settingUpdater.apply(build, Settings.EMPTY)); assertThat(ex, hasToString(equalTo("java.lang.IllegalArgumentException: illegal value can't update [foo.bar]"))); assertNull(ex.getCause()); } @AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/33135") public void testValidateStringSetting() { Settings settings = Settings.builder().putList("foo.bar", Arrays.asList("bla-a", "bla-b")).build(); Setting stringSetting = Setting.simpleString("foo.bar", Property.NodeScope); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> stringSetting.get(settings)); assertEquals("Found list type value for setting [foo.bar] but but did not expect a list for it.", e.getMessage()); } private static final Setting FOO_BAR_SETTING = new Setting<>( "foo.bar", "foobar", Function.identity(), new FooBarValidator(), Property.Dynamic, Property.NodeScope ); private static final Setting BAZ_QUX_SETTING = Setting.simpleString("baz.qux", Property.NodeScope); private static final Setting QUUX_QUUZ_SETTING = Setting.simpleString("quux.quuz", Property.NodeScope); static class FooBarValidator implements Setting.Validator { public static boolean invokedInIsolation; public static boolean invokedWithDependencies; @Override public void validate(final String value) { invokedInIsolation = true; assertThat(value, equalTo("foo.bar value")); } @Override public void validate(final String value, final Map, Object> settings) { invokedWithDependencies = true; assertTrue(settings.keySet().contains(BAZ_QUX_SETTING)); assertThat(settings.get(BAZ_QUX_SETTING), equalTo("baz.qux value")); assertTrue(settings.keySet().contains(QUUX_QUUZ_SETTING)); assertThat(settings.get(QUUX_QUUZ_SETTING), equalTo("quux.quuz value")); } @Override public Iterator> settings() { final List> settings = Arrays.asList(BAZ_QUX_SETTING, QUUX_QUUZ_SETTING); return settings.iterator(); } } // the purpose of this test is merely to ensure that a validator is invoked with the appropriate values public void testValidator() { final Settings settings = Settings.builder() .put("foo.bar", "foo.bar value") .put("baz.qux", "baz.qux value") .put("quux.quuz", "quux.quuz value") .build(); FOO_BAR_SETTING.get(settings); assertTrue(FooBarValidator.invokedInIsolation); assertTrue(FooBarValidator.invokedWithDependencies); } public void testRegexValidator() throws Exception { // A regex that matches one or more digits String expectedRegex = "\\d+"; Pattern expectedPattern = Pattern.compile(expectedRegex); RegexValidator regexValidator = new RegexValidator(expectedRegex); RegexValidator regexValidatorMatcherFalse = new RegexValidator(expectedRegex, false); // Test that the pattern is correctly initialized assertNotNull(expectedPattern); assertNotNull(regexValidator.getPattern()); assertEquals(expectedPattern.pattern(), regexValidator.getPattern().pattern()); // Test that checks the pattern and isMatching with the set value false parameters are working correctly during initialization assertNotNull(regexValidatorMatcherFalse); assertNotNull(regexValidatorMatcherFalse.getPattern()); assertEquals(expectedPattern.pattern(), regexValidatorMatcherFalse.getPattern().pattern()); // Test throw an exception when the value does not match final RegexValidator finalValidator = new RegexValidator(expectedRegex); assertThrows(IllegalArgumentException.class, () -> finalValidator.validate("foo")); try { regexValidator.validate("123"); } catch (IllegalArgumentException e) { fail("Expected validate() to not throw an exception, but it threw " + e); } // Test throws an exception when the value matches final RegexValidator finalValidatorFalse = new RegexValidator(expectedRegex); assertThrows(IllegalArgumentException.class, () -> finalValidatorFalse.validate(expectedRegex)); try { regexValidatorMatcherFalse.validate(expectedRegex); } catch (IllegalArgumentException e) { fail("Expected validate() to not throw an exception, but it threw " + e); } try (BytesStreamOutput out = new BytesStreamOutput()) { regexValidator.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { regexValidator = new RegexValidator(in); assertEquals(expectedPattern.pattern(), regexValidator.getPattern().pattern()); // Test that validate() throws an exception for invalid input final RegexValidator newFinalValidator = new RegexValidator(expectedRegex); assertThrows(IllegalArgumentException.class, () -> newFinalValidator.validate("foo")); // Test that validate() does not throw an exception for valid input try { regexValidator.validate("123"); } catch (IllegalArgumentException e) { fail("Expected validate() to not throw an exception, but it threw " + e); } } } } public void testValidatorForFilteredStringSetting() { final Setting filteredStringSetting = new Setting<>("foo.bar", "foobar", Function.identity(), value -> { throw new SettingsException("validate always fails"); }, Property.Filtered); final Settings settings = Settings.builder().put(filteredStringSetting.getKey(), filteredStringSetting.getKey() + " value").build(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> filteredStringSetting.get(settings)); assertThat(e, hasToString(containsString("Failed to parse value for setting [" + filteredStringSetting.getKey() + "]"))); assertThat(e.getCause(), instanceOf(SettingsException.class)); assertThat(e.getCause(), hasToString(containsString("validate always fails"))); } public void testFilteredFloatSetting() { final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> Setting.floatSetting("foo", 42.0f, 43.0f, Property.Filtered) ); assertThat(e, hasToString(containsString("Failed to parse value for setting [foo] must be >= 43.0"))); } public void testFilteredDoubleSetting() { final IllegalArgumentException e1 = expectThrows( IllegalArgumentException.class, () -> Setting.doubleSetting("foo", 42.0, 43.0, Property.Filtered) ); assertThat(e1, hasToString(containsString("Failed to parse value for setting [foo] must be >= 43.0"))); final IllegalArgumentException e2 = expectThrows( IllegalArgumentException.class, () -> Setting.doubleSetting("foo", 45.0, 43.0, 44.0, Property.Filtered) ); assertThat(e2, hasToString(containsString("Failed to parse value for setting [foo] must be <= 44.0"))); } public void testFilteredIntSetting() { final IllegalArgumentException e1 = expectThrows( IllegalArgumentException.class, () -> Setting.intSetting("foo", 42, 43, 44, Property.Filtered) ); assertThat(e1, hasToString(containsString("Failed to parse value for setting [foo] must be >= 43"))); final IllegalArgumentException e2 = expectThrows( IllegalArgumentException.class, () -> Setting.intSetting("foo", 45, 43, 44, Property.Filtered) ); assertThat(e2, hasToString(containsString("Failed to parse value for setting [foo] must be <= 44"))); } public void testFilteredLongSetting() { final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> Setting.longSetting("foo", 42L, 43L, Property.Filtered) ); assertThat(e, hasToString(containsString("Failed to parse value for setting [foo] must be >= 43"))); } public void testFilteredTimeSetting() { final IllegalArgumentException e1 = expectThrows( IllegalArgumentException.class, () -> Setting.timeSetting("foo", TimeValue.timeValueHours(1), TimeValue.timeValueHours(2), Property.Filtered) ); assertThat(e1, hasToString(containsString("failed to parse value for setting [foo], must be >= [2h]"))); final IllegalArgumentException e2 = expectThrows( IllegalArgumentException.class, () -> Setting.timeSetting( "foo", TimeValue.timeValueHours(4), TimeValue.timeValueHours(2), TimeValue.timeValueHours(3), Property.Filtered ) ); assertThat(e2, hasToString(containsString("failed to parse value for setting [foo], must be <= [3h]"))); final Setting minSetting = Setting.timeSetting("foo", TimeValue.timeValueHours(3), TimeValue.timeValueHours(2), Property.Filtered); final Settings minSettings = Settings.builder().put("foo", "not a time value").build(); final IllegalArgumentException e3 = expectThrows(IllegalArgumentException.class, () -> minSetting.get(minSettings)); assertThat(e3, hasToString(containsString("failed to parse value for setting [foo] as a time value"))); assertNull(e3.getCause()); final Setting maxSetting = Setting.timeSetting( "foo", TimeValue.timeValueHours(3), TimeValue.timeValueHours(2), TimeValue.timeValueHours(4), Property.Filtered ); final Settings maxSettings = Settings.builder().put("foo", "not a time value").build(); final IllegalArgumentException e4 = expectThrows(IllegalArgumentException.class, () -> maxSetting.get(maxSettings)); assertThat(e4, hasToString(containsString("failed to parse value for setting [foo] as a time value"))); assertNull(e4.getCause()); } public void testFilteredBooleanSetting() { Setting setting = Setting.boolSetting("foo", false, Property.Filtered); final Settings settings = Settings.builder().put("foo", "not a boolean value").build(); final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> setting.get(settings)); assertThat(e, hasToString(containsString("Failed to parse value for setting [foo]"))); assertNull(e.getCause()); } public void testUpdateNotDynamic() { Setting booleanSetting = Setting.boolSetting("foo.bar", false, Property.NodeScope); assertFalse(booleanSetting.isGroupSetting()); AtomicReference atomicBoolean = new AtomicReference<>(null); try { booleanSetting.newUpdater(atomicBoolean::set, logger); fail("not dynamic"); } catch (IllegalStateException ex) { assertEquals("setting [foo.bar] is not dynamic", ex.getMessage()); } } public void testUpdaterIsIsolated() { Setting booleanSetting = Setting.boolSetting("foo.bar", false, Property.Dynamic, Property.NodeScope); AtomicReference ab1 = new AtomicReference<>(null); AtomicReference ab2 = new AtomicReference<>(null); ClusterSettings.SettingUpdater settingUpdater = booleanSetting.newUpdater(ab1::set, logger); settingUpdater.apply(Settings.builder().put("foo.bar", true).build(), Settings.EMPTY); assertTrue(ab1.get()); assertNull(ab2.get()); } public void testDefault() { TimeValue defaultValue = TimeValue.timeValueMillis(randomIntBetween(0, 1000000)); Setting setting = Setting.positiveTimeSetting("my.time.value", defaultValue, Property.NodeScope); assertFalse(setting.isGroupSetting()); String aDefault = setting.getDefaultRaw(Settings.EMPTY); assertEquals(defaultValue.millis() + "ms", aDefault); assertEquals(defaultValue.millis(), setting.get(Settings.EMPTY).millis()); assertEquals(defaultValue, setting.getDefault(Settings.EMPTY)); Setting secondaryDefault = new Setting<>( "foo.bar", (s) -> s.get("old.foo.bar", "some_default"), Function.identity(), Property.NodeScope ); assertEquals("some_default", secondaryDefault.get(Settings.EMPTY)); assertEquals("42", secondaryDefault.get(Settings.builder().put("old.foo.bar", 42).build())); Setting secondaryDefaultViaSettings = new Setting<>("foo.bar", secondaryDefault, Function.identity(), Property.NodeScope); assertEquals("some_default", secondaryDefaultViaSettings.get(Settings.EMPTY)); assertEquals("42", secondaryDefaultViaSettings.get(Settings.builder().put("old.foo.bar", 42).build())); // It gets more complicated when there are two settings objects.... Settings hasFallback = Settings.builder().put("foo.bar", "o").build(); Setting fallsback = new Setting<>("foo.baz", secondaryDefault, Function.identity(), Property.NodeScope); assertEquals("o", fallsback.get(hasFallback)); assertEquals("some_default", fallsback.get(Settings.EMPTY)); assertEquals("some_default", fallsback.get(Settings.EMPTY, Settings.EMPTY)); assertEquals("o", fallsback.get(Settings.EMPTY, hasFallback)); assertEquals("o", fallsback.get(hasFallback, Settings.EMPTY)); assertEquals("a", fallsback.get(Settings.builder().put("foo.bar", "a").build(), Settings.builder().put("foo.bar", "b").build())); } public void testComplexType() { AtomicReference ref = new AtomicReference<>(null); Setting setting = new Setting<>("foo.bar", (s) -> "", (s) -> new ComplexType(s), Property.Dynamic, Property.NodeScope); assertFalse(setting.isGroupSetting()); ref.set(setting.get(Settings.EMPTY)); ComplexType type = ref.get(); ClusterSettings.SettingUpdater settingUpdater = setting.newUpdater(ref::set, logger); assertFalse(settingUpdater.apply(Settings.EMPTY, Settings.EMPTY)); assertSame("no update - type has not changed", type, ref.get()); // change from default assertTrue(settingUpdater.apply(Settings.builder().put("foo.bar", "2").build(), Settings.EMPTY)); assertNotSame("update - type has changed", type, ref.get()); assertEquals("2", ref.get().foo); // change back to default... assertTrue(settingUpdater.apply(Settings.EMPTY, Settings.builder().put("foo.bar", "2").build())); assertNotSame("update - type has changed", type, ref.get()); assertEquals("", ref.get().foo); } public void testType() { Setting integerSetting = Setting.intSetting("foo.int.bar", 1, Property.Dynamic, Property.NodeScope); assertThat(integerSetting.hasNodeScope(), is(true)); assertThat(integerSetting.hasIndexScope(), is(false)); integerSetting = Setting.intSetting("foo.int.bar", 1, Property.Dynamic, Property.IndexScope); assertThat(integerSetting.hasIndexScope(), is(true)); assertThat(integerSetting.hasNodeScope(), is(false)); } public void testGroups() { AtomicReference ref = new AtomicReference<>(null); Setting setting = Setting.groupSetting("foo.bar.", Property.Dynamic, Property.NodeScope); assertTrue(setting.isGroupSetting()); ClusterSettings.SettingUpdater settingUpdater = setting.newUpdater(ref::set, logger); Settings currentInput = Settings.builder() .put("foo.bar.1.value", "1") .put("foo.bar.2.value", "2") .put("foo.bar.3.value", "3") .build(); Settings previousInput = Settings.EMPTY; assertTrue(settingUpdater.apply(currentInput, previousInput)); assertNotNull(ref.get()); Settings settings = ref.get(); Map asMap = settings.getAsGroups(); assertEquals(3, asMap.size()); assertEquals(asMap.get("1").get("value"), "1"); assertEquals(asMap.get("2").get("value"), "2"); assertEquals(asMap.get("3").get("value"), "3"); previousInput = currentInput; currentInput = Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").put("foo.bar.3.value", "3").build(); Settings current = ref.get(); assertFalse(settingUpdater.apply(currentInput, previousInput)); assertSame(current, ref.get()); previousInput = currentInput; currentInput = Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").build(); // now update and check that we got it assertTrue(settingUpdater.apply(currentInput, previousInput)); assertNotSame(current, ref.get()); asMap = ref.get().getAsGroups(); assertEquals(2, asMap.size()); assertEquals(asMap.get("1").get("value"), "1"); assertEquals(asMap.get("2").get("value"), "2"); previousInput = currentInput; currentInput = Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "4").build(); // now update and check that we got it assertTrue(settingUpdater.apply(currentInput, previousInput)); assertNotSame(current, ref.get()); asMap = ref.get().getAsGroups(); assertEquals(2, asMap.size()); assertEquals(asMap.get("1").get("value"), "1"); assertEquals(asMap.get("2").get("value"), "4"); assertTrue(setting.match("foo.bar.baz")); assertFalse(setting.match("foo.baz.bar")); ClusterSettings.SettingUpdater predicateSettingUpdater = setting.newUpdater(ref::set, logger, (s) -> assertFalse(true)); try { predicateSettingUpdater.apply( Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").build(), Settings.EMPTY ); fail("not accepted"); } catch (IllegalArgumentException ex) { assertEquals(ex.getMessage(), "illegal value can't update [foo.bar.] from [{}] to [{\"1.value\":\"1\",\"2.value\":\"2\"}]"); } } public void testGroupSettingFallback() { Setting fallbackSetting = Setting.groupSetting("old.bar.", Property.Dynamic, Property.NodeScope); assertFalse(fallbackSetting.exists(Settings.EMPTY)); Setting groupSetting = Setting.groupSetting("new.bar.", fallbackSetting, Property.Dynamic, Property.NodeScope); assertFalse(groupSetting.exists(Settings.EMPTY)); Settings values = Settings.builder().put("old.bar.1.value", "value 1").put("old.bar.2.value", "value 2").build(); assertTrue(groupSetting.exists(values)); Map asMap = groupSetting.get(values).getAsGroups(); assertEquals(2, asMap.size()); assertEquals("value 1", asMap.get("1").get("value")); assertEquals("value 2", asMap.get("2").get("value")); } public void testFilteredGroups() { AtomicReference ref = new AtomicReference<>(null); Setting setting = Setting.groupSetting("foo.bar.", Property.Filtered, Property.Dynamic); ClusterSettings.SettingUpdater predicateSettingUpdater = setting.newUpdater(ref::set, logger, (s) -> assertFalse(true)); IllegalArgumentException ex = expectThrows( IllegalArgumentException.class, () -> predicateSettingUpdater.apply( Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").build(), Settings.EMPTY ) ); assertEquals("illegal value can't update [foo.bar.]", ex.getMessage()); } public static class ComplexType { final String foo; public ComplexType(String foo) { this.foo = foo; } } public static class Composite { private Integer b; private Integer a; public void set(Integer a, Integer b) { this.a = a; this.b = b; } public void validate(Integer a, Integer b) { if (Integer.signum(a) != Integer.signum(b)) { throw new IllegalArgumentException("boom"); } } } public void testComposite() { Composite c = new Composite(); Setting a = Setting.intSetting("foo.int.bar.a", 1, Property.Dynamic, Property.NodeScope); Setting b = Setting.intSetting("foo.int.bar.b", 1, Property.Dynamic, Property.NodeScope); ClusterSettings.SettingUpdater> settingUpdater = Setting.compoundUpdater(c::set, c::validate, a, b, logger); assertFalse(settingUpdater.apply(Settings.EMPTY, Settings.EMPTY)); assertNull(c.a); assertNull(c.b); Settings build = Settings.builder().put("foo.int.bar.a", 2).build(); assertTrue(settingUpdater.apply(build, Settings.EMPTY)); assertEquals(2, c.a.intValue()); assertEquals(1, c.b.intValue()); Integer aValue = c.a; assertFalse(settingUpdater.apply(build, build)); assertSame(aValue, c.a); Settings previous = build; build = Settings.builder().put("foo.int.bar.a", 2).put("foo.int.bar.b", 5).build(); assertTrue(settingUpdater.apply(build, previous)); assertEquals(2, c.a.intValue()); assertEquals(5, c.b.intValue()); // reset to default assertTrue(settingUpdater.apply(Settings.EMPTY, build)); assertEquals(1, c.a.intValue()); assertEquals(1, c.b.intValue()); } public void testCompositeValidator() { Composite c = new Composite(); Setting a = Setting.intSetting("foo.int.bar.a", 1, Property.Dynamic, Property.NodeScope); Setting b = Setting.intSetting("foo.int.bar.b", 1, Property.Dynamic, Property.NodeScope); ClusterSettings.SettingUpdater> settingUpdater = Setting.compoundUpdater(c::set, c::validate, a, b, logger); assertFalse(settingUpdater.apply(Settings.EMPTY, Settings.EMPTY)); assertNull(c.a); assertNull(c.b); Settings build = Settings.builder().put("foo.int.bar.a", 2).build(); assertTrue(settingUpdater.apply(build, Settings.EMPTY)); assertEquals(2, c.a.intValue()); assertEquals(1, c.b.intValue()); Integer aValue = c.a; assertFalse(settingUpdater.apply(build, build)); assertSame(aValue, c.a); Settings previous = build; build = Settings.builder().put("foo.int.bar.a", 2).put("foo.int.bar.b", 5).build(); assertTrue(settingUpdater.apply(build, previous)); assertEquals(2, c.a.intValue()); assertEquals(5, c.b.intValue()); Settings invalid = Settings.builder().put("foo.int.bar.a", -2).put("foo.int.bar.b", 5).build(); IllegalArgumentException exc = expectThrows(IllegalArgumentException.class, () -> settingUpdater.apply(invalid, previous)); assertThat(exc.getMessage(), equalTo("boom")); // reset to default assertTrue(settingUpdater.apply(Settings.EMPTY, build)); assertEquals(1, c.a.intValue()); assertEquals(1, c.b.intValue()); } public void testListSettingsDeprecated() { final Setting> deprecatedListSetting = Setting.listSetting( "foo.deprecated", Collections.singletonList("foo.deprecated"), Function.identity(), Property.Deprecated, Property.NodeScope ); final Setting> nonDeprecatedListSetting = Setting.listSetting( "foo.non_deprecated", Collections.singletonList("foo.non_deprecated"), Function.identity(), Property.NodeScope ); final Settings settings = Settings.builder() .put("foo.deprecated", "foo.deprecated1,foo.deprecated2") .put("foo.deprecated", "foo.non_deprecated1,foo.non_deprecated2") .build(); deprecatedListSetting.get(settings); nonDeprecatedListSetting.get(settings); assertSettingDeprecationsAndWarnings(new Setting[] { deprecatedListSetting }); } public void testListSettings() { Setting> listSetting = Setting.listSetting( "foo.bar", Arrays.asList("foo,bar"), (s) -> s.toString(), Property.Dynamic, Property.NodeScope ); List value = listSetting.get(Settings.EMPTY); assertFalse(listSetting.exists(Settings.EMPTY)); assertEquals(1, value.size()); assertEquals("foo,bar", value.get(0)); List input = Arrays.asList("test", "test1, test2", "test", ",,,,"); Settings.Builder builder = Settings.builder().putList("foo.bar", input.toArray(new String[0])); assertTrue(listSetting.exists(builder.build())); value = listSetting.get(builder.build()); assertEquals(input.size(), value.size()); assertArrayEquals(value.toArray(new String[0]), input.toArray(new String[0])); // try to parse this really annoying format builder = Settings.builder(); for (int i = 0; i < input.size(); i++) { builder.put("foo.bar." + i, input.get(i)); } value = listSetting.get(builder.build()); assertEquals(input.size(), value.size()); assertArrayEquals(value.toArray(new String[0]), input.toArray(new String[0])); assertTrue(listSetting.exists(builder.build())); AtomicReference> ref = new AtomicReference<>(); AbstractScopedSettings.SettingUpdater> settingUpdater = listSetting.newUpdater(ref::set, logger); assertTrue(settingUpdater.hasChanged(builder.build(), Settings.EMPTY)); settingUpdater.apply(builder.build(), Settings.EMPTY); assertEquals(input.size(), ref.get().size()); assertArrayEquals(ref.get().toArray(new String[0]), input.toArray(new String[0])); settingUpdater.apply(Settings.builder().putList("foo.bar", "123").build(), builder.build()); assertEquals(1, ref.get().size()); assertArrayEquals(ref.get().toArray(new String[0]), new String[] { "123" }); settingUpdater.apply(Settings.builder().put("foo.bar", "1,2,3").build(), Settings.builder().putList("foo.bar", "123").build()); assertEquals(3, ref.get().size()); assertArrayEquals(ref.get().toArray(new String[0]), new String[] { "1", "2", "3" }); settingUpdater.apply(Settings.EMPTY, Settings.builder().put("foo.bar", "1,2,3").build()); assertEquals(1, ref.get().size()); assertEquals("foo,bar", ref.get().get(0)); Setting> otherSettings = Setting.listSetting( "foo.bar", Collections.emptyList(), Integer::parseInt, Property.Dynamic, Property.NodeScope ); List defaultValue = otherSettings.get(Settings.EMPTY); assertEquals(0, defaultValue.size()); List intValues = otherSettings.get(Settings.builder().put("foo.bar", "0,1,2,3").build()); assertEquals(4, intValues.size()); for (int i = 0; i < intValues.size(); i++) { assertEquals(i, intValues.get(i).intValue()); } Setting> settingWithFallback = Setting.listSetting( "foo.baz", listSetting, Function.identity(), Property.Dynamic, Property.NodeScope ); value = settingWithFallback.get(Settings.EMPTY); assertEquals(1, value.size()); assertEquals("foo,bar", value.get(0)); value = settingWithFallback.get(Settings.builder().putList("foo.bar", "1", "2").build()); assertEquals(2, value.size()); assertEquals("1", value.get(0)); assertEquals("2", value.get(1)); value = settingWithFallback.get(Settings.builder().putList("foo.baz", "3", "4").build()); assertEquals(2, value.size()); assertEquals("3", value.get(0)); assertEquals("4", value.get(1)); value = settingWithFallback.get(Settings.builder().putList("foo.baz", "3", "4").putList("foo.bar", "1", "2").build()); assertEquals(2, value.size()); assertEquals("3", value.get(0)); assertEquals("4", value.get(1)); } public void testListSettingAcceptsNumberSyntax() { Setting> listSetting = Setting.listSetting( "foo.bar", Arrays.asList("foo,bar"), (s) -> s.toString(), Property.Dynamic, Property.NodeScope ); List input = Arrays.asList("test", "test1, test2", "test", ",,,,"); Settings.Builder builder = Settings.builder().putList("foo.bar", input.toArray(new String[0])); // try to parse this really annoying format for (String key : builder.keys()) { assertTrue("key: " + key + " doesn't match", listSetting.match(key)); } builder = Settings.builder().put("foo.bar", "1,2,3"); for (String key : builder.keys()) { assertTrue("key: " + key + " doesn't match", listSetting.match(key)); } assertFalse(listSetting.match("foo_bar")); assertFalse(listSetting.match("foo_bar.1")); assertTrue(listSetting.match("foo.bar")); assertTrue(listSetting.match("foo.bar." + randomIntBetween(0, 10000))); } public void testDynamicKeySetting() { Setting setting = Setting.prefixKeySetting("foo.", (key) -> Setting.boolSetting(key, false, Property.NodeScope)); assertTrue(setting.hasComplexMatcher()); assertTrue(setting.match("foo.bar")); assertFalse(setting.match("foo")); Setting concreteSetting = setting.getConcreteSetting("foo.bar"); assertTrue(concreteSetting.get(Settings.builder().put("foo.bar", "true").build())); assertFalse(concreteSetting.get(Settings.builder().put("foo.baz", "true").build())); try { setting.getConcreteSetting("foo"); fail(); } catch (IllegalArgumentException ex) { assertEquals("key [foo] must match [foo.] but didn't.", ex.getMessage()); } } public void testAffixKeySetting() { Setting setting = Setting.affixKeySetting("foo.", "enable", (key) -> Setting.boolSetting(key, false, Property.NodeScope)); assertTrue(setting.hasComplexMatcher()); assertTrue(setting.match("foo.bar.enable")); assertTrue(setting.match("foo.baz.enable")); assertFalse(setting.match("foo.bar.baz.enable")); assertFalse(setting.match("foo.bar")); assertFalse(setting.match("foo.bar.baz.enabled")); assertFalse(setting.match("foo")); Setting concreteSetting = setting.getConcreteSetting("foo.bar.enable"); assertTrue(concreteSetting.get(Settings.builder().put("foo.bar.enable", "true").build())); assertFalse(concreteSetting.get(Settings.builder().put("foo.baz.enable", "true").build())); IllegalArgumentException exc = expectThrows(IllegalArgumentException.class, () -> setting.getConcreteSetting("foo")); assertEquals("key [foo] must match [foo.*.enable] but didn't.", exc.getMessage()); exc = expectThrows( IllegalArgumentException.class, () -> Setting.affixKeySetting("foo", "enable", (key) -> Setting.boolSetting(key, false, Property.NodeScope)) ); assertEquals("prefix must end with a '.'", exc.getMessage()); Setting> listAffixSetting = Setting.affixKeySetting( "foo.", "bar", (key) -> Setting.listSetting(key, Collections.emptyList(), Function.identity(), Property.NodeScope) ); assertTrue(listAffixSetting.hasComplexMatcher()); assertTrue(listAffixSetting.match("foo.test.bar")); assertTrue(listAffixSetting.match("foo.test_1.bar")); assertFalse(listAffixSetting.match("foo.buzz.baz.bar")); assertFalse(listAffixSetting.match("foo.bar")); assertFalse(listAffixSetting.match("foo.baz")); assertFalse(listAffixSetting.match("foo")); } public void testAffixSettingNamespaces() { Setting.AffixSetting setting = Setting.affixKeySetting( "foo.", "enable", (key) -> Setting.boolSetting(key, false, Property.NodeScope) ); Settings build = Settings.builder() .put("foo.bar.enable", "true") .put("foo.baz.enable", "true") .put("foo.boom.enable", "true") .put("something.else", "true") .build(); Set namespaces = setting.getNamespaces(build); assertEquals(3, namespaces.size()); assertTrue(namespaces.contains("bar")); assertTrue(namespaces.contains("baz")); assertTrue(namespaces.contains("boom")); } public void testAffixAsMap() { Setting.AffixSetting setting = Setting.prefixKeySetting("foo.bar.", key -> Setting.simpleString(key, Property.NodeScope)); Settings build = Settings.builder().put("foo.bar.baz", 2).put("foo.bar.foobar", 3).build(); Map asMap = setting.getAsMap(build); assertEquals(2, asMap.size()); assertEquals("2", asMap.get("baz")); assertEquals("3", asMap.get("foobar")); setting = Setting.prefixKeySetting("foo.bar.", key -> Setting.simpleString(key, Property.NodeScope)); build = Settings.builder().put("foo.bar.baz", 2).put("foo.bar.foobar", 3).put("foo.bar.baz.deep", 45).build(); asMap = setting.getAsMap(build); assertEquals(3, asMap.size()); assertEquals("2", asMap.get("baz")); assertEquals("3", asMap.get("foobar")); assertEquals("45", asMap.get("baz.deep")); } public void testGetAllConcreteSettings() { Setting.AffixSetting> listAffixSetting = Setting.affixKeySetting( "foo.", "bar", (key) -> Setting.listSetting(key, Collections.emptyList(), Function.identity(), Property.NodeScope) ); Settings settings = Settings.builder() .putList("foo.1.bar", "1", "2") .putList("foo.2.bar", "3", "4", "5") .putList("foo.bar", "6") .putList("some.other", "6") .putList("foo.3.bar", "6") .build(); Stream>> allConcreteSettings = listAffixSetting.getAllConcreteSettings(settings); Map> collect = allConcreteSettings.collect(Collectors.toMap(Setting::getKey, (s) -> s.get(settings))); assertEquals(3, collect.size()); assertEquals(Arrays.asList("1", "2"), collect.get("foo.1.bar")); assertEquals(Arrays.asList("3", "4", "5"), collect.get("foo.2.bar")); assertEquals(Arrays.asList("6"), collect.get("foo.3.bar")); } public void testAffixSettingsFailOnGet() { Setting.AffixSetting> listAffixSetting = Setting.affixKeySetting( "foo.", "bar", (key) -> Setting.listSetting(key, Collections.singletonList("testelement"), Function.identity(), Property.NodeScope) ); expectThrows(UnsupportedOperationException.class, () -> listAffixSetting.get(Settings.EMPTY)); assertEquals(Collections.singletonList("testelement"), listAffixSetting.getDefault(Settings.EMPTY)); assertEquals("[\"testelement\"]", listAffixSetting.getDefaultRaw(Settings.EMPTY)); } public void testAffixSettingsValidatorDependencies() { Setting affix = Setting.affixKeySetting("abc.", "def", k -> Setting.intSetting(k, 10)); Setting fix0 = Setting.intSetting("abc.tuv", 20, 0); Setting fix1 = Setting.intSetting("abc.qrx", 20, 0, new Setting.Validator() { @Override public void validate(Integer value) {} String toString(Map, Object> s) { return s.entrySet() .stream() .map(e -> e.getKey().getKey() + ":" + e.getValue().toString()) .sorted() .collect(Collectors.joining(",")); } @Override public void validate(Integer value, Map, Object> settings, boolean isPresent) { if (settings.get(fix0).equals(fix0.getDefault(Settings.EMPTY))) { settings.remove(fix0); } if (settings.size() == 1) { throw new IllegalArgumentException(toString(settings)); } else if (settings.size() == 2) { throw new IllegalArgumentException(toString(settings)); } } @Override public Iterator> settings() { List> a = Arrays.asList(affix, fix0); return a.iterator(); } }); IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> fix1.get(Settings.builder().put("abc.1.def", 11).put("abc.2.def", 12).put("abc.qrx", 11).build()) ); assertThat(e.getMessage(), is("abc.1.def:11,abc.2.def:12")); e = expectThrows( IllegalArgumentException.class, () -> fix1.get(Settings.builder().put("abc.3.def", 13).put("abc.qrx", 20).build()) ); assertThat(e.getMessage(), is("abc.3.def:13")); e = expectThrows( IllegalArgumentException.class, () -> fix1.get(Settings.builder().put("abc.4.def", 14).put("abc.qrx", 20).put("abc.tuv", 50).build()) ); assertThat(e.getMessage(), is("abc.4.def:14,abc.tuv:50")); assertEquals( fix1.get(Settings.builder().put("abc.3.def", 13).put("abc.1.def", 11).put("abc.2.def", 12).put("abc.qrx", 20).build()), Integer.valueOf(20) ); assertEquals(fix1.get(Settings.builder().put("abc.qrx", 30).build()), Integer.valueOf(30)); } // Integer public void testIntWithDefaultValue() { Setting integerSetting = Setting.intSetting("foo.bar", 42); assertEquals(integerSetting.get(Settings.EMPTY), Integer.valueOf(42)); } public void testIntWithFallbackValue() { Setting fallbackSetting = Setting.intSetting("foo.baz", 2); Setting integerSetting = Setting.intSetting("foo.bar", fallbackSetting); assertEquals(integerSetting.get(Settings.EMPTY), Integer.valueOf(2)); assertEquals(integerSetting.get(Settings.builder().put("foo.bar", 3).build()), Integer.valueOf(3)); assertEquals(integerSetting.get(Settings.builder().put("foo.baz", 3).build()), Integer.valueOf(3)); } public void testIntWithMinMax() { Setting integerSetting = Setting.intSetting("foo.bar", 1, 0, 10, Property.NodeScope); try { integerSetting.get(Settings.builder().put("foo.bar", 11).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [11] for setting [foo.bar] must be <= 10", ex.getMessage()); } try { integerSetting.get(Settings.builder().put("foo.bar", -1).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [-1] for setting [foo.bar] must be >= 0", ex.getMessage()); } assertEquals(5, integerSetting.get(Settings.builder().put("foo.bar", 5).build()).intValue()); assertEquals(1, integerSetting.get(Settings.EMPTY).intValue()); } public void testIntegerParser() throws Exception { String expectedKey = "test key"; int expectedMinValue = Integer.MIN_VALUE; int expectedMaxValue = Integer.MAX_VALUE; boolean expectedFilteredStatus = true; IntegerParser integerParser = new IntegerParser(expectedMinValue, expectedMaxValue, expectedKey, expectedFilteredStatus); assertEquals(expectedKey, integerParser.getKey()); assertEquals(expectedMinValue, integerParser.getMin()); assertEquals(expectedMaxValue, integerParser.getMax()); assertEquals(expectedFilteredStatus, integerParser.getFilterStatus()); try (BytesStreamOutput out = new BytesStreamOutput()) { integerParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { integerParser = new IntegerParser(in); assertEquals(expectedKey, integerParser.getKey()); assertEquals(expectedMinValue, integerParser.getMin()); assertEquals(expectedMaxValue, integerParser.getMax()); assertEquals(expectedFilteredStatus, integerParser.getFilterStatus()); } } } // Long public void testLongWithDefaultValue() { Setting longSetting = Setting.longSetting("foo.bar", 42); assertEquals(longSetting.get(Settings.EMPTY), Long.valueOf(42)); } public void testLongWithFallbackValue() { Setting fallbackSetting = Setting.longSetting("foo.baz", 2); Setting longSetting = Setting.longSetting("foo.bar", fallbackSetting); assertEquals(longSetting.get(Settings.EMPTY), Long.valueOf(2)); assertEquals(longSetting.get(Settings.builder().put("foo.bar", 3).build()), Long.valueOf(3)); assertEquals(longSetting.get(Settings.builder().put("foo.baz", 3).build()), Long.valueOf(3)); } public void testLongWithMinMax() { Setting longSetting = Setting.longSetting("foo.bar", 1, 0, 10, Property.NodeScope); try { longSetting.get(Settings.builder().put("foo.bar", 11).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [11] for setting [foo.bar] must be <= 10", ex.getMessage()); } try { longSetting.get(Settings.builder().put("foo.bar", -1).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [-1] for setting [foo.bar] must be >= 0", ex.getMessage()); } assertEquals(5, longSetting.get(Settings.builder().put("foo.bar", 5).build()).longValue()); assertEquals(1, longSetting.get(Settings.EMPTY).longValue()); } public void testLongParser() throws Exception { String expectedKey = "test key"; long expectedMinValue = Long.MIN_VALUE; long expectedMaxValue = Long.MAX_VALUE; boolean expectedFilteredStatus = true; LongParser longParser = new LongParser(expectedMinValue, expectedMaxValue, expectedKey, expectedFilteredStatus); assertEquals(expectedKey, longParser.getKey()); assertEquals(expectedMinValue, longParser.getMin()); assertEquals(expectedMaxValue, longParser.getMax()); assertEquals(expectedFilteredStatus, longParser.getFilterStatus()); try (BytesStreamOutput out = new BytesStreamOutput()) { longParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { longParser = new LongParser(in); assertEquals(expectedKey, longParser.getKey()); assertEquals(expectedMinValue, longParser.getMin()); assertEquals(expectedMaxValue, longParser.getMax()); assertEquals(expectedFilteredStatus, longParser.getFilterStatus()); } } } // Float public void testFloatWithDefaultValue() { Setting floatSetting = Setting.floatSetting("foo.bar", (float) 42.1); assertEquals(floatSetting.get(Settings.EMPTY), Float.valueOf((float) 42.1)); } public void testFloatWithFallbackValue() { Setting fallbackSetting = Setting.floatSetting("foo.baz", (float) 2.1); Setting floatSetting = Setting.floatSetting("foo.bar", fallbackSetting); assertEquals(floatSetting.get(Settings.EMPTY), Float.valueOf((float) 2.1)); assertEquals(floatSetting.get(Settings.builder().put("foo.bar", 3.2).build()), Float.valueOf((float) 3.2)); assertEquals(floatSetting.get(Settings.builder().put("foo.baz", 3.2).build()), Float.valueOf((float) 3.2)); } public void testFloatWithMinMax() { Setting floatSetting = Setting.floatSetting("foo.bar", (float) 1.2, 0, 10, Property.NodeScope); try { floatSetting.get(Settings.builder().put("foo.bar", (float) 11.3).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [11.3] for setting [foo.bar] must be <= 10.0", ex.getMessage()); } try { floatSetting.get(Settings.builder().put("foo.bar", (float) -1.4).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [-1.4] for setting [foo.bar] must be >= 0.0", ex.getMessage()); } assertEquals(5.6, floatSetting.get(Settings.builder().put("foo.bar", (float) 5.6).build()).floatValue(), 0.01); assertEquals(1.2, floatSetting.get(Settings.EMPTY).floatValue(), 0.01); } public void testFloatParser() throws Exception { String expectedKey = "test key"; float expectedMinValue = Float.MIN_VALUE; float expectedMaxValue = Float.MAX_VALUE; boolean expectedFilteredStatus = true; FloatParser floatParser = new FloatParser(expectedMinValue, expectedMaxValue, expectedKey, expectedFilteredStatus); assertEquals(expectedKey, floatParser.getKey()); assertEquals(expectedMinValue, floatParser.getMin(), 0.01); assertEquals(expectedMaxValue, floatParser.getMax(), 0.01); assertEquals(expectedFilteredStatus, floatParser.getFilterStatus()); try (BytesStreamOutput out = new BytesStreamOutput()) { floatParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { floatParser = new FloatParser(in); assertEquals(expectedKey, floatParser.getKey()); assertEquals(expectedMinValue, floatParser.getMin(), 0.01); assertEquals(expectedMaxValue, floatParser.getMax(), 0.01); assertEquals(expectedFilteredStatus, floatParser.getFilterStatus()); } } } // Double public void testDoubleWithDefaultValue() { Setting doubleSetting = Setting.doubleSetting("foo.bar", 42.1); assertEquals(doubleSetting.get(Settings.EMPTY), Double.valueOf(42.1)); } public void testDoubleWithFallbackValue() { Setting fallbackSetting = Setting.doubleSetting("foo.baz", 2.1); Setting doubleSetting = Setting.doubleSetting("foo.bar", fallbackSetting); assertEquals(doubleSetting.get(Settings.EMPTY), Double.valueOf(2.1)); assertEquals(doubleSetting.get(Settings.builder().put("foo.bar", 3.2).build()), Double.valueOf(3.2)); assertEquals(doubleSetting.get(Settings.builder().put("foo.baz", 3.2).build()), Double.valueOf(3.2)); } public void testDoubleWithMinMax() throws Exception { Setting doubleSetting = Setting.doubleSetting("foo.bar", 1.2, 0, 10, Property.NodeScope); try { doubleSetting.get(Settings.builder().put("foo.bar", 11.3).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [11.3] for setting [foo.bar] must be <= 10.0", ex.getMessage()); } try { doubleSetting.get(Settings.builder().put("foo.bar", -1.4).build()); fail(); } catch (IllegalArgumentException ex) { assertEquals("Failed to parse value [-1.4] for setting [foo.bar] must be >= 0.0", ex.getMessage()); } assertEquals(5.6, doubleSetting.get(Settings.builder().put("foo.bar", 5.6).build()).doubleValue(), 0.01); assertEquals(1.2, doubleSetting.get(Settings.EMPTY).doubleValue(), 0.01); } public void testDoubleParser() throws Exception { String expectedKey = "test key"; double expectedMinValue = Double.MIN_VALUE; double expectedMaxValue = Double.MAX_VALUE; boolean expectedFilteredStatus = true; DoubleParser doubleParser = new DoubleParser(expectedMinValue, expectedMaxValue, expectedKey, expectedFilteredStatus); assertEquals(expectedKey, doubleParser.getKey()); assertEquals(expectedMinValue, doubleParser.getMin(), 0.01); assertEquals(expectedMaxValue, doubleParser.getMax(), 0.01); assertEquals(expectedFilteredStatus, doubleParser.getFilterStatus()); try (BytesStreamOutput out = new BytesStreamOutput()) { doubleParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { doubleParser = new DoubleParser(in); assertEquals(expectedKey, doubleParser.getKey()); assertEquals(expectedMinValue, doubleParser.getMin(), 0.01); assertEquals(expectedMaxValue, doubleParser.getMax(), 0.01); assertEquals(expectedFilteredStatus, doubleParser.getFilterStatus()); } } } // ByteSizeValue public void testByteSizeValueParser() throws Exception { String expectedKey = "test key"; ByteSizeValue expectedMinValue = new ByteSizeValue((long) 1); ByteSizeValue expectedMaxValue = new ByteSizeValue(Long.MAX_VALUE); ByteSizeValueParser byteSizeValueParser = new ByteSizeValueParser(expectedMinValue, expectedMaxValue, expectedKey); assertEquals(expectedKey, byteSizeValueParser.getKey()); assertEquals(expectedMinValue, byteSizeValueParser.getMin()); assertEquals(expectedMaxValue, byteSizeValueParser.getMax()); try (BytesStreamOutput out = new BytesStreamOutput()) { byteSizeValueParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { byteSizeValueParser = new ByteSizeValueParser(in); assertEquals(expectedKey, byteSizeValueParser.getKey()); assertEquals(expectedMinValue, byteSizeValueParser.getMin()); assertEquals(expectedMaxValue, byteSizeValueParser.getMax()); } } } // MemorySizeValue public void testMemorySizeValueParser() throws Exception { String expectedKey = "test key"; MemorySizeValueParser memorySizeValueParser = new MemorySizeValueParser(expectedKey); assertEquals(expectedKey, memorySizeValueParser.getKey()); try (BytesStreamOutput out = new BytesStreamOutput()) { memorySizeValueParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { memorySizeValueParser = new MemorySizeValueParser(in); assertEquals(expectedKey, memorySizeValueParser.getKey()); } } } /** * Only one single scope can be added to any setting */ public void testMutuallyExclusiveScopes() { // Those should pass Setting setting = Setting.simpleString("foo.bar", Property.NodeScope); assertThat(setting.hasNodeScope(), is(true)); assertThat(setting.hasIndexScope(), is(false)); setting = Setting.simpleString("foo.bar", Property.IndexScope); assertThat(setting.hasIndexScope(), is(true)); assertThat(setting.hasNodeScope(), is(false)); // We accept settings with no scope but they will be rejected when we register with SettingsModule.registerSetting setting = Setting.simpleString("foo.bar"); assertThat(setting.hasIndexScope(), is(false)); assertThat(setting.hasNodeScope(), is(false)); // We accept settings with multiple scopes but they will be rejected when we register with SettingsModule.registerSetting setting = Setting.simpleString("foo.bar", Property.IndexScope, Property.NodeScope); assertThat(setting.hasIndexScope(), is(true)); assertThat(setting.hasNodeScope(), is(true)); } /** * We can't have Null properties */ public void testRejectNullProperties() { IllegalArgumentException ex = expectThrows( IllegalArgumentException.class, () -> Setting.simpleString("foo.bar", (Property[]) null) ); assertThat(ex.getMessage(), containsString("properties cannot be null for setting")); } public void testRejectConflictingDynamicAndFinalProperties() { IllegalArgumentException ex = expectThrows( IllegalArgumentException.class, () -> Setting.simpleString("foo.bar", Property.Final, Property.Dynamic) ); assertThat(ex.getMessage(), containsString("final setting [foo.bar] cannot be dynamic")); } public void testRejectNonIndexScopedNotCopyableOnResizeSetting() { final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> Setting.simpleString("foo.bar", Property.NotCopyableOnResize) ); assertThat(e, hasToString(containsString("non-index-scoped setting [foo.bar] can not have property [NotCopyableOnResize]"))); } public void testRejectNonIndexScopedInternalIndexSetting() { final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> Setting.simpleString("foo.bar", Property.InternalIndex) ); assertThat(e, hasToString(containsString("non-index-scoped setting [foo.bar] can not have property [InternalIndex]"))); } public void testRejectNonIndexScopedPrivateIndexSetting() { final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> Setting.simpleString("foo.bar", Property.PrivateIndex) ); assertThat(e, hasToString(containsString("non-index-scoped setting [foo.bar] can not have property [PrivateIndex]"))); } public void testTimeValue() { final TimeValue random = TimeValue.parseTimeValue(randomTimeValue(), "test"); Setting setting = Setting.timeSetting("foo", random); assertThat(setting.get(Settings.EMPTY), equalTo(random)); final int factor = randomIntBetween(1, 10); setting = Setting.timeSetting("foo", (s) -> TimeValue.timeValueMillis(random.getMillis() * factor), TimeValue.ZERO); assertThat(setting.get(Settings.builder().put("foo", "12h").build()), equalTo(TimeValue.timeValueHours(12))); assertThat(setting.get(Settings.EMPTY).getMillis(), equalTo(random.getMillis() * factor)); } public void testMinTimeValueParser() throws Exception { String expectedKey = "test key"; TimeValue expectedMinValue = TimeValue.timeValueSeconds(0); boolean expectedFilteredStatus = true; MinTimeValueParser minTimeValueParser = new MinTimeValueParser(expectedKey, expectedMinValue, expectedFilteredStatus); assertEquals(expectedKey, minTimeValueParser.getKey()); assertEquals(expectedMinValue, minTimeValueParser.getMin()); assertEquals(expectedFilteredStatus, minTimeValueParser.getFilterStatus()); try (BytesStreamOutput out = new BytesStreamOutput()) { minTimeValueParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { minTimeValueParser = new MinTimeValueParser(in); assertEquals(expectedKey, minTimeValueParser.getKey()); assertEquals(expectedMinValue, minTimeValueParser.getMin()); assertEquals(expectedFilteredStatus, minTimeValueParser.getFilterStatus()); } } } public void testMinMaxTimeValueParser() throws Exception { String expectedKey = "test key"; TimeValue expectedMinValue = TimeValue.timeValueSeconds(0); TimeValue expectedMaxValue = TimeValue.MAX_VALUE; boolean expectedFilteredStatus = true; MinMaxTimeValueParser minMaxTimeValueParser = new MinMaxTimeValueParser( expectedKey, expectedMinValue, expectedMaxValue, expectedFilteredStatus ); assertEquals(expectedKey, minMaxTimeValueParser.getKey()); assertEquals(expectedMinValue, minMaxTimeValueParser.getMin()); assertEquals(expectedMaxValue, minMaxTimeValueParser.getMax()); assertEquals(expectedFilteredStatus, minMaxTimeValueParser.getFilterStatus()); try (BytesStreamOutput out = new BytesStreamOutput()) { minMaxTimeValueParser.writeTo(out); out.flush(); try (BytesStreamInput in = new BytesStreamInput(BytesReference.toBytes(out.bytes()))) { minMaxTimeValueParser = new MinMaxTimeValueParser(in); assertEquals(expectedKey, minMaxTimeValueParser.getKey()); assertEquals(expectedMinValue, minMaxTimeValueParser.getMin()); assertEquals(expectedMaxValue, minMaxTimeValueParser.getMax()); assertEquals(expectedFilteredStatus, minMaxTimeValueParser.getFilterStatus()); } } } public void testTimeValueBounds() { Setting settingWithLowerBound = Setting.timeSetting( "foo", TimeValue.timeValueSeconds(10), TimeValue.timeValueSeconds(5) ); assertThat(settingWithLowerBound.get(Settings.EMPTY), equalTo(TimeValue.timeValueSeconds(10))); assertThat(settingWithLowerBound.get(Settings.builder().put("foo", "5000ms").build()), equalTo(TimeValue.timeValueSeconds(5))); IllegalArgumentException illegalArgumentException = expectThrows( IllegalArgumentException.class, () -> settingWithLowerBound.get(Settings.builder().put("foo", "4999ms").build()) ); assertThat(illegalArgumentException.getMessage(), equalTo("failed to parse value [4999ms] for setting [foo], must be >= [5s]")); Setting settingWithBothBounds = Setting.timeSetting( "bar", TimeValue.timeValueSeconds(10), TimeValue.timeValueSeconds(5), TimeValue.timeValueSeconds(20) ); assertThat(settingWithBothBounds.get(Settings.EMPTY), equalTo(TimeValue.timeValueSeconds(10))); assertThat(settingWithBothBounds.get(Settings.builder().put("bar", "5000ms").build()), equalTo(TimeValue.timeValueSeconds(5))); assertThat(settingWithBothBounds.get(Settings.builder().put("bar", "20000ms").build()), equalTo(TimeValue.timeValueSeconds(20))); illegalArgumentException = expectThrows( IllegalArgumentException.class, () -> settingWithBothBounds.get(Settings.builder().put("bar", "4999ms").build()) ); assertThat(illegalArgumentException.getMessage(), equalTo("failed to parse value [4999ms] for setting [bar], must be >= [5s]")); illegalArgumentException = expectThrows( IllegalArgumentException.class, () -> settingWithBothBounds.get(Settings.builder().put("bar", "20001ms").build()) ); assertThat(illegalArgumentException.getMessage(), equalTo("failed to parse value [20001ms] for setting [bar], must be <= [20s]")); } public void testSettingsGroupUpdater() { Setting intSetting = Setting.intSetting("prefix.foo", 1, Property.NodeScope, Property.Dynamic); Setting intSetting2 = Setting.intSetting("prefix.same", 1, Property.NodeScope, Property.Dynamic); AbstractScopedSettings.SettingUpdater updater = Setting.groupedSettingsUpdater( s -> {}, Arrays.asList(intSetting, intSetting2) ); Settings current = Settings.builder().put("prefix.foo", 123).put("prefix.same", 5555).build(); Settings previous = Settings.builder().put("prefix.foo", 321).put("prefix.same", 5555).build(); assertTrue(updater.apply(current, previous)); } public void testSettingsGroupUpdaterRemoval() { Setting intSetting = Setting.intSetting("prefix.foo", 1, Property.NodeScope, Property.Dynamic); Setting intSetting2 = Setting.intSetting("prefix.same", 1, Property.NodeScope, Property.Dynamic); AbstractScopedSettings.SettingUpdater updater = Setting.groupedSettingsUpdater( s -> {}, Arrays.asList(intSetting, intSetting2) ); Settings current = Settings.builder().put("prefix.same", 5555).build(); Settings previous = Settings.builder().put("prefix.foo", 321).put("prefix.same", 5555).build(); assertTrue(updater.apply(current, previous)); } public void testSettingsGroupUpdaterWithAffixSetting() { Setting intSetting = Setting.intSetting("prefix.foo", 1, Property.NodeScope, Property.Dynamic); Setting.AffixSetting prefixKeySetting = Setting.prefixKeySetting( "prefix.foo.bar.", key -> Setting.simpleString(key, Property.NodeScope, Property.Dynamic) ); Setting.AffixSetting affixSetting = Setting.affixKeySetting( "prefix.foo.", "suffix", key -> Setting.simpleString(key, Property.NodeScope, Property.Dynamic) ); AbstractScopedSettings.SettingUpdater updater = Setting.groupedSettingsUpdater( s -> {}, Arrays.asList(intSetting, prefixKeySetting, affixSetting) ); Settings.Builder currentSettingsBuilder = Settings.builder().put("prefix.foo.bar.baz", "foo").put("prefix.foo.infix.suffix", "foo"); Settings.Builder previousSettingsBuilder = Settings.builder() .put("prefix.foo.bar.baz", "foo") .put("prefix.foo.infix.suffix", "foo"); boolean removePrefixKeySetting = randomBoolean(); boolean changePrefixKeySetting = randomBoolean(); boolean removeAffixKeySetting = randomBoolean(); boolean changeAffixKeySetting = randomBoolean(); boolean removeAffixNamespace = randomBoolean(); if (removePrefixKeySetting) { previousSettingsBuilder.remove("prefix.foo.bar.baz"); } if (changePrefixKeySetting) { currentSettingsBuilder.put("prefix.foo.bar.baz", "bar"); } if (removeAffixKeySetting) { previousSettingsBuilder.remove("prefix.foo.infix.suffix"); } if (changeAffixKeySetting) { currentSettingsBuilder.put("prefix.foo.infix.suffix", "bar"); } if (removeAffixKeySetting == false && changeAffixKeySetting == false && removeAffixNamespace) { currentSettingsBuilder.remove("prefix.foo.infix.suffix"); currentSettingsBuilder.put("prefix.foo.infix2.suffix", "bar"); previousSettingsBuilder.put("prefix.foo.infix2.suffix", "bar"); } boolean expectedChange = removeAffixKeySetting || removePrefixKeySetting || changeAffixKeySetting || changePrefixKeySetting || removeAffixNamespace; assertThat(updater.apply(currentSettingsBuilder.build(), previousSettingsBuilder.build()), is(expectedChange)); } public void testAffixNamespacesWithGroupSetting() { final Setting.AffixSetting affixSetting = Setting.affixKeySetting( "prefix.", "suffix", (key) -> Setting.groupSetting(key + ".", Setting.Property.Dynamic, Setting.Property.NodeScope) ); assertThat(affixSetting.getNamespaces(Settings.builder().put("prefix.infix.suffix", "anything").build()), hasSize(1)); assertThat(affixSetting.getNamespaces(Settings.builder().put("prefix.infix.suffix.anything", "anything").build()), hasSize(1)); } public void testGroupSettingUpdaterValidator() { final Setting.AffixSetting affixSetting = Setting.affixKeySetting( "prefix.", "suffix", (key) -> Setting.intSetting(key, 5, Setting.Property.Dynamic, Setting.Property.NodeScope) ); Setting fixSetting = Setting.intSetting("abc", 1, Property.NodeScope); Consumer validator = s -> { if (affixSetting.getNamespaces(s).contains("foo")) { if (fixSetting.get(s) == 2) { throw new IllegalArgumentException("foo and 2 can't go together"); } } else if (affixSetting.getNamespaces(s).contains("bar")) { throw new IllegalArgumentException("no bar"); } }; AbstractScopedSettings.SettingUpdater updater = Setting.groupedSettingsUpdater( s -> {}, Arrays.asList(affixSetting, fixSetting), validator ); IllegalArgumentException illegal = expectThrows(IllegalArgumentException.class, () -> { updater.getValue(Settings.builder().put("prefix.foo.suffix", 5).put("abc", 2).build(), Settings.EMPTY); }); assertEquals("foo and 2 can't go together", illegal.getMessage()); illegal = expectThrows(IllegalArgumentException.class, () -> { updater.getValue(Settings.builder().put("prefix.bar.suffix", 6).put("abc", 3).build(), Settings.EMPTY); }); assertEquals("no bar", illegal.getMessage()); Settings s = updater.getValue( Settings.builder().put("prefix.foo.suffix", 5).put("prefix.bar.suffix", 5).put("abc", 3).build(), Settings.EMPTY ); assertNotNull(s); } public void testExists() { final Setting fooSetting = Setting.simpleString("foo", Property.NodeScope); assertFalse(fooSetting.exists(Settings.EMPTY)); assertTrue(fooSetting.exists(Settings.builder().put("foo", "bar").build())); } public void testExistsWithFallback() { final int count = randomIntBetween(1, 16); Setting current = Setting.simpleString("fallback0", Property.NodeScope); for (int i = 1; i < count; i++) { final Setting next = new Setting<>( new Setting.SimpleKey("fallback" + i), current, Function.identity(), Property.NodeScope ); current = next; } final Setting fooSetting = new Setting<>(new Setting.SimpleKey("foo"), current, Function.identity(), Property.NodeScope); assertFalse(fooSetting.exists(Settings.EMPTY)); if (randomBoolean()) { assertTrue(fooSetting.exists(Settings.builder().put("foo", "bar").build())); } else { final String setting = "fallback" + randomIntBetween(0, count - 1); assertFalse(fooSetting.exists(Settings.builder().put(setting, "bar").build())); assertTrue(fooSetting.existsOrFallbackExists(Settings.builder().put(setting, "bar").build())); } } public void testAffixMapUpdateWithNullSettingValue() { // GIVEN an affix setting changed from "prefix._foo"="bar" to "prefix._foo"=null final Settings current = Settings.builder().put("prefix._foo", (String) null).build(); final Settings previous = Settings.builder().put("prefix._foo", "bar").build(); final Setting.AffixSetting affixSetting = Setting.prefixKeySetting( "prefix" + ".", key -> Setting.simpleString(key, Property.Dynamic, Property.NodeScope) ); final Consumer> consumer = (map) -> {}; final BiConsumer validator = (s1, s2) -> {}; // WHEN creating an affix updater final SettingUpdater> updater = affixSetting.newAffixMapUpdater(consumer, logger, validator); // THEN affix updater is always expected to have changed (even when defaults are omitted) assertTrue(updater.hasChanged(current, previous)); // THEN changes are expected when defaults aren't omitted final Map updatedSettings = updater.getValue(current, previous); assertNotNull(updatedSettings); assertEquals(1, updatedSettings.size()); // THEN changes are reported when defaults aren't omitted final String key = updatedSettings.keySet().iterator().next(); final String value = updatedSettings.get(key); assertEquals("_foo", key); assertEquals("", value); } public void testNonSecureSettingInKeystore() { MockSecureSettings secureSettings = new MockSecureSettings(); secureSettings.setString("foo", "bar"); final Settings settings = Settings.builder().setSecureSettings(secureSettings).build(); Setting setting = Setting.simpleString("foo", Property.NodeScope); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> setting.get(settings)); assertThat(e.getMessage(), containsString("must be stored inside opensearch.yml")); } @TestLogging(value = "org.opensearch.common.settings.IndexScopedSettings:INFO", reason = "to ensure we log INFO-level messages from IndexScopedSettings") public void testLogSettingUpdate() throws Exception { final IndexMetadata metadata = newIndexMeta( "index1", Settings.builder().put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "20s").build() ); final IndexSettings settings = new IndexSettings(metadata, Settings.EMPTY); final Logger logger = LogManager.getLogger(IndexScopedSettings.class); try (MockLogAppender mockLogAppender = MockLogAppender.createForLoggers(logger)) { mockLogAppender.addExpectation( new MockLogAppender.SeenEventExpectation( "message", "org.opensearch.common.settings.IndexScopedSettings", Level.INFO, "updating [index.refresh_interval] from [20s] to [10s]" ) { @Override public boolean innerMatch(LogEvent event) { return event.getMarker().getName().equals(" [index1]"); } } ); settings.updateIndexMetadata( newIndexMeta("index1", Settings.builder().put(IndexSettings.INDEX_REFRESH_INTERVAL_SETTING.getKey(), "10s").build()) ); mockLogAppender.assertAllExpectationsMatched(); } } }