Browse Source

[ML] Switch from Version.CURRENT to Build.current() for index meta (#99942)

ML index mappings contain both `managed_index_mappings_version` and
`version` meta fields.

`managed_index_mappings_version` is used from 8.10 onwards in the
mappings management code to determine if the mappings are up to
date. However, the `version` field is still required to stop
pre-8.10 and post-8.10 nodes fighting and causing repeated mappings
updates in mixed-version clusters. The pre-8.10 nodes will replace
the mappings if the `version` field is not present, on the assumption
that the mappings are _really_ old (5.x or something like that).

To reduce use of the `Version.CURRENT` constant this PR changes the
`version` field in ML index mappings metadata to be hardcoded as
`8.11.0`. Pre-8.10 nodes will see _all_ nodes from 8.11.0 onwards
as 8.11.0 for the purposes of determining whether ML index mappings
need updating. However, that still works as, from the older node's
perspective, it means "newer than I understand".
David Roberts 2 years ago
parent
commit
fe02cf0b2b

+ 20 - 1
server/src/main/java/org/elasticsearch/indices/SystemIndexDescriptor.java

@@ -281,7 +281,7 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
             if (settings.getAsInt(IndexMetadata.INDEX_FORMAT_SETTING.getKey(), 0) != indexFormat) {
                 throw new IllegalArgumentException("Descriptor index format does not match index format in managed settings");
             }
-            this.mappingsNodeVersion = extractNodeVersionFromMappings(mappings, mappingsNodeVersionMetaKey);
+            this.mappingsNodeVersion = bestEffortExtractNodeVersionFromMappings(mappings, mappingsNodeVersionMetaKey);
             this.mappingsVersion = extractVersionFromMappings(mappings);
             assert mappingsVersion.version >= 0 : "The mappings version must not be negative";
 
@@ -953,6 +953,25 @@ public class SystemIndexDescriptor implements IndexPatternMatcher, Comparable<Sy
         return new MappingsVersion(value, Objects.hash(properties));
     }
 
