From ab7355dbd49002864d179c1454f54a0bc2487c8b Mon Sep 17 00:00:00 2001 From: Matteo Mazzola Date: Mon, 1 Dec 2025 11:29:30 +0000 Subject: [PATCH] Improved validation message for unknown archived settings --- .../elasticsearch/common/ReferenceDocs.java | 1 + .../settings/AbstractScopedSettings.java | 62 +++++++++++++------ .../common/reference-docs-links.txt | 1 + .../common/settings/ScopedSettingsTests.java | 17 +++++ 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java b/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java index 2567268415051..399fda9c8de2e 100644 --- a/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java +++ b/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java @@ -86,6 +86,7 @@ public enum ReferenceDocs { CLUSTER_SHARD_LIMIT, DEPLOY_CLOUD_DIFF_FROM_STATEFUL, DELETE_INDEX_BLOCK, + ARCHIVED_SETTINGS, // this comment keeps the ';' on the next line so every entry above has a trailing ',' which makes the diff for adding new links cleaner ; diff --git a/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java index e90db928a8323..afb8f4d0baf09 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/AbstractScopedSettings.java @@ -14,6 +14,8 @@ import org.apache.lucene.search.spell.LevenshteinDistance; import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.common.ReferenceDocs; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.util.Maps; import org.elasticsearch.common.util.set.Sets; @@ -560,29 +562,40 @@ void validate(final String key, final Settings settings, final boolean validateV void validate(final String key, final Settings settings, final boolean validateValue, final boolean validateInternalOrPrivateIndex) { Setting setting = getRaw(key); if (setting == null) { - LevenshteinDistance ld = new LevenshteinDistance(); - List> scoredKeys = new ArrayList<>(); - for (String k : this.keySettings.keySet()) { - float distance = ld.getDistance(key, k); - if (distance > 0.7f) { - scoredKeys.add(new Tuple<>(distance, k)); - } - } - CollectionUtil.timSort(scoredKeys, (a, b) -> b.v1().compareTo(a.v1())); - String msgPrefix = "unknown setting"; SecureSettings secureSettings = settings.getSecureSettings(); - if (secureSettings != null && settings.getSecureSettings().getSettingNames().contains(key)) { - msgPrefix = "unknown secure setting"; + String msgPrefix = (secureSettings != null && secureSettings.getSettingNames().contains(key)) + ? "unknown secure setting" + : "unknown setting"; + if (key.startsWith(ARCHIVED_SETTINGS_PREFIX)) { + throw new IllegalArgumentException( + Strings.format( + "%s [%s] was archived after upgrading, and must be removed. See [%s] for details.", + msgPrefix, + key, + ReferenceDocs.ARCHIVED_SETTINGS + ) + ); } - String msg = msgPrefix + " [" + key + "]"; - List keys = scoredKeys.stream().map((a) -> a.v2()).toList(); + List keys = findSimilarKeys(key); if (keys.isEmpty() == false) { - msg += " did you mean " + (keys.size() == 1 ? "[" + keys.get(0) + "]" : "any of " + keys.toString()) + "?"; + throw new IllegalArgumentException( + Strings.format( + "%s [%s] did you mean %s?", + msgPrefix, + key, + (keys.size() == 1 ? "[" + keys.getFirst() + "]" : "any of " + keys) + ) + ); } else { - msg += " please check that any required plugins are installed, or check the breaking changes documentation for removed " - + "settings"; + throw new IllegalArgumentException( + Strings.format( + "%s [%s] please check that any required plugins are installed," + + " or check the breaking changes documentation for removed settings", + msgPrefix, + key + ) + ); } - throw new IllegalArgumentException(msg); } else { Set settingsDependencies = setting.getSettingsDependencies(key); if (setting.hasComplexMatcher()) { @@ -635,6 +648,19 @@ void validate(final String key, final Settings settings, final boolean validateV } } + private List findSimilarKeys(String key) { + LevenshteinDistance ld = new LevenshteinDistance(); + List> scoredKeys = new ArrayList<>(); + for (String k : this.keySettings.keySet()) { + float distance = ld.getDistance(key, k); + if (distance > 0.7f) { + scoredKeys.add(new Tuple<>(distance, k)); + } + } + CollectionUtil.timSort(scoredKeys, (a, b) -> b.v1().compareTo(a.v1())); + return scoredKeys.stream().map((a) -> a.v2()).toList(); + } + /** * Transactional interface to update settings. * @see Setting diff --git a/server/src/main/resources/org/elasticsearch/common/reference-docs-links.txt b/server/src/main/resources/org/elasticsearch/common/reference-docs-links.txt index 5d8ae6c5498a5..c304d3ee66aaf 100644 --- a/server/src/main/resources/org/elasticsearch/common/reference-docs-links.txt +++ b/server/src/main/resources/org/elasticsearch/common/reference-docs-links.txt @@ -48,3 +48,4 @@ SECURE_SETTINGS deploy-manage/se CLUSTER_SHARD_LIMIT reference/elasticsearch/configuration-reference/miscellaneous-cluster-settings#cluster-shard-limit DEPLOY_CLOUD_DIFF_FROM_STATEFUL deploy-manage/deploy/elastic-cloud/differences-from-other-elasticsearch-offerings DELETE_INDEX_BLOCK api/doc/elasticsearch/operation/operation-indices-remove-block +ARCHIVED_SETTINGS deploy-manage/upgrade/deployment-or-cluster/archived-settings diff --git a/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java b/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java index fd04040839fee..39da4f8851105 100644 --- a/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java +++ b/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java @@ -335,6 +335,23 @@ public Iterator> settings() { assertThat(e3.getMessage(), equalTo("too long")); } + public void testValidateArchivedSetting() { + IndexScopedSettings settings = new IndexScopedSettings(Settings.EMPTY, IndexScopedSettings.BUILT_IN_INDEX_SETTINGS); + final IllegalArgumentException e = expectThrows( + IllegalArgumentException.class, + () -> settings.validate(Settings.builder().put("archived.index.store.type", "boom").build(), false) + ); + assertThat( + e.getMessage(), + equalTo( + "unknown setting [archived.index.store.type] was archived after upgrading, and must be removed." + + " See [https://www.elastic.co/docs/deploy-manage/upgrade/deployment-or-cluster/archived-settings?version=master] " + + "for details." + ) + ); + + } + public void testTupleAffixUpdateConsumer() { String prefix = randomAlphaOfLength(3) + "foo."; String intSuffix = randomAlphaOfLength(3);