forked from elastic/logstash
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature: A way to install/remove a plugin pack
A pack in this context is a *bundle* of plugins that can be distributed outside of rubygems; it is similar to what ES and kibana are doing, and the user interface is modeled after them. See https://www.elastic.co/downloads/x-pack **Do not mix it with the `bin/logstash-plugin pack/unpack` command.** - it contains one or more plugins that need to be installed - it is self-contains with the gems and the needed jars - it is distributed as a zip file - the file structure needs to follow some rules. - As a reserved name name on elastic.co download http server - `bin/plugin install logstash-mypack` will check on the download server if a pack for the current specific logstash version exist and it will be downloaded, if it doesn't exist we fallback on rubygems. - The file on the server will follow this convention `logstash-mypack-{LOGSTASH_VERSION}.zip` - As a fully qualified url - `bin/plugin install http://test.abc/logstash-mypack.zip`, if it exists it will be downloaded and installed if it does not we raise an error. - As a local file - `bin/plugin install file:///tmp/logstash-mypack.zip`, if it exists it will be installed Fixes elastic#6168
- Loading branch information
Showing
47 changed files
with
1,629 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
.*.swp | ||
*.gem | ||
/*.gem | ||
logstash*/*.gem | ||
pkg/*.deb | ||
pkg/*.rpm | ||
*.class | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# encoding: utf-8 | ||
require "bootstrap/environment" | ||
require "bundler" | ||
require "bundler/definition" | ||
require "bundler/dependency" | ||
require "bundler/dsl" | ||
require "bundler/injector" | ||
|
||
# This class cannot be in the logstash namespace, because of the way the DSL | ||
# class interact with the other libraries | ||
module Bundler | ||
class LogstashInjector < ::Bundler::Injector | ||
def self.inject!(new_deps, options = { :gemfile => LogStash::Environment::GEMFILE, :lockfile => LogStash::Environment::LOCKFILE }) | ||
gemfile = options.delete(:gemfile) | ||
lockfile = options.delete(:lockfile) | ||
|
||
bundler_format = Array(new_deps).collect { |plugin| ::Bundler::Dependency.new(plugin.name, "=#{plugin.version}")} | ||
|
||
injector = new(bundler_format) | ||
injector.inject(gemfile, lockfile) | ||
end | ||
|
||
|
||
# This class is pretty similar to what bundler's injector class is doing | ||
# but we only accept a local resolution of the dependencies instead of calling rubygems. | ||
# so we removed `definition.resolve_remotely!` | ||
def inject(gemfile_path, lockfile_path) | ||
if Bundler.settings[:frozen] | ||
# ensure the lock and Gemfile are synced | ||
Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true) | ||
# temporarily remove frozen while we inject | ||
frozen = Bundler.settings.delete(:frozen) | ||
end | ||
|
||
builder = Dsl.new | ||
builder.eval_gemfile(gemfile_path) | ||
|
||
@new_deps -= builder.dependencies | ||
|
||
builder.eval_gemfile("injected gems", new_gem_lines) if @new_deps.any? | ||
definition = builder.to_definition(lockfile_path, {}) | ||
append_to(gemfile_path) if @new_deps.any? | ||
definition.lock(lockfile_path) | ||
|
||
return @new_deps | ||
ensure | ||
Bundler.settings[:frozen] = "1" if frozen | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# encoding: utf-8 | ||
require "bootstrap/environment" | ||
require "bundler" | ||
require "bundler/definition" | ||
require "bundler/dependency" | ||
require "bundler/dsl" | ||
require "bundler/injector" | ||
require "pluginmanager/gemfile" | ||
|
||
# This class cannot be in the logstash namespace, because of the way the DSL | ||
# class interact with the other libraries | ||
module Bundler | ||
class LogstashUninstall | ||
attr_reader :gemfile_path, :lockfile_path | ||
|
||
def initialize(gemfile_path, lockfile_path) | ||
@gemfile_path = gemfile_path | ||
@lockfile_path = lockfile_path | ||
end | ||
|
||
# To be uninstalled the candidate gems need to be standalone. | ||
def dependants_gems(gem_name) | ||
builder = Dsl.new | ||
builder.eval_gemfile("original gemfile", File.read(gemfile_path)) | ||
definition = builder.to_definition(lockfile_path, {}) | ||
|
||
definition.specs | ||
.select { |spec| spec.dependencies.collect(&:name).include?(gem_name) } | ||
.collect(&:name).sort.uniq | ||
end | ||
|
||
def uninstall!(gem_name) | ||
unfreeze_gemfile do | ||
|
||
dependencies_from = dependants_gems(gem_name) | ||
|
||
if dependencies_from.size > 0 | ||
display_cant_remove_message(gem_name, dependencies_from) | ||
false | ||
else | ||
remove_gem(gem_name) | ||
true | ||
end | ||
end | ||
end | ||
|
||
def remove_gem(gem_name) | ||
builder = Dsl.new | ||
file = File.new(gemfile_path, "r+") | ||
|
||
gemfile = LogStash::Gemfile.new(file).load | ||
gemfile.remove(gem_name) | ||
builder.eval_gemfile("gemfile to changes", gemfile.generate) | ||
|
||
definition = builder.to_definition(lockfile_path, {}) | ||
definition.lock(lockfile_path) | ||
gemfile.save | ||
|
||
LogStash::PluginManager.ui.info("Successfully removed #{gem_name}") | ||
ensure | ||
file.close if file | ||
end | ||
|
||
def display_cant_remove_message(gem_name, dependencies_from) | ||
message =<<-eos | ||
Failed to remove \"#{gem_name}\" because the following plugins or libraries depend on it: | ||
* #{dependencies_from.join("\n* ")} | ||
eos | ||
LogStash::PluginManager.ui.info(message) | ||
end | ||
|
||
def unfreeze_gemfile | ||
if Bundler.settings[:frozen] | ||
Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true) | ||
frozen = Bundler.settings.delete(:frozen) | ||
end | ||
yield | ||
ensure | ||
Bundler.settings[:frozen] = "1" if frozen | ||
end | ||
|
||
def self.uninstall!(gem_name, options = { :gemfile => LogStash::Environment::GEMFILE, :lockfile => LogStash::Environment::LOCKFILE }) | ||
gemfile_path = options[:gemfile] | ||
lockfile_path = options[:lockfile] | ||
LogstashUninstall.new(gemfile_path, lockfile_path).uninstall!(gem_name) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# encoding: utf-8 | ||
module LogStash module PluginManager | ||
class PluginManagerError < StandardError; end | ||
class FileNotFoundError < PluginManagerError; end | ||
class InvalidPackError < PluginManagerError; end | ||
class InstallError < PluginManagerError | ||
attr_reader :original_exception | ||
|
||
def initialize(original_exception) | ||
@original_exception = original_exception | ||
end | ||
end | ||
end end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# encoding: utf-8 | ||
require "pluginmanager/ui" | ||
require "pathname" | ||
require "rubygems/package" | ||
|
||
module LogStash module PluginManager | ||
# Install a physical gem package to the appropriate location inside logstash | ||
# - Extract the gem | ||
# - Generate the specifications | ||
# - Copy the data in the right folders | ||
class GemInstaller | ||
GEM_HOME = Pathname.new(::File.join(LogStash::Environment::BUNDLE_DIR, "jruby", "1.9")) | ||
SPECIFICATIONS_DIR = "specifications" | ||
GEMS_DIR = "gems" | ||
|
||
attr_reader :gem_home | ||
|
||
def initialize(gem_file, display_post_install_message = false, gem_home = GEM_HOME) | ||
@gem = ::Gem::Package.new(gem_file) | ||
@gem_home = Pathname.new(gem_home) | ||
@display_post_install_message = display_post_install_message | ||
end | ||
|
||
def install | ||
create_destination_folders | ||
extract_files | ||
write_specification | ||
display_post_install_message | ||
end | ||
|
||
def self.install(gem_file, display_post_install_message = false, gem_home = GEM_HOME) | ||
self.new(gem_file, display_post_install_message, gem_home).install | ||
end | ||
|
||
private | ||
def spec | ||
@gem.spec | ||
end | ||
|
||
def spec_dir | ||
gem_home.join(SPECIFICATIONS_DIR) | ||
end | ||
|
||
def spec_file | ||
spec_dir.join("#{spec.full_name}.gemspec") | ||
end | ||
|
||
def gem_dir | ||
gem_home.join(GEMS_DIR, spec.full_name) | ||
end | ||
|
||
def extract_files | ||
@gem.extract_files gem_dir | ||
end | ||
|
||
def write_specification | ||
::File.open(spec_file, 'w') do |file| | ||
spec.installed_by_version = ::Gem.rubygems_version | ||
file.puts spec.to_ruby_for_cache | ||
file.fsync rescue nil # Force writing to disk | ||
end | ||
end | ||
|
||
def display_post_install_message | ||
PluginManager.ui.info(spec.post_install_message) if display_post_install_message? | ||
end | ||
|
||
def display_post_install_message? | ||
@display_post_install_message && !spec.post_install_message.nil? | ||
end | ||
|
||
def create_destination_folders | ||
FileUtils.mkdir_p(gem_home) | ||
FileUtils.mkdir_p(gem_dir) | ||
FileUtils.mkdir_p(spec_dir) | ||
end | ||
end | ||
end end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.