Răsfoiți Sursa

Refactor deprecation plugin for easier extensibility (#65284)

This refactor for the deprecation plugin makes extending and adding more plugin settings deprecation checks simpler.
Benjamin Trent 4 ani în urmă
părinte
comite
d4d56cb5ff

+ 40 - 27
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/deprecation/DeprecationInfoAction.java

@@ -5,6 +5,8 @@
  */
 package org.elasticsearch.xpack.core.deprecation;
 
+import org.elasticsearch.ElasticsearchStatusException;
+import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionRequestValidationException;
 import org.elasticsearch.action.ActionResponse;
 import org.elasticsearch.action.IndicesRequest;
@@ -20,20 +22,20 @@ import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
-import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.util.set.Sets;
 import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig;
+import org.elasticsearch.rest.RestStatus;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -78,27 +80,42 @@ public class DeprecationInfoAction extends ActionType<DeprecationInfoAction.Resp
     }
 
     public static class Response extends ActionResponse implements ToXContentObject {
-        private List<DeprecationIssue> clusterSettingsIssues;
-        private List<DeprecationIssue> nodeSettingsIssues;
-        private Map<String, List<DeprecationIssue>> indexSettingsIssues;
-        private List<DeprecationIssue> mlSettingsIssues;
+        static final Set<String> RESERVED_NAMES = Sets.newHashSet("cluster_settings", "node_settings", "index_settings");
+        private final List<DeprecationIssue> clusterSettingsIssues;
+        private final List<DeprecationIssue> nodeSettingsIssues;
+        private final Map<String, List<DeprecationIssue>> indexSettingsIssues;
+        private final Map<String, List<DeprecationIssue>> pluginSettingsIssues;
 
         public Response(StreamInput in) throws IOException {
             super(in);
             clusterSettingsIssues = in.readList(DeprecationIssue::new);
             nodeSettingsIssues = in.readList(DeprecationIssue::new);
             indexSettingsIssues = in.readMapOfLists(StreamInput::readString, DeprecationIssue::new);
-            mlSettingsIssues = in.readList(DeprecationIssue::new);
+            if (in.getVersion().before(Version.V_7_11_0)) {
+                List<DeprecationIssue> mlIssues = in.readList(DeprecationIssue::new);
+                pluginSettingsIssues = new HashMap<>();
+                pluginSettingsIssues.put("ml_settings", mlIssues);
+            } else {
+                pluginSettingsIssues = in.readMapOfLists(StreamInput::readString, DeprecationIssue::new);
+            }
         }
 
         public Response(List<DeprecationIssue> clusterSettingsIssues,
                         List<DeprecationIssue> nodeSettingsIssues,
                         Map<String, List<DeprecationIssue>> indexSettingsIssues,
-                        List<DeprecationIssue> mlSettingsIssues) {
+                        Map<String, List<DeprecationIssue>> pluginSettingsIssues) {
             this.clusterSettingsIssues = clusterSettingsIssues;
             this.nodeSettingsIssues = nodeSettingsIssues;
             this.indexSettingsIssues = indexSettingsIssues;
-            this.mlSettingsIssues = mlSettingsIssues;
+            Set<String> intersection = Sets.intersection(RESERVED_NAMES, pluginSettingsIssues.keySet());
+            if (intersection.isEmpty() == false) {
+                throw new ElasticsearchStatusException(
+                    "Unable to discover deprecations as plugin deprecation names overlap with reserved names {}",
+                    RestStatus.INTERNAL_SERVER_ERROR,
+                    intersection
+                );
+            }
+            this.pluginSettingsIssues = pluginSettingsIssues;
         }
 
         public List<DeprecationIssue> getClusterSettingsIssues() {
@@ -113,8 +130,8 @@ public class DeprecationInfoAction extends ActionType<DeprecationInfoAction.Resp
             return indexSettingsIssues;
         }
 
-        public List<DeprecationIssue> getMlSettingsIssues() {
-            return mlSettingsIssues;
+        public Map<String, List<DeprecationIssue>> getPluginSettingsIssues() {
+            return pluginSettingsIssues;
         }
 
         @Override
@@ -122,7 +139,11 @@ public class DeprecationInfoAction extends ActionType<DeprecationInfoAction.Resp
             out.writeList(clusterSettingsIssues);
             out.writeList(nodeSettingsIssues);
             out.writeMapOfLists(indexSettingsIssues, StreamOutput::writeString, (o, v) -> v.writeTo(o));
-            out.writeList(mlSettingsIssues);
+            if (out.getVersion().before(Version.V_7_11_0)) {
+                out.writeList(pluginSettingsIssues.getOrDefault("ml_settings", Collections.emptyList()));
+            } else {
+                out.writeMapOfLists(pluginSettingsIssues, StreamOutput::writeString, (o, v) -> v.writeTo(o));
+            }
         }
 
         @Override
@@ -132,7 +153,7 @@ public class DeprecationInfoAction extends ActionType<DeprecationInfoAction.Resp
                 .array("node_settings", nodeSettingsIssues.toArray())
                 .field("index_settings")
                 .map(indexSettingsIssues)
-                .array("ml_settings", mlSettingsIssues.toArray())
+                .mapContents(pluginSettingsIssues)
                 .endObject();
         }
 
