Skip to content
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

Add the ability to bundle multiple plugins into a meta plugin #28022

Merged
merged 17 commits into from
Jan 9, 2018

Conversation

jimczi
Copy link
Contributor

@jimczi jimczi commented Dec 29, 2017

This commit adds the ability to package multiple plugins in a single zip.
The zip file for a meta plugin must contains the following structure:

|____elasticsearch/
| |____   <plugin1> <-- The plugin files for plugin1 (the content of the elastisearch directory)
| |____   <plugin2>  <-- The plugin files for plugin2
| |____   meta-plugin-descriptor.properties <-- example contents below

The meta plugin properties descriptor is mandatory and must contain the following properties:

  • description: simple summary of the meta plugin.
  • name: the meta plugin name

The installation process installs each plugin in a sub-folder inside the meta plugin directory.
The example above would create the following structure in the plugins directory:

|_____ plugins
| |____   <name_of_the_meta_plugin>
| | |____   meta-plugin-descriptor.properties
| | |____   <plugin1>
| | |____   <plugin2>

If the sub plugins contain a config or a bin directory, they are copied in a sub folder inside the meta plugin config/bin directory.

|_____ config
| |____   <name_of_the_meta_plugin>
| | |____   <plugin1>
| | |____   <plugin2>

|_____ bin
| |____   <name_of_the_meta_plugin>
| | |____   <plugin1>
| | |____   <plugin2>

The sub-plugins are loaded at startup like normal plugins with the same restrictions; they have a separate class loader and a sub-plugin
cannot have the same name than another plugin (or a sub-plugin inside another meta plugin).

It is also not possible to remove a sub-plugin inside a meta plugin, only full removal of the meta plugin is allowed.

Closes #27316

This commit adds the ability to package multiple plugins in a single zip.
The zip file for an uber plugin must contains the following structure:

```
|____elasticsearch/
| |____   <plugin1> <-- The plugin files for plugin1 (the content of the elastisearch directory)
| |____   <plugin2>  <-- The plugin files for plugin2
| |____   uber-plugin-descriptor.properties <-- example contents below
```

The uber-plugin properties descriptor is mandatory and must contain the following properties:
 * `description`: simple summary of the uber plugin.
 *  `name`: the uber plugin name
 * `plugins`: a coma separated list of the sub-plugin names bundled in the uber plugin.

The installation process installs each sub-plugin in a sub-folder inside the uber plugin directory.
The example above would create the following structure in the plugins directory:

```
|_____ plugins
| |____   <name_of_the_uber_plugin>
| | |____   uber-plugin-descriptor.properties
| | |____   <plugin1>
| | |____   <plugin2>
```

If the sub plugins contain a config or a bin directory, they are copied in a sub folder inside the uber plugin config/bin directory.

```
|_____ config
| |____   <name_of_the_uber_plugin>
| | |____   uber-plugin-descriptor.properties
| | |____   <plugin1>
| | |____   <plugin2>

|_____ bin
| |____   <name_of_the_uber_plugin>
| | |____   uber-plugin-descriptor.properties
| | |____   <plugin1>
| | |____   <plugin2>
```

The sub-plugins are loaded at startup like normal plugins with the same restrictions; they have a separate class loader and a sub-plugin
cannot have the same name than another plugin (or a sub-plugin inside another uber plugin).

It is also not possible to remove a sub-plugin inside an uber plugin, only full removal of the uber plugin is allowed.

Closes elastic#27316
@jimczi jimczi requested a review from rjernst December 29, 2017 08:49
Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some questions. The biggest request is to rename "uber" to "meta".

@@ -0,0 +1,25 @@
# Elasticsearch uber plugin descriptor file
# This file must exist as 'uber-plugin-descriptor.properties' in a folder named `elasticsearch`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think better name than "uber" would be "meta" (as in meta physical, it isn't really a plugin)

# 'name': the uber plugin name
name=${name}
#
# 'plugins': a coma separated list of the sub-plugin names bundled in the uber plugin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: coma -> comma

Also, I would not call these "sub" plugins (that has a connotation of subclassing), but instead "bundled" plugins?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But is this plugins list actually needed? The loading doesn't use it (and shouldn't, no leniency for extra junk at the root). I don't see it used anywhere but in tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I removed it.

}

