-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Split plugin loading into two different phases to support entitlements #116998
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
Conversation
Pinging @elastic/es-core-infra (Team:Core/Infra) |
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.
Looks good! A couple of (optional) notes.
I've tried to follow the movement of code from PluginsService to PluginsLoader, and it seems pretty much matching.
server/src/main/java/org/elasticsearch/plugins/PluginsLoader.java
Outdated
Show resolved
Hide resolved
server/src/main/java/org/elasticsearch/plugins/PluginsService.java
Outdated
Show resolved
Hide resolved
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, a few suggestions but most of them are subjective and could be followups if we decide to do them.
null, | ||
null, | ||
Path.of(System.getProperty("plugins.dir")) | ||
new PluginsLoader(null, Path.of(System.getProperty("plugins.dir"))) |
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: Could this pass an empty directory for module dir? Then we could enforce non-null paths. We needed to support null before because the mock plugins service needed to call its super, but I think we could get around that with the mock loader.
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.
Let's do this as a follow up PR as part of adding MockPluginsLoader
potentially.
server/src/test/java/org/elasticsearch/plugins/PluginsServiceTests.java
Outdated
Show resolved
Hide resolved
*/ | ||
public MockPluginsService(Settings settings, Environment environment, Collection<Class<? extends Plugin>> classpathPlugins) { | ||
super(settings, environment.configFile(), environment.modulesFile(), environment.pluginsFile()); | ||
super(settings, environment.configFile(), new PluginsLoader(environment.modulesFile(), environment.pluginsFile()) { |
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.
Perhaps we should have a MockPluginsLoader? This anonymous class is difficult to read within a super call
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.
Another idea I had in my POC: what if we remove the protected method, and we pass down a ServerExportsService
(or better name!) to the ctor?
Something like
@FunctionalInterface
public interface ServerExportsService {
void add(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports);
}
And then have
public static void noServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {}
public static void defaultServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {
// current addServerExportsService implementation
}
The ctor called from prod code will pass down PluginsLoader::defaultServerExportsService
, but test can call a full ctor passing explicitly PluginsLoader::noServerExportsService
:
PluginsLoader(/* same args as today */) { // Called by Elasticsearch.java
this(..., PluginsLoader::defaultServerExportsService)
}
PluginsLoader(/* same args as today */, ServerExportsService additionalServerExports) {
this.additionalServerExports = additionalServerExports; // and then call additionalServerExports.add(...);
}
Test will call
new PluginsLoader(..., PluginsLoader::noServerExportService)
(or whatever they want)
(this will also remove the need to suppress for this-escape)
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.
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.
If you both like my idea, I think it should go in a follow-up PR anyway :)
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.
On second thought, with multiple different ideas here, let's get together to discuss this as a follow up PR.
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.
Yup, totally agree!
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.
🤔 FWIW I'm not seeing the problem. The code looks pretty clear to me.
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.
The problem is (1) having an anonymous subclass with an overridden method inside a super call packs a lot into that super call. Additionally (2) the overridden implementation is the same in both places (makes the method a no-op).
But we can work this out in a followup.
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 agree the anonymous class should go. It's confusing, but I'm wondering if we should also add in @ldematte's idea to replace the exports method. It does seem clearer to me on the surface. The main downside I see to some of these changes it pushes test-only code into PluginsLoader
.
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.
LGTM2; the optional suggestions still stand, but can go in another PR (or even discarded)
*/ | ||
public MockPluginsService(Settings settings, Environment environment, Collection<Class<? extends Plugin>> classpathPlugins) { | ||
super(settings, environment.configFile(), environment.modulesFile(), environment.pluginsFile()); | ||
super(settings, environment.configFile(), new PluginsLoader(environment.modulesFile(), environment.pluginsFile()) { |
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.
Another idea I had in my POC: what if we remove the protected method, and we pass down a ServerExportsService
(or better name!) to the ctor?
Something like
@FunctionalInterface
public interface ServerExportsService {
void add(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports);
}
And then have
public static void noServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {}
public static void defaultServerExportsService(Map<String, List<ModuleQualifiedExportsService>> qualifiedExports) {
// current addServerExportsService implementation
}
The ctor called from prod code will pass down PluginsLoader::defaultServerExportsService
, but test can call a full ctor passing explicitly PluginsLoader::noServerExportsService
:
PluginsLoader(/* same args as today */) { // Called by Elasticsearch.java
this(..., PluginsLoader::defaultServerExportsService)
}
PluginsLoader(/* same args as today */, ServerExportsService additionalServerExports) {
this.additionalServerExports = additionalServerExports; // and then call additionalServerExports.add(...);
}
Test will call
new PluginsLoader(..., PluginsLoader::noServerExportService)
(or whatever they want)
(this will also remove the need to suppress for this-escape)
server/src/main/java/org/elasticsearch/plugins/PluginsService.java
Outdated
Show resolved
Hide resolved
Definitely, thanks for the changes, looks very clean! Merge at will, for me. |
elastic#116998) This change loads all the modules and creates the module layers for plugins prior to entitlement checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist for both validation and checking prior to actually loading any plugin classes (in a follow up change). There are now two classes: PluginsLoader which does the module loading and layer creation PluginsService which uses a PluginsLoader to create the main plugin classes and start the plugins
#116998) (#117215) This change loads all the modules and creates the module layers for plugins prior to entitlement checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist for both validation and checking prior to actually loading any plugin classes (in a follow up change). There are now two classes: PluginsLoader which does the module loading and layer creation PluginsService which uses a PluginsLoader to create the main plugin classes and start the plugins
elastic#116998) This change loads all the modules and creates the module layers for plugins prior to entitlement checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist for both validation and checking prior to actually loading any plugin classes (in a follow up change). There are now two classes: PluginsLoader which does the module loading and layer creation PluginsService which uses a PluginsLoader to create the main plugin classes and start the plugins
This change loads all the modules and creates the module layers for plugins prior to entitlement checking during the 2nd phase of bootstrap initialization. This will allow us to know what modules exist for both validation and checking prior to actually loading any plugin classes (in a follow up change).
There are now two classes:
PluginsLoader
which does the module loading and layer creationPluginsService
which uses aPluginsLoader
to create the main plugin classes and start the plugins