mirror of
https://github.com/elastic/logstash.git
synced 2025-06-28 17:53:28 -04:00
Removal works in a single pass by finding plugins that would have unmet dependencies if all of the specified plugins were to be removed, and proceeding with the removal only if no conflicts were created. > ~~~ > ╭─{ rye@perhaps:~/src/elastic/logstash@main (pluginmanager-remove-multiple ✘) } > ╰─● bin/logstash-plugin remove logstash-input-syslog logstash-filter-grok > Using system java: /Users/rye/.jenv/shims/java > Resolving dependencies...... > Successfully removed logstash-input-syslog > Successfully removed logstash-filter-grok > [success (00:00:05)] ~~~
110 lines
4 KiB
Ruby
110 lines
4 KiB
Ruby
# Licensed to Elasticsearch B.V. under one or more contributor
|
|
# license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright
|
|
# ownership. Elasticsearch B.V. licenses this file to you under
|
|
# the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
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
|
|
|
|
def uninstall!(gems_to_remove)
|
|
gems_to_remove = Array(gems_to_remove)
|
|
|
|
unsatisfied_dependency_mapping = Dsl.evaluate(gemfile_path, lockfile_path, {}).specs.each_with_object({}) do |spec, memo|
|
|
next if gems_to_remove.include?(spec.name)
|
|
deps = spec.runtime_dependencies.collect(&:name)
|
|
deps.intersection(gems_to_remove).each do |missing_dependency|
|
|
memo[missing_dependency] ||= []
|
|
memo[missing_dependency] << spec.name
|
|
end
|
|
end
|
|
if unsatisfied_dependency_mapping.any?
|
|
unsatisfied_dependency_mapping.each { |gem_to_remove, gems_depending_on_removed| display_cant_remove_message(gem_to_remove, gems_depending_on_removed) }
|
|
LogStash::PluginManager.ui.info("No plugins were removed.")
|
|
return false
|
|
end
|
|
|
|
with_mutable_gemfile do |gemfile|
|
|
gems_to_remove.each { |gem_name| gemfile.remove(gem_name) }
|
|
|
|
builder = Dsl.new
|
|
builder.eval_gemfile(::File.join(::File.dirname(gemfile_path), "gemfile to changes"), gemfile.generate)
|
|
|
|
# build a definition, providing an intentionally-empty "unlock" mapping
|
|
# to ensure that all gem versions remain locked
|
|
definition = builder.to_definition(lockfile_path, {})
|
|
|
|
# lock the definition and save our modified gemfile
|
|
definition.lock(lockfile_path)
|
|
gemfile.save
|
|
|
|
gems_to_remove.each do |gem_name|
|
|
LogStash::PluginManager.ui.info("Successfully removed #{gem_name}")
|
|
end
|
|
|
|
return true
|
|
end
|
|
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
|
|
Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true) if Bundler.settings[:frozen]
|
|
|
|
Bundler.settings.temporary(:frozen => false) do
|
|
yield
|
|
end
|
|
end
|
|
|
|
##
|
|
# Yields a mutable gemfile backed by an open, writable file handle.
|
|
# It is the responsibility of the caller to send `LogStash::Gemfile#save` to persist the result.
|
|
# @yieldparam [LogStash::Gemfile]
|
|
def with_mutable_gemfile
|
|
unfreeze_gemfile do
|
|
File.open(gemfile_path, 'r+') do |file|
|
|
yield LogStash::Gemfile.new(file).load
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.uninstall!(gem_names, options={})
|
|
gemfile_path = options[:gemfile] || LogStash::Environment::GEMFILE
|
|
lockfile_path = options[:lockfile] || LogStash::Environment::LOCKFILE
|
|
LogstashUninstall.new(gemfile_path, lockfile_path).uninstall!(Array(gem_names))
|
|
end
|
|
end
|
|
end
|