diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/BuildPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/BuildPluginFuncTest.groovy index 03b044583add0..63bb732d8a11d 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/BuildPluginFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/BuildPluginFuncTest.groovy @@ -119,7 +119,7 @@ class BuildPluginFuncTest extends AbstractGradleFuncTest { noticeFile.set(file("NOTICE")) """ when: - def result = gradleRunner("assemble", "-x", "generateHistoricalFeaturesMetadata").build() + def result = gradleRunner("assemble", "-x", "generateClusterFeaturesMetadata").build() then: result.task(":assemble").outcome == TaskOutcome.SUCCESS file("build/distributions/hello-world.jar").exists() diff --git a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/PublishPluginFuncTest.groovy b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/PublishPluginFuncTest.groovy index c7e11ba96c7dd..a199ff9d3eac5 100644 --- a/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/PublishPluginFuncTest.groovy +++ b/build-tools-internal/src/integTest/groovy/org/elasticsearch/gradle/internal/PublishPluginFuncTest.groovy @@ -303,7 +303,7 @@ class PublishPluginFuncTest extends AbstractGradleFuncTest { """ when: - def result = gradleRunner('assemble', '--stacktrace', '-x', 'generateHistoricalFeaturesMetadata').build() + def result = gradleRunner('assemble', '--stacktrace', '-x', 'generateClusterFeaturesMetadata').build() then: result.task(":generatePom").outcome == TaskOutcome.SUCCESS diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BaseInternalPluginBuildPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BaseInternalPluginBuildPlugin.java index 49887dac5b6fd..2b79bc2b9173e 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BaseInternalPluginBuildPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BaseInternalPluginBuildPlugin.java @@ -14,7 +14,7 @@ import org.elasticsearch.gradle.internal.conventions.util.Util; import org.elasticsearch.gradle.internal.info.BuildParameterExtension; import org.elasticsearch.gradle.internal.precommit.JarHellPrecommitPlugin; -import org.elasticsearch.gradle.internal.test.HistoricalFeaturesMetadataPlugin; +import org.elasticsearch.gradle.internal.test.ClusterFeaturesMetadataPlugin; import org.elasticsearch.gradle.plugin.PluginBuildPlugin; import org.elasticsearch.gradle.plugin.PluginPropertiesExtension; import org.elasticsearch.gradle.testclusters.ElasticsearchCluster; @@ -38,7 +38,7 @@ public void apply(Project project) { project.getPluginManager().apply(PluginBuildPlugin.class); project.getPluginManager().apply(JarHellPrecommitPlugin.class); project.getPluginManager().apply(ElasticsearchJavaPlugin.class); - project.getPluginManager().apply(HistoricalFeaturesMetadataPlugin.class); + project.getPluginManager().apply(ClusterFeaturesMetadataPlugin.class); boolean isCi = project.getRootProject().getExtensions().getByType(BuildParameterExtension.class).isCi(); // Clear default dependencies added by public PluginBuildPlugin as we add our // own project dependencies for internal builds diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BuildPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BuildPlugin.java index 75984e1bc6998..fb8a9858e24d5 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BuildPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/BuildPlugin.java @@ -12,7 +12,7 @@ import org.elasticsearch.gradle.internal.info.GlobalBuildInfoPlugin; import org.elasticsearch.gradle.internal.precommit.InternalPrecommitTasks; import org.elasticsearch.gradle.internal.snyk.SnykDependencyMonitoringGradlePlugin; -import org.elasticsearch.gradle.internal.test.HistoricalFeaturesMetadataPlugin; +import org.elasticsearch.gradle.internal.test.ClusterFeaturesMetadataPlugin; import org.gradle.api.InvalidUserDataException; import org.gradle.api.Plugin; import org.gradle.api.Project; @@ -63,7 +63,7 @@ public void apply(final Project project) { project.getPluginManager().apply(ElasticsearchJavadocPlugin.class); project.getPluginManager().apply(DependenciesInfoPlugin.class); project.getPluginManager().apply(SnykDependencyMonitoringGradlePlugin.class); - project.getPluginManager().apply(HistoricalFeaturesMetadataPlugin.class); + project.getPluginManager().apply(ClusterFeaturesMetadataPlugin.class); InternalPrecommitTasks.create(project, true); configureLicenseAndNotice(project); } diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/HistoricalFeaturesMetadataPlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/ClusterFeaturesMetadataPlugin.java similarity index 83% rename from build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/HistoricalFeaturesMetadataPlugin.java rename to build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/ClusterFeaturesMetadataPlugin.java index be972f11d4586..0c8a99fa82398 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/HistoricalFeaturesMetadataPlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/ClusterFeaturesMetadataPlugin.java @@ -21,10 +21,10 @@ import java.util.Map; /** - * Extracts historical feature metadata into a machine-readable format for use in backward compatibility testing. + * Extracts cluster feature metadata into a machine-readable format for use in backward compatibility testing. */ -public class HistoricalFeaturesMetadataPlugin implements Plugin { - public static final String HISTORICAL_FEATURES_JSON = "historical-features.json"; +public class ClusterFeaturesMetadataPlugin implements Plugin { + public static final String CLUSTER_FEATURES_JSON = "cluster-features.json"; public static final String FEATURES_METADATA_TYPE = "features-metadata-json"; public static final String FEATURES_METADATA_CONFIGURATION = "featuresMetadata"; @@ -40,13 +40,13 @@ public void apply(Project project) { SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class); SourceSet mainSourceSet = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME); - TaskProvider generateTask = project.getTasks() - .register("generateHistoricalFeaturesMetadata", HistoricalFeaturesMetadataTask.class, task -> { + TaskProvider generateTask = project.getTasks() + .register("generateClusterFeaturesMetadata", ClusterFeaturesMetadataTask.class, task -> { task.setClasspath( featureMetadataExtractorConfig.plus(mainSourceSet.getRuntimeClasspath()) .plus(project.getConfigurations().getByName(CompileOnlyResolvePlugin.RESOLVEABLE_COMPILE_ONLY_CONFIGURATION_NAME)) ); - task.getOutputFile().convention(project.getLayout().getBuildDirectory().file(HISTORICAL_FEATURES_JSON)); + task.getOutputFile().convention(project.getLayout().getBuildDirectory().file(CLUSTER_FEATURES_JSON)); }); Configuration featuresMetadataArtifactConfig = project.getConfigurations().create(FEATURES_METADATA_CONFIGURATION, c -> { diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/HistoricalFeaturesMetadataTask.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/ClusterFeaturesMetadataTask.java similarity index 81% rename from build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/HistoricalFeaturesMetadataTask.java rename to build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/ClusterFeaturesMetadataTask.java index a2ea7af210dfd..aa4f90e4d2367 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/HistoricalFeaturesMetadataTask.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/ClusterFeaturesMetadataTask.java @@ -26,7 +26,7 @@ import javax.inject.Inject; @CacheableTask -public abstract class HistoricalFeaturesMetadataTask extends DefaultTask { +public abstract class ClusterFeaturesMetadataTask extends DefaultTask { private FileCollection classpath; @OutputFile @@ -46,30 +46,30 @@ public void setClasspath(FileCollection classpath) { @TaskAction public void execute() { - getWorkerExecutor().noIsolation().submit(HistoricalFeaturesMetadataWorkAction.class, params -> { + getWorkerExecutor().noIsolation().submit(ClusterFeaturesMetadataWorkAction.class, params -> { params.getClasspath().setFrom(getClasspath()); params.getOutputFile().set(getOutputFile()); }); } - public interface HistoricalFeaturesWorkParameters extends WorkParameters { + public interface ClusterFeaturesWorkParameters extends WorkParameters { ConfigurableFileCollection getClasspath(); RegularFileProperty getOutputFile(); } - public abstract static class HistoricalFeaturesMetadataWorkAction implements WorkAction { + public abstract static class ClusterFeaturesMetadataWorkAction implements WorkAction { private final ExecOperations execOperations; @Inject - public HistoricalFeaturesMetadataWorkAction(ExecOperations execOperations) { + public ClusterFeaturesMetadataWorkAction(ExecOperations execOperations) { this.execOperations = execOperations; } @Override public void execute() { LoggedExec.javaexec(execOperations, spec -> { - spec.getMainClass().set("org.elasticsearch.extractor.features.HistoricalFeaturesMetadataExtractor"); + spec.getMainClass().set("org.elasticsearch.extractor.features.ClusterFeaturesMetadataExtractor"); spec.classpath(getParameters().getClasspath()); spec.args(getParameters().getOutputFile().get().getAsFile().getAbsolutePath()); }); diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/RestTestBasePlugin.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/RestTestBasePlugin.java index b44cfdac69ba7..559c0f60abc08 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/RestTestBasePlugin.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/test/rest/RestTestBasePlugin.java @@ -20,8 +20,8 @@ import org.elasticsearch.gradle.distribution.ElasticsearchDistributionTypes; import org.elasticsearch.gradle.internal.ElasticsearchJavaBasePlugin; import org.elasticsearch.gradle.internal.InternalDistributionDownloadPlugin; +import org.elasticsearch.gradle.internal.test.ClusterFeaturesMetadataPlugin; import org.elasticsearch.gradle.internal.test.ErrorReportingTestListener; -import org.elasticsearch.gradle.internal.test.HistoricalFeaturesMetadataPlugin; import org.elasticsearch.gradle.plugin.BasePluginBuildPlugin; import org.elasticsearch.gradle.plugin.PluginBuildPlugin; import org.elasticsearch.gradle.plugin.PluginPropertiesExtension; @@ -116,12 +116,12 @@ public void apply(Project project) { extractedPluginsConfiguration.extendsFrom(pluginsConfiguration); configureArtifactTransforms(project); - // Create configuration for aggregating historical feature metadata + // Create configuration for aggregating feature metadata FileCollection featureMetadataConfig = project.getConfigurations().create(FEATURES_METADATA_CONFIGURATION, c -> { c.setCanBeConsumed(false); c.setCanBeResolved(true); c.attributes( - a -> a.attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, HistoricalFeaturesMetadataPlugin.FEATURES_METADATA_TYPE) + a -> a.attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ClusterFeaturesMetadataPlugin.FEATURES_METADATA_TYPE) ); c.defaultDependencies(d -> d.add(project.getDependencies().project(Map.of("path", ":server")))); c.withDependencies(dependencies -> { @@ -136,10 +136,7 @@ public void apply(Project project) { c.setCanBeConsumed(false); c.setCanBeResolved(true); c.attributes( - a -> a.attribute( - ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, - HistoricalFeaturesMetadataPlugin.FEATURES_METADATA_TYPE - ) + a -> a.attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ClusterFeaturesMetadataPlugin.FEATURES_METADATA_TYPE) ); c.defaultDependencies( d -> d.add(project.getDependencies().project(Map.of("path", ":distribution", "configuration", "featuresMetadata"))) diff --git a/distribution/build.gradle b/distribution/build.gradle index e3481706ef230..bfbf10ac85e2f 100644 --- a/distribution/build.gradle +++ b/distribution/build.gradle @@ -14,7 +14,7 @@ import org.elasticsearch.gradle.VersionProperties import org.elasticsearch.gradle.internal.ConcatFilesTask import org.elasticsearch.gradle.internal.DependenciesInfoPlugin import org.elasticsearch.gradle.internal.NoticeTask -import org.elasticsearch.gradle.internal.test.HistoricalFeaturesMetadataPlugin +import org.elasticsearch.gradle.internal.test.ClusterFeaturesMetadataPlugin import java.nio.file.Files import java.nio.file.Path @@ -33,7 +33,7 @@ configurations { } featuresMetadata { attributes { - attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, HistoricalFeaturesMetadataPlugin.FEATURES_METADATA_TYPE) + attribute(ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE, ClusterFeaturesMetadataPlugin.FEATURES_METADATA_TYPE) } } } diff --git a/server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java b/server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java index 57b90454c7e8b..ad285cbd391cd 100644 --- a/server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java +++ b/server/src/main/java/org/elasticsearch/cluster/ClusterFeatures.java @@ -95,7 +95,7 @@ public Set allNodeFeatures() { /** * {@code true} if {@code feature} is present on all nodes in the cluster. *

- * NOTE: This should not be used directly, as it does not read historical features. + * NOTE: This should not be used directly. * Please use {@link org.elasticsearch.features.FeatureService#clusterHasFeature} instead. */ @SuppressForbidden(reason = "directly reading cluster features") diff --git a/server/src/main/java/org/elasticsearch/features/FeatureData.java b/server/src/main/java/org/elasticsearch/features/FeatureData.java index 991bb4d82be3d..65b95eae27e06 100644 --- a/server/src/main/java/org/elasticsearch/features/FeatureData.java +++ b/server/src/main/java/org/elasticsearch/features/FeatureData.java @@ -9,25 +9,19 @@ package org.elasticsearch.features; -import org.elasticsearch.Version; import org.elasticsearch.common.Strings; import org.elasticsearch.logging.LogManager; import org.elasticsearch.logging.Logger; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.NavigableMap; import java.util.Set; -import java.util.TreeMap; - -import static org.elasticsearch.features.FeatureService.CLUSTER_FEATURES_ADDED_VERSION; /** - * Reads and consolidate features exposed by a list {@link FeatureSpecification}, grouping them into historical features and node - * features for the consumption of {@link FeatureService} + * Reads and consolidate features exposed by a list {@link FeatureSpecification}, + * grouping them together for the consumption of {@link FeatureService} */ public class FeatureData { @@ -40,19 +34,14 @@ public class FeatureData { } } - private final NavigableMap> historicalFeatures; private final Map nodeFeatures; - private FeatureData(NavigableMap> historicalFeatures, Map nodeFeatures) { - this.historicalFeatures = historicalFeatures; + private FeatureData(Map nodeFeatures) { this.nodeFeatures = nodeFeatures; } public static FeatureData createFromSpecifications(List specs) { Map allFeatures = new HashMap<>(); - - // Initialize historicalFeatures with empty version to guarantee there's a floor entry for every version - NavigableMap> historicalFeatures = new TreeMap<>(Map.of(Version.V_EMPTY, Set.of())); Map nodeFeatures = new HashMap<>(); for (FeatureSpecification spec : specs) { Set specFeatures = spec.getFeatures(); @@ -61,39 +50,6 @@ public static FeatureData createFromSpecifications(List new HashSet<>()).add(hfe.getKey().id()); - } - for (NodeFeature f : specFeatures) { FeatureSpecification existing = allFeatures.putIfAbsent(f.id(), spec); if (existing != null && existing.getClass() != spec.getClass()) { @@ -106,24 +62,7 @@ public static FeatureData createFromSpecifications(List> consolidateHistoricalFeatures( - NavigableMap> declaredHistoricalFeatures - ) { - // update each version by adding in all features from previous versions - Set featureAggregator = new HashSet<>(); - for (Map.Entry> versions : declaredHistoricalFeatures.entrySet()) { - featureAggregator.addAll(versions.getValue()); - versions.setValue(Set.copyOf(featureAggregator)); - } - - return Collections.unmodifiableNavigableMap(declaredHistoricalFeatures); - } - - public NavigableMap> getHistoricalFeatures() { - return historicalFeatures; + return new FeatureData(Map.copyOf(nodeFeatures)); } public Map getNodeFeatures() { diff --git a/server/src/main/java/org/elasticsearch/features/FeatureService.java b/server/src/main/java/org/elasticsearch/features/FeatureService.java index 1d911a75a4838..9a0ac7cafc183 100644 --- a/server/src/main/java/org/elasticsearch/features/FeatureService.java +++ b/server/src/main/java/org/elasticsearch/features/FeatureService.java @@ -9,7 +9,6 @@ package org.elasticsearch.features; -import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.core.SuppressForbidden; import org.elasticsearch.logging.LogManager; @@ -17,8 +16,6 @@ import java.util.List; import java.util.Map; -import java.util.NavigableMap; -import java.util.Set; /** * Manages information on the features supported by nodes in the cluster. @@ -34,9 +31,6 @@ public class FeatureService { private static final Logger logger = LogManager.getLogger(FeatureService.class); - public static final Version CLUSTER_FEATURES_ADDED_VERSION = Version.V_8_12_0; - - private final NavigableMap> historicalFeatures; private final Map nodeFeatures; /** @@ -47,13 +41,12 @@ public FeatureService(List specs) { var featureData = FeatureData.createFromSpecifications(specs); nodeFeatures = featureData.getNodeFeatures(); - historicalFeatures = featureData.getHistoricalFeatures(); logger.info("Registered local node features {}", nodeFeatures.keySet().stream().sorted().toList()); } /** - * The non-historical features supported by this node. + * The features supported by this node. * @return Map of {@code feature-id} to its declaring {@code NodeFeature} object. */ public Map getNodeFeatures() { @@ -65,11 +58,6 @@ public Map getNodeFeatures() { */ @SuppressForbidden(reason = "We need basic feature information from cluster state") public boolean clusterHasFeature(ClusterState state, NodeFeature feature) { - if (state.clusterFeatures().clusterHasFeature(feature)) { - return true; - } - - var features = historicalFeatures.floorEntry(state.getNodes().getMinNodeVersion()); - return features != null && features.getValue().contains(feature.id()); + return state.clusterFeatures().clusterHasFeature(feature); } } diff --git a/server/src/main/java/org/elasticsearch/features/FeatureSpecification.java b/server/src/main/java/org/elasticsearch/features/FeatureSpecification.java index 03f0dd89f172e..c37bc4488f109 100644 --- a/server/src/main/java/org/elasticsearch/features/FeatureSpecification.java +++ b/server/src/main/java/org/elasticsearch/features/FeatureSpecification.java @@ -9,9 +9,6 @@ package org.elasticsearch.features; -import org.elasticsearch.Version; - -import java.util.Map; import java.util.Set; /** @@ -49,12 +46,4 @@ default Set getFeatures() { default Set getTestFeatures() { return Set.of(); } - - /** - * Returns information on historical features that should be deemed to be present on all nodes - * on or above the {@link Version} specified. - */ - default Map getHistoricalFeatures() { - return Map.of(); - } } diff --git a/server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java b/server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java index e103704c89649..874a6a96313e4 100644 --- a/server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java +++ b/server/src/test/java/org/elasticsearch/features/FeatureServiceTests.java @@ -9,15 +9,9 @@ package org.elasticsearch.features; -import org.elasticsearch.Version; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; -import org.elasticsearch.cluster.node.DiscoveryNodeUtils; -import org.elasticsearch.cluster.node.DiscoveryNodes; -import org.elasticsearch.index.IndexVersion; -import org.elasticsearch.index.IndexVersions; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.VersionUtils; import java.util.List; import java.util.Map; @@ -30,79 +24,36 @@ public class FeatureServiceTests extends ESTestCase { private static class TestFeatureSpecification implements FeatureSpecification { private final Set features; - private final Map historicalFeatures; - private TestFeatureSpecification(Set features, Map historicalFeatures) { + private TestFeatureSpecification(Set features) { this.features = features; - this.historicalFeatures = historicalFeatures; } @Override public Set getFeatures() { return features; } - - @Override - public Map getHistoricalFeatures() { - return historicalFeatures; - } } public void testFailsDuplicateFeatures() { // these all need to be separate classes to trigger the exception - FeatureSpecification fs1 = new TestFeatureSpecification(Set.of(new NodeFeature("f1")), Map.of()) { - }; - FeatureSpecification fs2 = new TestFeatureSpecification(Set.of(new NodeFeature("f1")), Map.of()) { - }; - FeatureSpecification hfs1 = new TestFeatureSpecification(Set.of(), Map.of(new NodeFeature("f1"), Version.V_8_11_0)) { + FeatureSpecification fs1 = new TestFeatureSpecification(Set.of(new NodeFeature("f1"))) { }; - FeatureSpecification hfs2 = new TestFeatureSpecification(Set.of(), Map.of(new NodeFeature("f1"), Version.V_8_11_0)) { + FeatureSpecification fs2 = new TestFeatureSpecification(Set.of(new NodeFeature("f1"))) { }; assertThat( expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(fs1, fs2))).getMessage(), containsString("Duplicate feature") ); - assertThat( - expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(hfs1, hfs2))).getMessage(), - containsString("Duplicate feature") - ); - assertThat( - expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(fs1, hfs1))).getMessage(), - containsString("Duplicate feature") - ); - } - - public void testFailsNonHistoricalVersion() { - FeatureSpecification fs = new TestFeatureSpecification( - Set.of(), - Map.of(new NodeFeature("f1"), Version.fromId(FeatureService.CLUSTER_FEATURES_ADDED_VERSION.id + 1)) - ); - - assertThat( - expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(fs))).getMessage(), - containsString("not a historical version") - ); - } - - public void testFailsSameRegularAndHistoricalFeature() { - FeatureSpecification fs = new TestFeatureSpecification( - Set.of(new NodeFeature("f1")), - Map.of(new NodeFeature("f1"), Version.V_8_12_0) - ); - - assertThat( - expectThrows(IllegalArgumentException.class, () -> new FeatureService(List.of(fs))).getMessage(), - containsString("cannot be declared as both a regular and historical feature") - ); } public void testGetNodeFeaturesCombinesAllSpecs() { List specs = List.of( - new TestFeatureSpecification(Set.of(new NodeFeature("f1"), new NodeFeature("f2")), Map.of()), - new TestFeatureSpecification(Set.of(new NodeFeature("f3")), Map.of()), - new TestFeatureSpecification(Set.of(new NodeFeature("f4"), new NodeFeature("f5")), Map.of()), - new TestFeatureSpecification(Set.of(), Map.of()) + new TestFeatureSpecification(Set.of(new NodeFeature("f1"), new NodeFeature("f2"))), + new TestFeatureSpecification(Set.of(new NodeFeature("f3"))), + new TestFeatureSpecification(Set.of(new NodeFeature("f4"), new NodeFeature("f5"))), + new TestFeatureSpecification(Set.of()) ); FeatureService service = new FeatureService(specs); @@ -111,10 +62,10 @@ public void testGetNodeFeaturesCombinesAllSpecs() { public void testStateHasFeatures() { List specs = List.of( - new TestFeatureSpecification(Set.of(new NodeFeature("f1"), new NodeFeature("f2")), Map.of()), - new TestFeatureSpecification(Set.of(new NodeFeature("f3")), Map.of()), - new TestFeatureSpecification(Set.of(new NodeFeature("f4"), new NodeFeature("f5")), Map.of()), - new TestFeatureSpecification(Set.of(), Map.of()) + new TestFeatureSpecification(Set.of(new NodeFeature("f1"), new NodeFeature("f2"))), + new TestFeatureSpecification(Set.of(new NodeFeature("f3"))), + new TestFeatureSpecification(Set.of(new NodeFeature("f4"), new NodeFeature("f5"))), + new TestFeatureSpecification(Set.of()) ); ClusterState state = ClusterState.builder(ClusterName.DEFAULT) @@ -130,50 +81,4 @@ public void testStateHasFeatures() { assertFalse(service.clusterHasFeature(state, new NodeFeature("nf2"))); assertFalse(service.clusterHasFeature(state, new NodeFeature("nf3"))); } - - private static ClusterState stateWithMinVersion(Version version) { - DiscoveryNodes.Builder nodes = DiscoveryNodes.builder(); - nodes.add(DiscoveryNodeUtils.builder("node").version(version, IndexVersions.ZERO, IndexVersion.current()).build()); - for (int n = randomInt(5); n >= 0; n--) { - nodes.add( - DiscoveryNodeUtils.builder("node" + n) - .version( - VersionUtils.randomVersionBetween(random(), version, Version.CURRENT), - IndexVersions.ZERO, - IndexVersion.current() - ) - .build() - ); - } - - return ClusterState.builder(ClusterName.DEFAULT).nodes(nodes).build(); - } - - public void testStateHasHistoricalFeatures() { - NodeFeature v8_11_0 = new NodeFeature("hf_8.11.0"); - NodeFeature v8_10_0 = new NodeFeature("hf_8.10.0"); - NodeFeature v7_17_0 = new NodeFeature("hf_7.17.0"); - List specs = List.of( - new TestFeatureSpecification(Set.of(), Map.of(v8_11_0, Version.V_8_11_0)), - new TestFeatureSpecification(Set.of(), Map.of(v8_10_0, Version.V_8_10_0)), - new TestFeatureSpecification(Set.of(), Map.of(v7_17_0, Version.V_7_17_0)) - ); - - FeatureService service = new FeatureService(specs); - assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_11_0), v8_11_0)); - assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_11_0), v8_10_0)); - assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_11_0), v7_17_0)); - - assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_8_10_0), v8_11_0)); - assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_10_0), v8_10_0)); - assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_8_10_0), v7_17_0)); - - assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_17_0), v8_11_0)); - assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_17_0), v8_10_0)); - assertTrue(service.clusterHasFeature(stateWithMinVersion(Version.V_7_17_0), v7_17_0)); - - assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_16_0), v8_11_0)); - assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_16_0), v8_10_0)); - assertFalse(service.clusterHasFeature(stateWithMinVersion(Version.V_7_16_0), v7_17_0)); - } } diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java index a4195a07e7621..8ca9c0709b359 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java @@ -63,7 +63,6 @@ import org.elasticsearch.core.PathUtils; import org.elasticsearch.core.TimeValue; import org.elasticsearch.core.UpdateForV9; -import org.elasticsearch.features.FeatureSpecification; import org.elasticsearch.features.NodeFeature; import org.elasticsearch.health.node.selection.HealthNode; import org.elasticsearch.index.IndexSettings; @@ -398,29 +397,11 @@ public void initClient() throws IOException { assert nodesVersions != null; } - /** - * Override to provide additional test-only historical features. - * - * Note: This extension point cannot be used to add cluster features. The provided {@link FeatureSpecification}s - * must contain only historical features, otherwise an assertion error is thrown. - */ - protected List additionalTestOnlyHistoricalFeatures() { - return List.of(); - } - protected final TestFeatureService createTestFeatureService( Map> clusterStateFeatures, Set semanticNodeVersions ) { - // Historical features information is unavailable when using legacy test plugins - if (ESRestTestFeatureService.hasFeatureMetadata() == false) { - logger.warn( - "This test is running on the legacy test framework; historical features from production code will not be available. " - + "You need to port the test to the new test plugins in order to use historical features from production code. " - + "If this is a legacy feature used only in tests, you can add it to a test-only FeatureSpecification." - ); - } - return new ESRestTestFeatureService(additionalTestOnlyHistoricalFeatures(), semanticNodeVersions, clusterStateFeatures.values()); + return new ESRestTestFeatureService(semanticNodeVersions, clusterStateFeatures.values()); } protected static boolean has(ProductFeature feature) { diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestFeatureService.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestFeatureService.java index cd3406e7ddac5..9054dc6f94182 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestFeatureService.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestFeatureService.java @@ -13,9 +13,6 @@ import org.elasticsearch.core.PathUtils; import org.elasticsearch.core.Strings; import org.elasticsearch.core.SuppressForbidden; -import org.elasticsearch.features.FeatureData; -import org.elasticsearch.features.FeatureSpecification; -import org.elasticsearch.features.NodeFeature; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.XContentParserConfiguration; import org.elasticsearch.xcontent.json.JsonXContent; @@ -25,13 +22,9 @@ import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Files; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Predicate; @@ -48,33 +41,12 @@ class ESRestTestFeatureService implements TestFeatureService { */ private static final Pattern VERSION_FEATURE_PATTERN = Pattern.compile("gte_v(\\d+\\.\\d+\\.\\d+)"); - private final Set knownHistoricalFeatureNames; private final Collection nodeVersions; private final Collection> nodeFeatures; - private final Collection> nodeHistoricalFeatures; - ESRestTestFeatureService(List featureSpecs, Set nodeVersions, Collection> nodeFeatures) { - List specs = new ArrayList<>(featureSpecs); - if (MetadataHolder.HISTORICAL_FEATURES != null) { - specs.add(MetadataHolder.HISTORICAL_FEATURES); - } - FeatureData featureData = FeatureData.createFromSpecifications(specs); - assert featureData.getNodeFeatures().isEmpty() - : Strings.format( - "Only historical features can be injected via ESRestTestCase#additionalTestOnlyHistoricalFeatures(), rejecting %s", - featureData.getNodeFeatures().keySet() - ); - this.knownHistoricalFeatureNames = featureData.getHistoricalFeatures().lastEntry().getValue(); + ESRestTestFeatureService(Set nodeVersions, Collection> nodeFeatures) { this.nodeVersions = nodeVersions; this.nodeFeatures = nodeFeatures; - this.nodeHistoricalFeatures = nodeVersions.stream() - .map(featureData.getHistoricalFeatures()::floorEntry) - .map(Map.Entry::getValue) - .toList(); - } - - public static boolean hasFeatureMetadata() { - return MetadataHolder.HISTORICAL_FEATURES != null; } private static boolean checkCollection(Collection coll, Predicate pred, boolean any) { @@ -83,11 +55,10 @@ private static boolean checkCollection(Collection coll, Predicate pred @Override public boolean clusterHasFeature(String featureId, boolean any) { - if (checkCollection(nodeFeatures, s -> s.contains(featureId), any) - || checkCollection(nodeHistoricalFeatures, s -> s.contains(featureId), any)) { + if (checkCollection(nodeFeatures, s -> s.contains(featureId), any)) { return true; } - if (MetadataHolder.FEATURE_NAMES.contains(featureId) || knownHistoricalFeatureNames.contains(featureId)) { + if (MetadataHolder.FEATURE_NAMES.contains(featureId)) { return false; // feature known but not present } @@ -131,24 +102,20 @@ public boolean clusterHasFeature(String featureId, boolean any) { return false; } + public static boolean hasFeatureMetadata() { + return MetadataHolder.FEATURE_NAMES.isEmpty() == false; + } + private static class MetadataHolder { - private static final FeatureSpecification HISTORICAL_FEATURES; private static final Set FEATURE_NAMES; static { String metadataPath = System.getProperty("tests.features.metadata.path"); if (metadataPath == null) { FEATURE_NAMES = emptySet(); - HISTORICAL_FEATURES = null; } else { Set featureNames = new HashSet<>(); - Map historicalFeatures = new HashMap<>(); loadFeatureMetadata(metadataPath, (key, value) -> { - if (key.equals("historical_features") && value instanceof Map map) { - for (var entry : map.entrySet()) { - historicalFeatures.put(new NodeFeature((String) entry.getKey()), Version.fromString((String) entry.getValue())); - } - } if (key.equals("feature_names") && value instanceof Collection collection) { for (var entry : collection) { featureNames.add((String) entry); @@ -156,13 +123,6 @@ private static class MetadataHolder { } }); FEATURE_NAMES = Collections.unmodifiableSet(featureNames); - Map unmodifiableHistoricalFeatures = Collections.unmodifiableMap(historicalFeatures); - HISTORICAL_FEATURES = new FeatureSpecification() { - @Override - public Map getHistoricalFeatures() { - return unmodifiableHistoricalFeatures; - } - }; } } diff --git a/test/metadata-extractor/src/main/java/org/elasticsearch/extractor/features/HistoricalFeaturesMetadataExtractor.java b/test/metadata-extractor/src/main/java/org/elasticsearch/extractor/features/ClusterFeaturesMetadataExtractor.java similarity index 69% rename from test/metadata-extractor/src/main/java/org/elasticsearch/extractor/features/HistoricalFeaturesMetadataExtractor.java rename to test/metadata-extractor/src/main/java/org/elasticsearch/extractor/features/ClusterFeaturesMetadataExtractor.java index 3ffa27126fac8..3a090a1b3fadc 100644 --- a/test/metadata-extractor/src/main/java/org/elasticsearch/extractor/features/HistoricalFeaturesMetadataExtractor.java +++ b/test/metadata-extractor/src/main/java/org/elasticsearch/extractor/features/ClusterFeaturesMetadataExtractor.java @@ -9,9 +9,8 @@ package org.elasticsearch.extractor.features; -import org.elasticsearch.Version; -import org.elasticsearch.common.CheckedBiConsumer; import org.elasticsearch.common.logging.LogConfigurator; +import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.features.FeatureSpecification; import org.elasticsearch.features.NodeFeature; import org.elasticsearch.xcontent.XContentGenerator; @@ -25,14 +24,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Stream; -public class HistoricalFeaturesMetadataExtractor { +public class ClusterFeaturesMetadataExtractor { private final ClassLoader classLoader; static { @@ -40,7 +37,7 @@ public class HistoricalFeaturesMetadataExtractor { LogConfigurator.configureESLogging(); } - public HistoricalFeaturesMetadataExtractor(ClassLoader classLoader) { + public ClusterFeaturesMetadataExtractor(ClassLoader classLoader) { this.classLoader = classLoader; } @@ -56,9 +53,7 @@ public static void main(String[] args) { printUsageAndExit(); } - new HistoricalFeaturesMetadataExtractor(HistoricalFeaturesMetadataExtractor.class.getClassLoader()).generateMetadataFile( - outputFile - ); + new ClusterFeaturesMetadataExtractor(ClusterFeaturesMetadataExtractor.class.getClassLoader()).generateMetadataFile(outputFile); } public void generateMetadataFile(Path outputFile) { @@ -67,13 +62,7 @@ public void generateMetadataFile(Path outputFile) { XContentGenerator generator = JsonXContent.jsonXContent.createGenerator(os) ) { generator.writeStartObject(); - extractHistoricalFeatureMetadata((historical, names) -> { - generator.writeFieldName("historical_features"); - generator.writeStartObject(); - for (Map.Entry entry : historical.entrySet()) { - generator.writeStringField(entry.getKey().id(), entry.getValue().toString()); - } - generator.writeEndObject(); + extractClusterFeaturesMetadata(names -> { generator.writeFieldName("feature_names"); generator.writeStartArray(); for (var entry : names) { @@ -87,22 +76,19 @@ public void generateMetadataFile(Path outputFile) { } } - void extractHistoricalFeatureMetadata(CheckedBiConsumer, Set, IOException> metadataConsumer) - throws IOException { - Map historicalFeatures = new HashMap<>(); + void extractClusterFeaturesMetadata(CheckedConsumer, IOException> metadataConsumer) throws IOException { Set featureNames = new HashSet<>(); ServiceLoader featureSpecLoader = ServiceLoader.load(FeatureSpecification.class, classLoader); for (FeatureSpecification featureSpecification : featureSpecLoader) { - historicalFeatures.putAll(featureSpecification.getHistoricalFeatures()); Stream.concat(featureSpecification.getFeatures().stream(), featureSpecification.getTestFeatures().stream()) .map(NodeFeature::id) .forEach(featureNames::add); } - metadataConsumer.accept(historicalFeatures, featureNames); + metadataConsumer.accept(featureNames); } private static void printUsageAndExit() { - System.err.println("Usage: HistoricalFeaturesMetadataExtractor "); + System.err.println("Usage: ClusterFeaturesMetadataExtractor "); System.exit(1); } } diff --git a/test/metadata-extractor/src/test/java/org/elasticsearch/extractor/features/HistoricalFeaturesMetadataExtractorTests.java b/test/metadata-extractor/src/test/java/org/elasticsearch/extractor/features/ClusterFeaturesMetadataExtractorTests.java similarity index 67% rename from test/metadata-extractor/src/test/java/org/elasticsearch/extractor/features/HistoricalFeaturesMetadataExtractorTests.java rename to test/metadata-extractor/src/test/java/org/elasticsearch/extractor/features/ClusterFeaturesMetadataExtractorTests.java index d810f17ae552e..af69aaff86cc5 100644 --- a/test/metadata-extractor/src/test/java/org/elasticsearch/extractor/features/HistoricalFeaturesMetadataExtractorTests.java +++ b/test/metadata-extractor/src/test/java/org/elasticsearch/extractor/features/ClusterFeaturesMetadataExtractorTests.java @@ -9,8 +9,6 @@ package org.elasticsearch.extractor.features; -import org.elasticsearch.Version; -import org.elasticsearch.features.NodeFeature; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.json.JsonXContent; @@ -21,7 +19,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -29,25 +26,19 @@ import static org.elasticsearch.xcontent.XContentParserConfiguration.EMPTY; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.not; -public class HistoricalFeaturesMetadataExtractorTests extends ESTestCase { +public class ClusterFeaturesMetadataExtractorTests extends ESTestCase { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); public void testExtractHistoricalMetadata() throws IOException { - HistoricalFeaturesMetadataExtractor extractor = new HistoricalFeaturesMetadataExtractor(this.getClass().getClassLoader()); - Map nodeFeatureVersionMap = new HashMap<>(); + ClusterFeaturesMetadataExtractor extractor = new ClusterFeaturesMetadataExtractor(this.getClass().getClassLoader()); Set featureNamesSet = new HashSet<>(); - extractor.extractHistoricalFeatureMetadata((historical, names) -> { - nodeFeatureVersionMap.putAll(historical); - featureNamesSet.addAll(names); - }); - // assertThat(nodeFeatureVersionMap, not(anEmptyMap())); + extractor.extractClusterFeaturesMetadata(featureNamesSet::addAll); assertThat(featureNamesSet, not(empty())); assertThat(featureNamesSet, hasItem("test_features_enabled")); @@ -55,11 +46,7 @@ public void testExtractHistoricalMetadata() throws IOException { extractor.generateMetadataFile(outputFile); try (XContentParser parser = JsonXContent.jsonXContent.createParser(EMPTY, Files.newInputStream(outputFile))) { Map parsedMap = parser.map(); - assertThat(parsedMap, hasKey("historical_features")); assertThat(parsedMap, hasKey("feature_names")); - @SuppressWarnings("unchecked") - Map historicalFeaturesMap = (Map) (parsedMap.get("historical_features")); - nodeFeatureVersionMap.forEach((key, value) -> assertThat(historicalFeaturesMap, hasEntry(key.id(), value.toString()))); @SuppressWarnings("unchecked") Collection featureNamesList = (Collection) (parsedMap.get("feature_names")); diff --git a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java index 6ebf05755ef5e..265d9f7bd8cd5 100644 --- a/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java +++ b/x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/EsqlSpecTestCase.java @@ -51,7 +51,6 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.LongStream; -import java.util.stream.Stream; import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude; import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude; @@ -207,10 +206,7 @@ protected static void checkCapabilities(RestClient client, TestFeatureService te } } - var features = Stream.concat( - new EsqlFeatures().getFeatures().stream(), - new EsqlFeatures().getHistoricalFeatures().keySet().stream() - ).map(NodeFeature::id).collect(Collectors.toSet()); + var features = new EsqlFeatures().getFeatures().stream().map(NodeFeature::id).collect(Collectors.toSet()); for (String feature : testCase.requiredCapabilities) { var esqlFeature = "esql." + feature; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java index a186b784e95fb..d675f772b5a3b 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java @@ -567,9 +567,6 @@ public static Set capabilities(boolean all) { for (NodeFeature feature : new EsqlFeatures().getFeatures()) { caps.add(cap(feature)); } - for (NodeFeature feature : new EsqlFeatures().getHistoricalFeatures().keySet()) { - caps.add(cap(feature)); - } return Set.copyOf(caps); }