@@ -144,12 +165,12 @@ public class DeprecationInfoAction extends ActionType<DeprecationInfoAction.Resp
             return Objects.equals(clusterSettingsIssues, response.clusterSettingsIssues) &&
                 Objects.equals(nodeSettingsIssues, response.nodeSettingsIssues) &&
                 Objects.equals(indexSettingsIssues, response.indexSettingsIssues) &&
-                Objects.equals(mlSettingsIssues, response.mlSettingsIssues);
+                Objects.equals(pluginSettingsIssues, response.pluginSettingsIssues);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(clusterSettingsIssues, nodeSettingsIssues, indexSettingsIssues, mlSettingsIssues);
+            return Objects.hash(clusterSettingsIssues, nodeSettingsIssues, indexSettingsIssues, pluginSettingsIssues);
         }
 
         /**
@@ -161,30 +182,22 @@ public class DeprecationInfoAction extends ActionType<DeprecationInfoAction.Resp
          * @param state The cluster state
          * @param indexNameExpressionResolver Used to resolve indices into their concrete names
          * @param request The originating request containing the index expressions to evaluate
-         * @param datafeeds The ml datafeed configurations
          * @param nodeDeprecationResponse The response containing the deprecation issues found on each node
          * @param indexSettingsChecks The list of index-level checks that will be run across all specified
          *                            concrete indices
          * @param clusterSettingsChecks The list of cluster-level checks
-         * @param mlSettingsCheck The list of ml checks
          * @return The list of deprecation issues found in the cluster
          */
         public static DeprecationInfoAction.Response from(ClusterState state,
-                                                          NamedXContentRegistry xContentRegistry,
                                                           IndexNameExpressionResolver indexNameExpressionResolver,
-                                                          Request request, List<DatafeedConfig> datafeeds,
+                                                          Request request,
                                                           NodesDeprecationCheckResponse nodeDeprecationResponse,
                                                           List<Function<IndexMetadata, DeprecationIssue>> indexSettingsChecks,
                                                           List<Function<ClusterState, DeprecationIssue>> clusterSettingsChecks,
-                                                          List<BiFunction<DatafeedConfig, NamedXContentRegistry, DeprecationIssue>>
-                                                              mlSettingsCheck) {
+                                                          Map<String, List<DeprecationIssue>> pluginSettingIssues) {
             List<DeprecationIssue> clusterSettingsIssues = filterChecks(clusterSettingsChecks,
                 (c) -> c.apply(state));
             List<DeprecationIssue> nodeSettingsIssues = mergeNodeIssues(nodeDeprecationResponse);
-            List<DeprecationIssue> mlSettingsIssues = new ArrayList<>();
-            for (DatafeedConfig config : datafeeds) {
-                mlSettingsIssues.addAll(filterChecks(mlSettingsCheck, (c) -> c.apply(config, xContentRegistry)));
-            }
 
             String[] concreteIndexNames = indexNameExpressionResolver.concreteIndexNames(state, request);
 
@@ -198,7 +211,7 @@ public class DeprecationInfoAction extends ActionType<DeprecationInfoAction.Resp
                 }
             }
 
