From 117372d8db320f571c24268038f12f1f16adab7a Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Thu, 8 Feb 2018 14:15:41 -0800 Subject: [PATCH 1/2] Plugins: Remove intermeidate "elasticsearch" directory within plugin zips This commit removes the extra layer of all plugin files existing under "elasticsearch" within plugin zips. This simplifies building plugin zips and removes the need for special logic of modules vs plugins. --- .../plugin/MetaPluginBuildPlugin.groovy | 14 +++------ .../gradle/plugin/PluginBuildPlugin.groovy | 3 -- .../plugins/InstallPluginCommand.java | 13 +------- .../plugins/InstallPluginCommandTests.java | 31 +++---------------- 4 files changed, 11 insertions(+), 50 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/MetaPluginBuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/MetaPluginBuildPlugin.groovy index 3df9b604c1309..4dc355a48608a 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/MetaPluginBuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/MetaPluginBuildPlugin.groovy @@ -58,11 +58,9 @@ class MetaPluginBuildPlugin implements Plugin { // create the actual bundle task, which zips up all the files for the plugin Zip bundle = project.tasks.create(name: 'bundlePlugin', type: Zip, dependsOn: [buildProperties]) { - into('elasticsearch') { - from(buildProperties.descriptorOutput.parentFile) { - // plugin properties file - include(buildProperties.descriptorOutput.name) - } + from(buildProperties.descriptorOutput.parentFile) { + // plugin properties file + include(buildProperties.descriptorOutput.name) } // due to how the renames work for each bundled plugin, we must exclude empty dirs or every subdir // within bundled plugin zips will show up at the root as an empty dir @@ -85,10 +83,8 @@ class MetaPluginBuildPlugin implements Plugin { dependsOn bundledPluginProject.bundlePlugin from(project.zipTree(bundledPluginProject.bundlePlugin.outputs.files.singleFile)) { eachFile { FileCopyDetails details -> - // paths in the individual plugins begin with elasticsearch, and we want to add in the - // bundled plugin name between that and each filename - details.relativePath = new RelativePath(true, 'elasticsearch', bundledPluginProjectName, - details.relativePath.toString().replace('elasticsearch/', '')) + // we want each path to have the plugin name interjected + details.relativePath = new RelativePath(true, bundledPluginProjectName, details.relativePath.toString()) } } } diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy index 950acad9a5eb4..e96fd20316a6a 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy @@ -140,9 +140,6 @@ public class PluginBuildPlugin extends BuildPlugin { include 'config/**' include 'bin/**' } - if (project.path.startsWith(':modules:') == false) { - into('elasticsearch') - } } project.assemble.dependsOn(bundle) diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index 255c2e991b714..8f0b900a283d6 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -462,17 +462,11 @@ private Path unzip(Path zip, Path pluginsDir) throws IOException, UserException final Path target = stagingDirectory(pluginsDir); pathsToDeleteOnShutdown.add(target); - boolean hasEsDir = false; try (ZipInputStream zipInput = new ZipInputStream(Files.newInputStream(zip))) { ZipEntry entry; byte[] buffer = new byte[8192]; while ((entry = zipInput.getNextEntry()) != null) { - if (entry.getName().startsWith("elasticsearch/") == false) { - // only extract the elasticsearch directory - continue; - } - hasEsDir = true; - Path targetFile = target.resolve(entry.getName().substring("elasticsearch/".length())); + Path targetFile = target.resolve(entry.getName()); // Using the entry name as a path can result in an entry outside of the plugin dir, // either if the name starts with the root of the filesystem, or it is a relative @@ -501,11 +495,6 @@ private Path unzip(Path zip, Path pluginsDir) throws IOException, UserException } } Files.delete(zip); - if (hasEsDir == false) { - IOUtils.rm(target); - throw new UserException(PLUGIN_MALFORMED, - "`elasticsearch` directory is missing in the plugin zip"); - } return target; } diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java index 724c222af2ba9..e296a296066bd 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java @@ -199,14 +199,13 @@ static void writeJar(Path jar, String... classes) throws IOException { } } - static Path writeZip(Path structure, String prefix) throws IOException { + static Path writeZip(Path structure) throws IOException { Path zip = createTempDir().resolve(structure.getFileName() + ".zip"); try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) { Files.walkFileTree(structure, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String target = (prefix == null ? "" : prefix + "/") + structure.relativize(file).toString(); - stream.putNextEntry(new ZipEntry(target)); + stream.putNextEntry(new ZipEntry(structure.relativize(file).toString())); Files.copy(file, stream); return FileVisitResult.CONTINUE; } @@ -259,12 +258,12 @@ static void writePluginSecurityPolicy(Path pluginDir, String... permissions) thr static Path createPlugin(String name, Path structure, String... additionalProps) throws IOException { writePlugin(name, structure, additionalProps); - return writeZip(structure, "elasticsearch"); + return writeZip(structure); } static Path createMetaPlugin(String name, Path structure) throws IOException { writeMetaPlugin(name, structure); - return writeZip(structure, "elasticsearch"); + return writeZip(structure); } void installPlugin(String pluginUrl, Path home) throws Exception { @@ -811,7 +810,7 @@ public void testMissingDescriptor() throws Exception { Path pluginDir = metaDir.resolve("fake"); Files.createDirectory(pluginDir); Files.createFile(pluginDir.resolve("fake.yml")); - String pluginZip = writeZip(pluginDir, "elasticsearch").toUri().toURL().toString(); + String pluginZip = writeZip(pluginDir).toUri().toURL().toString(); NoSuchFileException e = expectThrows(NoSuchFileException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("plugin-descriptor.properties")); assertInstallCleaned(env.v2()); @@ -822,26 +821,6 @@ public void testMissingDescriptor() throws Exception { assertInstallCleaned(env.v2()); } - public void testMissingDirectory() throws Exception { - Tuple env = createEnv(fs, temp); - Path pluginDir = createPluginDir(temp); - Files.createFile(pluginDir.resolve(PluginInfo.ES_PLUGIN_PROPERTIES)); - String pluginZip = writeZip(pluginDir, null).toUri().toURL().toString(); - UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); - assertTrue(e.getMessage(), e.getMessage().contains("`elasticsearch` directory is missing in the plugin zip")); - assertInstallCleaned(env.v2()); - } - - public void testMissingDirectoryMeta() throws Exception { - Tuple env = createEnv(fs, temp); - Path pluginDir = createPluginDir(temp); - Files.createFile(pluginDir.resolve(MetaPluginInfo.ES_META_PLUGIN_PROPERTIES)); - String pluginZip = writeZip(pluginDir, null).toUri().toURL().toString(); - UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); - assertTrue(e.getMessage(), e.getMessage().contains("`elasticsearch` directory is missing in the plugin zip")); - assertInstallCleaned(env.v2()); - } - public void testZipRelativeOutsideEntryName() throws Exception { Tuple env = createEnv(fs, temp); Path zip = createTempDir().resolve("broken.zip"); From 8b8bd7c406c98611393b176557e8a35cc180c886 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Fri, 9 Feb 2018 15:39:34 -0800 Subject: [PATCH 2/2] add error when old format is used --- .../plugins/InstallPluginCommand.java | 7 ++++ .../plugins/InstallPluginCommandTests.java | 34 +++++++++++++++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java index 8f0b900a283d6..0969fb60f3311 100644 --- a/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java +++ b/distribution/tools/plugin-cli/src/main/java/org/elasticsearch/plugins/InstallPluginCommand.java @@ -466,6 +466,10 @@ private Path unzip(Path zip, Path pluginsDir) throws IOException, UserException ZipEntry entry; byte[] buffer = new byte[8192]; while ((entry = zipInput.getNextEntry()) != null) { + if (entry.getName().startsWith("elasticsearch/")) { + throw new UserException(PLUGIN_MALFORMED, "This plugin was built with an older plugin structure." + + " Contact the plugin author to remove the intermediate \"elasticsearch\" directory within the plugin zip."); + } Path targetFile = target.resolve(entry.getName()); // Using the entry name as a path can result in an entry outside of the plugin dir, @@ -493,6 +497,9 @@ private Path unzip(Path zip, Path pluginsDir) throws IOException, UserException } zipInput.closeEntry(); } + } catch (UserException e) { + IOUtils.rm(target); + throw e; } Files.delete(zip); return target; diff --git a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java index e296a296066bd..4e0cecae12f31 100644 --- a/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java +++ b/distribution/tools/plugin-cli/src/test/java/org/elasticsearch/plugins/InstallPluginCommandTests.java @@ -199,13 +199,14 @@ static void writeJar(Path jar, String... classes) throws IOException { } } - static Path writeZip(Path structure) throws IOException { + static Path writeZip(Path structure, String prefix) throws IOException { Path zip = createTempDir().resolve(structure.getFileName() + ".zip"); try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) { Files.walkFileTree(structure, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - stream.putNextEntry(new ZipEntry(structure.relativize(file).toString())); + String target = (prefix == null ? "" : prefix + "/") + structure.relativize(file).toString(); + stream.putNextEntry(new ZipEntry(target)); Files.copy(file, stream); return FileVisitResult.CONTINUE; } @@ -258,12 +259,12 @@ static void writePluginSecurityPolicy(Path pluginDir, String... permissions) thr static Path createPlugin(String name, Path structure, String... additionalProps) throws IOException { writePlugin(name, structure, additionalProps); - return writeZip(structure); + return writeZip(structure, null); } static Path createMetaPlugin(String name, Path structure) throws IOException { writeMetaPlugin(name, structure); - return writeZip(structure); + return writeZip(structure, null); } void installPlugin(String pluginUrl, Path home) throws Exception { @@ -810,7 +811,7 @@ public void testMissingDescriptor() throws Exception { Path pluginDir = metaDir.resolve("fake"); Files.createDirectory(pluginDir); Files.createFile(pluginDir.resolve("fake.yml")); - String pluginZip = writeZip(pluginDir).toUri().toURL().toString(); + String pluginZip = writeZip(pluginDir, null).toUri().toURL().toString(); NoSuchFileException e = expectThrows(NoSuchFileException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("plugin-descriptor.properties")); assertInstallCleaned(env.v2()); @@ -821,15 +822,36 @@ public void testMissingDescriptor() throws Exception { assertInstallCleaned(env.v2()); } + public void testContainsIntermediateDirectory() throws Exception { + Tuple env = createEnv(fs, temp); + Path pluginDir = createPluginDir(temp); + Files.createFile(pluginDir.resolve(PluginInfo.ES_PLUGIN_PROPERTIES)); + String pluginZip = writeZip(pluginDir, "elasticsearch").toUri().toURL().toString(); + UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); + assertThat(e.getMessage(), containsString("This plugin was built with an older plugin structure")); + assertInstallCleaned(env.v2()); + } + + public void testContainsIntermediateDirectoryMeta() throws Exception { + Tuple env = createEnv(fs, temp); + Path pluginDir = createPluginDir(temp); + Files.createFile(pluginDir.resolve(MetaPluginInfo.ES_META_PLUGIN_PROPERTIES)); + String pluginZip = writeZip(pluginDir, "elasticsearch").toUri().toURL().toString(); + UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); + assertThat(e.getMessage(), containsString("This plugin was built with an older plugin structure")); + assertInstallCleaned(env.v2()); + } + public void testZipRelativeOutsideEntryName() throws Exception { Tuple env = createEnv(fs, temp); Path zip = createTempDir().resolve("broken.zip"); try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zip))) { - stream.putNextEntry(new ZipEntry("elasticsearch/../blah")); + stream.putNextEntry(new ZipEntry("../blah")); } String pluginZip = zip.toUri().toURL().toString(); UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1())); assertTrue(e.getMessage(), e.getMessage().contains("resolving outside of plugin directory")); + assertInstallCleaned(env.v2()); } public void testOfficialPluginsHelpSorted() throws Exception {