diff --git a/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java b/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java index 6054d03c896fc..3e5cf3013c66b 100644 --- a/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java +++ b/server/src/main/java/org/elasticsearch/common/ReferenceDocs.java @@ -85,6 +85,7 @@ public enum ReferenceDocs { SECURE_SETTINGS, CLUSTER_SHARD_LIMIT, 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 80bb5a20a3349..1211d01a20067 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; @@ -543,29 +545,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.get(0) + "]" : "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()) { @@ -609,6 +622,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 94992c2c3dda4..4debe85c78ba1 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 @@ -47,3 +47,4 @@ ALLOCATION_EXPLAIN_MAX_RETRY cluster-allocati SECURE_SETTINGS secure-settings.html CLUSTER_SHARD_LIMIT misc-cluster-settings.html#cluster-shard-limit 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 c2b0e1d6f9c6c..d639a7809a41f 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,24 @@ 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/guide/en/elasticsearch/reference/8.19/" + + "deploy-manage/upgrade/deployment-or-cluster/archived-settings] " + + "for details." + ) + ); + + } + public void testTupleAffixUpdateConsumer() { String prefix = randomAlphaOfLength(3) + "foo."; String intSuffix = randomAlphaOfLength(3);