Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

vistahigherlearning / logstash   deb

Repository URL to install this package:

/ opt / logstash / lib / pluginmanager / install.rb

# encoding: utf-8
require "pluginmanager/command"
require "jar-dependencies"
require "jar_install_post_install_hook"
require "file-dependencies/gem"
require "fileutils"

class LogStash::PluginManager::Install < LogStash::PluginManager::Command
  parameter "[PLUGIN] ...", "plugin name(s) or file", :attribute_name => :plugins_arg
  option "--version", "VERSION", "version of the plugin to install"
  option "--[no-]verify", :flag, "verify plugin validity before installation", :default => true
  option "--development", :flag, "install all development dependencies of currently installed plugins", :default => false

  # the install logic below support installing multiple plugins with each a version specification
  # but the argument parsing does not support it for now so currently if specifying --version only
  # one plugin name can be also specified.
  def execute
    validate_cli_options!

    if local_gems?
      gems = extract_local_gems_plugins
    elsif development?
      gems = plugins_development_gems
    else
      gems = plugins_gems
      verify_remote!(gems) if verify?
    end

    install_gems_list!(gems)
    remove_unused_locally_installed_gems!
  end

  private
  def validate_cli_options!
    if development?
      signal_usage_error("Cannot specify plugin(s) with --development, it will add the development dependencies of the currently installed plugins") unless plugins_arg.empty?
    else
      signal_usage_error("No plugin specified") if plugins_arg.empty? && verify?
      # TODO: find right syntax to allow specifying list of plugins with optional version specification for each
      signal_usage_error("Only 1 plugin name can be specified with --version") if version && plugins_arg.size > 1
    end
    signal_error("File #{LogStash::Environment::GEMFILE_PATH} does not exist or is not writable, aborting") unless ::File.writable?(LogStash::Environment::GEMFILE_PATH)
  end

  # Check if the specified gems contains
  # the logstash `metadata`
  def verify_remote!(gems)
    gems.each do |plugin, version|
      puts("Validating #{[plugin, version].compact.join("-")}")
      signal_error("Installation aborted, verification failed for #{plugin} #{version}") unless LogStash::PluginManager.logstash_plugin?(plugin, version)
    end
  end

  def plugins_development_gems
    # Get currently defined gems and their dev dependencies
    specs = []

    specs = LogStash::PluginManager.all_installed_plugins_gem_specs(gemfile)

    # Construct the list of dependencies to add to the current gemfile
    specs.each_with_object([]) do |spec, install_list|
      dependencies = spec.dependencies
        .select { |dep| dep.type == :development }
        .map { |dep| [dep.name] + dep.requirement.as_list }

      install_list.concat(dependencies)
    end
  end

  def plugins_gems
    version ? [plugins_arg << version] : plugins_arg.map { |plugin| [plugin, nil] }
  end

  # install_list will be an array of [plugin name, version, options] tuples, version it
  # can be nil at this point we know that plugins_arg is not empty and if the
  # --version is specified there is only one plugin in plugins_arg
  #
  def install_gems_list!(install_list)
    # If something goes wrong during the installation `LogStash::Gemfile` will restore a backup version.
    install_list = LogStash::PluginManager.merge_duplicates(install_list)

    # Add plugins/gems to the current gemfile
    puts("Installing" + (install_list.empty? ? "..." : " " + install_list.collect(&:first).join(", ")))
    install_list.each { |plugin, version, options| gemfile.update(plugin, version, options) }

    # Sync gemfiles changes to disk to make them available to the `bundler install`'s API
    gemfile.save

    bundler_options = {:install => true}
    bundler_options[:without] = [] if development?
    bundler_options[:rubygems_source] = gemfile.gemset.sources

    output = LogStash::Bundler.invoke!(bundler_options)

    puts("Installation successful")
  rescue => exception
    gemfile.restore!
    report_exception("Installation Aborted", exception)
  ensure
    display_bundler_output(output)
  end

  # Extract the specified local gems in a predefined local path
  # Update the gemfile to use a relative path to this plugin and run
  # Bundler, this will mark the gem not updatable by `bin/plugin update`
  # This is the most reliable way to make it work in bundler without
  # hacking with `how bundler works`
  #
  # Bundler 2.0, will have support for plugins source we could create a .gem source
  # to support it.
  def extract_local_gems_plugins
    plugins_arg.collect do |plugin|
      # We do the verify before extracting the gem so we dont have to deal with unused path
      if verify?
        puts("Validating #{plugin}")
        signal_error("Installation aborted, verification failed for #{plugin}") unless LogStash::PluginManager.logstash_plugin?(plugin, version)
      end

      package, path = LogStash::Rubygems.unpack(plugin, LogStash::Environment::LOCAL_GEM_PATH)
      [package.spec.name, package.spec.version, { :path => relative_path(path) }]
    end
  end

  # We cannot install both .gem and normal plugin in one call of `plugin install`
  def local_gems?
    return false if plugins_arg.empty?

    local_gem = plugins_arg.collect { |plugin| ::File.extname(plugin) == ".gem" }.uniq

    if local_gem.size == 1
      return local_gem.first
    else
      signal_usage_error("Mixed source of plugins, you can't mix local `.gem` and remote gems")
    end
  end
end # class Logstash::PluginManager