Plugins: Add plugin cli specific exit codes (#23599)
We currently use POSIX exit codes in all of our CLIs. However, posix only suggests these exit codes are standard across tools. It does not prescribe particular uses for codes outside of that range. This commit adds 2 exit codes specific to plugin installation to make distinguishing an incorrectly built plugin and a plugin already existing easier. closes #15295
This commit is contained in:
parent
111e703cde
commit
b31ed6a75c
|
@ -105,6 +105,13 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
|
||||
private static final String PROPERTY_STAGING_ID = "es.plugins.staging";
|
||||
|
||||
// exit codes for install
|
||||
/** A plugin with the same name is already installed. */
|
||||
static final int PLUGIN_EXISTS = 1;
|
||||
/** The plugin zip is not properly structured. */
|
||||
static final int PLUGIN_MALFORMED = 2;
|
||||
|
||||
|
||||
/** The builtin modules, which are plugins, but cannot be installed or removed. */
|
||||
static final Set<String> MODULES;
|
||||
static {
|
||||
|
@ -333,7 +340,8 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
byte[] zipbytes = Files.readAllBytes(zip);
|
||||
String gotChecksum = MessageDigests.toHexString(MessageDigests.sha1().digest(zipbytes));
|
||||
if (expectedChecksum.equals(gotChecksum) == false) {
|
||||
throw new UserException(ExitCodes.IO_ERROR, "SHA1 mismatch, expected " + expectedChecksum + " but got " + gotChecksum);
|
||||
throw new UserException(ExitCodes.IO_ERROR,
|
||||
"SHA1 mismatch, expected " + expectedChecksum + " but got " + gotChecksum);
|
||||
}
|
||||
|
||||
return zip;
|
||||
|
@ -357,12 +365,14 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
hasEsDir = true;
|
||||
Path targetFile = target.resolve(entry.getName().substring("elasticsearch/".length()));
|
||||
|
||||
// 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 entry like ../whatever.
|
||||
// This check attempts to identify both cases by first normalizing the path (which removes foo/..)
|
||||
// and ensuring the normalized entry is still rooted with the target plugin directory.
|
||||
// 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
|
||||
// entry like ../whatever. This check attempts to identify both cases by first
|
||||
// normalizing the path (which removes foo/..) and ensuring the normalized entry
|
||||
// is still rooted with the target plugin directory.
|
||||
if (targetFile.normalize().startsWith(target) == false) {
|
||||
throw new IOException("Zip contains entry name '" + entry.getName() + "' resolving outside of plugin directory");
|
||||
throw new UserException(PLUGIN_MALFORMED, "Zip contains entry name '" +
|
||||
entry.getName() + "' resolving outside of plugin directory");
|
||||
}
|
||||
|
||||
// be on the safe side: do not rely on that directories are always extracted
|
||||
|
@ -384,7 +394,8 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
Files.delete(zip);
|
||||
if (hasEsDir == false) {
|
||||
IOUtils.rm(target);
|
||||
throw new UserException(ExitCodes.DATA_ERROR, "`elasticsearch` directory is missing in the plugin zip");
|
||||
throw new UserException(PLUGIN_MALFORMED,
|
||||
"`elasticsearch` directory is missing in the plugin zip");
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
@ -424,10 +435,11 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
if (Files.exists(destination)) {
|
||||
final String message = String.format(
|
||||
Locale.ROOT,
|
||||
"plugin directory [%s] already exists; if you need to update the plugin, uninstall it first using command 'remove %s'",
|
||||
"plugin directory [%s] already exists; if you need to update the plugin, " +
|
||||
"uninstall it first using command 'remove %s'",
|
||||
destination.toAbsolutePath(),
|
||||
info.getName());
|
||||
throw new UserException(ExitCodes.CONFIG, message);
|
||||
throw new UserException(PLUGIN_EXISTS, message);
|
||||
}
|
||||
|
||||
terminal.println(VERBOSE, info.toString());
|
||||
|
@ -435,8 +447,8 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
// don't let user install plugin as a module...
|
||||
// they might be unavoidably in maven central and are packaged up the same way)
|
||||
if (MODULES.contains(info.getName())) {
|
||||
throw new UserException(
|
||||
ExitCodes.USAGE, "plugin '" + info.getName() + "' cannot be installed like this, it is a system module");
|
||||
throw new UserException(ExitCodes.USAGE, "plugin '" + info.getName() +
|
||||
"' cannot be installed like this, it is a system module");
|
||||
}
|
||||
|
||||
// check for jar hell before any copying
|
||||
|
@ -534,7 +546,7 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
/** Copies the files from {@code tmpBinDir} into {@code destBinDir}, along with permissions from dest dirs parent. */
|
||||
private void installBin(PluginInfo info, Path tmpBinDir, Path destBinDir) throws Exception {
|
||||
if (Files.isDirectory(tmpBinDir) == false) {
|
||||
throw new UserException(ExitCodes.IO_ERROR, "bin in plugin " + info.getName() + " is not a directory");
|
||||
throw new UserException(PLUGIN_MALFORMED, "bin in plugin " + info.getName() + " is not a directory");
|
||||
}
|
||||
Files.createDirectory(destBinDir);
|
||||
setFileAttributes(destBinDir, BIN_DIR_PERMS);
|
||||
|
@ -542,9 +554,8 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(tmpBinDir)) {
|
||||
for (Path srcFile : stream) {
|
||||
if (Files.isDirectory(srcFile)) {
|
||||
throw new UserException(
|
||||
ExitCodes.DATA_ERROR,
|
||||
"Directories not allowed in bin dir for plugin " + info.getName() + ", found " + srcFile.getFileName());
|
||||
throw new UserException(PLUGIN_MALFORMED, "Directories not allowed in bin dir " +
|
||||
"for plugin " + info.getName() + ", found " + srcFile.getFileName());
|
||||
}
|
||||
|
||||
Path destFile = destBinDir.resolve(tmpBinDir.relativize(srcFile));
|
||||
|
@ -561,7 +572,8 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
*/
|
||||
private void installConfig(PluginInfo info, Path tmpConfigDir, Path destConfigDir) throws Exception {
|
||||
if (Files.isDirectory(tmpConfigDir) == false) {
|
||||
throw new UserException(ExitCodes.IO_ERROR, "config in plugin " + info.getName() + " is not a directory");
|
||||
throw new UserException(PLUGIN_MALFORMED,
|
||||
"config in plugin " + info.getName() + " is not a directory");
|
||||
}
|
||||
|
||||
Files.createDirectories(destConfigDir);
|
||||
|
@ -577,7 +589,8 @@ class InstallPluginCommand extends EnvironmentAwareCommand {
|
|||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(tmpConfigDir)) {
|
||||
for (Path srcFile : stream) {
|
||||
if (Files.isDirectory(srcFile)) {
|
||||
throw new UserException(ExitCodes.DATA_ERROR, "Directories not allowed in config dir for plugin " + info.getName());
|
||||
throw new UserException(PLUGIN_MALFORMED,
|
||||
"Directories not allowed in config dir for plugin " + info.getName());
|
||||
}
|
||||
|
||||
Path destFile = destConfigDir.resolve(tmpConfigDir.relativize(srcFile));
|
||||
|
|
|
@ -595,7 +595,7 @@ public class InstallPluginCommandTests extends ESTestCase {
|
|||
stream.putNextEntry(new ZipEntry("elasticsearch/../blah"));
|
||||
}
|
||||
String pluginZip = zip.toUri().toURL().toString();
|
||||
IOException e = expectThrows(IOException.class, () -> installPlugin(pluginZip, env.v1()));
|
||||
UserException e = expectThrows(UserException.class, () -> installPlugin(pluginZip, env.v1()));
|
||||
assertTrue(e.getMessage(), e.getMessage().contains("resolving outside of plugin directory"));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue