Exemple #1
0
  public void downloadAndExtract(String name, Terminal terminal) throws IOException {
    if (name == null && url == null) {
      throw new IllegalArgumentException("plugin name or url must be supplied with install.");
    }

    if (!Files.exists(environment.pluginsFile())) {
      terminal.println(
          "Plugins directory [%s] does not exist. Creating...", environment.pluginsFile());
      Files.createDirectory(environment.pluginsFile());
    }

    if (!Environment.isWritable(environment.pluginsFile())) {
      throw new IOException("plugin directory " + environment.pluginsFile() + " is read only");
    }

    PluginHandle pluginHandle;
    if (name != null) {
      pluginHandle = PluginHandle.parse(name);
      checkForForbiddenName(pluginHandle.name);
    } else {
      // if we have no name but url, use temporary name that will be overwritten later
      pluginHandle = new PluginHandle("temp_name" + new Random().nextInt(), null, null);
    }

    Path pluginFile = download(pluginHandle, terminal);
    extract(pluginHandle, terminal, pluginFile);
  }
Exemple #2
0
  public void removePlugin(String name, Terminal terminal) throws IOException {
    if (name == null) {
      throw new IllegalArgumentException("plugin name must be supplied with remove [name].");
    }
    PluginHandle pluginHandle = PluginHandle.parse(name);
    boolean removed = false;

    checkForForbiddenName(pluginHandle.name);
    Path pluginToDelete = pluginHandle.extractedDir(environment);
    if (Files.exists(pluginToDelete)) {
      terminal.println(VERBOSE, "Removing: %s", pluginToDelete);
      try {
        IOUtils.rm(pluginToDelete);
      } catch (IOException ex) {
        throw new IOException(
            "Unable to remove "
                + pluginHandle.name
                + ". Check file permissions on "
                + pluginToDelete.toString(),
            ex);
      }
      removed = true;
    }
    Path binLocation = pluginHandle.binDir(environment);
    if (Files.exists(binLocation)) {
      terminal.println(VERBOSE, "Removing: %s", binLocation);
      try {
        IOUtils.rm(binLocation);
      } catch (IOException ex) {
        throw new IOException(
            "Unable to remove "
                + pluginHandle.name
                + ". Check file permissions on "
                + binLocation.toString(),
            ex);
      }
      removed = true;
    }

    if (removed) {
      terminal.println("Removed %s", name);
    } else {
      terminal.println(
          "Plugin %s not found. Run \"plugin list\" to get list of installed plugins.", name);
    }
  }
