-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plugins: Add plugin extension capabilities #27881
Conversation
This commit adds the infrastructure to plugin building and loading to allow one plugin to extend another. That is, one plugin may extend another by the "parent" plugin allowing itself to be extended through java SPI. When all plugins extending a plugin are finished loading, the "parent" plugin has a callback (through the ExtensiblePlugin interface) allowing it to reload SPI. This commit also adds an example plugin which uses as-yet implemented extensibility (adding to the painless whitelist).
Note this PR currently has a couple nocommits for testing and moving the createClassLoader permission out to a separate jar (we don't actually want to add this back to core). But I wanted to get this PR up so everything else can be checked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some commetns. LGTM in general
@@ -34,7 +35,7 @@ | |||
/** | |||
* Registers Painless as a plugin. | |||
*/ | |||
public final class PainlessPlugin extends Plugin implements ScriptPlugin { | |||
public final class PainlessPlugin extends Plugin implements ScriptPlugin, ExtensiblePlugin { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this implement reloadSPI
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Painless doesn't have any SPI interfaces yet. This is just the scaffolding to add it (as an example of how SPI works in general for this PR).
|
||
import org.elasticsearch.plugins.Plugin; | ||
|
||
public class MyWhitelistPlugin extends Plugin { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm it's unclear to me what this extends can we have a real extension here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have any actual SPI to extend yet. This will be filled in when painless whitelisting is finished.
private static class ExtendedPluginsClassLoader extends ClassLoader { | ||
|
||
/** Loaders of plugins extended by a plugin. */ | ||
private List<ClassLoader> extendedLoaders; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final?
@@ -82,6 +88,11 @@ public PluginInfo(final StreamInput in) throws IOException { | |||
this.description = in.readString(); | |||
this.version = in.readString(); | |||
this.classname = in.readString(); | |||
if (in.getVersion().onOrAfter(Version.V_6_1_0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be 6.2 right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ooops, yes. Old branch!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to be updated to 6.2.
@@ -100,6 +111,9 @@ public void writeTo(final StreamOutput out) throws IOException { | |||
out.writeString(description); | |||
out.writeString(version); | |||
out.writeString(classname); | |||
if (out.getVersion().onOrAfter(Version.V_6_1_0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be 6.2
@@ -131,4 +131,7 @@ grant { | |||
permission java.io.FilePermission "/sys/fs/cgroup/cpuacct/-", "read"; | |||
permission java.io.FilePermission "/sys/fs/cgroup/memory", "read"; | |||
permission java.io.FilePermission "/sys/fs/cgroup/memory/-", "read"; | |||
|
|||
// nocommit: needed by plugin service for extensible plugins, move to a separate jar |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we want to add a elastic-commons jar to do that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After discussing with @jasontedor this is my plan (although a jar on its own with the extension class loader, not in a general commons jar, since I want to reduce what code is allowed the extra permission to the bare minimum).
BootstrapForTesting to fill in libs codebases when testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good. I left a few comments, nothing major.
@@ -265,6 +291,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws | |||
builder.field("version", version); | |||
builder.field("description", description); | |||
builder.field("classname", classname); | |||
builder.array("extended_plugins", extendsPlugins); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: For consistency, should all the extends plugins
be extendedPlugins
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went back and forth on the naming, but at this point I'm fine with that. I'll change them all.
@@ -47,6 +47,7 @@ public void testReadFromProperties() throws Exception { | |||
assertEquals("fake desc", info.getDescription()); | |||
assertEquals("1.0", info.getVersion()); | |||
assertEquals("FakePlugin", info.getClassname()); | |||
assertTrue(info.getExtendsPlugins().isEmpty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer:
diff --git a/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java b/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java
index 2a96538e83..a188f3c765 100644
--- a/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java
+++ b/core/src/test/java/org/elasticsearch/plugins/PluginInfoTests.java
@@ -30,6 +30,7 @@ import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
public class PluginInfoTests extends ESTestCase {
@@ -47,7 +48,7 @@ public class PluginInfoTests extends ESTestCase {
assertEquals("fake desc", info.getDescription());
assertEquals("1.0", info.getVersion());
assertEquals("FakePlugin", info.getClassname());
- assertTrue(info.getExtendsPlugins().isEmpty());
+ assertThat(info.getExtendsPlugins(), empty());
}
public void testReadFromPropertiesNameMissing() throws Exception {
because it gives better output if the assertion fails.
"classname", "FakePlugin", | ||
"extends.plugins", "foo"); | ||
PluginInfo info = PluginInfo.readFromProperties(pluginDir); | ||
assertThat(info.getExtendsPlugins(), contains("foo")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can assert stronger here, that it is equal to the singleton list "foo"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contains
does an exact match. From the javadocs:
For a positive match, the examined iterable must be of the same length as the number of specified items.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forget this every time because it's so horribly named. Thanks.
"classname", "FakePlugin", | ||
"extends.plugins", "foo,bar,baz"); | ||
PluginInfo info = PluginInfo.readFromProperties(pluginDir); | ||
assertThat(info.getExtendsPlugins(), contains("foo", "bar", "baz")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can assert stronger here, that it is equal to in any order the list "foo", "bar", "baz" (there is a matcher for this)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. I think expecting the exact order is fine too, since getExtendedPlugins returns a List.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's poorly named and it traps me every time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as before, this is already an exact match.
"classname", "FakePlugin", | ||
"extends.plugins", ""); | ||
PluginInfo info = PluginInfo.readFromProperties(pluginDir); | ||
assertTrue(info.getExtendsPlugins().isEmpty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use the empty collections matchers here?
@@ -82,6 +88,11 @@ public PluginInfo(final StreamInput in) throws IOException { | |||
this.description = in.readString(); | |||
this.version = in.readString(); | |||
this.classname = in.readString(); | |||
if (in.getVersion().onOrAfter(Version.V_6_1_0)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still needs to be updated to 6.2.
throw new IllegalStateException("Already added " + name + " codebase for testing"); | ||
} | ||
} catch (ClassNotFoundException e) { | ||
// no class, fall through to not add |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When can this happen that it's okay to continue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would happen in any tests other than core, since we only add the plugin-classloader jar to the core tests runtime classpath. I'll add a comment.
@jasontedor @s1monw I believe I have addressed all your comments and all the nocommits are now gone. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
@elasticmachine test this please |
This commit disables the nio transport as an option for the test transport in integration tests. This is because it does not currently run properly in intellij due to socket permissions. It should be reenabled once elastic#27881 is merged (and the proper permissions are added).
This commit disables the nio transport as an option for the test transport in integration tests. This is because it does not currently run properly in intellij due to socket permissions. It should be reenabled once #27881 is merged (and the proper permissions are added).
This commit adds the infrastructure to plugin building and loading to allow one plugin to extend another. That is, one plugin may extend another by the "parent" plugin allowing itself to be extended through java SPI. When all plugins extending a plugin are finished loading, the "parent" plugin has a callback (through the ExtensiblePlugin interface) allowing it to reload SPI. This commit also adds an example plugin which uses as-yet implemented extensibility (adding to the painless whitelist).
* es/master: (53 commits) Bump compat version for local depdendent test to 6.2.0 Pass `java.locale.providers=COMPAT` to Java 9 onwards (#28080) Allow shrinking of indices from a previous major (#28076) Remove deprecated exceptions (#28059) Add Writeable.Reader support to TransportResponseHandler (#28010) Plugins: Add plugin extension capabilities (#27881) Fix cluster.routing.allocation.enable and cluster.routing.rebalance.enable casing (#28037) [Test] Fix scores for dcg in RankEvalRequestIT and RankEvalYamlIT [Docs] Add note on limitation for significant_text with nested objects (#28052) Enable convert processor to support Long and Double. (#27957) Enable Wildfly tests on JDK 9 and JDK 10 [Test] Fix allowed delta for calculated scores in DiscountedCumulativeGainTests [Test] Mute DiscountedCumulativeGainTests on ARM Only bind loopback addresses when binding to local Fix assertion in Wildfly build Fix typo in comment in Wildfly build Use ephemeral ports in Wildfly tests Update fuzzy-query.asciidoc (#28032) Just another elasticsearch library (#27996) Disable nio test transport (#28028) ...
* master: Set the elasticsearch-nio codebase for tests (#28067) Bump compat version for local depdendent test to 6.2.0 Pass `java.locale.providers=COMPAT` to Java 9 onwards (#28080) Allow shrinking of indices from a previous major (#28076) Remove deprecated exceptions (#28059) Add Writeable.Reader support to TransportResponseHandler (#28010) Plugins: Add plugin extension capabilities (#27881)
* master: test: replaced try-catch statements with expectThrows(...) Add getWarmer and getTranslog method to NodeIndicesStats (elastic#28092) fix doc mistake Added ASN support for Ingest GeoIP plugin. Fix global aggregation that requires breadth first and scores (elastic#27942) Introduce Gradle wrapper Ignore GIT_COMMIT when calculating commit hash Re-enable bwc tests after elastic#27881 was backported
* master: (25 commits) Remove Gradle cheatsheet Fix reproduction info to point to Gradle wrapper Update platforms tests to use Gradle wrapper Update testing docs to reflect Gradle wrapper Painless: Modify Loader to Load Classes Directly from Definition (elastic#28088) Update contributing docs to use the Gradle wrapper Create nio-transport plugin for NioTransport (elastic#27949) test: replaced try-catch statements with expectThrows(...) Add getWarmer and getTranslog method to NodeIndicesStats (elastic#28092) fix doc mistake Added ASN support for Ingest GeoIP plugin. Fix global aggregation that requires breadth first and scores (elastic#27942) Introduce Gradle wrapper Ignore GIT_COMMIT when calculating commit hash Re-enable bwc tests after elastic#27881 was backported Set the elasticsearch-nio codebase for tests (elastic#28067) Bump compat version for local depdendent test to 6.2.0 Pass `java.locale.providers=COMPAT` to Java 9 onwards (elastic#28080) Allow shrinking of indices from a previous major (elastic#28076) Remove deprecated exceptions (elastic#28059) ...
Hi guys, is there a way to load
And I keep on getting:
|
@sscarduzio Please ask questions on our forum. A comment on a closed PR is likely to be missed. And more information would be helpful (eg what you are running which results in the missing class). I had intended this to not be published as it is an implementation detail of how plugins are loaded, but I am happy to investigate if that won't work for plugin authors. |
This commit adds the infrastructure to plugin building and loading to
allow one plugin to extend another. That is, one plugin may extend
another by the "parent" plugin allowing itself to be extended through
java SPI. When all plugins extending a plugin are finished loading, the
"parent" plugin has a callback (through the ExtensiblePlugin interface)
allowing it to reload SPI.
This commit also adds an example plugin which uses as-yet implemented
extensibility (adding to the painless whitelist).