/** check a candidate plugin for jar hell before installing it */
void jarHellCheck(Path candidate, Path pluginsDir) throws Exception {
void jarHellCheck(PluginInfo candidateInfo, Path candidate, Path pluginsDir) throws Exception {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you please rename candidate to candidateDir to be parallel with candidateInfo?

// read existing bundles excluding the current candidate.
// this does some checks on the installation too.
Path excludePath = candidateInfo.getUberPlugin() != null ? candidate.getParent() : candidate;
PluginsService.getPluginBundles(pluginsDir, Collections.singleton(excludePath.getFileName().toString()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this exclusion should be here once this is synced with SPI work. We need all the bundles in order to do a jarhell check.

terminal.println("Elasticsearch keystore is required by plugin [" + info.getName() + "], creating...");
keystore = KeyStoreWrapper.create(new char[0]);
keystore.save(env.configFile());
if (UberPluginInfo.isUberPlugin(tmpRoot)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does installation need anything meta plugin specific? It should just copy all the contents of the elasticsearch directory right, just like a normal plugin right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to know if we are inside a meta plugin in order to validate each plugin separately.

"uber_plugin:fake_plugin1",
"- Plugin information:",
"Uber Plugin: uber_plugin",
"Name: fake_plugin1",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have better grouping to show these are part of the meta plugin in the output. Perhaps printing the plugin info should be indented a few spaces under a meta plugin?

"Plugins directory: " + env.pluginsFile(),
"uber_plugin:fake_plugin1",
"- Plugin information:",
"Uber Plugin: uber_plugin",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems silly to print out the meta plugin this is part of, given it is already grouped under the the meta plugin.

boolean hasNativeController, boolean requiresKeystore) {
this.uberPlugin = uberPlugin;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this link back to the meta plugin needed for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep track of the meta plugin name when the node loads the plugin but it's not needed. I removed it from the pr.

@jimczi jimczi changed the title Add the ability to bundle multiple plugins into an uber plugin Add the ability to bundle multiple plugins into a meta plugin Jan 7, 2018
@jimczi
Copy link
Contributor Author

jimczi commented Jan 7, 2018

Thanks @rjernst . I merged with master and pushed some changes to address your comments. Can you take another look ?

Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jimczi, I left some more comments.

#
# meta-foo.zip <-- zip file for the meta plugin, with this structure:
#|____elasticsearch/
#| |____ <sub_plugin_1> <-- The plugin files for sub_plugin_1 (the content of the elastisearch directory)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sub -> bundled (as suggested in a previous comment)

@@ -87,7 +89,7 @@
* </ul>
*
* Plugins are packaged as zip files. Each packaged plugin must contain a
* plugin properties file. See {@link PluginInfo}.
* plugin properties file See {@link PluginInfo} or a meta plugin properties file See {@link MetaPluginInfo}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't really make sense grammar-wise. I suggest:

plugin properties file or a meta plugin properties file. See {@link PluginInfo} and {@link MetaPluginInfo}, respectively.

IOUtils.rm(tmpRoot);
terminal.println("-> Installed " + metaInfo.getName());
} else {
installPlugin(terminal, isBatch, tmpRoot, env.pluginsFile(), env.binFile(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to have normal plugin installation separated into another method, then I would put the code above for meta plugin installation into its own method as well.

}
// clean up installation
IOUtils.rm(tmpRoot);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHouldn't this cleanup and printing be down outside of the if/else since it should apply to both types of plugins?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup is not needed for normal plugin since they are just "moved" from the tmp directory into their final destination. For meta plugin we explicitly create the meta directory in the plugins dir so we need to cleanup the temporary directory at the end (the plugins inside the meta directory are moved to their final destination but the root directory remains untouched).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm, I did wonder about that. Why is it we can't just move the meta plugin like a normal plugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored the code to move the meta plugin like a normal plugin:
e3083ba


It is also possible to bundle multiple plugins into a meta plugin.
A directory for each sub-plugin must be contained in a directory called `elasticsearch.
The meta plugin must also contain a file called `meta-plugin-descriptor.properties` in the folder named
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

folder -> directory (use *nix terminology, not mixing with windows terminology).

The meta plugin must also contain a file called `meta-plugin-descriptor.properties` in the folder named
`elasticsearch`. The format for this file is described in detail here:

https://github.com/elastic/elasticsearch/blob/master/buildSrc/src/main/resources/plugin-descriptor.properties[`/buildSrc/src/main/resources/meta-plugin-descriptor.properties`].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm hesitant to add this link. It links to master, but this will be incorrect (as it could be different) once we backport to 6.x.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it be incorrect after the backport ? We do the same for the plugin-descriptor.properties of a normal plugin.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It must be changed on the backport (and every time we branch) to be correct. For example, if we add any properties to the file in master, but the docs for 6.2 reference the master file, it would cause an error when loading (unknown property). I didn't know we do the same for the regular plugin descriptor...that is unfortunate. This is broken IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed 5043500 to print the properties file in the docs instead of linking to a specific file. WDYT ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is much better!


// A meta plugin packaging example that bundles two plugins in a single zip.

apply plugin: 'elasticsearch.standalone-rest-test'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a followup, I think we should have a gradle plugin for a metaplugin.

// unzip the rescore plugin in a tmp directory
task unzipRescorePlugins(type:Copy, dependsOn:':example-plugins:rescore:bundlePlugin') {
File pluginsRescore = new File(plugins, 'example-rescore')
from { zipTree(project(':example-plugins:rescore').bundlePlugin.outputs.files.singleFile) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you create 2 dummy plugins inside this example (as sub projects within the source here) instead of trying to use other existing examples? What is here now will cause the examples to not be installable at the same time, and we don't need real functionality out of the bundled plugins, just an example that it is possible and they are loaded.

@jimczi
Copy link
Contributor Author

jimczi commented Jan 9, 2018

Thanks @rjernst . I left some questions and pushed another commit to address your comments.
I think it's ready for another review.

Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Jim! LGTM.

@jimczi jimczi merged commit 36729d1 into elastic:master Jan 9, 2018
@jimczi jimczi deleted the feature/uber_plugin branch January 9, 2018 17:28
jasontedor added a commit to jasontedor/elasticsearch that referenced this pull request Jan 9, 2018
* master:
  Set watermarks in single-node test cases
  Add the ability to bundle multiple plugins into a meta plugin (elastic#28022)
  Declare empty package dirs as output dirs
  Consistent updates of IndexShardSnapshotStatus (elastic#28130)
jimczi added a commit that referenced this pull request Jan 9, 2018
This commit adds the ability to package multiple plugins in a single zip.
The zip file for a meta plugin must contains the following structure:

|____elasticsearch/
| |____   <plugin1> <-- The plugin files for plugin1 (the content of the elastisearch directory)
| |____   <plugin2>  <-- The plugin files for plugin2
| |____   meta-plugin-descriptor.properties <-- example contents below
The meta plugin properties descriptor is mandatory and must contain the following properties:

description: simple summary of the meta plugin.
name: the meta plugin name
The installation process installs each plugin in a sub-folder inside the meta plugin directory.
The example above would create the following structure in the plugins directory:

|_____ plugins
| |____   <name_of_the_meta_plugin>
| | |____   meta-plugin-descriptor.properties
| | |____   <plugin1>
| | |____   <plugin2>
If the sub plugins contain a config or a bin directory, they are copied in a sub folder inside the meta plugin config/bin directory.

|_____ config
| |____   <name_of_the_meta_plugin>
| | |____   <plugin1>
| | |____   <plugin2>

|_____ bin
| |____   <name_of_the_meta_plugin>
| | |____   <plugin1>
| | |____   <plugin2>
The sub-plugins are loaded at startup like normal plugins with the same restrictions; they have a separate class loader and a sub-plugin
cannot have the same name than another plugin (or a sub-plugin inside another meta plugin).

It is also not possible to remove a sub-plugin inside a meta plugin, only full removal of the meta plugin is allowed.

Closes #27316
@jimczi jimczi added v7.0.0-beta1 and removed v7.0.0 labels Feb 7, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants