diff --git a/logstash-core/lib/logstash/patches/clamp.rb b/logstash-core/lib/logstash/patches/clamp.rb index 1fc953718c4..bea0fd26acd 100644 --- a/logstash-core/lib/logstash/patches/clamp.rb +++ b/logstash-core/lib/logstash/patches/clamp.rb @@ -65,8 +65,21 @@ def define_reader_for(option) end def define_appender_for(option) - define_method(option.append_method) do |value| - LogStash::SETTINGS.get_value(option.attribute_name) << value + if option.attribute_name == "SETTINGS_PASSTHROUGH" + define_method(option.append_method) do |kv_pair| + key, value = kv_pair.split(/[:=]/, 2) + signal_usage_error("failed to parse setting `#{kv_pair}`") unless key && value + signal_usage_error("failed to apply setting `#{kv_pair}`: unknown setting `#{key}`") unless LogStash::SETTINGS.registered?(key) + if value.empty? + LogStash::SETTINGS.get_setting(key).reset rescue signal_usage_error("failed to reset setting `#{kv_pair}`: #{$!.message}") + else + LogStash::SETTINGS.set_value(key, value) rescue signal_usage_error("failed to apply setting `#{kv_pair}`: #{$!.message}") + end + end + else + define_method(option.append_method) do |value| + LogStash::SETTINGS.get_value(option.attribute_name) << value + end end end diff --git a/logstash-core/lib/logstash/runner.rb b/logstash-core/lib/logstash/runner.rb index 1df665712ac..d6075554285 100644 --- a/logstash-core/lib/logstash/runner.rb +++ b/logstash-core/lib/logstash/runner.rb @@ -211,6 +211,12 @@ class LogStash::Runner < Clamp::StrictCommand :attribute_name => "path.settings", :default => LogStash::SETTINGS.get_default("path.settings") + option(['--setting', '-S'], "KEY=VALUE", + "individual setting", + :attribute_name => "SETTINGS_PASSTHROUGH", + :multivalued => true, + :hidden => true) + ### DEPRECATED FLAGS ### deprecated_option ["--verbose"], :flag, I18n.t("logstash.runner.flag.verbose"), diff --git a/logstash-core/spec/logstash/runner_spec.rb b/logstash-core/spec/logstash/runner_spec.rb index 2e35abc1f82..5e325c739b0 100644 --- a/logstash-core/spec/logstash/runner_spec.rb +++ b/logstash-core/spec/logstash/runner_spec.rb @@ -94,6 +94,71 @@ end end + context "arbitrary settings" do + shared_examples "arbitrary settings" do |option_formatter| + before(:each) do + # suppress noise + allow(LogStash::Config::Source::MultiLocal).to receive(:logger).and_return(double('logger').as_null_object) + end + + [ + {'node.name' => 'my-fancy-node-name'}, # single + {'api.enabled' => false}, # single + {'node.name' => 'my-fancy-node-name', 'api.enabled' => false}, # multiple + ].each do |settings| + command_line_args = settings.flat_map { |k,v| option_formatter.call(k,v) } + + context "when specifying `#{command_line_args.join(' ')}`" do + before(:each) do + LogStash::Runner.new("").run(["--config.string=input{generator{count=>1}}", *command_line_args]) + end + + settings.each do |name, value| + it "overrides setting `#{name}` to `#{value}`" do + setting = LogStash::SETTINGS.get_setting(name) + expect(setting).to be_set + expect(setting.value).to eq value + end + end + end + end + + {'banana' => "yellow"}.tap do |settings| + command_line_args = settings.flat_map { |k,v| option_formatter.call(k,v) } + context "when specifying unregistered `#{command_line_args.join(' ')}`" do + it "raises a helpful usage error about the unknown setting" do + expect do + LogStash::Runner.new("").run(["--config.string=input{generator{count=>1}}", *command_line_args]) + end.to raise_error.with_message(/failed to apply setting `banana[:=]yellow`: unknown setting `banana`/) + end + end + end + + {'api.enabled' => "yellow"}.tap do |settings| + command_line_args = settings.flat_map { |k,v| option_formatter.call(k,v) } + context "when specifying uncoercible `#{command_line_args.join(' ')}`" do + it "raises a helpful usage error about failed coercion" do + expect do + LogStash::Runner.new("").run(["--config.string=input{generator{count=>1}}", *command_line_args]) + end.to raise_error.with_message(/failed to apply setting `api.enabled[:=]yellow`: Cannot coerce `yellow` to boolean/) + end + end + end + end + + context "-Skey=value formatted options" do + include_examples "arbitrary settings", ->(setting_name, setting_value) { "-S#{setting_name}=#{setting_value}" } + end + + context "--setting key:value formatted options" do + include_examples "arbitrary settings", ->(setting_name, setting_value) { ["--setting", "#{setting_name}:#{setting_value}"] } + end + + context "--setting=key:value formatted options" do + include_examples "arbitrary settings", ->(setting_name, setting_value) { "--setting=#{setting_name}:#{setting_value}" } + end + end + context "--pluginpath" do subject { LogStash::Runner.new("") } let(:valid_directory) { Stud::Temporary.directory }