-            return new DeprecationInfoAction.Response(clusterSettingsIssues, nodeSettingsIssues, indexSettingsIssues, mlSettingsIssues);
+            return new DeprecationInfoAction.Response(clusterSettingsIssues, nodeSettingsIssues, indexSettingsIssues, pluginSettingIssues);
         }
     }
 

+ 32 - 15
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/deprecation/DeprecationInfoActionResponseTests.java

@@ -5,6 +5,7 @@
  */
 package org.elasticsearch.xpack.core.deprecation;
 
+import org.elasticsearch.ElasticsearchStatusException;
 import org.elasticsearch.Version;
 import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.cluster.ClusterName;
@@ -18,7 +19,6 @@ import org.elasticsearch.common.io.stream.Writeable;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.transport.TransportAddress;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
-import org.elasticsearch.common.xcontent.NamedXContentRegistry;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.elasticsearch.test.AbstractWireSerializingTestCase;
@@ -28,14 +28,16 @@ import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfigTests;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.function.BiFunction;
+import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static java.util.Collections.emptyList;
+import static org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction.Response.RESERVED_NAMES;
 import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.core.IsEqual.equalTo;
 
@@ -47,15 +49,19 @@ public class DeprecationInfoActionResponseTests extends AbstractWireSerializingT
             .limit(randomIntBetween(0, 10)).collect(Collectors.toList());
         List<DeprecationIssue> nodeIssues = Stream.generate(DeprecationIssueTests::createTestInstance)
             .limit(randomIntBetween(0, 10)).collect(Collectors.toList());
-        List<DeprecationIssue> mlIssues = Stream.generate(DeprecationIssueTests::createTestInstance)
-                .limit(randomIntBetween(0, 10)).collect(Collectors.toList());
         Map<String, List<DeprecationIssue>> indexIssues = new HashMap<>();
         for (int i = 0; i < randomIntBetween(0, 10); i++) {
             List<DeprecationIssue> perIndexIssues = Stream.generate(DeprecationIssueTests::createTestInstance)
                 .limit(randomIntBetween(0, 10)).collect(Collectors.toList());
             indexIssues.put(randomAlphaOfLength(10), perIndexIssues);
         }
-        return new DeprecationInfoAction.Response(clusterIssues, nodeIssues, indexIssues, mlIssues);
+        Map<String, List<DeprecationIssue>> pluginIssues = new HashMap<>();
+        for (int i = 0; i < randomIntBetween(0, 10); i++) {
+            List<DeprecationIssue> perPluginIssues = Stream.generate(DeprecationIssueTests::createTestInstance)
+                .limit(randomIntBetween(0, 10)).collect(Collectors.toList());
+            pluginIssues.put(randomAlphaOfLength(10), perPluginIssues);
+        }
+        return new DeprecationInfoAction.Response(clusterIssues, nodeIssues, indexIssues, pluginIssues);
     }
 
     @Override
@@ -85,12 +91,9 @@ public class DeprecationInfoActionResponseTests extends AbstractWireSerializingT
         boolean clusterIssueFound = randomBoolean();
         boolean nodeIssueFound = randomBoolean();
         boolean indexIssueFound = randomBoolean();
-        boolean mlIssueFound = randomBoolean();
         DeprecationIssue foundIssue = DeprecationIssueTests.createTestInstance();
         List<Function<ClusterState, DeprecationIssue>> clusterSettingsChecks = List.of((s) -> clusterIssueFound ? foundIssue : null);
         List<Function<IndexMetadata, DeprecationIssue>> indexSettingsChecks = List.of((idx) -> indexIssueFound ? foundIssue : null);
-        List<BiFunction<DatafeedConfig, NamedXContentRegistry, DeprecationIssue>> mlSettingsChecks =
-                List.of((idx, unused) -> mlIssueFound ? foundIssue : null);
 
         NodesDeprecationCheckResponse nodeDeprecationIssues = new NodesDeprecationCheckResponse(
             new ClusterName(randomAlphaOfLength(5)),
@@ -101,9 +104,13 @@ public class DeprecationInfoActionResponseTests extends AbstractWireSerializingT
             emptyList());
 
         DeprecationInfoAction.Request request = new DeprecationInfoAction.Request(Strings.EMPTY_ARRAY);
-        DeprecationInfoAction.Response response = DeprecationInfoAction.Response.from(state, NamedXContentRegistry.EMPTY,
-            resolver, request, datafeeds,
-            nodeDeprecationIssues, indexSettingsChecks, clusterSettingsChecks, mlSettingsChecks);
+        DeprecationInfoAction.Response response = DeprecationInfoAction.Response.from(state,
+            resolver,
+            request,
+            nodeDeprecationIssues,
+            indexSettingsChecks,
+            clusterSettingsChecks,
+            Collections.emptyMap());
 
         if (clusterIssueFound) {
             assertThat(response.getClusterSettingsIssues(), equalTo(Collections.singletonList(foundIssue)));
@@ -126,11 +133,21 @@ public class DeprecationInfoActionResponseTests extends AbstractWireSerializingT
         } else {
             assertTrue(response.getIndexSettingsIssues().isEmpty());
         }
+    }
 
-        if (mlIssueFound) {
-            assertThat(response.getMlSettingsIssues(), equalTo(Collections.singletonList(foundIssue)));
-        } else {
-            assertTrue(response.getMlSettingsIssues().isEmpty());
+    public void testCtorFailure() {
+        Map<String, List<DeprecationIssue>> indexNames = Stream.generate(() -> randomAlphaOfLength(10))
+            .limit(10)
+            .collect(Collectors.toMap(Function.identity(), (_k) -> Collections.emptyList()));
+        Set<String> shouldCauseFailure = new HashSet<>(RESERVED_NAMES);
+        for(int i = 0; i < NUMBER_OF_TEST_RUNS; i++) {
+            Map<String, List<DeprecationIssue>> pluginSettingsIssues = randomSubsetOf(3, shouldCauseFailure)
+                .stream()
+                .collect(Collectors.toMap(Function.identity(), (_k) -> Collections.emptyList()));
+            expectThrows(
+                ElasticsearchStatusException.class,
+                () -> new DeprecationInfoAction.Response(Collections.emptyList(), Collections.emptyList(), indexNames, pluginSettingsIssues)
+            );
         }
     }
 }

+ 84 - 0
x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecker.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+package org.elasticsearch.xpack.deprecation;
+
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.client.OriginSettingClient;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
+
+import java.util.List;
+
+public interface DeprecationChecker {
+
+    /**
+     * Should this deprecation checker be enabled?
+     *
+     * @param settings Cluster settings
+     * @return True if enabled given the settings
+     */
+    boolean enabled(Settings settings);
+
+    /**
+     * This runs the checks for the current deprecation checker.
+     *
+     * @param components The components provided for the checker
+     * @param deprecationIssueListener The issues found
+     */
+    void check(Components components, ActionListener<CheckResult> deprecationIssueListener);
+
+    /**
+     * @return The name of the checker
+     */
+    String getName();
+
+    class CheckResult {
+        private final String checkerName;
+        private final List<DeprecationIssue> issues;
+
+        public CheckResult(String checkerName, List<DeprecationIssue> issues) {
+            this.checkerName = checkerName;
+            this.issues = issues;
+        }
+
+        public String getCheckerName() {
+            return checkerName;
+        }
+
+        public List<DeprecationIssue> getIssues() {
+            return issues;
+        }
+    }
+
+    class Components {
+
+        private final NamedXContentRegistry xContentRegistry;
+        private final Settings settings;
+        private final Client client;
+
+        Components(NamedXContentRegistry xContentRegistry, Settings settings, OriginSettingClient client) {
+            this.xContentRegistry = xContentRegistry;
+            this.settings = settings;
+            this.client = client;
+        }
+
+        public NamedXContentRegistry xContentRegistry() {
+            return xContentRegistry;
+        }
+
+        public Settings settings() {
+            return settings;
+        }
+
+        public Client client() {
+            return client;
+        }
+
+    }
+}

