Skip to content

Commit 3e43664

Browse files
committed
Backport PR #13015 to 7.15: Bundler: freeze lockfile on run, and "normalize" platform on plugin changes
Backport PR #13015 to 7.15 branch. Original Message: This PR enables the upgrade of bundler to the latest version. Prior to this PR, the ability to do so was blocked by bundler.setup in versions of bundler > `2.23` making runtime changes to `Gemfile.lock` (unless the lock file was `frozen`) based on the specific platform the application was being run on, overriding any platforms (including generic `java` platform) set during build time. This was in conflict with changes made in #12782, which prevented the logstash user writing to files in `/usr/share/logstash`. This PR will freeze the lockfile when logstash is run, and unfreeze it when manipulating plugins (install, update, remove, install from offline pack) to allow new plugins to be added. While unfrozen, changes are also made to ensure that the platform list remains as the generic `java` platform, and not changed to the specific platform for the runtime JVM. This PR also introduces a new runtime flag, `--enable-local-plugin-development`. This flag is intended for use by Logstash developers only, and enables a mode of operation where a Gemfile can be manipulated, eg ``` gem "logstash-integration-kafka", :path => '/users/developer/code/plugins/logstash-integration-kafka' ``` to facilitate quick and simple plugin testing. This PR also sets the `silence_root_warning` flag to avoid bundler printing out alarming looking warning messages when `sudo` is used. This warning message was concerning for users - it would be printed out during normal operation of `bin/logstash-plugin install/update/remove` when run under `sudo`, which is the expected mode of operation when logstash is installed to run as a service via rpm/deb packages. This PR also updates the vagrant based integration tests to ensure that Logstash still runs after plugin update/install/remove operations, fixes up some regular expressions that would cause test failures, and removes some dead code from tests. * Updated Bundler to latest version * Ensured that `Gemfile.lock` are appropriately frozen * Added new developer-only flag to facilitate local plugin development to allow unfrozen lockfile in a development environment (cherry picked from commit 4707cb)
1 parent 10e3cc0 commit 3e43664

File tree

28 files changed

+207
-82
lines changed

28 files changed

+207
-82
lines changed

ci/acceptance_tests.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ QA_DIR="$PWD/qa"
2727

