Merge pull request #16453 from jimferenczi/packaging_plugin_zip

Pack all the plugin files into a single folder named elasticsearch at the root of the plugin zip.
This commit is contained in:
Jim Ferenczi 2016-02-10 10:14:21 +01:00
commit ba20fc2d0a
6 changed files with 63 additions and 16 deletions

View File

@ -112,6 +112,9 @@ public class PluginBuildPlugin extends BuildPlugin {
include 'config/**' include 'config/**'
include 'bin/**' include 'bin/**'
} }
if (project.path.startsWith(':modules:') == false) {
into('elasticsearch')
}
} }
project.assemble.dependsOn(bundle) project.assemble.dependsOn(bundle)

View File

@ -1,13 +1,14 @@
# Elasticsearch plugin descriptor file # Elasticsearch plugin descriptor file
# This file must exist as 'plugin-descriptor.properties' at # This file must exist as 'plugin-descriptor.properties' in a folder named `elasticsearch`
# the root directory of all plugins. # inside all plugins.
# #
### example plugin for "foo" ### example plugin for "foo"
# #
# foo.zip <-- zip file for the plugin, with this structure: # foo.zip <-- zip file for the plugin, with this structure:
# <arbitrary name1>.jar <-- classes, resources, dependencies #|____elasticsearch/
# <arbitrary nameN>.jar <-- any number of jars #| |____ <arbitrary name1>.jar <-- classes, resources, dependencies
# plugin-descriptor.properties <-- example contents below: #| |____ <arbitrary nameN>.jar <-- any number of jars
#| |____ plugin-descriptor.properties <-- example contents below:
# #
# classname=foo.bar.BazPlugin # classname=foo.bar.BazPlugin
# description=My cool plugin # description=My cool plugin

View File

