Better method of ensuring that args are not mutated by plugin initialize

add test for subclasses of codec, filter, input and output

Register codec clone, use String clone not dup so frozen strings stay so

Fixes #4444

Fixes #4473
This commit is contained in:
Guy Boertje 2016-01-13 14:44:20 +00:00
parent 2952abedc6
commit 681719bd9a
7 changed files with 54 additions and 8 deletions

View file

@ -11,7 +11,7 @@ module LogStash::Codecs; class Base < LogStash::Plugin
def initialize(params={})
super
config_init(params)
config_init(@params)
register if respond_to?(:register)
end
@ -27,7 +27,7 @@ module LogStash::Codecs; class Base < LogStash::Plugin
raise "#{self.class}#encode must be overidden"
end # def encode
public
public
def close; end;
# @param block [Proc(event, data)] the callback proc passing the original event and the encoded event

View file

@ -120,7 +120,7 @@ class LogStash::Filters::Base < LogStash::Plugin
public
def initialize(params)
super
config_init(params)
config_init(@params)
@threadsafe = true
end # def initialize

View file

@ -53,7 +53,7 @@ class LogStash::Inputs::Base < LogStash::Plugin
super
@threadable = false
@stop_called = Concurrent::AtomicBoolean.new(false)
config_init(params)
config_init(@params)
@tags ||= []
end # def initialize

View file

@ -60,7 +60,7 @@ class LogStash::Outputs::Base < LogStash::Plugin
public
def initialize(params={})
super
config_init(params)
config_init(@params)
# If we're running with a single thread we must enforce single-threaded concurrency by default
# Maybe in a future version we'll assume output plugins are threadsafe
@ -88,4 +88,4 @@ class LogStash::Outputs::Base < LogStash::Plugin
# TODO: noop for now, remove this once we delete this call from all plugins
true
end # def output?
end # class LogStash::Outputs::Base
end # class LogStash::Outputs::Base

View file

@ -24,7 +24,7 @@ class LogStash::Plugin
public
def initialize(params=nil)
@params = params
@params = LogStash::Util.deep_clone(params)
@logger = Cabin::Channel.get(LogStash)
end

View file

@ -183,4 +183,21 @@ module LogStash::Util
o
end
end
def self.deep_clone(o)
case o
when Hash
o.inject({}) {|h, (k,v)| h[k] = deep_clone(v); h }
when Array
o.map {|v| deep_clone(v) }
when Fixnum, Symbol, IO, TrueClass, FalseClass, NilClass
o
when LogStash::Codecs::Base
o.clone.tap {|c| c.register }
when String
o.clone #need to keep internal state e.g. frozen
else
Marshal.load(Marshal.dump(o))
end
end
end # module LogStash::Util

View file

@ -108,7 +108,7 @@ describe LogStash::Plugin do
subject.validate({})
end
it 'logs a warning if the plugin use the milestone option' do
expect_any_instance_of(Cabin::Channel).to receive(:warn)
@ -120,4 +120,33 @@ describe LogStash::Plugin do
end
end
end
describe "subclass initialize" do
let(:args) { Hash.new }
[
StromaeCodec = Class.new(LogStash::Codecs::Base) do
config_name "stromae"
config :foo_tag, :validate => :string, :default => "bar"
end,
StromaeFilter = Class.new(LogStash::Filters::Base) do
config_name "stromae"
config :foo_tag, :validate => :string, :default => "bar"
end,
StromaeInput = Class.new(LogStash::Inputs::Base) do
config_name "stromae"
config :foo_tag, :validate => :string, :default => "bar"
end,
StromaeOutput = Class.new(LogStash::Outputs::Base) do
config_name "stromae"
config :foo_tag, :validate => :string, :default => "bar"
end
].each do |klass|
it "subclass #{klass.name} does not modify params" do
instance = klass.new(args)
expect(args).to be_empty
end
end
end
end