Exemple #3
0
  private void extract(PluginHandle pluginHandle, Terminal terminal, Path pluginFile)
      throws IOException {
    // unzip plugin to a staging temp dir, named for the plugin
    Path tmp = Files.createTempDirectory(environment.tmpFile(), null);
    Path root = tmp.resolve(pluginHandle.name);
    unzipPlugin(pluginFile, root);

    // find the actual root (in case its unzipped with extra directory wrapping)
    root = findPluginRoot(root);

    // read and validate the plugin descriptor
    PluginInfo info = PluginInfo.readFromProperties(root);
    terminal.println(VERBOSE, "%s", info);

    // update name in handle based on 'name' property found in descriptor file
    pluginHandle = new PluginHandle(info.getName(), pluginHandle.version, pluginHandle.user);
    final Path extractLocation = pluginHandle.extractedDir(environment);
    if (Files.exists(extractLocation)) {
      throw new IOException(
          "plugin directory "
              + extractLocation.toAbsolutePath()
              + " already exists. To update the plugin, uninstall it first using 'remove "
              + pluginHandle.name
              + "' command");
    }

    // check for jar hell before any copying
    if (info.isJvm()) {
      jarHellCheck(root, info.isIsolated());
    }

    // install plugin
    FileSystemUtils.copyDirectoryRecursively(root, extractLocation);
    terminal.println("Installed %s into %s", pluginHandle.name, extractLocation.toAbsolutePath());

    // cleanup
    tryToDeletePath(terminal, tmp, pluginFile);

    // take care of bin/ by moving and applying permissions if needed
    Path sourcePluginBinDirectory = extractLocation.resolve("bin");
    Path destPluginBinDirectory = pluginHandle.binDir(environment);
    boolean needToCopyBinDirectory = Files.exists(sourcePluginBinDirectory);
    if (needToCopyBinDirectory) {
      if (Files.exists(destPluginBinDirectory) && !Files.isDirectory(destPluginBinDirectory)) {
        tryToDeletePath(terminal, extractLocation);
        throw new IOException(
            "plugin bin directory " + destPluginBinDirectory + " is not a directory");
      }

      try {
        copyBinDirectory(
            sourcePluginBinDirectory, destPluginBinDirectory, pluginHandle.name, terminal);
      } catch (IOException e) {
        // rollback and remove potentially before installed leftovers
        terminal.printError(
            "Error copying bin directory [%s] to [%s], cleaning up, reason: %s",
            sourcePluginBinDirectory, destPluginBinDirectory, ExceptionsHelper.detailedMessage(e));
        tryToDeletePath(terminal, extractLocation, pluginHandle.binDir(environment));
        throw e;
      }
    }

    Path sourceConfigDirectory = extractLocation.resolve("config");
    Path destConfigDirectory = pluginHandle.configDir(environment);
    boolean needToCopyConfigDirectory = Files.exists(sourceConfigDirectory);
    if (needToCopyConfigDirectory) {
      if (Files.exists(destConfigDirectory) && !Files.isDirectory(destConfigDirectory)) {
        tryToDeletePath(terminal, extractLocation, destPluginBinDirectory);
        throw new IOException(
            "plugin config directory " + destConfigDirectory + " is not a directory");
      }

      try {
        terminal.println(
            VERBOSE, "Found config, moving to %s", destConfigDirectory.toAbsolutePath());
        moveFilesWithoutOverwriting(sourceConfigDirectory, destConfigDirectory, ".new");

        if (Environment.getFileStore(destConfigDirectory)
            .supportsFileAttributeView(PosixFileAttributeView.class)) {
          // We copy owner, group and permissions from the parent ES_CONFIG directory, assuming they
          // were properly set depending
          // on how es was installed in the first place: can be root:elasticsearch (750) if es was
          // installed from rpm/deb packages
          // or most likely elasticsearch:elasticsearch if installed from tar/zip. As for
          // permissions we don't rely on umask.
          final PosixFileAttributes parentDirAttributes =
              Files.getFileAttributeView(
                      destConfigDirectory.getParent(), PosixFileAttributeView.class)
                  .readAttributes();
          // for files though, we make sure not to copy execute permissions from the parent dir and
          // leave them untouched
          final Set<PosixFilePermission> baseFilePermissions = new HashSet<>();
          for (PosixFilePermission posixFilePermission : parentDirAttributes.permissions()) {
            switch (posixFilePermission) {
              case OWNER_EXECUTE:
              case GROUP_EXECUTE:
              case OTHERS_EXECUTE:
                break;
              default:
                baseFilePermissions.add(posixFilePermission);
            }
          }
          Files.walkFileTree(
              destConfigDirectory,
              new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                    throws IOException {
                  if (attrs.isRegularFile()) {
                    Set<PosixFilePermission> newFilePermissions =
                        new HashSet<>(baseFilePermissions);
                    Set<PosixFilePermission> currentFilePermissions =
                        Files.getPosixFilePermissions(file);
                    for (PosixFilePermission posixFilePermission : currentFilePermissions) {
                      switch (posixFilePermission) {
                        case OWNER_EXECUTE:
                        case GROUP_EXECUTE:
                        case OTHERS_EXECUTE:
                          newFilePermissions.add(posixFilePermission);
                      }
                    }
                    setPosixFileAttributes(
                        file,
                        parentDirAttributes.owner(),
                        parentDirAttributes.group(),
                        newFilePermissions);
                  }
                  return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                    throws IOException {
                  setPosixFileAttributes(
                      dir,
                      parentDirAttributes.owner(),
                      parentDirAttributes.group(),
                      parentDirAttributes.permissions());
                  return FileVisitResult.CONTINUE;
                }
              });
        } else {
          terminal.println(
              VERBOSE, "Skipping posix permissions - filestore doesn't support posix permission");
        }

        terminal.println(
            VERBOSE,
            "Installed %s into %s",
            pluginHandle.name,
            destConfigDirectory.toAbsolutePath());
      } catch (IOException e) {
        terminal.printError(
            "Error copying config directory [%s] to [%s], cleaning up, reason: %s",
            sourceConfigDirectory, destConfigDirectory, ExceptionsHelper.detailedMessage(e));
        tryToDeletePath(terminal, extractLocation, destPluginBinDirectory, destConfigDirectory);
        throw e;
      }
    }
  }