+ 0 - 5
x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/DeprecationChecks.java

@@ -9,10 +9,8 @@ import org.elasticsearch.action.admin.cluster.node.info.PluginsAndModules;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.common.xcontent.NamedXContentRegistry;
 import org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction;
 import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
-import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig;
 
 import java.util.Collections;
 import java.util.List;
@@ -38,9 +36,6 @@ public class DeprecationChecks {
     static List<Function<IndexMetadata, DeprecationIssue>> INDEX_SETTINGS_CHECKS =
             List.of(IndexDeprecationChecks::oldIndicesCheck, IndexDeprecationChecks::translogRetentionSettingCheck);
 
-    static List<BiFunction<DatafeedConfig, NamedXContentRegistry, DeprecationIssue>> ML_SETTINGS_CHECKS =
-            List.of(MlDeprecationChecks::checkDataFeedAggregations, MlDeprecationChecks::checkDataFeedQuery);
-
     /**
      * helper utility function to reduce repeat of running a specific {@link List} of checks.
      *

+ 79 - 0
x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/MlDeprecationChecker.java

@@ -0,0 +1,79 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+package org.elasticsearch.xpack.deprecation;
+
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.xpack.core.XPackSettings;
+import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
+import org.elasticsearch.xpack.core.ml.action.GetDatafeedsAction;
+import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+public class MlDeprecationChecker implements DeprecationChecker {
+
+    static Optional<DeprecationIssue> checkDataFeedQuery(DatafeedConfig datafeedConfig, NamedXContentRegistry xContentRegistry) {
+        List<String> deprecations = datafeedConfig.getQueryDeprecations(xContentRegistry);
+        if (deprecations.isEmpty()) {
+            return Optional.empty();
+        } else {
+            return Optional.of(new DeprecationIssue(DeprecationIssue.Level.WARNING,
+                "Datafeed [" + datafeedConfig.getId() + "] uses deprecated query options",
+                "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html#breaking_70_search_changes",
+                deprecations.toString()));
+        }
+    }
+
+    static Optional<DeprecationIssue> checkDataFeedAggregations(DatafeedConfig datafeedConfig, NamedXContentRegistry xContentRegistry) {
+        List<String> deprecations = datafeedConfig.getAggDeprecations(xContentRegistry);
+        if (deprecations.isEmpty()) {
+            return Optional.empty();
+        } else {
+            return Optional.of(new DeprecationIssue(DeprecationIssue.Level.WARNING,
+                "Datafeed [" + datafeedConfig.getId() + "] uses deprecated aggregation options",
+                "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html" +
+                    "#breaking_70_aggregations_changes", deprecations.toString()));
+        }
+    }
+
+    @Override
+    public boolean enabled(Settings settings) {
+        return XPackSettings.MACHINE_LEARNING_ENABLED.get(settings);
+    }
+
+    @Override
+    public void check(Components components, ActionListener<CheckResult> deprecationIssueListener) {
+        if (XPackSettings.MACHINE_LEARNING_ENABLED.get(components.settings()) == false) {
+            deprecationIssueListener.onResponse(new CheckResult(getName(), Collections.emptyList()));
+            return;
+        }
+        components.client().execute(
+            GetDatafeedsAction.INSTANCE,
+            new GetDatafeedsAction.Request(GetDatafeedsAction.ALL), ActionListener.wrap(
+                datafeedsResponse -> {
+                    List<DeprecationIssue> issues = new ArrayList<>();
+                    for (DatafeedConfig df : datafeedsResponse.getResponse().results()) {
+                        checkDataFeedAggregations(df, components.xContentRegistry()).ifPresent(issues::add);
+                        checkDataFeedQuery(df, components.xContentRegistry()).ifPresent(issues::add);
+                    }
+                    deprecationIssueListener.onResponse(new CheckResult(getName(), issues));
+                },
+                deprecationIssueListener::onFailure
+            )
+        );
+    }
+
+    @Override
+    public String getName() {
+        return "ml_settings";
+    }
+}

+ 0 - 45
x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/MlDeprecationChecks.java

@@ -1,45 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-package org.elasticsearch.xpack.deprecation;
-
-import org.elasticsearch.common.xcontent.NamedXContentRegistry;
-import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
-import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig;
-
-import java.util.List;
-
-/**
- * Check the {@link DatafeedConfig} query and aggregations for deprecated usages.
- */
-final class MlDeprecationChecks {
-
-    private MlDeprecationChecks() {
-    }
-
-    static DeprecationIssue checkDataFeedQuery(DatafeedConfig datafeedConfig, NamedXContentRegistry xContentRegistry) {
-        List<String> deprecations = datafeedConfig.getQueryDeprecations(xContentRegistry);
-        if (deprecations.isEmpty()) {
-            return null;
-        } else {
-            return new DeprecationIssue(DeprecationIssue.Level.WARNING,
-                    "Datafeed [" + datafeedConfig.getId() + "] uses deprecated query options",
-                    "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html#breaking_70_search_changes",
-                    deprecations.toString());
-        }
-    }
-
-    static DeprecationIssue checkDataFeedAggregations(DatafeedConfig datafeedConfig, NamedXContentRegistry xContentRegistry) {
-        List<String> deprecations = datafeedConfig.getAggDeprecations(xContentRegistry);
-        if (deprecations.isEmpty()) {
-            return null;
-        } else {
-            return new DeprecationIssue(DeprecationIssue.Level.WARNING,
-                    "Datafeed [" + datafeedConfig.getId() + "] uses deprecated aggregation options",
-                    "https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking-changes-7.0.html" +
-                            "#breaking_70_aggregations_changes", deprecations.toString());
-        }
-    }
-}

+ 36 - 20
x-pack/plugin/deprecation/src/main/java/org/elasticsearch/xpack/deprecation/TransportDeprecationInfoAction.java

@@ -10,7 +10,9 @@ import org.apache.logging.log4j.Logger;
 import org.elasticsearch.action.ActionListener;
 import org.elasticsearch.action.FailedNodeException;
 import org.elasticsearch.action.support.ActionFilters;
+import org.elasticsearch.action.support.GroupedActionListener;
 import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
+import org.elasticsearch.client.OriginSettingClient;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.cluster.ClusterState;
 import org.elasticsearch.cluster.block.ClusterBlockException;
@@ -27,23 +29,22 @@ import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.core.ClientHelper;
 import org.elasticsearch.xpack.core.XPackField;
-import org.elasticsearch.xpack.core.XPackSettings;
 import org.elasticsearch.xpack.core.deprecation.DeprecationInfoAction;
+import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
 import org.elasticsearch.xpack.core.deprecation.NodesDeprecationCheckAction;
 import org.elasticsearch.xpack.core.deprecation.NodesDeprecationCheckRequest;
-import org.elasticsearch.xpack.core.ml.action.GetDatafeedsAction;
-import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 import static org.elasticsearch.xpack.deprecation.DeprecationChecks.CLUSTER_SETTINGS_CHECKS;
 import static org.elasticsearch.xpack.deprecation.DeprecationChecks.INDEX_SETTINGS_CHECKS;
-import static org.elasticsearch.xpack.deprecation.DeprecationChecks.ML_SETTINGS_CHECKS;
 
 public class TransportDeprecationInfoAction extends TransportMasterNodeReadAction<DeprecationInfoAction.Request,
         DeprecationInfoAction.Response> {
+    private static final List<DeprecationChecker> PLUGIN_CHECKERS = List.of(new MlDeprecationChecker());
     private static final Logger logger = LogManager.getLogger(TransportDeprecationInfoAction.class);
 
     private final XPackLicenseState licenseState;
@@ -90,13 +91,17 @@ public class TransportDeprecationInfoAction extends TransportMasterNodeReadActio
                         logger.debug("node {} failed to run deprecation checks: {}", failure.nodeId(), failure);
                     }
                 }