@ -208,17 +208,23 @@ class InstallPluginCommand extends CliTool.Command {
return zip; return zip;
} }
private Path unzip(Path zip, Path pluginsDir) throws IOException { private Path unzip(Path zip, Path pluginsDir) throws IOException, UserError {
// unzip plugin to a staging temp dir // unzip plugin to a staging temp dir
Path target = Files.createTempDirectory(pluginsDir, ".installing-"); Path target = Files.createTempDirectory(pluginsDir, ".installing-");
Files.createDirectories(target); Files.createDirectories(target);
boolean hasEsDir = false;
// TODO: we should wrap this in a try/catch and try deleting the target dir on failure? // TODO: we should wrap this in a try/catch and try deleting the target dir on failure?
try (ZipInputStream zipInput = new ZipInputStream(Files.newInputStream(zip))) { try (ZipInputStream zipInput = new ZipInputStream(Files.newInputStream(zip))) {
ZipEntry entry; ZipEntry entry;
byte[] buffer = new byte[8192]; byte[] buffer = new byte[8192];
while ((entry = zipInput.getNextEntry()) != null) { while ((entry = zipInput.getNextEntry()) != null) {
Path targetFile = target.resolve(entry.getName()); if (entry.getName().startsWith("elasticsearch/") == false) {
// only extract the elasticsearch directory
continue;
}
hasEsDir = true;
Path targetFile = target.resolve(entry.getName().substring("elasticsearch/".length()));
// TODO: handle name being an absolute path // TODO: handle name being an absolute path
// be on the safe side: do not rely on that directories are always extracted // be on the safe side: do not rely on that directories are always extracted
@ -236,6 +242,10 @@ class InstallPluginCommand extends CliTool.Command {
} }
} }
Files.delete(zip); Files.delete(zip);
if (hasEsDir == false) {
IOUtils.rm(target);
throw new UserError(CliTool.ExitStatus.DATA_ERROR, "`elasticsearch` directory is missing in the plugin zip");
}
return target; return target;
} }

View File

@ -10,12 +10,16 @@ These examples provide the bare bones needed to get started. For more
information about how to write a plugin, we recommend looking at the plugins information about how to write a plugin, we recommend looking at the plugins
listed in this documentation for inspiration. listed in this documentation for inspiration.
[float]
=== Plugin Structure
All plugin files must be contained in a directory called `elasticsearch`.
[float] [float]
=== Plugin descriptor file === Plugin descriptor file
All plugins, be they site or Java plugins, must contain a file called All plugins must contain a file called `plugin-descriptor.properties` in the folder named `elasticsearch`. The format
`plugin-descriptor.properties` in the root directory. The format for this file for this file is described in detail here:
is described in detail here:
https://github.com/elastic/elasticsearch/blob/master/buildSrc/src/main/resources/plugin-descriptor.properties[`/buildSrc/src/main/resources/plugin-descriptor.properties`]. https://github.com/elastic/elasticsearch/blob/master/buildSrc/src/main/resources/plugin-descriptor.properties[`/buildSrc/src/main/resources/plugin-descriptor.properties`].
@ -50,7 +54,7 @@ of nonnegative decimal integers separated by "."'s and may have leading zeros.
|======================================================================= |=======================================================================
Note that only jar files in the root directory are added to the classpath for the plugin! Note that only jar files in the 'elasticsearch' directory are added to the classpath for the plugin!
If you need other resources, package them into a resources jar. If you need other resources, package them into a resources jar.
[IMPORTANT] [IMPORTANT]

View File

@ -346,6 +346,8 @@ disable doc values is by using the `doc_values` property of mappings.
=== Plugin changes === Plugin changes
The command `bin/plugin` has been renamed to `bin/elasticsearch-plugin`. The command `bin/plugin` has been renamed to `bin/elasticsearch-plugin`.
The structure of the plugin has changed. All the plugin files must be contained in a directory called `elasticsearch`.
If you use the gradle build, this structure is automatically generated.
==== Site plugins removed ==== Site plugins removed

View File

@ -27,6 +27,7 @@ import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor; import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
@ -46,6 +47,7 @@ import org.elasticsearch.common.cli.CliTool;
import org.elasticsearch.common.cli.CliToolTestCase; import org.elasticsearch.common.cli.CliToolTestCase;
import org.elasticsearch.common.cli.Terminal; import org.elasticsearch.common.cli.Terminal;
import org.elasticsearch.common.cli.UserError; import org.elasticsearch.common.cli.UserError;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
@ -102,13 +104,14 @@ public class InstallPluginCommandTests extends ESTestCase {
} }
} }
static String writeZip(Path structure) throws IOException { static String writeZip(Path structure, Path prefix) throws IOException {
Path zip = createTempDir().resolve(structure.getFileName() + ".zip"); Path zip = createTempDir().resolve(structure.getFileName() + ".zip");
try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) { try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) {
Files.walkFileTree(structure, new SimpleFileVisitor<Path>() { Files.walkFileTree(structure, new SimpleFileVisitor<Path>() {
@Override @Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
stream.putNextEntry(new ZipEntry(structure.relativize(file).toString())); Path target = prefix.resolve(structure.relativize(file));
stream.putNextEntry(new ZipEntry(target.toString()));
Files.copy(file, stream); Files.copy(file, stream);
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
@ -127,7 +130,7 @@ public class InstallPluginCommandTests extends ESTestCase {
"java.version", System.getProperty("java.specification.version"), "java.version", System.getProperty("java.specification.version"),
"classname", "FakePlugin"); "classname", "FakePlugin");
writeJar(structure.resolve("plugin.jar"), "FakePlugin"); writeJar(structure.resolve("plugin.jar"), "FakePlugin");
return writeZip(structure); return writeZip(structure, PathUtils.get("elasticsearch"));
} }
static CliToolTestCase.CaptureOutputTerminal installPlugin(String pluginUrl, Environment env) throws Exception { static CliToolTestCase.CaptureOutputTerminal installPlugin(String pluginUrl, Environment env) throws Exception {
@ -284,7 +287,7 @@ public class InstallPluginCommandTests extends ESTestCase {
"classname", "FakePlugin", "classname", "FakePlugin",
"isolated", "false"); "isolated", "false");
writeJar(pluginDir1.resolve("plugin.jar"), "FakePlugin"); writeJar(pluginDir1.resolve("plugin.jar"), "FakePlugin");
String pluginZip1 = writeZip(pluginDir1); String pluginZip1 = writeZip(pluginDir1, PathUtils.get("elasticsearch"));
installPlugin(pluginZip1, env); installPlugin(pluginZip1, env);
Path pluginDir2 = createTempDir(); Path pluginDir2 = createTempDir();
@ -297,7 +300,7 @@ public class InstallPluginCommandTests extends ESTestCase {
"classname", "FakePlugin", "classname", "FakePlugin",
"isolated", "false"); "isolated", "false");
writeJar(pluginDir2.resolve("plugin.jar"), "FakePlugin"); writeJar(pluginDir2.resolve("plugin.jar"), "FakePlugin");
String pluginZip2 = writeZip(pluginDir2); String pluginZip2 = writeZip(pluginDir2, PathUtils.get("elasticsearch"));
IllegalStateException e = expectThrows(IllegalStateException.class, () -> { IllegalStateException e = expectThrows(IllegalStateException.class, () -> {
installPlugin(pluginZip2, env); installPlugin(pluginZip2, env);
}); });
@ -457,6 +460,30 @@ public class InstallPluginCommandTests extends ESTestCase {
assertInstallCleaned(env); assertInstallCleaned(env);
} }
public void testMissingDescriptor() throws Exception {
Environment env = createEnv();
Path pluginDir = createTempDir();
Files.createFile(pluginDir.resolve("fake.yml"));
String pluginZip = writeZip(pluginDir, PathUtils.get("elasticsearch"));
NoSuchFileException e = expectThrows(NoSuchFileException.class, () -> {
installPlugin(pluginZip, env);
});
assertTrue(e.getMessage(), e.getMessage().contains("plugin-descriptor.properties"));
assertInstallCleaned(env);
}
public void testMissingDirectory() throws Exception {
Environment env = createEnv();
Path pluginDir = createTempDir();
Files.createFile(pluginDir.resolve(PluginInfo.ES_PLUGIN_PROPERTIES));
String pluginZip = writeZip(pluginDir, PathUtils.get(""));
UserError e = expectThrows(UserError.class, () -> {
installPlugin(pluginZip, env);
});
assertTrue(e.getMessage(), e.getMessage().contains("`elasticsearch` directory is missing in the plugin zip"));
assertInstallCleaned(env);
}
// TODO: test batch flag? // TODO: test batch flag?
// TODO: test checksum (need maven/official below) // TODO: test checksum (need maven/official below)
// TODO: test maven, official, and staging install...need tests with fixtures... // TODO: test maven, official, and staging install...need tests with fixtures...