Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin manager: add --level=[major|minor|patch] (default: minor) #16899

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/static/plugin-manager.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@ bin/logstash-plugin update logstash-input-github <2>
<1> updates all installed plugins
<2> updates only the plugin you specify

[NOTE]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preview: https://logstash_bk_16899.docs-preview.app.elstc.co/guide/en/logstash/master/working-with-plugins.html#updating-plugins

I added this as an admonition because in the normal case users shouldn't need to concern themselves with this and will be happy to get the latest fully-compatible version available. It is rare that a user would want to jump across major versions of a plugin because we are taking care to backport non-breaking features and fixes to the previous minor.

.Major-version plugin updates
==============================
By default, the plugin manager will consume _minor_ updates containing non-breaking features and fixes.
yaauie marked this conversation as resolved.
Show resolved Hide resolved
If you wish to also include breaking changes, specify `--level=major`.
[source,shell]
----------------------------------
bin/logstash-plugin update --level=major <1>
bin/logstash-plugin update --level=major logstash-input-github <2>
----------------------------------
<1> updates all installed plugins to latest, including major-version breaking changes
<2> updates only the plugin you specify to latest, including major-version breaking changes
==============================

[discrete]
[[removing-plugins]]
=== Removing plugins
Expand Down
1 change: 1 addition & 0 deletions lib/bootstrap/bundler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ def bundler_arguments(options = {})
elsif options[:update]
arguments << "update"
arguments << expand_logstash_mixin_dependencies(options[:update])
arguments << "--#{options[:level] || 'minor'}"
arguments << "--local" if options[:local]
arguments << "--conservative" if options[:conservative]
elsif options[:clean]
Expand Down
7 changes: 7 additions & 0 deletions lib/pluginmanager/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ class LogStash::PluginManager::Update < LogStash::PluginManager::Command
# These are local gems used by LS and needs to be filtered out of other plugin gems
NON_PLUGIN_LOCAL_GEMS = ["logstash-core", "logstash-core-plugin-api"]

SUPPORTED_LEVELS = %w(major minor patch)

parameter "[PLUGIN] ...", "Plugin name(s) to upgrade to latest version", :attribute_name => :plugins_arg
option "--level", "LEVEL", "restrict updates to given semantic version level (one of #{SUPPORTED_LEVELS})", :default => "minor" do |given_level|
fail("unsupported level `#{given_level}`; expected one of #{SUPPORTED_LEVELS}") unless SUPPORTED_LEVELS.include?(given_level)
given_level
end
option "--[no-]verify", :flag, "verify plugin validity before installation", :default => true
option "--local", :flag, "force local-only plugin update. see bin/logstash-plugin package|unpack", :default => false
option "--[no-]conservative", :flag, "do a conservative update of plugin's dependencies", :default => true
Expand Down Expand Up @@ -82,6 +88,7 @@ def update_gems!
# Bundler cannot update and clean gems in one operation so we have to call the CLI twice.
Bundler.settings.temporary(:frozen => false) do # Unfreeze the bundle when updating gems
output = LogStash::Bundler.invoke! update: plugins,
level: level,
rubygems_source: gemfile.gemset.sources,
local: local?,
conservative: conservative?
Expand Down
29 changes: 28 additions & 1 deletion spec/unit/bootstrap/bundler_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,41 @@
end
end

context "level: major" do
let(:options) { super().merge(:level => "major") }
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--major")
end
end

context "level: minor" do
let(:options) { super().merge(:level => "minor") }
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--minor")
end
end

context "level: patch" do
let(:options) { super().merge(:level => "patch") }
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--patch")
end
end

context "level: unspecified" do
it "invokes bundler with --minor" do
expect(bundler_arguments).to include("--minor")
end
end

context 'with ecs_compatibility' do
let(:plugin_name) { 'logstash-output-elasticsearch' }
let(:options) { { :update => plugin_name } }

it "also update dependencies" do
expect(bundler_arguments).to include('logstash-mixin-ecs_compatibility_support', plugin_name)

mixin_libs = bundler_arguments - ["update", plugin_name]
mixin_libs = bundler_arguments - ["update", "--minor", plugin_name]
mixin_libs.each do |gem_name|
dep = ::Gem::Dependency.new(gem_name)
expect(dep.type).to eq(:runtime)
Expand Down
60 changes: 52 additions & 8 deletions spec/unit/plugin_manager/update_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,24 @@
describe LogStash::PluginManager::Update do
let(:cmd) { LogStash::PluginManager::Update.new("update") }
let(:sources) { cmd.gemfile.gemset.sources }
let(:expect_preflight_error) { false } # hack to bypass before-hook expectations

before(:each) do
expect(cmd).to receive(:find_latest_gem_specs).and_return({})
allow(cmd).to receive(:warn_local_gems).and_return(nil)
expect(cmd).to receive(:display_updated_plugins).and_return(nil)
unless expect_preflight_error
expect(cmd).to receive(:find_latest_gem_specs).and_return({})
allow(cmd).to receive(:warn_local_gems).and_return(nil)
expect(cmd).to receive(:display_updated_plugins).and_return(nil)
end
end

it "pass all gem sources to the bundle update command" do
sources = cmd.gemfile.gemset.sources
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(
:update => [], :rubygems_source => sources,
:conservative => true, :local => false
:update => [],
:rubygems_source => sources,
:conservative => true,
:local => false,
:level => "minor" # default
)
cmd.execute
end
Expand All @@ -46,14 +52,52 @@
expect(cmd.gemfile).to receive(:save).and_return(nil)
expect(cmd).to receive(:plugins_to_update).and_return([plugin])
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(
hash_including(:update => [plugin], :rubygems_source => sources)
hash_including(:update => [plugin], :rubygems_source => sources, :level => "minor")
).and_return(nil)
end

it "skips version verification when ask for it" do
cmd.verify = false
expect(cmd).to_not receive(:validates_version)
cmd.execute
cmd.run(["--no-verify"])
end
end

context "with explicit `--level` flag" do
LogStash::PluginManager::Update::SUPPORTED_LEVELS.each do |level|
context "with --level=#{level} (valid)" do
let(:requested_level) { level }

let(:cmd) { LogStash::PluginManager::Update.new("update") }
let(:plugin) { OpenStruct.new(:name => "dummy", :options => {}) }

before(:each) do
cmd.verify = false
end

it "propagates the level flag as an option to Bundler#invoke!" do
expect(cmd.gemfile).to receive(:find).with(plugin).and_return(plugin)
expect(cmd.gemfile).to receive(:save).and_return(nil)
expect(cmd).to receive(:plugins_to_update).and_return([plugin])
expect_any_instance_of(LogStash::Bundler).to receive(:invoke!).with(
hash_including(:update => [plugin], :rubygems_source => sources, :level => requested_level)
).and_return(nil)

cmd.run(["--level=#{requested_level}"])
end
end
end

context "with --level=eVeRyThInG (invalid)" do
let(:requested_level) { "eVeRyThInG" }
let(:expect_preflight_error) { true }

let(:cmd) { LogStash::PluginManager::Update.new("update") }
let(:plugin) { OpenStruct.new(:name => "dummy", :options => {}) }

it "errors helpfully" do
expect { cmd.run(["--level=#{requested_level}"]) }
.to raise_error.with_message(including("unsupported level `#{requested_level}`"))
end
end
end
end
Loading