-                getDatafeedConfigs(ActionListener.wrap(
-                    datafeeds -> {
-                        listener.onResponse(
-                            DeprecationInfoAction.Response.from(state, xContentRegistry, indexNameExpressionResolver,
-                                request, datafeeds, response, INDEX_SETTINGS_CHECKS, CLUSTER_SETTINGS_CHECKS,
-                                ML_SETTINGS_CHECKS));
-                    },
+
+                DeprecationChecker.Components components = new DeprecationChecker.Components(
+                    xContentRegistry,
+                    settings,
+                    new OriginSettingClient(client, ClientHelper.DEPRECATION_ORIGIN)
+                );
+                pluginSettingIssues(PLUGIN_CHECKERS, components, ActionListener.wrap(
+                    deprecationIssues -> listener.onResponse(
+                        DeprecationInfoAction.Response.from(state, indexNameExpressionResolver,
+                            request, response, INDEX_SETTINGS_CHECKS, CLUSTER_SETTINGS_CHECKS,
+                            deprecationIssues)),
                     listener::onFailure
                 ));
 
@@ -106,15 +111,26 @@ public class TransportDeprecationInfoAction extends TransportMasterNodeReadActio
         }
     }
 
-    private void getDatafeedConfigs(ActionListener<List<DatafeedConfig>> listener) {
-        if (XPackSettings.MACHINE_LEARNING_ENABLED.get(settings) == false) {
-            listener.onResponse(Collections.emptyList());
-        } else {
-            ClientHelper.executeAsyncWithOrigin(client, ClientHelper.DEPRECATION_ORIGIN, GetDatafeedsAction.INSTANCE,
-                    new GetDatafeedsAction.Request(GetDatafeedsAction.ALL), ActionListener.wrap(
-                            datafeedsResponse -> listener.onResponse(datafeedsResponse.getResponse().results()),
-                            listener::onFailure
-                    ));
+    static void pluginSettingIssues(List<DeprecationChecker> checkers,
+                                    DeprecationChecker.Components components,
+                                    ActionListener<Map<String, List<DeprecationIssue>>> listener) {
+        List<DeprecationChecker> enabledCheckers = checkers
+            .stream()
+            .filter(c -> c.enabled(components.settings()))
+            .collect(Collectors.toList());
+        if (enabledCheckers.isEmpty()) {
+            listener.onResponse(Collections.emptyMap());
+            return;
+        }
+        GroupedActionListener<DeprecationChecker.CheckResult> groupedActionListener = new GroupedActionListener<>(ActionListener.wrap(
+            checkResults -> listener.onResponse(checkResults
+                    .stream()
+                    .collect(Collectors.toMap(DeprecationChecker.CheckResult::getCheckerName, DeprecationChecker.CheckResult::getIssues))),
+            listener::onFailure
+        ), enabledCheckers.size());
+        for(DeprecationChecker checker : checkers) {
+            checker.check(components, groupedActionListener);
         }
     }
+
 }

+ 13 - 2
x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/MlDeprecationChecksTests.java → x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/MlDeprecationCheckerTests.java

@@ -11,11 +11,14 @@ import org.elasticsearch.common.xcontent.NamedXContentRegistry;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.search.SearchModule;
 import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.core.XPackSettings;
 import org.elasticsearch.xpack.core.ml.datafeed.DatafeedConfig;
 
 import java.util.Collections;
 
-public class MlDeprecationChecksTests extends ESTestCase {
+import static org.hamcrest.Matchers.is;
+
+public class MlDeprecationCheckerTests extends ESTestCase {
 
     @Override
     protected NamedXContentRegistry xContentRegistry() {
@@ -28,11 +31,19 @@ public class MlDeprecationChecksTests extends ESTestCase {
         return false;
     }
 
+    public void testEnabled() {
+        MlDeprecationChecker mlDeprecationChecker = new MlDeprecationChecker();
+        assertThat(mlDeprecationChecker.enabled(Settings.EMPTY), is(true));
+        assertThat(mlDeprecationChecker.enabled(Settings.builder()
+            .put(XPackSettings.MACHINE_LEARNING_ENABLED.getKey(), Boolean.toString(false))
+            .build()), is(false));
+    }
+
     public void testCheckDataFeedQuery() {
         DatafeedConfig.Builder goodDatafeed = new DatafeedConfig.Builder("good-df", "job-id");
         goodDatafeed.setIndices(Collections.singletonList("some-index"));
         goodDatafeed.setParsedQuery(QueryBuilders.termQuery("foo", "bar"));
-        assertNull(MlDeprecationChecks.checkDataFeedQuery(goodDatafeed.build(), xContentRegistry()));
+        assertThat(MlDeprecationChecker.checkDataFeedQuery(goodDatafeed.build(), xContentRegistry()).isPresent(), is(false));
 
         DatafeedConfig.Builder deprecatedDatafeed = new DatafeedConfig.Builder("df-with-deprecated-query", "job-id");
         deprecatedDatafeed.setIndices(Collections.singletonList("some-index"));

+ 91 - 0
x-pack/plugin/deprecation/src/test/java/org/elasticsearch/xpack/deprecation/TransportDeprecationInfoActionTests.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+package org.elasticsearch.xpack.deprecation;
+
+import org.elasticsearch.action.ActionListener;
+import org.elasticsearch.action.support.PlainActionFuture;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.core.deprecation.DeprecationIssue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+
+public class TransportDeprecationInfoActionTests extends ESTestCase {
+
+    public void testPluginSettingIssues() {
+        DeprecationChecker.Components components = new DeprecationChecker.Components(null, Settings.EMPTY, null);
+        PlainActionFuture<Map<String, List<DeprecationIssue>>> future = new PlainActionFuture<>();
+        TransportDeprecationInfoAction.pluginSettingIssues(Arrays.asList(
+            new NamedChecker("foo", Collections.emptyList(), false),
+            new NamedChecker("bar",
+                Collections.singletonList(new DeprecationIssue(DeprecationIssue.Level.WARNING, "bar msg", "", null)),
+                false)),
+            components,
+            future
+            );
+        Map<String, List<DeprecationIssue>> issueMap = future.actionGet();
+        assertThat(issueMap.size(), equalTo(2));
+        assertThat(issueMap.get("foo"), is(empty()));
+        assertThat(issueMap.get("bar").get(0).getMessage(), equalTo("bar msg"));
+    }
+
+    public void testPluginSettingIssuesWithFailures() {
+        DeprecationChecker.Components components = new DeprecationChecker.Components(null, Settings.EMPTY, null);
+        PlainActionFuture<Map<String, List<DeprecationIssue>>> future = new PlainActionFuture<>();
+        TransportDeprecationInfoAction.pluginSettingIssues(Arrays.asList(
+            new NamedChecker("foo", Collections.emptyList(), false),
+            new NamedChecker("bar",
+                Collections.singletonList(new DeprecationIssue(DeprecationIssue.Level.WARNING, "bar msg", "", null)),
+                true)),
+            components,
+            future
+        );
+        Exception exception = expectThrows(Exception.class, future::actionGet);
+        assertThat(exception.getCause().getMessage(), containsString("boom"));
+    }
+
+    private static class NamedChecker implements DeprecationChecker {
+
+        private final String name;
+        private final List<DeprecationIssue> issues;
+        private final boolean shouldFail;
+
+        NamedChecker(String name, List<DeprecationIssue> issues, boolean shouldFail) {
+            this.name = name;
+            this.issues = issues;
+            this.shouldFail = shouldFail;
+        }
+
+        @Override
+        public boolean enabled(Settings settings) {
+            return true;
+        }
+
+        @Override
+        public void check(DeprecationChecker.Components components, ActionListener<CheckResult> deprecationIssueListener) {
+            if (shouldFail) {
+                deprecationIssueListener.onFailure(new Exception("boom"));
+                return;
+            }
+            deprecationIssueListener.onResponse(new CheckResult(name, issues));
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+    }
+
+}