Exemple #4
0
  private Path download(PluginHandle pluginHandle, Terminal terminal) throws IOException {
    Path pluginFile = pluginHandle.newDistroFile(environment);

    HttpDownloadHelper downloadHelper = new HttpDownloadHelper();
    boolean downloaded = false;
    boolean verified = false;
    HttpDownloadHelper.DownloadProgress progress;
    if (outputMode == OutputMode.SILENT) {
      progress = new HttpDownloadHelper.NullProgress();
    } else {
      progress = new HttpDownloadHelper.VerboseProgress(terminal.writer());
    }

    // first, try directly from the URL provided
    if (url != null) {
      URL pluginUrl = url;
      boolean isSecureProcotol = "https".equalsIgnoreCase(pluginUrl.getProtocol());
      boolean isAuthInfoSet = !Strings.isNullOrEmpty(pluginUrl.getUserInfo());
      if (isAuthInfoSet && !isSecureProcotol) {
        throw new IOException("Basic auth is only supported for HTTPS!");
      }

      terminal.println("Trying %s ...", pluginUrl.toExternalForm());
      try {
        downloadHelper.download(pluginUrl, pluginFile, progress, this.timeout);
        downloaded = true;
        terminal.println("Verifying %s checksums if available ...", pluginUrl.toExternalForm());
        Tuple<URL, Path> sha1Info =
            pluginHandle.newChecksumUrlAndFile(environment, pluginUrl, "sha1");
        verified =
            downloadHelper.downloadAndVerifyChecksum(
                sha1Info.v1(),
                pluginFile,
                sha1Info.v2(),
                progress,
                this.timeout,
                HttpDownloadHelper.SHA1_CHECKSUM);
        Tuple<URL, Path> md5Info =
            pluginHandle.newChecksumUrlAndFile(environment, pluginUrl, "md5");
        verified =
            verified
                || downloadHelper.downloadAndVerifyChecksum(
                    md5Info.v1(),
                    pluginFile,
                    md5Info.v2(),
                    progress,
                    this.timeout,
                    HttpDownloadHelper.MD5_CHECKSUM);
      } catch (ElasticsearchTimeoutException | ElasticsearchCorruptionException e) {
        throw e;
      } catch (Exception e) {
        // ignore
        terminal.println("Failed: %s", ExceptionsHelper.detailedMessage(e));
      }
    } else {
      if (PluginHandle.isOfficialPlugin(
          pluginHandle.name, pluginHandle.user, pluginHandle.version)) {
        checkForOfficialPlugins(pluginHandle.name);
      }
    }

    if (!downloaded && url == null) {
      // We try all possible locations
      for (URL url : pluginHandle.urls()) {
        terminal.println("Trying %s ...", url.toExternalForm());
        try {
          downloadHelper.download(url, pluginFile, progress, this.timeout);
          downloaded = true;
          terminal.println("Verifying %s checksums if available ...", url.toExternalForm());
          Tuple<URL, Path> sha1Info = pluginHandle.newChecksumUrlAndFile(environment, url, "sha1");
          verified =
              downloadHelper.downloadAndVerifyChecksum(
                  sha1Info.v1(),
                  pluginFile,
                  sha1Info.v2(),
                  progress,
                  this.timeout,
                  HttpDownloadHelper.SHA1_CHECKSUM);
          Tuple<URL, Path> md5Info = pluginHandle.newChecksumUrlAndFile(environment, url, "md5");
          verified =
              verified
                  || downloadHelper.downloadAndVerifyChecksum(
                      md5Info.v1(),
                      pluginFile,
                      md5Info.v2(),
                      progress,
                      this.timeout,
                      HttpDownloadHelper.MD5_CHECKSUM);
          break;
        } catch (ElasticsearchTimeoutException | ElasticsearchCorruptionException e) {
          throw e;
        } catch (Exception e) {
          terminal.println(VERBOSE, "Failed: %s", ExceptionsHelper.detailedMessage(e));
        }
      }
    }

    if (!downloaded) {
      // try to cleanup what we downloaded
      IOUtils.deleteFilesIgnoringExceptions(pluginFile);
      throw new IOException(
          "failed to download out of all possible locations..., use --verbose to get detailed information");
    }

    if (verified == false) {
      terminal.println(
          "NOTE: Unable to verify checksum for downloaded plugin (unable to find .sha1 or .md5 file to verify)");
    }
    return pluginFile;
  }