2828
# Always run the halt, even if the test times out or an exit is sent
2929
cleanup() {
30+
3031
cd $QA_DIR
3132
bundle check || bundle install
3233
bundle exec rake qa:vm:halt
@@ -39,6 +40,7 @@ cleanup
3940
if [[ $SELECTED_TEST_SUITE == $"redhat" ]]; then
4041
echo "Generating the RPM, make sure you start with a clean environment before generating other packages."
4142
cd $LS_HOME
43+
./gradlew clean bootstrap
4244
rake artifact:rpm
4345
echo "Acceptance: Installing dependencies"
4446
cd $QA_DIR
@@ -52,6 +54,7 @@ if [[ $SELECTED_TEST_SUITE == $"redhat" ]]; then
5254
elif [[ $SELECTED_TEST_SUITE == $"debian" ]]; then
5355
echo "Generating the DEB, make sure you start with a clean environment before generating other packages."
5456
cd $LS_HOME
57+
./gradlew clean bootstrap
5558
rake artifact:deb
5659
echo "Acceptance: Installing dependencies"
5760
cd $QA_DIR
@@ -65,6 +68,7 @@ elif [[ $SELECTED_TEST_SUITE == $"debian" ]]; then
6568
elif [[ $SELECTED_TEST_SUITE == $"all" ]]; then
6669
echo "Building Logstash artifacts"
6770
cd $LS_HOME
71+
./gradlew clean bootstrap
6872
rake artifact:all
6973

7074
echo "Acceptance: Installing dependencies"

docs/static/running-logstash-command-line.asciidoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,5 +227,14 @@ With this command, Logstash concatenates three config files, `/tmp/one`, `/tmp/t
227227
as the log4j logging configuration. This can also be set through the LS_SETTINGS_DIR environment variable.
228228
The default is the `config` directory under Logstash home.
229229

230+
*`--enable-local-plugin-development`*::
231+
This flag enables developers to update their local Gemfile without running into issues caused by a frozen lockfile.
232+
This flag can be helpful when you are developing/testing plugins locally.
233+
234+
NOTE: This flag is for Logstash developers only. End users should not need it.
235+
236+
237+
238+
230239
*`-h, --help`*::
231240
Print help

lib/bootstrap/bundler.rb

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def setup!(options = {})
7373
# in the context of Bundler.setup it looks like this is useless here because Gemfile path can only be specified using
7474
# the ENV, see https://github.com/bundler/bundler/blob/v1.8.3/lib/bundler/shared_helpers.rb#L103
7575
::Bundler.settings.set_local(:gemfile, Environment::GEMFILE_PATH)
76-
76+
::Bundler.settings.set_local(:frozen, true) unless options[:allow_gemfile_changes]
7777
::Bundler.reset!
7878
::Bundler.setup
7979
end
@@ -95,7 +95,6 @@ def invoke!(options = {})
9595
:jobs => 12, :all => false, :package => false, :without => [:development]}.merge(options)
9696
options[:without] = Array(options[:without])
9797
options[:update] = Array(options[:update]) if options[:update]
98-
9998
::Gem.clear_paths
10099
ENV['GEM_HOME'] = ENV['GEM_PATH'] = LogStash::Environment.logstash_gem_home
101100
::Gem.paths = ENV
@@ -128,13 +127,18 @@ def invoke!(options = {})
128127
::Bundler.settings.set_local(:gemfile, LogStash::Environment::GEMFILE_PATH)
129128
::Bundler.settings.set_local(:without, options[:without])
130129
::Bundler.settings.set_local(:force, options[:force])
131-
132-
if !debug?
133-
# Will deal with transient network errors
134-
execute_bundler_with_retry(options)
135-
else
136-
options[:verbose] = true
137-
execute_bundler(options)
130+
# This env setting avoids the warning given when bundler is run as root, as is required
131+
# to update plugins when logstash is run as a service
132+
# Note: Using an `ENV` here because ::Bundler.settings.set_local(:silence_root_warning, true)
133+
# does not work (set_global *does*, but that seems too drastic a change)
134+
with_env("BUNDLE_SILENCE_ROOT_WARNING" => "true") do
135+
if !debug?
136+
# Will deal with transient network errors
137+
execute_bundler_with_retry(options)
138+
else
139+
options[:verbose] = true
140+
execute_bundler(options)
141+
end
138142
end
139143
end
140144

@@ -176,6 +180,18 @@ def execute_bundler(options)
176180
::Bundler::CLI.start(bundler_arguments(options))
177181
end
178182

183+
def specific_platforms(platforms=::Gem.platforms)
184+
platforms.find_all {|plat| plat.is_a?(::Gem::Platform) && plat.os=='java' && !plat.cpu.nil?}
185+
end
186+
187+
def genericize_platform
188+
output = LogStash::Bundler.invoke!({:add_platform => 'java'})
189+
specific_platforms.each do |platform|
190+
output << LogStash::Bundler.invoke!({:remove_platform => platform})
191+
end
192+
output
193+
end
194+
179195
def debug?
180196
ENV["DEBUG"]
181197
end
@@ -185,7 +201,6 @@ def debug?
185201
# @return [Array<String>] Bundler::CLI.start string arguments array
186202
def bundler_arguments(options = {})
187203
arguments = []
188-
189204
if options[:install]
190205
arguments << "install"
191206
arguments << "--clean" if options[:clean]
@@ -202,14 +217,30 @@ def bundler_arguments(options = {})
202217
elsif options[:package]
203218
arguments << "package"
204219
arguments << "--all" if options[:all]
220+
elsif options[:add_platform]
221+
arguments << "lock"
222+
arguments << "--add_platform"
223+
arguments << options[:add_platform]
224+
elsif options[:remove_platform]
225+
arguments << "lock"
226+
arguments << "--remove_platform"
227+
arguments << options[:remove_platform]
205228
end
206229

207230
arguments << "--verbose" if options[:verbose]
208-
209231
arguments.flatten
210232
end
211233

212-
# capture any $stdout from the passed block. also trap any exception in that block, in which case the trapped exception will be returned
234+
def with_env(modifications)
235+
backup_env = ENV.to_hash
236+
ENV.replace(backup_env.merge(modifications))
237+
238+
yield
239+
ensure
240+
ENV.replace(backup_env)
241+
end
242+
243+
# capture any $stdout from the passed block. also trap any exception in that block, in which case the trapped exception will be returned
213244
# @param [Proc] the code block to execute
214245
# @return [String, Exception] the captured $stdout string and any trapped exception or nil if none
215246
def capture_stdout(&block)

lib/bootstrap/environment.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,12 @@ def pattern_path(path)
8181
# defined and exposing the LogStash::Runner#main instance method which will be called with the current ARGV
8282
# currently lib/logstash/runner.rb and lib/pluginmanager/main.rb are called using this.
8383
if $0 == __FILE__
84-
LogStash::Bundler.setup!({:without => [:build, :development]})
84+
bundler_options = {:without => [:build, :development]}
85+
## Check for dev flags - this needs to be done before the runner is invoked to set bundler options
86+
if ARGV.include?("--enable-local-plugin-development")
87+
bundler_options[:allow_gemfile_changes] = true
88+
end
89+
LogStash::Bundler.setup!(bundler_options)
8590
require_relative "patches/jar_dependencies"
8691

8792
require ARGV.shift

lib/pluginmanager/bundler/logstash_injector.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def inject(gemfile_path, lockfile_path, dependencies)
9393

9494
builder.eval_gemfile("bundler file", gemfile.generate())
9595
definition = builder.to_definition(lockfile_path, {})
96+
LogStash::Bundler.specific_platforms(definition.platforms).each do |specific_platform|
97+
definition.remove_platform(specific_platform)
98+
end
99+
definition.add_platform(Gem::Platform.new('java'))
96100
definition.lock(lockfile_path)
97101
gemfile.save
98102
rescue => e

lib/pluginmanager/install.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,12 @@ def install_gems_list!(install_list)
202202
bundler_options[:without] = [] if development?
203203
bundler_options[:rubygems_source] = gemfile.gemset.sources
204204
bundler_options[:local] = true if local?
205-
206-
output = LogStash::Bundler.invoke!(bundler_options)
207-
205+
output = nil
206+
# Unfreeze the bundle when installing gems
207+
Bundler.settings.temporary({:frozen => false}) do
208+
output = LogStash::Bundler.invoke!(bundler_options)
209+
output << LogStash::Bundler.genericize_platform.to_s
210+
end
208211
puts("Installation successful")
209212
rescue => exception
210213
gemfile.restore!

lib/pluginmanager/remove.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def execute
5454
signal_error("This plugin has not been previously installed") unless LogStash::PluginManager.installed_plugin?(plugin, gemfile)
5555

5656
exit(1) unless ::Bundler::LogstashUninstall.uninstall!(plugin)
57-
57+
LogStash::Bundler.genericize_platform
5858
remove_unused_locally_installed_gems!
5959
rescue => exception
6060
report_exception("Operation aborted, cannot remove plugin", exception)

lib/pluginmanager/update.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,13 @@ def update_gems!
8080
# Bundler cannot update and clean gems in one operation so we have to call the CLI twice.
8181
options = {:update => plugins, :rubygems_source => gemfile.gemset.sources}
8282
options[:local] = true if local?
83-
output = LogStash::Bundler.invoke!(options)
83+
output=nil
84+
# Unfreeze the bundle when updating gems
85+
Bundler.settings.temporary({:frozen => false}) do
86+
output = LogStash::Bundler.invoke!(options)
87+
output << LogStash::Bundler.genericize_platform unless output.nil?
88+
end
89+
8490
# We currently dont removed unused gems from the logstash installation
8591
# see: https://github.com/elastic/logstash/issues/6339
8692
# output = LogStash::Bundler.invoke!(:clean => true)

logstash-core/lib/logstash/environment.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ module Environment
6969
Setting::String.new("log.level", "info", true, ["fatal", "error", "warn", "debug", "info", "trace"]),
7070
Setting::Boolean.new("version", false),
7171
Setting::Boolean.new("help", false),
72+
Setting::Boolean.new("enable-local-plugin-development", false),
7273
Setting::String.new("log.format", "plain", true, ["json", "plain"]),
7374
Setting::Boolean.new("http.enabled", true),
7475
Setting::String.new("http.host", "127.0.0.1"),

logstash-core/lib/logstash/runner.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ class LogStash::Runner < Clamp::StrictCommand
7373
:attribute_name => "node.name",
7474
:default => LogStash::SETTINGS.get_default("node.name")
7575

76+
option ["--enable-local-plugin-development"], :flag,
77+
I18n.t("logstash.runner.flag.enable-local-plugin-development"),
78+
:attribute_name => "enable-local-plugin-development",
79+
:default => LogStash::SETTINGS.get_default("enable-local-plugin-development")
80+
7681
# Config Settings
7782
option ["-f", "--path.config"], "CONFIG_PATH",
7883
I18n.t("logstash.runner.flag.config"),

0 commit comments

Comments
 (0)