diff --git a/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/PluginBuildPluginFuncTest.groovy b/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/PluginBuildPluginFuncTest.groovy index 72c67514f3a5c..d9d3f3bb7c477 100644 --- a/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/PluginBuildPluginFuncTest.groovy +++ b/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/PluginBuildPluginFuncTest.groovy @@ -126,7 +126,7 @@ class PluginBuildPluginFuncTest extends AbstractGradleFuncTest { props.get("has.native.controller") == null props.get("extended.plugins") == null props.get("modulename") == null - props.size() == 6 + props.size() == 7 } def "module name is inferred by plugin properties"() { diff --git a/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/StablePluginBuildPluginFuncTest.groovy b/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/StablePluginBuildPluginFuncTest.groovy index 697d2027a707a..1a2ba65d868d7 100644 --- a/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/StablePluginBuildPluginFuncTest.groovy +++ b/build-tools/src/integTest/groovy/org/elasticsearch/gradle/plugin/StablePluginBuildPluginFuncTest.groovy @@ -73,7 +73,7 @@ class StablePluginBuildPluginFuncTest extends AbstractGradleFuncTest { props.get("modulename") == null props.get("extended.plugins") == null props.get("has.native.controller") == null - props.size() == 5 + props.size() == 6 } diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/plugin/BasePluginBuildPlugin.java b/build-tools/src/main/java/org/elasticsearch/gradle/plugin/BasePluginBuildPlugin.java index e00a4b45f8a61..b8e2d4e3aea01 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/plugin/BasePluginBuildPlugin.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/plugin/BasePluginBuildPlugin.java @@ -119,6 +119,7 @@ private TaskProvider createBundleTasks(final Project project, PluginPropert task.getHasNativeController().set(providerFactory.provider(extension::isHasNativeController)); task.getRequiresKeystore().set(providerFactory.provider(extension::isRequiresKeystore)); task.getIsLicensed().set(providerFactory.provider(extension::isLicensed)); + task.getDeploymentTarget().set(providerFactory.provider(extension::getDeploymentTarget)); var mainSourceSet = project.getExtensions().getByType(SourceSetContainer.class).getByName(SourceSet.MAIN_SOURCE_SET_NAME); FileCollection moduleInfoFile = mainSourceSet.getOutput() diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java b/build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java index 6cf01814a45ef..66b4f85f6cdb5 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/plugin/GeneratePluginPropertiesTask.java @@ -39,6 +39,7 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.Set; import javax.inject.Inject; @@ -49,6 +50,8 @@ public abstract class GeneratePluginPropertiesTask extends DefaultTask { public static final String STABLE_PROPERTIES_FILENAME = "stable-plugin-descriptor.properties"; private static final String DESCRIPTION = "Generates Elasticsearch Plugin descriptor file"; + private static final Set DEPLOYMENT_TARGETS = Set.of("ALL", "STATELESS_ONLY", "STATEFUL_ONLY"); + @Inject public GeneratePluginPropertiesTask(ProjectLayout projectLayout) { setDescription(DESCRIPTION); @@ -95,6 +98,10 @@ public GeneratePluginPropertiesTask(ProjectLayout projectLayout) { @Input public abstract Property getIsStable(); + @Input + @Optional + public abstract Property getDeploymentTarget(); + @TaskAction public void generatePropertiesFile() throws IOException { String classname = getClassname().getOrElse(""); @@ -119,6 +126,14 @@ public void generatePropertiesFile() throws IOException { props.put("licensed", getIsLicensed().get()); props.put("modulename", findModuleName()); + String deploymentTarget = getDeploymentTarget().getOrElse("ALL"); + if (DEPLOYMENT_TARGETS.contains(deploymentTarget) == false) { + throw new InvalidUserDataException( + "invalid deploymentTarget '" + deploymentTarget + "', expected one of " + DEPLOYMENT_TARGETS + ); + } + props.put("deploymentTarget", deploymentTarget); + SimpleTemplateEngine engine = new SimpleTemplateEngine(); Path outputFile = getOutputFile().get().getAsFile().toPath(); Files.createDirectories(outputFile.getParent()); diff --git a/build-tools/src/main/java/org/elasticsearch/gradle/plugin/PluginPropertiesExtension.java b/build-tools/src/main/java/org/elasticsearch/gradle/plugin/PluginPropertiesExtension.java index e87d2e50ee121..18cb21a354107 100644 --- a/build-tools/src/main/java/org/elasticsearch/gradle/plugin/PluginPropertiesExtension.java +++ b/build-tools/src/main/java/org/elasticsearch/gradle/plugin/PluginPropertiesExtension.java @@ -43,6 +43,9 @@ public class PluginPropertiesExtension { /** True if the plugin requires the elasticsearch keystore to exist, false otherwise. */ private boolean requiresKeystore; + /** The optional deployment target of this plugin. */ + private String deploymentTarget; + /** A license file that should be included in the built plugin zip. */ private File licenseFile; @@ -117,6 +120,14 @@ public boolean isRequiresKeystore() { return requiresKeystore; } + public String getDeploymentTarget() { + return deploymentTarget; + } + + public void setDeploymentTarget(String deploymentTarget) { + this.deploymentTarget = deploymentTarget; + } + public void setRequiresKeystore(boolean requiresKeystore) { this.requiresKeystore = requiresKeystore; } diff --git a/build-tools/src/main/resources/plugin-descriptor.properties b/build-tools/src/main/resources/plugin-descriptor.properties index 5f744fab8abbb..09e77823a2b2d 100644 --- a/build-tools/src/main/resources/plugin-descriptor.properties +++ b/build-tools/src/main/resources/plugin-descriptor.properties @@ -63,3 +63,6 @@ has.native.controller=${hasNativeController} # This plugin requires that a license agreement be accepted before installation licensed=${licensed} <% } %> + +# The deployment target describing when to include this plugin +deployment.target=${deploymentTarget} diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/cli/ListPluginsCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/cli/ListPluginsCommandTests.java index 5249aeefc2f2d..8df6452493445 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/cli/ListPluginsCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/cli/ListPluginsCommandTests.java @@ -123,7 +123,8 @@ public void testPluginWithVerbose() throws Exception { "Native Controller: false", "Licensed: false", "Extended Plugins: []", - " * Classname: org.fake" + " * Classname: org.fake", + "Deployment Target: ALL" ), terminal.getOutput() ); @@ -145,7 +146,8 @@ public void testPluginWithNativeController() throws Exception { "Native Controller: true", "Licensed: false", "Extended Plugins: []", - " * Classname: org.fake" + " * Classname: org.fake", + "Deployment Target: ALL" ), terminal.getOutput() ); @@ -169,6 +171,7 @@ public void testPluginWithVerboseMultiplePlugins() throws Exception { "Licensed: false", "Extended Plugins: []", " * Classname: org.fake", + "Deployment Target: ALL", "fake_plugin2", "- Plugin information:", "Name: fake_plugin2", @@ -179,7 +182,8 @@ public void testPluginWithVerboseMultiplePlugins() throws Exception { "Native Controller: false", "Licensed: false", "Extended Plugins: []", - " * Classname: org.fake2" + " * Classname: org.fake2", + "Deployment Target: ALL" ), terminal.getOutput() ); diff --git a/modules/health-shards-availability/build.gradle b/modules/health-shards-availability/build.gradle index 62686a44a9336..46fc7bb56ccb7 100644 --- a/modules/health-shards-availability/build.gradle +++ b/modules/health-shards-availability/build.gradle @@ -13,6 +13,7 @@ apply plugin: 'elasticsearch.internal-cluster-test' esplugin { description = 'Health report API extension providing the shards_availability output' classname = 'org.elasticsearch.health.plugin.ShardsAvailabilityPlugin' + deploymentTarget = 'STATEFUL_ONLY' } restResources { diff --git a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java index 7ac4f7fa11e8d..dbdb698b74236 100644 --- a/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java +++ b/server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java @@ -22,6 +22,7 @@ import org.elasticsearch.ReleaseVersions; import org.elasticsearch.action.support.SubscribableListener; import org.elasticsearch.cluster.ClusterName; +import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.common.ReferenceDocs; import org.elasticsearch.common.io.stream.InputStreamStreamInput; import org.elasticsearch.common.logging.LogConfigurator; @@ -223,8 +224,9 @@ private static void initPhase2(Bootstrap bootstrap) throws IOException { ); // load the plugin Java modules and layers now for use in entitlements - var modulesBundles = PluginsLoader.loadModulesBundles(nodeEnv.modulesDir()); - var pluginsBundles = PluginsLoader.loadPluginsBundles(nodeEnv.pluginsDir()); + boolean isStatelessMode = DiscoveryNode.isStateless(nodeEnv.settings()); + var modulesBundles = PluginsLoader.loadModulesBundles(nodeEnv.modulesDir(), isStatelessMode); + var pluginsBundles = PluginsLoader.loadPluginsBundles(nodeEnv.pluginsDir(), isStatelessMode); final PluginsLoader pluginsLoader; diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginDescriptor.java b/server/src/main/java/org/elasticsearch/plugins/PluginDescriptor.java index 7e911059b8c11..08eff623f0c99 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginDescriptor.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginDescriptor.java @@ -23,6 +23,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -43,6 +44,18 @@ public class PluginDescriptor implements Writeable, ToXContentObject { public static final String STABLE_DESCRIPTOR_FILENAME = "stable-plugin-descriptor.properties"; public static final String NAMED_COMPONENTS_FILENAME = "named_components.json"; + /** + * Deployment target describing when to load a plugin (stateful, stateless, or both). + */ + public enum DeploymentTarget { + /** Only load plugin on stateful deployments (stateless mode disabled) */ + STATEFUL_ONLY, + /** Only load plugin on stateless deployments (stateless mode enabled) */ + STATELESS_ONLY, + /** All deployment targets (default) */ + ALL + } + private final String name; private final String description; private final String version; @@ -55,6 +68,7 @@ public class PluginDescriptor implements Writeable, ToXContentObject { private final boolean isLicensed; private final boolean isModular; private final boolean isStable; + private final DeploymentTarget deploymentTarget; /** * Construct plugin info. @@ -71,6 +85,7 @@ public class PluginDescriptor implements Writeable, ToXContentObject { * @param isLicensed whether is this a licensed plugin * @param isModular whether this plugin should be loaded in a module layer * @param isStable whether this plugin is implemented using the stable plugin API + * @param deploymentTarget when to load this plugin */ public PluginDescriptor( String name, @@ -84,7 +99,8 @@ public PluginDescriptor( boolean hasNativeController, boolean isLicensed, boolean isModular, - boolean isStable + boolean isStable, + DeploymentTarget deploymentTarget ) { this.name = name; this.description = description; @@ -98,6 +114,7 @@ public PluginDescriptor( this.isLicensed = isLicensed; this.isModular = isModular; this.isStable = isStable; + this.deploymentTarget = deploymentTarget; ensureCorrectArgumentsForPluginType(); } @@ -123,6 +140,7 @@ public PluginDescriptor(final StreamInput in) throws IOException { isModular = in.readBoolean(); isStable = in.readBoolean(); + deploymentTarget = DeploymentTarget.ALL; // only read from descriptor property files, not serialized ensureCorrectArgumentsForPluginType(); } @@ -253,8 +271,42 @@ private static PluginDescriptor readerInternalDescriptor(Map pro boolean isLicensed = readBoolean(propsMap, name, "licensed"); boolean modular = module != null; - - return new PluginDescriptor(name, desc, ver, esVer, javaVer, classname, module, extended, nativeCont, isLicensed, modular, false); + DeploymentTarget deploymentTarget = readDeploymentTarget(propsMap, name); + + return new PluginDescriptor( + name, + desc, + ver, + esVer, + javaVer, + classname, + module, + extended, + nativeCont, + isLicensed, + modular, + false, + deploymentTarget + ); + } + + private static DeploymentTarget readDeploymentTarget(Map propsMap, String pluginId) { + String rawValue = propsMap.remove("deployment.target"); + if (rawValue == null) { + return DeploymentTarget.ALL; + } + try { + return DeploymentTarget.valueOf(rawValue.trim()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException( + Strings.format( + "Descriptor of plugin [%s] contains invalid deployment.target [%s], expected one of %s", + pluginId, + rawValue, + Arrays.toString(DeploymentTarget.values()) + ) + ); + } } private static PluginDescriptor readerStableDescriptor(Map propsMap, String filename) { @@ -264,8 +316,23 @@ private static PluginDescriptor readerStableDescriptor(Map props String esVer = readElasticsearchVersion(propsMap, name); String javaVer = readJavaVersion(propsMap, name); boolean isModular = readBoolean(propsMap, name, "modular"); - - return new PluginDescriptor(name, desc, ver, esVer, javaVer, null, null, List.of(), false, false, isModular, true); + DeploymentTarget deploymentTarget = readDeploymentTarget(propsMap, name); + + return new PluginDescriptor( + name, + desc, + ver, + esVer, + javaVer, + null, + null, + List.of(), + false, + false, + isModular, + true, + deploymentTarget + ); } private static String readNonEmptyString(Map propsMap, String pluginId, String name) { @@ -415,6 +482,13 @@ public boolean isStable() { return isStable; } + /** + * The deployment target of this plugin, specifically if to include the plugin in stateful and/or stateless mode. + */ + public DeploymentTarget getDeploymentTarget() { + return deploymentTarget; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -433,7 +507,6 @@ public XContentBuilder toXContentFragment(XContentBuilder builder, Params params builder.field("extended_plugins", extendedPlugins); builder.field("has_native_controller", hasNativeController); builder.field("licensed", isLicensed); - return builder; } @@ -470,6 +543,7 @@ public String toString(String prefix) { appendLine(lines, prefix, "Licensed: ", isLicensed); appendLine(lines, prefix, "Extended Plugins: ", extendedPlugins.toString()); appendLine(lines, prefix, " * Classname: ", classname); + appendLine(lines, prefix, "Deployment Target: ", deploymentTarget); return String.join(System.lineSeparator(), lines); } diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginsLoader.java b/server/src/main/java/org/elasticsearch/plugins/PluginsLoader.java index c30cc28d2f6fa..f49794f72c8f4 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginsLoader.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginsLoader.java @@ -38,6 +38,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Stream; import static org.elasticsearch.common.io.FileSystemUtils.isAccessibleDirectory; @@ -126,16 +127,17 @@ public static LayerAndLoader ofUberModuleLoader(UberModuleClassLoader loader) { private final Set pluginBundles; /** - * Loads a set of PluginBundles from the modules directory + * Loads a set of PluginBundles from the modules directory. * * @param modulesDirectory The directory modules exist in, or null if modules should not be loaded from the filesystem + * @param isStatelessMode If running in stateless mode to load the distribution accordingly. */ - public static Set loadModulesBundles(Path modulesDirectory) { + public static Set loadModulesBundles(Path modulesDirectory, boolean isStatelessMode) { // load (elasticsearch) module layers final Set modules; if (modulesDirectory != null) { try { - modules = PluginsUtils.getModuleBundles(modulesDirectory); + modules = PluginsUtils.getModuleBundles(modulesDirectory, deploymentTargetPredicate(isStatelessMode)); } catch (IOException ex) { throw new IllegalStateException("Unable to initialize modules", ex); } @@ -146,18 +148,20 @@ public static Set loadModulesBundles(Path modulesDirectory) { } /** - * Loads a set of PluginBundles from the plugins directory + * Loads a set of PluginBundles from the plugins directory. + * Bundles are filtered by {@code dynamic_bundle} type and {@code stateless.enabled}. * * @param pluginsDirectory The directory plugins exist in, or null if plugins should not be loaded from the filesystem + * @param isStatelessMode If running in stateless mode to load the distribution accordingly. */ - public static Set loadPluginsBundles(Path pluginsDirectory) { + public static Set loadPluginsBundles(Path pluginsDirectory, boolean isStatelessMode) { final Set plugins; if (pluginsDirectory != null) { try { // TODO: remove this leniency, but tests bogusly rely on it if (isAccessibleDirectory(pluginsDirectory, logger)) { PluginsUtils.checkForFailedPluginRemovals(pluginsDirectory); - plugins = PluginsUtils.getPluginBundles(pluginsDirectory); + plugins = PluginsUtils.getPluginBundles(pluginsDirectory, deploymentTargetPredicate(isStatelessMode)); } else { plugins = Collections.emptySet(); } @@ -170,6 +174,14 @@ public static Set loadPluginsBundles(Path pluginsDirectory) { return plugins; } + private static Predicate deploymentTargetPredicate(boolean isStatelessMode) { + return descriptor -> switch (descriptor.getDeploymentTarget()) { + case STATEFUL_ONLY -> isStatelessMode == false; + case STATELESS_ONLY -> isStatelessMode; + case ALL -> true; + }; + } + /** * Constructs a new PluginsLoader * diff --git a/server/src/main/java/org/elasticsearch/plugins/PluginsUtils.java b/server/src/main/java/org/elasticsearch/plugins/PluginsUtils.java index 155cff57a0ebf..921223403bbc3 100644 --- a/server/src/main/java/org/elasticsearch/plugins/PluginsUtils.java +++ b/server/src/main/java/org/elasticsearch/plugins/PluginsUtils.java @@ -14,6 +14,7 @@ import org.elasticsearch.Build; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.FileSystemUtils; +import org.elasticsearch.core.Predicates; import org.elasticsearch.jdk.JarHell; import java.io.IOException; @@ -31,6 +32,7 @@ import java.util.Map; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -210,13 +212,14 @@ public static void checkForFailedPluginRemovals(final Path pluginsDirectory) thr } /** Get bundles for plugins installed in the given modules directory. */ - public static Set getModuleBundles(Path modulesDirectory) throws IOException { - return findBundles(modulesDirectory, "module"); + public static Set getModuleBundles(Path modulesDirectory, Predicate predicate) throws IOException { + return findBundles(modulesDirectory, "module", predicate); } /** Get bundles for plugins installed in the given plugins directory. */ - public static Set getPluginBundles(final Path pluginsDirectory) throws IOException { - return findBundles(pluginsDirectory, "plugin"); + public static Set getPluginBundles(final Path pluginsDirectory, Predicate predicate) + throws IOException { + return findBundles(pluginsDirectory, "plugin", predicate); } /** @@ -226,17 +229,21 @@ public static Set getPluginBundles(final Path pluginsDirectory) th * @throws IOException if there is an error reading the plugins */ public static Map> getDependencyMapView(final Path pluginsDirectory) throws IOException { - return getPluginBundles(pluginsDirectory).stream() + return getPluginBundles(pluginsDirectory, Predicates.always()).stream() .collect(Collectors.toMap(b -> b.plugin.getName(), b -> b.plugin.getExtendedPlugins())); } // searches subdirectories under the given directory for plugin directories - private static Set findBundles(final Path directory, String type) throws IOException { + private static Set findBundles(final Path directory, String type, Predicate predicate) + throws IOException { final Set bundles = new HashSet<>(); + final List excludedBundles = new ArrayList<>(); for (final Path plugin : findPluginDirs(directory)) { final PluginBundle bundle = readPluginBundle(plugin, type); - // PluginInfo hashes on plugin name, so this will catch name clashes - if (bundles.add(bundle) == false) { + if (predicate.test(bundle.pluginDescriptor()) == false) { + excludedBundles.add(bundle.plugin.getName()); + } else if (bundles.add(bundle) == false) { + // PluginInfo hashes on plugin name, so this will catch name clashes throw new IllegalStateException("duplicate " + type + ": " + bundle.plugin); } if (type.equals("module") && bundle.plugin.getName().startsWith("test-") && Build.current().isSnapshot() == false) { @@ -245,6 +252,9 @@ private static Set findBundles(final Path directory, String type) } logger.trace(() -> "findBundles(" + type + ") returning: " + bundles.stream().map(b -> b.plugin.getName()).sorted().toList()); + if (excludedBundles.size() > 0) { + logger.info(() -> Strings.format("Excluded incompatible %s bundles: %s", type, excludedBundles.stream().sorted().toList())); + } return bundles; } @@ -285,8 +295,8 @@ public static void preInstallJarHellCheck( // create list of current jars in classpath // read existing bundles. this does some checks on the installation too. - Set bundles = new HashSet<>(getPluginBundles(pluginsDir)); - bundles.addAll(getModuleBundles(modulesDir)); + Set bundles = new HashSet<>(getPluginBundles(pluginsDir, Predicates.always())); + bundles.addAll(getModuleBundles(modulesDir, Predicates.always())); bundles.add(new PluginBundle(candidateInfo, candidateDir)); List sortedBundles = sortBundles(bundles); diff --git a/server/src/test/java/org/elasticsearch/bootstrap/ScopeResolverTests.java b/server/src/test/java/org/elasticsearch/bootstrap/ScopeResolverTests.java index afd5d79b5c300..ca1b8fd59fd5d 100644 --- a/server/src/test/java/org/elasticsearch/bootstrap/ScopeResolverTests.java +++ b/server/src/test/java/org/elasticsearch/bootstrap/ScopeResolverTests.java @@ -169,7 +169,8 @@ private static PluginBundle createMockBundle(String pluginName, String moduleNam false, false, true, - false + false, + PluginDescriptor.DeploymentTarget.ALL ); PluginBundle bundle = mock(PluginBundle.class); diff --git a/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java b/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java index 9ae3203c8348d..1e7991a772e2d 100644 --- a/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java +++ b/server/src/test/java/org/elasticsearch/nodesinfo/NodeInfoStreamingTests.java @@ -173,7 +173,8 @@ private static NodeInfo createNodeInfo() { randomBoolean(), randomBoolean(), randomBoolean(), - isStable + isStable, + PluginDescriptor.DeploymentTarget.ALL ) ); } @@ -195,7 +196,8 @@ private static NodeInfo createNodeInfo() { randomBoolean(), randomBoolean(), randomBoolean(), - isStable + isStable, + PluginDescriptor.DeploymentTarget.ALL ) ); } diff --git a/server/src/test/java/org/elasticsearch/plugins/PluginDescriptorTests.java b/server/src/test/java/org/elasticsearch/plugins/PluginDescriptorTests.java index c6eeb201f631a..c74ea7f45b667 100644 --- a/server/src/test/java/org/elasticsearch/plugins/PluginDescriptorTests.java +++ b/server/src/test/java/org/elasticsearch/plugins/PluginDescriptorTests.java @@ -212,6 +212,28 @@ public void testExtendedPluginsEmpty() throws Exception { assertThat(info.getExtendedPlugins(), empty()); } + public void testReadDeploymentTarget() throws Exception { + assertThat(mockInternalDescriptor().getDeploymentTarget(), is(PluginDescriptor.DeploymentTarget.ALL)); + assertThat( + mockInternalDescriptor("deployment.target", "STATEFUL_ONLY").getDeploymentTarget(), + is(PluginDescriptor.DeploymentTarget.STATEFUL_ONLY) + ); + assertThat( + mockInternalDescriptor("deployment.target", "STATELESS_ONLY").getDeploymentTarget(), + is(PluginDescriptor.DeploymentTarget.STATELESS_ONLY) + ); + assertThat(mockInternalDescriptor("deployment.target", "ALL").getDeploymentTarget(), is(PluginDescriptor.DeploymentTarget.ALL)); + } + + public void testReadDeploymentTargetInvalid() throws Exception { + IllegalArgumentException e = expectThrows( + IllegalArgumentException.class, + () -> mockInternalDescriptor("deployment.target", "INVALID") + ); + assertThat(e.getMessage(), containsString("invalid deployment.target [INVALID]")); + assertThat(e.getMessage(), containsString("expected one of [STATEFUL_ONLY, STATELESS_ONLY, ALL]")); + } + public void testIsModular() throws Exception { PluginDescriptor info = mockStableDescriptor("modular", "false"); assertThat(info.isModular(), is(false)); @@ -230,7 +252,8 @@ public void testSerialize() throws Exception { randomBoolean(), randomBoolean(), randomBoolean(), - false + false, + PluginDescriptor.DeploymentTarget.ALL ); BytesStreamOutput output = new BytesStreamOutput(); info.writeTo(output); @@ -253,7 +276,8 @@ public void testSerializeWithModuleName() throws Exception { randomBoolean(), randomBoolean(), randomBoolean(), - false + false, + PluginDescriptor.DeploymentTarget.ALL ); BytesStreamOutput output = new BytesStreamOutput(); info.writeTo(output); @@ -286,7 +310,8 @@ PluginDescriptor newMockDescriptor(String name) { randomBoolean(), randomBoolean(), randomBoolean(), - false + false, + PluginDescriptor.DeploymentTarget.ALL ); } @@ -312,7 +337,7 @@ public void testUnknownProperties() throws Exception { } /** - * This is important because {@link PluginsUtils#getPluginBundles(Path)} will + * This is important because {@link PluginsUtils#getPluginBundles(Path, java.util.function.Predicate)} will * use the hashcode to catch duplicate names */ public void testPluginEqualityAndHash() { @@ -330,7 +355,8 @@ public void testPluginEqualityAndHash() { randomBoolean(), randomBoolean(), randomBoolean(), - isStable + isStable, + PluginDescriptor.DeploymentTarget.ALL ); // everything but name is different from descriptor1 PluginDescriptor descriptor2 = new PluginDescriptor( @@ -347,7 +373,8 @@ public void testPluginEqualityAndHash() { descriptor1.hasNativeController() == false, descriptor1.isLicensed() == false, descriptor1.isModular() == false, - descriptor1.isStable() == false + descriptor1.isStable() == false, + PluginDescriptor.DeploymentTarget.ALL ); // only name is different from descriptor1 PluginDescriptor descriptor3 = new PluginDescriptor( @@ -362,7 +389,8 @@ public void testPluginEqualityAndHash() { descriptor1.hasNativeController(), descriptor1.isLicensed(), descriptor1.isModular(), - descriptor1.isStable() + descriptor1.isStable(), + PluginDescriptor.DeploymentTarget.ALL ); assertThat(descriptor1, equalTo(descriptor2)); diff --git a/server/src/test/java/org/elasticsearch/plugins/PluginsLoaderTests.java b/server/src/test/java/org/elasticsearch/plugins/PluginsLoaderTests.java index efc3c069b4ab3..36614820a6cf1 100644 --- a/server/src/test/java/org/elasticsearch/plugins/PluginsLoaderTests.java +++ b/server/src/test/java/org/elasticsearch/plugins/PluginsLoaderTests.java @@ -50,7 +50,7 @@ public class PluginsLoaderTests extends ESTestCase { static PluginsLoader newPluginsLoader(Settings settings) { return PluginsLoader.createPluginsLoader( Set.of(), - PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir()), + PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir(), false), Map.of(), false ); @@ -119,7 +119,7 @@ public void testStablePluginWithNativeAccess() throws Exception { var pluginsLoader = PluginsLoader.createPluginsLoader( Set.of(), - PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir()), + PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir(), false), Map.of(STABLE_PLUGIN_NAME, Set.of(STABLE_PLUGIN_MODULE_NAME)), false ); @@ -180,7 +180,7 @@ public void testModularPluginLoadingWithNativeAccess() throws Exception { var pluginsLoader = PluginsLoader.createPluginsLoader( Set.of(), - PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir()), + PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir(), false), Map.of(MODULAR_PLUGIN_NAME, Set.of(MODULAR_PLUGIN_MODULE_NAME)), false ); diff --git a/server/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java b/server/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java index d18b7f52b8d08..249639e3338d2 100644 --- a/server/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java +++ b/server/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java @@ -68,7 +68,7 @@ static PluginsService newPluginsService(Settings settings) { null, PluginsLoader.createPluginsLoader( Set.of(), - PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir()), + PluginsLoader.loadPluginsBundles(TestEnvironment.newEnvironment(settings).pluginsDir(), false), Map.of(), false ) @@ -466,7 +466,21 @@ public void testExtensiblePlugin() { PluginsService.loadExtensions( List.of( new PluginsService.LoadedPlugin( - new PluginDescriptor("extensible", null, null, null, null, classname, null, List.of(), false, false, false, false), + new PluginDescriptor( + "extensible", + null, + null, + null, + null, + classname, + null, + List.of(), + false, + false, + false, + false, + PluginDescriptor.DeploymentTarget.ALL + ), extensiblePlugin, null ) @@ -481,7 +495,21 @@ public void testExtensiblePlugin() { PluginsService.loadExtensions( List.of( new PluginsService.LoadedPlugin( - new PluginDescriptor("extensible", null, null, null, null, classname, null, List.of(), false, false, false, false), + new PluginDescriptor( + "extensible", + null, + null, + null, + null, + classname, + null, + List.of(), + false, + false, + false, + false, + PluginDescriptor.DeploymentTarget.ALL + ), extensiblePlugin, null ), @@ -498,7 +526,8 @@ public void testExtensiblePlugin() { false, false, false, - false + false, + PluginDescriptor.DeploymentTarget.ALL ), testPlugin, null diff --git a/server/src/test/java/org/elasticsearch/plugins/PluginsUtilsTests.java b/server/src/test/java/org/elasticsearch/plugins/PluginsUtilsTests.java index 932f7d7b31e45..5b9264486d097 100644 --- a/server/src/test/java/org/elasticsearch/plugins/PluginsUtilsTests.java +++ b/server/src/test/java/org/elasticsearch/plugins/PluginsUtilsTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.Build; import org.elasticsearch.Version; import org.elasticsearch.core.PathUtils; +import org.elasticsearch.core.Predicates; import org.elasticsearch.jdk.JarHell; import org.elasticsearch.test.ESTestCase; import org.hamcrest.Matchers; @@ -53,14 +54,18 @@ PluginDescriptor newTestDescriptor(String name, List deps) { false, false, false, - false + false, + PluginDescriptor.DeploymentTarget.ALL ); } public void testExistingPluginMissingDescriptor() throws Exception { Path pluginsDir = createTempDir(); Files.createDirectory(pluginsDir.resolve("plugin-missing-descriptor")); - IllegalStateException e = expectThrows(IllegalStateException.class, () -> PluginsUtils.getPluginBundles(pluginsDir)); + IllegalStateException e = expectThrows( + IllegalStateException.class, + () -> PluginsUtils.getPluginBundles(pluginsDir, Predicates.always()) + ); assertThat(e.getMessage(), containsString("Plugin [plugin-missing-descriptor] is missing a descriptor properties file")); } @@ -465,7 +470,8 @@ private static PluginDescriptor getPluginDescriptorForVersion(String id, String false, false, false, - isStable + isStable, + PluginDescriptor.DeploymentTarget.ALL ); return info; } diff --git a/test/framework/src/main/java/org/elasticsearch/plugins/MockPluginsService.java b/test/framework/src/main/java/org/elasticsearch/plugins/MockPluginsService.java index 9fa124e637848..ccab0005ab663 100644 --- a/test/framework/src/main/java/org/elasticsearch/plugins/MockPluginsService.java +++ b/test/framework/src/main/java/org/elasticsearch/plugins/MockPluginsService.java @@ -60,7 +60,8 @@ public MockPluginsService(Settings settings, Environment environment, Collection false, false, false, - false + false, + PluginDescriptor.DeploymentTarget.ALL ); if (logger.isTraceEnabled()) { logger.trace("plugin loaded from classpath [{}]", pluginInfo); diff --git a/x-pack/plugin/ilm/build.gradle b/x-pack/plugin/ilm/build.gradle index d7dd454578cfc..359dafcdf18d6 100644 --- a/x-pack/plugin/ilm/build.gradle +++ b/x-pack/plugin/ilm/build.gradle @@ -6,10 +6,11 @@ apply plugin: 'elasticsearch.internal-java-rest-test' esplugin { name = 'x-pack-ilm' description = 'Elasticsearch Expanded Pack Plugin - Index Lifecycle Management' - classname ='org.elasticsearch.xpack.ilm.IndexLifecycle' + classname = 'org.elasticsearch.xpack.ilm.IndexLifecycle' extendedPlugins = ['x-pack-core'] - hasNativeController =false - requiresKeystore =true + hasNativeController = false + requiresKeystore = true + deploymentTarget = 'STATEFUL_ONLY' } base { archivesName = 'x-pack-ilm' diff --git a/x-pack/plugin/monitoring/build.gradle b/x-pack/plugin/monitoring/build.gradle index c10f33d936530..3730f54fb7436 100644 --- a/x-pack/plugin/monitoring/build.gradle +++ b/x-pack/plugin/monitoring/build.gradle @@ -7,6 +7,7 @@ esplugin { description = 'Elasticsearch Expanded Pack Plugin - Monitoring' classname ='org.elasticsearch.xpack.monitoring.Monitoring' extendedPlugins = ['x-pack-core'] + deploymentTarget = 'STATEFUL_ONLY' } base { archivesName = 'x-pack-monitoring' diff --git a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java index b759d0b5ff62c..4ebc10668fbf9 100644 --- a/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java +++ b/x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java @@ -347,7 +347,8 @@ public void testToXContent() throws IOException { false, false, false, - false + false, + PluginDescriptor.DeploymentTarget.ALL ); final PluginRuntimeInfo pluginRuntimeInfo = new PluginRuntimeInfo(pluginDescriptor); when(mockPluginsAndModules.getPluginInfos()).thenReturn(List.of(pluginRuntimeInfo)); diff --git a/x-pack/plugin/rollup/build.gradle b/x-pack/plugin/rollup/build.gradle index 83fd08cb18f96..b4148416ef2a8 100644 --- a/x-pack/plugin/rollup/build.gradle +++ b/x-pack/plugin/rollup/build.gradle @@ -4,6 +4,7 @@ esplugin { description = 'Elasticsearch Expanded Pack Plugin - Rollup' classname ='org.elasticsearch.xpack.rollup.Rollup' extendedPlugins = ['x-pack-core'] + deploymentTarget = 'STATEFUL_ONLY' } base { diff --git a/x-pack/plugin/searchable-snapshots/build.gradle b/x-pack/plugin/searchable-snapshots/build.gradle index 571ee3c1101cb..96ebc2834a205 100644 --- a/x-pack/plugin/searchable-snapshots/build.gradle +++ b/x-pack/plugin/searchable-snapshots/build.gradle @@ -7,6 +7,7 @@ esplugin { description = 'A plugin for the searchable snapshots functionality' classname = 'org.elasticsearch.xpack.searchablesnapshots.SearchableSnapshots' extendedPlugins = ['x-pack-core', 'blob-cache'] + deploymentTarget = 'STATEFUL_ONLY' } base { archivesName = 'x-pack-searchable-snapshots' diff --git a/x-pack/plugin/shutdown/build.gradle b/x-pack/plugin/shutdown/build.gradle index df1a7575cdd05..d9cc21bb59493 100644 --- a/x-pack/plugin/shutdown/build.gradle +++ b/x-pack/plugin/shutdown/build.gradle @@ -8,6 +8,7 @@ esplugin { description = 'Elasticsearch Expanded Pack Plugin - Shutdown' classname ='org.elasticsearch.xpack.shutdown.ShutdownPlugin' extendedPlugins = ['x-pack-core'] + deploymentTarget = 'STATEFUL_ONLY' } base { archivesName = 'x-pack-shutdown' diff --git a/x-pack/plugin/snapshot-based-recoveries/build.gradle b/x-pack/plugin/snapshot-based-recoveries/build.gradle index d11f78a13f95d..80a4d2a165de9 100644 --- a/x-pack/plugin/snapshot-based-recoveries/build.gradle +++ b/x-pack/plugin/snapshot-based-recoveries/build.gradle @@ -7,6 +7,7 @@ esplugin { description = 'A plugin that enables snapshot based recoveries' classname ='org.elasticsearch.xpack.snapshotbasedrecoveries.SnapshotBasedRecoveriesPlugin' extendedPlugins = ['x-pack-core'] + deploymentTarget = 'STATEFUL_ONLY' } base { archivesName = 'x-pack-snapshot-based-recoveries' diff --git a/x-pack/plugin/voting-only-node/build.gradle b/x-pack/plugin/voting-only-node/build.gradle index 1202f90977da7..92fdd26e88da5 100644 --- a/x-pack/plugin/voting-only-node/build.gradle +++ b/x-pack/plugin/voting-only-node/build.gradle @@ -5,6 +5,7 @@ esplugin { description = 'Elasticsearch Expanded Pack Plugin - Voting-only node' classname ='org.elasticsearch.cluster.coordination.votingonly.VotingOnlyNodePlugin' extendedPlugins = ['x-pack-core'] + deploymentTarget = 'STATEFUL_ONLY' } dependencies { diff --git a/x-pack/plugin/watcher/build.gradle b/x-pack/plugin/watcher/build.gradle index a4ea624b9553d..52f101ecfb920 100644 --- a/x-pack/plugin/watcher/build.gradle +++ b/x-pack/plugin/watcher/build.gradle @@ -11,6 +11,7 @@ esplugin { hasNativeController = false requiresKeystore = false extendedPlugins = ['x-pack-core', 'lang-painless'] + deploymentTarget = 'STATEFUL_ONLY' } base { diff --git a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherScheduleEngineBenchmark.java b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherScheduleEngineBenchmark.java index 093959978b0d1..936f672f373ae 100644 --- a/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherScheduleEngineBenchmark.java +++ b/x-pack/plugin/watcher/src/test/java/org/elasticsearch/xpack/watcher/test/bench/WatcherScheduleEngineBenchmark.java @@ -112,8 +112,8 @@ public static void main(String[] args) throws Exception { Node node = new Node( internalNodeEnv, PluginsLoader.createPluginsLoader( - PluginsLoader.loadModulesBundles(internalNodeEnv.modulesDir()), - PluginsLoader.loadPluginsBundles(internalNodeEnv.pluginsDir()), + PluginsLoader.loadModulesBundles(internalNodeEnv.modulesDir(), false), + PluginsLoader.loadPluginsBundles(internalNodeEnv.pluginsDir(), false), Map.of() ) ).start()