|
@@ -21,7 +21,6 @@ package org.elasticsearch.plugins;
|
|
|
|
|
|
import joptsimple.OptionSet;
|
|
|
import joptsimple.OptionSpec;
|
|
|
-import org.apache.lucene.util.Constants;
|
|
|
import org.apache.lucene.util.IOUtils;
|
|
|
import org.elasticsearch.Build;
|
|
|
import org.elasticsearch.Version;
|
|
@@ -56,6 +55,7 @@ import java.util.HashSet;
|
|
|
import java.util.LinkedHashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Locale;
|
|
|
+import java.util.Objects;
|
|
|
import java.util.Set;
|
|
|
import java.util.zip.ZipEntry;
|
|
|
import java.util.zip.ZipInputStream;
|
|
@@ -183,11 +183,18 @@ class InstallPluginCommand extends Command {
|
|
|
final String version = Version.CURRENT.toString();
|
|
|
final String url;
|
|
|
if (System.getProperty(PROPERTY_SUPPORT_STAGING_URLS, "false").equals("true")) {
|
|
|
- url = String.format(Locale.ROOT, "https://download.elastic.co/elasticsearch/staging/%1$s-%2$s/org/elasticsearch/plugin/%3$s/%1$s/%3$s-%1$s.zip",
|
|
|
- version, Build.CURRENT.shortHash(), pluginId);
|
|
|
+ url = String.format(
|
|
|
+ Locale.ROOT,
|
|
|
+ "https://download.elastic.co/elasticsearch/staging/%1$s-%2$s/org/elasticsearch/plugin/%3$s/%1$s/%3$s-%1$s.zip",
|
|
|
+ version,
|
|
|
+ Build.CURRENT.shortHash(),
|
|
|
+ pluginId);
|
|
|
} else {
|
|
|
- url = String.format(Locale.ROOT, "https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/%1$s/%2$s/%1$s-%2$s.zip",
|
|
|
- pluginId, version);
|
|
|
+ url = String.format(
|
|
|
+ Locale.ROOT,
|
|
|
+ "https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/%1$s/%2$s/%1$s-%2$s.zip",
|
|
|
+ pluginId,
|
|
|
+ version);
|
|
|
}
|
|
|
terminal.println("-> Downloading " + pluginId + " from elastic");
|
|
|
return downloadZipAndChecksum(url, tmpDir);
|
|
@@ -243,21 +250,8 @@ class InstallPluginCommand extends Command {
|
|
|
|
|
|
private Path unzip(Path zip, Path pluginsDir) throws IOException, UserError {
|
|
|
// unzip plugin to a staging temp dir
|
|
|
- final Path target;
|
|
|
- if (Constants.WINDOWS) {
|
|
|
- target = Files.createTempDirectory(pluginsDir, ".installing-");
|
|
|
- } else {
|
|
|
- Set<PosixFilePermission> perms = new HashSet<>();
|
|
|
- perms.add(PosixFilePermission.OWNER_EXECUTE);
|
|
|
- perms.add(PosixFilePermission.OWNER_READ);
|
|
|
- perms.add(PosixFilePermission.OWNER_WRITE);
|
|
|
- perms.add(PosixFilePermission.GROUP_READ);
|
|
|
- perms.add(PosixFilePermission.GROUP_EXECUTE);
|
|
|
- perms.add(PosixFilePermission.OTHERS_READ);
|
|
|
- perms.add(PosixFilePermission.OTHERS_EXECUTE);
|
|
|
- target = Files.createTempDirectory(pluginsDir, ".installing-", PosixFilePermissions.asFileAttribute(perms));
|
|
|
- }
|
|
|
- Files.createDirectories(target);
|
|
|
+
|
|
|
+ final Path target = stagingDirectory(pluginsDir);
|
|
|
|
|
|
boolean hasEsDir = false;
|
|
|
// TODO: we should wrap this in a try/catch and try deleting the target dir on failure?
|
|
@@ -302,6 +296,39 @@ class InstallPluginCommand extends Command {
|
|
|
return target;
|
|
|
}
|
|
|
|
|
|
+ private Path stagingDirectory(Path pluginsDir) throws IOException {
|
|
|
+ try {
|
|
|
+ Set<PosixFilePermission> perms = new HashSet<>();
|
|
|
+ perms.add(PosixFilePermission.OWNER_EXECUTE);
|
|
|
+ perms.add(PosixFilePermission.OWNER_READ);
|
|
|
+ perms.add(PosixFilePermission.OWNER_WRITE);
|
|
|
+ perms.add(PosixFilePermission.GROUP_READ);
|
|
|
+ perms.add(PosixFilePermission.GROUP_EXECUTE);
|
|
|
+ perms.add(PosixFilePermission.OTHERS_READ);
|
|
|
+ perms.add(PosixFilePermission.OTHERS_EXECUTE);
|
|
|
+ return Files.createTempDirectory(pluginsDir, ".installing-", PosixFilePermissions.asFileAttribute(perms));
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ // Jimfs throws an IAE where it should throw an UOE
|
|
|
+ // remove when google/jimfs#30 is integrated into Jimfs
|
|
|
+ // and the Jimfs test dependency is upgraded to include
|
|
|
+ // this pull request
|
|
|
+ final StackTraceElement[] elements = e.getStackTrace();
|
|
|
+ if (elements.length >= 1 &&
|
|
|
+ elements[0].getClassName().equals("com.google.common.jimfs.AttributeService") &&
|
|
|
+ elements[0].getMethodName().equals("setAttributeInternal")) {
|
|
|
+ return stagingDirectoryWithoutPosixPermissions(pluginsDir);
|
|
|
+ } else {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ } catch (UnsupportedOperationException e) {
|
|
|
+ return stagingDirectoryWithoutPosixPermissions(pluginsDir);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Path stagingDirectoryWithoutPosixPermissions(Path pluginsDir) throws IOException {
|
|
|
+ return Files.createTempDirectory(pluginsDir, ".installing-");
|
|
|
+ }
|
|
|
+
|
|
|
/** Load information about the plugin, and verify it can be installed with no errors. */
|
|
|
private PluginInfo verify(Terminal terminal, Path pluginRoot, boolean isBatch) throws Exception {
|
|
|
// read and validate the plugin descriptor
|
|
@@ -315,7 +342,7 @@ class InstallPluginCommand extends Command {
|
|
|
}
|
|
|
|
|
|
// check for jar hell before any copying
|
|
|
- jarHellCheck(pluginRoot, env.pluginsFile(), info.isIsolated());
|
|
|
+ jarHellCheck(pluginRoot, env.pluginsFile());
|
|
|
|
|
|
// read optional security policy (extra permissions)
|
|
|
// if it exists, confirm or warn the user
|
|
@@ -328,19 +355,13 @@ class InstallPluginCommand extends Command {
|
|
|
}
|
|
|
|
|
|
/** check a candidate plugin for jar hell before installing it */
|
|
|
- private void jarHellCheck(Path candidate, Path pluginsDir, boolean isolated) throws Exception {
|
|
|
+ void jarHellCheck(Path candidate, Path pluginsDir) throws Exception {
|
|
|
// create list of current jars in classpath
|
|
|
final List<URL> jars = new ArrayList<>();
|
|
|
jars.addAll(Arrays.asList(JarHell.parseClassPath()));
|
|
|
|
|
|
// read existing bundles. this does some checks on the installation too.
|
|
|
- List<PluginsService.Bundle> bundles = PluginsService.getPluginBundles(pluginsDir);
|
|
|
-
|
|
|
- // if we aren't isolated, we need to jarhellcheck against any other non-isolated plugins
|
|
|
- // that's always the first bundle
|
|
|
- if (isolated == false) {
|
|
|
- jars.addAll(bundles.get(0).urls);
|
|
|
- }
|
|
|
+ PluginsService.getPluginBundles(pluginsDir);
|
|
|
|
|
|
// add plugin jars to the list
|
|
|
Path pluginJars[] = FileSystemUtils.files(candidate, "*.jar");
|
|
@@ -367,7 +388,10 @@ class InstallPluginCommand extends Command {
|
|
|
|
|
|
final Path destination = env.pluginsFile().resolve(info.getName());
|
|
|
if (Files.exists(destination)) {
|
|
|
- throw new UserError(ExitCodes.USAGE, "plugin directory " + destination.toAbsolutePath() + " already exists. To update the plugin, uninstall it first using 'remove " + info.getName() + "' command");
|
|
|
+ throw new UserError(
|
|
|
+ ExitCodes.USAGE,
|
|
|
+ "plugin directory " + destination.toAbsolutePath() +
|
|
|
+ " already exists. To update the plugin, uninstall it first using 'remove " + info.getName() + "' command");
|
|
|
}
|
|
|
|
|
|
Path tmpBinDir = tmpRoot.resolve("bin");
|
|
@@ -404,30 +428,30 @@ class InstallPluginCommand extends Command {
|
|
|
}
|
|
|
Files.createDirectory(destBinDir);
|
|
|
|
|
|
- Set<PosixFilePermission> perms = new HashSet<>();
|
|
|
- if (Constants.WINDOWS == false) {
|
|
|
- // setup file attributes for the installed files to those of the parent dir
|
|
|
- PosixFileAttributeView binAttrs = Files.getFileAttributeView(destBinDir.getParent(), PosixFileAttributeView.class);
|
|
|
- if (binAttrs != null) {
|
|
|
- perms = new HashSet<>(binAttrs.readAttributes().permissions());
|
|
|
- // setting execute bits, since this just means "the file is executable", and actual execution requires read
|
|
|
- perms.add(PosixFilePermission.OWNER_EXECUTE);
|
|
|
- perms.add(PosixFilePermission.GROUP_EXECUTE);
|
|
|
- perms.add(PosixFilePermission.OTHERS_EXECUTE);
|
|
|
- }
|
|
|
+ // setup file attributes for the installed files to those of the parent dir
|
|
|
+ final Set<PosixFilePermission> perms = new HashSet<>();
|
|
|
+ final PosixFileAttributeView binAttributeView = Files.getFileAttributeView(destBinDir.getParent(), PosixFileAttributeView.class);
|
|
|
+ if (binAttributeView != null) {
|
|
|
+ perms.addAll(binAttributeView.readAttributes().permissions());
|
|
|
+ // setting execute bits, since this just means "the file is executable", and actual execution requires read
|
|
|
+ perms.add(PosixFilePermission.OWNER_EXECUTE);
|
|
|
+ perms.add(PosixFilePermission.GROUP_EXECUTE);
|
|
|
+ perms.add(PosixFilePermission.OTHERS_EXECUTE);
|
|
|
}
|
|
|
|
|
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(tmpBinDir)) {
|
|
|
for (Path srcFile : stream) {
|
|
|
if (Files.isDirectory(srcFile)) {
|
|
|
- throw new UserError(ExitCodes.DATA_ERROR, "Directories not allowed in bin dir for plugin " + info.getName() + ", found " + srcFile.getFileName());
|
|
|
+ throw new UserError(
|
|
|
+ ExitCodes.DATA_ERROR,
|
|
|
+ "Directories not allowed in bin dir for plugin " + info.getName() + ", found " + srcFile.getFileName());
|
|
|
}
|
|
|
|
|
|
Path destFile = destBinDir.resolve(tmpBinDir.relativize(srcFile));
|
|
|
Files.copy(srcFile, destFile);
|
|
|
|
|
|
- if (perms.isEmpty() == false) {
|
|
|
- PosixFileAttributeView view = Files.getFileAttributeView(destFile, PosixFileAttributeView.class);
|
|
|
+ final PosixFileAttributeView view = Files.getFileAttributeView(destFile, PosixFileAttributeView.class);
|
|
|
+ if (view != null) {
|
|
|
view.setPermissions(perms);
|
|
|
}
|
|
|
}
|
|
@@ -446,15 +470,12 @@ class InstallPluginCommand extends Command {
|
|
|
|
|
|
// create the plugin's config dir "if necessary"
|
|
|
Files.createDirectories(destConfigDir);
|
|
|
-
|
|
|
- final PosixFileAttributes destConfigDirAttributes;
|
|
|
- if (Constants.WINDOWS) {
|
|
|
- destConfigDirAttributes = null;
|
|
|
- } else {
|
|
|
- destConfigDirAttributes =
|
|
|
- Files.getFileAttributeView(destConfigDir.getParent(), PosixFileAttributeView.class).readAttributes();
|
|
|
+ final PosixFileAttributeView destConfigDirAttributesView =
|
|
|
+ Files.getFileAttributeView(destConfigDir.getParent(), PosixFileAttributeView.class);
|
|
|
+ final PosixFileAttributes destConfigDirAttributes =
|
|
|
+ destConfigDirAttributesView != null ? destConfigDirAttributesView.readAttributes() : null;
|
|
|
+ if (destConfigDirAttributes != null) {
|
|
|
setOwnerGroup(destConfigDir, destConfigDirAttributes);
|
|
|
-
|
|
|
}
|
|
|
|
|
|
try (DirectoryStream<Path> stream = Files.newDirectoryStream(tmpConfigDir)) {
|
|
@@ -466,7 +487,7 @@ class InstallPluginCommand extends Command {
|
|
|
Path destFile = destConfigDir.resolve(tmpConfigDir.relativize(srcFile));
|
|
|
if (Files.exists(destFile) == false) {
|
|
|
Files.copy(srcFile, destFile);
|
|
|
- if (Constants.WINDOWS == false) {
|
|
|
+ if (destConfigDirAttributes != null) {
|
|
|
setOwnerGroup(destFile, destConfigDirAttributes);
|
|
|
}
|
|
|
}
|
|
@@ -475,8 +496,10 @@ class InstallPluginCommand extends Command {
|
|
|
IOUtils.rm(tmpConfigDir); // clean up what we just copied
|
|
|
}
|
|
|
|
|
|
- private static void setOwnerGroup(Path path, PosixFileAttributes attributes) throws IOException {
|
|
|
+ private static void setOwnerGroup(final Path path, final PosixFileAttributes attributes) throws IOException {
|
|
|
+ Objects.requireNonNull(attributes);
|
|
|
PosixFileAttributeView fileAttributeView = Files.getFileAttributeView(path, PosixFileAttributeView.class);
|
|
|
+ assert fileAttributeView != null;
|
|
|
fileAttributeView.setOwner(attributes.owner());
|
|
|
fileAttributeView.setGroup(attributes.group());
|
|
|
}
|