This is causing a CI failure, but it seem to be a real problem with how `MLConfigVersion` is working on 8.11 nodes

From reading #97699, I think the expectation is that for nodes with a new (>8.10) ES release version, there will always be a node attribute with the ML config version.
https://github.com/elastic/elasticsearch/blob/11e62154f7ec1f69d820cd1adf4d2f881fbd39ac/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java#L314

But in this test that seems to not be true - it's falling back to guessing the config version from the node version, which doesn't work because that logic explicitly rejects versions after 8.10 
https://github.com/elastic/elasticsearch/blob/11e62154f7ec1f69d820cd1adf4d2f881fbd39ac/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/MlConfigVersion.java#L273-L275

I'm not sure what the fix is here - whether we need to find a way to enforce that the node attribute exists, or make the version checking more robust to version bumps, but some part of the config version's assumptions are being violated.

**Build scan:**
https://gradle-enterprise.elastic.co/s/3xyywtredddma/tests/:x-pack:plugin:ml:test/org.elasticsearch.xpack.ml.job.JobNodeSelectorTests/testSelectMlNodeOnlyOutOfCandidates

**Reproduction line:**
```
./gradlew ':x-pack:plugin:ml:test' --tests "org.elasticsearch.xpack.ml.job.JobNodeSelectorTests.testSelectMlNodeOnlyOutOfCandidates" -Dtests.seed=5E38DEFC54AFD4C4 -Dtests.locale=en-PH -Dtests.timezone=Australia/Lord_Howe -Druntime.java=20
```

**Applicable branches:**
main

**Reproduces locally?:**
Yes

**Failure history:**
https://gradle-enterprise.elastic.co/scans/tests?tests.container=org.elasticsearch.xpack.ml.job.JobNodeSelectorTests&tests.test=testSelectMlNodeOnlyOutOfCandidates

**Failure excerpt:**
```
java.lang.IllegalArgumentException: Cannot convert 8.11.0. Incompatible version

  at __randomizedtesting.SeedInfo.seed([5E38DEFC54AFD4C4:D0A0E4F42F189B50]:0)
  at org.elasticsearch.xpack.core.ml.MlConfigVersion.fromVersion(MlConfigVersion.java:274)
  at org.elasticsearch.xpack.core.ml.MlConfigVersion.getMlConfigVersionForNode(MlConfigVersion.java:316)
  at org.elasticsearch.xpack.ml.job.task.OpenJobPersistentTasksExecutor.nodeFilter(OpenJobPersistentTasksExecutor.java:186)
  at org.elasticsearch.xpack.ml.job.JobNodeSelectorTests.lambda$testSelectMlNodeOnlyOutOfCandidates$18(JobNodeSelectorTests.java:1033)
  at org.elasticsearch.xpack.ml.job.JobNodeSelector.lambda$new$0(JobNodeSelector.java:97)
  at org.elasticsearch.xpack.ml.job.JobNodeSelector.selectNode(JobNodeSelector.java:162)
  at org.elasticsearch.xpack.ml.job.JobNodeSelector.selectNode(JobNodeSelector.java:131)
  at org.elasticsearch.xpack.ml.job.JobNodeSelectorTests.testSelectMlNodeOnlyOutOfCandidates(JobNodeSelectorTests.java:1035)
  at jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
  at java.lang.reflect.Method.invoke(Method.java:578)
  at com.carrotsearch.randomizedtesting.RandomizedRunner.invoke(RandomizedRunner.java:1758)
  at com.carrotsearch.randomizedtesting.RandomizedRunner$8.evaluate(RandomizedRunner.java:946)
  at com.carrotsearch.randomizedtesting.RandomizedRunner$9.evaluate(RandomizedRunner.java:982)
  at com.carrotsearch.randomizedtesting.RandomizedRunner$10.evaluate(RandomizedRunner.java:996)
  at com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
  at org.apache.lucene.tests.util.TestRuleSetupTeardownChained$1.evaluate(TestRuleSetupTeardownChained.java:48)
  at org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
  at org.apache.lucene.tests.util.TestRuleThreadAndTestName$1.evaluate(TestRuleThreadAndTestName.java:45)
  at org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
  at org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
  at com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
  at com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
  at com.carrotsearch.randomizedtesting.ThreadLeakControl.forkTimeoutingTask(ThreadLeakControl.java:843)
  at com.carrotsearch.randomizedtesting.ThreadLeakControl$3.evaluate(ThreadLeakControl.java:490)
  at com.carrotsearch.randomizedtesting.RandomizedRunner.runSingleTest(RandomizedRunner.java:955)
  at com.carrotsearch.randomizedtesting.RandomizedRunner$5.evaluate(RandomizedRunner.java:840)
  at com.carrotsearch.randomizedtesting.RandomizedRunner$6.evaluate(RandomizedRunner.java:891)
  at com.carrotsearch.randomizedtesting.RandomizedRunner$7.evaluate(RandomizedRunner.java:902)
  at org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
  at com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
  at org.apache.lucene.tests.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:38)
  at com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
  at com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
  at com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
  at com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
  at org.apache.lucene.tests.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:53)
  at org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
  at org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
  at org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
  at org.apache.lucene.tests.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:47)
  at com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
  at com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
  at com.carrotsearch.randomizedtesting.ThreadLeakControl.lambda$forkTimeoutingTask$0(ThreadLeakControl.java:850)
  at java.lang.Thread.run(Thread.java:1623)

```
x-pack/plugin/ml/src/test/java/org/elasticsearch/xpack/ml/job/JobNodeSelectorTests.java
nodes.add(DiscoveryNodeUtils.create("_node_name" + i, nodeId, address, nodeAttr, ROLES_WITH_ML));
            for (int j = 0; j < numRunningJobsPerNode; j++) {
                int id = j + (numRunningJobsPerNode * i);
                
                if (randomBoolean()) {
                    jobIds[id] = "job_id" + id;
                    OpenJobPersistentTasksExecutorTests.addJobTask(jobIds[id], nodeId, anomalyDetectionJobState, tasksBuilder);
                } else {
                    jobIds[id] = "data_frame_analytics_id" + id;
                    addDataFrameAnalyticsJobTask(jobIds[id], nodeId, dfAnalyticsJobState, tasksBuilder);
                }
            }
        }
        PersistentTasksCustomMetadata tasks = tasksBuilder.build();
        ClusterState.Builder cs = ClusterState.builder(new ClusterName("_name"));
        Metadata.Builder metadata = Metadata.builder();
        cs.nodes(nodes);
        metadata.putCustom(PersistentTasksCustomMetadata.TYPE, tasks);
        cs.metadata(metadata);
        return cs;
    }
    static Collection<DiscoveryNode> shuffled(Collection<DiscoveryNode> nodes) {
        List<DiscoveryNode> toShuffle = new ArrayList<>(nodes);
        Randomness.shuffle(toShuffle);
        return toShuffle;
    }
    static void addDataFrameAnalyticsJobTask(
        String id,
        String nodeId,
        DataFrameAnalyticsState state,