Skip to content

Commit 8f8e307

Browse files
Support dependent validation for index settings
A setting validator can declare settings that the validation depends on, but when updating index settings, we eagerly validate the settings before submitting the cluster state update and here we do not know the existing settings. With this commit, we ensure that the pre-validation works with such validators, letting the validator validate syntax (validate(value)) only. Relates elastic#70141.
1 parent e107a3c commit 8f8e307

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ public final void validate(
454454
* Validates that all settings are registered and valid.
455455
*
456456
* @param settings the settings
457-
* @param validateDependencies true if dependent settings should be validated
457+
* @param validateDependencies true if dependent settings and settings where validation has dependencies should be validated
458458
* @param ignorePrivateSettings true if private settings should be ignored during validation
459459
* @param ignoreArchivedSettings true if archived settings should be ignored during validation
460460
* @param validateInternalOrPrivateIndex true if index internal settings should be validated
@@ -489,7 +489,7 @@ public final void validate(
489489
*
490490
* @param key the key of the setting to validate
491491
* @param settings the settings
492-
* @param validateDependencies true if dependent settings should be validated
492+
* @param validateDependencies true if dependent settings and settings where validation has dependencies should be validated
493493
* @throws IllegalArgumentException if the setting is invalid
494494
*/
495495
void validate(final String key, final Settings settings, final boolean validateDependencies) {
@@ -501,7 +501,7 @@ void validate(final String key, final Settings settings, final boolean validateD
501501
*
502502
* @param key the key of the setting to validate
503503
* @param settings the settings
504-
* @param validateDependencies true if dependent settings should be validated
504+
* @param validateDependencies true if dependent settings and settings where validation has dependencies should be validated
505505
* @param validateInternalOrPrivateIndex true if internal index settings should be validated
506506
* @throws IllegalArgumentException if the setting is invalid
507507
*/
@@ -564,7 +564,11 @@ void validate(
564564
}
565565
}
566566
}
567-
setting.get(settings);
567+
if (validateDependencies) {
568+
setting.get(settings);
569+
} else {
570+
setting.validateWithoutDependencies(settings);
571+
}
568572
}
569573

570574
/**

server/src/main/java/org/elasticsearch/common/settings/SecureSetting.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ public T get(Settings settings) {
8282
}
8383
}
8484

85+
@Override
86+
void validateWithoutDependencies(Settings settings) {
87+
// secure settings have no Setting.Validator
88+
get(settings);
89+
}
90+
8591
/**
8692
* Returns the digest of this secure setting's value or {@code null} if the setting is missing (inside the keystore). This method can be
8793
* called even after the {@code SecureSettings} have been closed, unlike {@code #get(Settings)}. The digest is used to check for changes

server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
import java.util.Collections;
2727
import java.util.HashMap;
2828
import java.util.HashSet;
29+
import java.util.Iterator;
2930
import java.util.LinkedHashSet;
3031
import java.util.List;
3132
import java.util.Map;
33+
import java.util.Objects;
3234
import java.util.Set;
3335
import java.util.concurrent.atomic.AtomicInteger;
3436
import java.util.concurrent.atomic.AtomicReference;
@@ -259,6 +261,46 @@ public void testDependentSettingsWithFallback() {
259261
service.validate(Settings.builder().put("fallback.test.name", "test").put("foo.test.bar", 7).build(), true);
260262
}
261263

264+
public void testValidatorWithDependencies() {
265+
final boolean nodeSetting = randomBoolean();
266+
final String prefix = nodeSetting ? "" : "index.";
267+
final Property scopeProperty = nodeSetting ? Property.NodeScope : Property.IndexScope;
268+
final Setting<String> baseSetting = Setting.simpleString(prefix + "foo.base", Property.Dynamic, scopeProperty);
269+
final Setting.Validator<String> validator = new Setting.Validator<String>() {
270+
@Override
271+
public void validate(String value) {
272+
}
273+
274+
@Override
275+
public void validate(String value, Map<Setting<?>, Object> settings, boolean isPresent) {
276+
if (Objects.equals(value, settings.get(baseSetting)) == false) {
277+
throw new IllegalArgumentException("must have same value");
278+
}
279+
}
280+
281+
@Override
282+
public Iterator<Setting<?>> settings() {
283+
return List.<Setting<?>>of(baseSetting).iterator();
284+
}
285+
};
286+
final Setting<String> dependingSetting = Setting.simpleString(prefix + "foo.depending", validator, scopeProperty);
287+
288+
final AbstractScopedSettings service = nodeSetting ? new ClusterSettings(Settings.EMPTY, Set.of(baseSetting, dependingSetting)) :
289+
new IndexScopedSettings(Settings.EMPTY, Set.of(baseSetting, dependingSetting));
290+
291+
service.validate(Settings.builder().put(baseSetting.getKey(), "1").put(dependingSetting.getKey(), 1).build(), true);
292+
service.validate(Settings.builder().put(dependingSetting.getKey(), "1").build(), false);
293+
final IllegalArgumentException e = expectThrows(
294+
IllegalArgumentException.class,
295+
() -> service.validate(Settings.builder().put(dependingSetting.getKey(), "1").build(), true));
296+
assertThat(e.getMessage(), equalTo("must have same value"));
297+
298+
final IllegalArgumentException e2 = expectThrows(
299+
IllegalArgumentException.class,
300+
() -> service.validate(Settings.builder().put(baseSetting.getKey(), "2").put(dependingSetting.getKey(), "1").build(), true));
301+
assertThat(e2.getMessage(), equalTo("must have same value"));
302+
}
303+
262304
public void testTupleAffixUpdateConsumer() {
263305
String prefix = randomAlphaOfLength(3) + "foo.";
264306
String intSuffix = randomAlphaOfLength(3);

0 commit comments

Comments
 (0)