+    /**
+     * An accurate node version is no longer required in system index mappings metadata.
+     * because the mappings version should be used to determine if an upgrade is required,
+     * not the node version. However, some parts of the code are still relying on
+     * <code>mappingsNodeVersion</code>. This method allows sections of the code to stop
+     * accurately setting node version in their mappings while other sections continue to
+     * use it. Once all uses of <code>mappingsNodeVersion</code> are removed this method
+     * can be removed too.
+     */
+    @Deprecated
+    private static Version bestEffortExtractNodeVersionFromMappings(String mappings, String versionMetaKey) {
+        try {
+            return extractNodeVersionFromMappings(mappings, versionMetaKey);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    @Deprecated
     @SuppressWarnings("unchecked")
     private static Version extractNodeVersionFromMappings(String mappings, String versionMetaKey) {
         final Map<String, Object> mappingsMap = XContentHelper.convertToMap(XContentType.JSON.xContent(), mappings, false);

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigIndex.java

@@ -6,10 +6,10 @@
  */
 package org.elasticsearch.xpack.core.ml;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.IndexSettings;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
 import org.elasticsearch.xpack.core.template.TemplateUtils;
 
 import java.util.Map;
@@ -34,7 +34,7 @@ public final class MlConfigIndex {
     public static String mapping() {
         return TemplateUtils.loadTemplate(
             "/ml/config_index_mappings.json",
-            Version.CURRENT.toString(),
+            MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes
             MAPPINGS_VERSION_VARIABLE,
             Map.of("xpack.ml.managed.index.version", Integer.toString(CONFIG_INDEX_MAPPINGS_VERSION))
         );

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlMetaIndex.java

@@ -6,9 +6,9 @@
  */
 package org.elasticsearch.xpack.core.ml;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
 import org.elasticsearch.xpack.core.template.TemplateUtils;
 
 import java.util.Map;
@@ -32,7 +32,7 @@ public final class MlMetaIndex {
     public static String mapping() {
         return TemplateUtils.loadTemplate(
             "/ml/meta_index_mappings.json",
-            Version.CURRENT.toString(),
+            MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes
             MAPPINGS_VERSION_VARIABLE,
             Map.of("xpack.ml.managed.index.version", Integer.toString(META_INDEX_MAPPINGS_VERSION))
         );

+ 1 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlStatsIndex.java

@@ -6,7 +6,6 @@
  */
 package org.elasticsearch.xpack.core.ml;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.client.internal.Client;
 import org.elasticsearch.cluster.ClusterState;
@@ -40,7 +39,7 @@ public class MlStatsIndex {
     public static String mapping() {
         return TemplateUtils.loadTemplate(
             "/ml/stats_index_mappings.json",
-            Version.CURRENT.toString(),
+            MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes
             MAPPINGS_VERSION_VARIABLE,
             Map.of("xpack.ml.managed.index.version", Integer.toString(STATS_INDEX_MAPPINGS_VERSION))
         );

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/annotations/AnnotationIndex.java

@@ -9,7 +9,6 @@ package org.elasticsearch.xpack.core.ml.annotations;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.elasticsearch.ResourceAlreadyExistsException;
-import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
 import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
@@ -28,6 +27,7 @@ import org.elasticsearch.index.Index;
 import org.elasticsearch.xpack.core.ml.MlMetadata;
 import org.elasticsearch.xpack.core.ml.job.persistence.ElasticsearchMappings;
 import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
 import org.elasticsearch.xpack.core.template.TemplateUtils;
 
 import java.util.List;
@@ -213,7 +213,7 @@ public class AnnotationIndex {
     public static String annotationsMapping() {
         return TemplateUtils.loadTemplate(
             "/ml/annotations_index_mappings.json",
-            Version.CURRENT.toString(),
+            MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes
             MAPPINGS_VERSION_VARIABLE,
             Map.of("xpack.ml.managed.index.version", Integer.toString(ANNOTATION_INDEX_MAPPINGS_VERSION))
         );

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/inference/persistence/InferenceIndexConstants.java

@@ -6,10 +6,10 @@
  */
 package org.elasticsearch.xpack.core.ml.inference.persistence;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.xcontent.ParseField;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
 import org.elasticsearch.xpack.core.template.TemplateUtils;
 
 import java.util.Map;
@@ -51,7 +51,7 @@ public final class InferenceIndexConstants {
     public static String mapping() {
         return TemplateUtils.loadTemplate(
             "/ml/inference_index_mappings.json",
-            Version.CURRENT.toString(),
+            MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes
             MAPPINGS_VERSION_VARIABLE,
             Map.of("xpack.ml.managed.index.version", Integer.toString(INFERENCE_INDEX_MAPPINGS_VERSION))
         );

+ 1 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/job/persistence/AnomalyDetectorsIndex.java

@@ -6,7 +6,6 @@
  */
 package org.elasticsearch.xpack.core.ml.job.persistence;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
 import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
@@ -137,7 +136,7 @@ public final class AnomalyDetectorsIndex {
     public static String resultsMapping() {
         return TemplateUtils.loadTemplate(
             RESOURCE_PATH + "results_index_mappings.json",
-            Version.CURRENT.toString(),
+            MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes
             RESULTS_MAPPINGS_VERSION_VARIABLE,
             Map.of("xpack.ml.managed.index.version", Integer.toString(RESULTS_INDEX_MAPPINGS_VERSION))
         );

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/notifications/NotificationsIndex.java

@@ -6,7 +6,7 @@
  */
 package org.elasticsearch.xpack.core.ml.notifications;
 
-import org.elasticsearch.Version;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
 import org.elasticsearch.xpack.core.template.TemplateUtils;
 
 import java.util.Map;
@@ -24,7 +24,7 @@ public final class NotificationsIndex {
     public static String mapping() {
         return TemplateUtils.loadTemplate(
             RESOURCE_PATH + "notifications_index_mappings.json",
-            Version.CURRENT.toString(),
+            MlIndexAndAlias.BWC_MAPPINGS_VERSION, // Only needed for BWC with pre-8.10.0 nodes
             MAPPINGS_VERSION_VARIABLE,
             Map.of("xpack.ml.managed.index.version", Integer.toString(NOTIFICATIONS_INDEX_MAPPINGS_VERSION))
         );

+ 12 - 0
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/utils/MlIndexAndAlias.java

@@ -50,6 +50,18 @@ import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin;
  */
 public final class MlIndexAndAlias {
 
+    /**
+     * ML managed index mappings used to be updated based on the product version.
+     * They are now updated based on per-index mappings versions. However, older
+     * nodes will still look for a product version in the mappings metadata, so
+     * we have to put <em>something</em> in that field that will allow the older
+     * node to realise that the mappings are ahead of what it knows about. The
+     * easiest solution is to hardcode 8.11.0 in this field, because any node
+     * from 8.10.0 onwards should be using per-index mappings versions to determine
+     * whether mappings are up-to-date.
+     */
+    public static final String BWC_MAPPINGS_VERSION = "8.11.0";
+
     private static final Logger logger = LogManager.getLogger(MlIndexAndAlias.class);
 
     // Visible for testing

+ 105 - 52
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/ml/job/persistence/ElasticsearchMappingsTests.java

@@ -6,7 +6,6 @@
  */
 package org.elasticsearch.xpack.core.ml.job.persistence;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction;
 import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
@@ -19,6 +18,7 @@ import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.MappingMetadata;
 import org.elasticsearch.cluster.metadata.Metadata;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
 import org.elasticsearch.index.IndexVersion;
@@ -40,6 +40,7 @@ import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.TimingStats;
 import org.elasticsearch.xpack.core.ml.job.results.CategoryDefinition;
 import org.elasticsearch.xpack.core.ml.job.results.ReservedFieldNames;
 import org.elasticsearch.xpack.core.ml.job.results.Result;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
 import org.junit.Assert;
 import org.mockito.ArgumentCaptor;
 
@@ -126,8 +127,7 @@ public class ElasticsearchMappingsTests extends ESTestCase {
     }
 
     public void testMappingRequiresUpdateNoMapping() {
-        ClusterState.Builder csBuilder = ClusterState.builder(new ClusterName("_name"));
-        ClusterState cs = csBuilder.build();
+        ClusterState cs = ClusterState.builder(new ClusterName("_name")).build();
         String[] indices = new String[] { "no_index" };
 
         assertArrayEquals(new String[] { "no_index" }, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
@@ -145,86 +145,139 @@ public class ElasticsearchMappingsTests extends ESTestCase {
         assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
     }
 
-    public void testMappingRequiresUpdateRecentMappingVersion() {
+    /**
+     * In 8.10 we switched from using the product version to using the per-index mappings
+     * version to determine whether mappings need updating. So any case of the per-index
+     * mappings version not being present should result in the mappings being updated.
+     */
+    public void testMappingRequiresUpdateOnlyProductVersion() {
+        int newVersion = randomIntBetween(1, 100);
         {
             ClusterState cs = getClusterStateWithMappingsWithMetadata(
-                Collections.singletonMap("version_current", Version.CURRENT.toString())
+                Collections.singletonMap("product_version_only", MlIndexAndAlias.BWC_MAPPINGS_VERSION)
             );
-            String[] indices = new String[] { "version_current" };
-            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+            String[] indices = new String[] { "product_version_only" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
+        }
+        {
+            ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("product_version_only", "8.10.2"));
+            String[] indices = new String[] { "product_version_only" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
+        }
+        {
+            ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("product_version_only", "7.17.13"));
+            String[] indices = new String[] { "product_version_only" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
+        }
+        {
+            // Serverless versions may be build hashes
+            ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("product_version_only", "a1b2c3d4"));
+            String[] indices = new String[] { "product_version_only" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
         }
+    }
+
+    /**
+     * In 8.10 we switched from using the product version to using the per-index mappings
+     * version to determine whether mappings need updating. The per-index mappings version
+     * should determine the need for update regardless of the value of the product version
+     * field.
+     */
+    public void testMappingRequiresUpdateGivenProductVersionAndMappingsVersion() {
+        int currentVersion = randomIntBetween(1, 100);
+        int newVersion = currentVersion + randomIntBetween(1, 100);
         {
             ClusterState cs = getClusterStateWithMappingsWithMetadata(
-                Collections.singletonMap("version_current", Version.CURRENT.toString()),
-                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1)
+                Collections.singletonMap("both", MlIndexAndAlias.BWC_MAPPINGS_VERSION),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
             );
-            String[] indices = new String[] { "version_current" };
-            assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+            String[] indices = new String[] { "both" };
+            assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion));
         }
         {
             ClusterState cs = getClusterStateWithMappingsWithMetadata(
-                Collections.singletonMap("version_current", Version.CURRENT.toString()),
-                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1)
+                Collections.singletonMap("both", "8.10.2"),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
             );
-            String[] indices = new String[] { "version_current" };
-            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 2));
+            String[] indices = new String[] { "both" };
+            assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion));
         }
-    }
-
-    public void testMappingRequiresUpdateMaliciousMappingVersion() {
-        ClusterState cs = getClusterStateWithMappingsWithMetadata(
-            Collections.singletonMap("version_current", Collections.singletonMap("nested", "1.0"))
-        );
-        String[] indices = new String[] { "version_nested" };
-        assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
-    }
-
-    public void testMappingRequiresUpdateBogusMappingVersion() {
-        ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_bogus", "0.0"));
-        String[] indices = new String[] { "version_bogus" };
-        assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
-    }
-
-    public void testMappingRequiresUpdateNewerMappingVersion() {
         {
-            ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_newer", 2));
-            String[] indices = new String[] { "version_newer" };
-            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+            // Serverless versions may be build hashes
+            ClusterState cs = getClusterStateWithMappingsWithMetadata(
+                Collections.singletonMap("both", "a1b2c3d4"),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
+            );
+            String[] indices = new String[] { "both" };
+            assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion));
         }
         {
             ClusterState cs = getClusterStateWithMappingsWithMetadata(
-                Collections.singletonMap("version_newer", Version.CURRENT),
-                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 2)
+                Collections.singletonMap("both", MlIndexAndAlias.BWC_MAPPINGS_VERSION),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
             );
-            String[] indices = new String[] { "version_newer" };
-            assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+            String[] indices = new String[] { "both" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
         }
         {
             ClusterState cs = getClusterStateWithMappingsWithMetadata(
-                Collections.singletonMap("version_newer", Version.CURRENT),
-                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1)
+                Collections.singletonMap("both", "8.10.2"),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
             );
-            String[] indices = new String[] { "version_newer" };
-            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 2));
+            String[] indices = new String[] { "both" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
+        }
+        {
+            // Serverless versions may be build hashes
+            ClusterState cs = getClusterStateWithMappingsWithMetadata(
+                Collections.singletonMap("both", "a1b2c3d4"),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
+            );
+            String[] indices = new String[] { "both" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
         }
     }
 
-    public void testMappingRequiresUpdateNewerMappingVersionMinor() {
+    /**
+     * In 8.10 we switched from using the product version to using the per-index mappings
+     * version to determine whether mappings need updating. The per-index mappings version
+     * should determine the need for update even if the product version field is not present.
+     */
+    public void testMappingRequiresUpdateGivenOnlyMappingsVersion() {
+        int currentVersion = randomIntBetween(1, 100);
+        int newVersion = currentVersion + randomIntBetween(1, 100);
         {
-            ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_newer_minor", 1));
-            String[] indices = new String[] { "version_newer_minor" };
-            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+            ClusterState cs = getClusterStateWithMappingsWithMetadata(
+                Collections.singletonMap("mappings_version_only", "NO_VERSION_FIELD"),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
+            );
+            String[] indices = new String[] { "mappings_version_only" };
+            assertArrayEquals(Strings.EMPTY_ARRAY, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, currentVersion));
         }
         {
             ClusterState cs = getClusterStateWithMappingsWithMetadata(
-                Collections.singletonMap("version_newer_minor", Version.CURRENT),
-                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, 1)
+                Collections.singletonMap("mappings_version_only", "NO_VERSION_FIELD"),
+                Collections.singletonMap(SystemIndexDescriptor.VERSION_META_KEY, currentVersion)
             );
-            String[] indices = new String[] { "version_newer_minor" };
-            assertArrayEquals(new String[] {}, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+            String[] indices = new String[] { "mappings_version_only" };
+            assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, newVersion));
         }
     }
 
+    public void testMappingRequiresUpdateMaliciousMappingVersion() {
+        ClusterState cs = getClusterStateWithMappingsWithMetadata(
+            Collections.singletonMap("version_nested", Collections.singletonMap("nested", "1.0"))
+        );
+        String[] indices = new String[] { "version_nested" };
+        assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+    }
+
+    public void testMappingRequiresUpdateBogusMappingVersion() {
+        ClusterState cs = getClusterStateWithMappingsWithMetadata(Collections.singletonMap("version_bogus", "0.0"));
+        String[] indices = new String[] { "version_bogus" };
+        assertArrayEquals(indices, ElasticsearchMappings.mappingRequiresUpdate(cs, indices, 1));
+    }
+
     @SuppressWarnings({ "unchecked" })
     public void testAddDocMappingIfMissing() {
         ThreadPool threadPool = mock(ThreadPool.class);
@@ -287,7 +340,7 @@ public class ElasticsearchMappingsTests extends ESTestCase {
                 meta.put("version", version);
             }
             if (metaData != null) {
-                metaData.forEach((k, v) -> meta.putIfAbsent(k, v));
+                metaData.forEach(meta::putIfAbsent);
             }
             mapping.put("_meta", meta);
 

+ 3 - 2
x-pack/plugin/ml/src/internalClusterTest/java/org/elasticsearch/xpack/ml/integration/JobResultsProviderIT.java

@@ -6,7 +6,6 @@
  */
 package org.elasticsearch.xpack.ml.integration;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionFuture;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest;
@@ -64,6 +63,7 @@ import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.ModelSizeSta
 import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.ModelSnapshot;
 import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.Quantiles;
 import org.elasticsearch.xpack.core.ml.job.process.autodetect.state.TimingStats;
+import org.elasticsearch.xpack.core.ml.utils.MlIndexAndAlias;
 import org.elasticsearch.xpack.core.ml.utils.ToXContentParams;
 import org.elasticsearch.xpack.ml.MlSingleNodeTestCase;
 import org.elasticsearch.xpack.ml.inference.ingest.InferenceProcessor;
@@ -519,7 +519,8 @@ public class JobResultsProviderIT extends MlSingleNodeTestCase {
         @SuppressWarnings("unchecked")
         Map<String, Object> meta = (Map<String, Object>) mappings.get("_meta");
         assertThat(meta.keySet(), hasItem("version"));
-        assertThat(meta.get("version"), equalTo(Version.CURRENT.toString()));
+        assertThat(meta.get("version"), equalTo(MlIndexAndAlias.BWC_MAPPINGS_VERSION));
+        assertThat(meta.get("managed_index_mappings_version"), equalTo(AnomalyDetectorsIndex.RESULTS_INDEX_MAPPINGS_VERSION));
 
         @SuppressWarnings("unchecked")
         Map<String, Object> properties = (Map<String, Object>) mappings.get("properties");

+ 0 - 4
x-pack/qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/MlMappingsUpgradeIT.java

@@ -6,7 +6,6 @@
  */
 package org.elasticsearch.upgrades;
 
-import org.elasticsearch.Version;
 import org.elasticsearch.client.Request;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
@@ -131,7 +130,6 @@ public class MlMappingsUpgradeIT extends AbstractUpgradeTestCase {
             }
             assertNotNull(indexLevel);
 
-            assertEquals(Version.CURRENT.toString(), extractValue("mappings._meta.version", indexLevel));
             assertEquals(
                 AnomalyDetectorsIndex.RESULTS_INDEX_MAPPINGS_VERSION,
                 extractValue("mappings._meta.managed_index_mappings_version", indexLevel)
@@ -168,7 +166,6 @@ public class MlMappingsUpgradeIT extends AbstractUpgradeTestCase {
             }
             assertNotNull(indexLevel);
 
-            assertEquals(Version.CURRENT.toString(), extractValue("mappings._meta.version", indexLevel));
             assertEquals(
                 AnnotationIndex.ANNOTATION_INDEX_MAPPINGS_VERSION,
                 extractValue("mappings._meta.managed_index_mappings_version", indexLevel)
@@ -226,7 +223,6 @@ public class MlMappingsUpgradeIT extends AbstractUpgradeTestCase {
             Map<String, Object> indexLevel = (Map<String, Object>) responseLevel.get(".ml-config");
             assertNotNull(indexLevel);
 
-            assertEquals(Version.CURRENT.toString(), extractValue("mappings._meta.version", indexLevel));
             assertEquals(
                 MlConfigIndex.CONFIG_INDEX_MAPPINGS_VERSION,
                 extractValue("mappings._meta.managed_index_mappings_version", indexLevel)