logstash-mixin-ecs_compatibility_support 1.1.0-java → 1.2.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08a8069171fd7a69e9adec65ef751e693ef6a3979e7d9b6346773f880d07a753'
4
- data.tar.gz: 7ff7dc6c1bbabc1db323e253dc2c7e99bd7e08b701b59ce3e69bda5dc2c3012d
3
+ metadata.gz: a6ff73bf503783cc17b6fbc2ebebcf23398d52ffa01cfc2a9d397566833f9706
4
+ data.tar.gz: 69d950da27ad60c8fdb09a08e72f7a4ffce9ede7e85cad0bfb130854cf40a07b
5
5
  SHA512:
6
- metadata.gz: 732274d0877f02583087f9181e862c8ce47c4eb8a8c8aba42557b5e149e3d17cfdabb5b23c240107c38194f730511f107084fe4d5ba2bd376fb56b36382ba811
7
- data.tar.gz: d80b1895da371395f72dab57cb2cb61f858f68d9a0a001416ab7406ebb94c4ba9fbe74efeab7adc9e22ae7be50a2c58f7b628aa1c3de7e1bcac823a0cbd74865
6
+ metadata.gz: 5334c125b5e9b24567500ae94546b3acd5260d7f83232eaa173439d68e8d974ca294f4a3f5b0c33d0d760ac4d843ea67bcb920b0713f480848e6d5044a43895b
7
+ data.tar.gz: 3b5bddca9732f6f03f79d4bf8ebf0dbeedb120dd7b23703f68c7e8410f5896e7124e343a2a843ce0346eeee6463c02bf738d1add11fed102f7c12e4147707e8f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
- # 1.0.0
1
+ # 1.2.0
2
+ - Added support for resolution aliases, allowing a plugin that uses `ecs_select` to support multiple ECS versions with a single declaration.
3
+
4
+ # 1.1.0
5
+ - Added support for `ecs_select` helper, allowing plugins to declare mappings that are selected during plugin instantiation.
2
6
 
7
+ # 1.0.0
3
8
  - Support Mixin for ensuring a plugin has an `ecs_compatibility` method that is configurable from an `ecs_compatibility` option that accepts the literal `disabled` or a v-prefixed integer representing a major ECS version (e.g., `v1`), using the implementation from Logstash core if available.
data/README.md CHANGED
@@ -67,13 +67,13 @@ during initialization based on the instantiated plugin's effective
67
67
  mappings, because it allows those mappings to be side-by-side where they are
68
68
  unlikely to diverge and introduce bugs.
69
69
 
70
- 1. Add version `~>1.1` of this gem as a runtime dependency of your Logstash plugin's `gemspec`:
70
+ 1. Add version `~>1.2` of this gem as a runtime dependency of your Logstash plugin's `gemspec`:
71
71
 
72
72
  ~~~ ruby
73
73
  Gem::Specification.new do |s|
74
74
  # ...
75
75
 
76
- s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.1'
76
+ s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.2'
77
77
  end
78
78
  ~~~
79
79
 
@@ -95,6 +95,20 @@ unlikely to diverge and introduce bugs.
95
95
  whether that mode was explicitly defined for the plugin instance or implictly
96
96
  defined by the pipeline in which the plugin is run.
97
97
 
98
+ You can also optionally provide an alias mapping, for when your plugin supports
99
+ multiple versions of ECS that are largely identical to each other. This can be
100
+ especially helpful when using `ecs_select`.
101
+
102
+ ~~~ ruby
103
+ require 'logstash/plugin_mixins/ecs_compatibility_support'
104
+
105
+ class LogStash::Inputs::Foo < Logstash::Inputs::Base
106
+ include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled,:v1,:v8 => :v1)
107
+
108
+ # ...
109
+ end
110
+ ~~~
111
+
98
112
  3. As in the simple usage example, you can use the `ecs_compatibility` method.
99
113
 
100
114
  But when supported versions are specified, you can also use the `ecs_select`
@@ -108,6 +122,9 @@ unlikely to diverge and introduce bugs.
108
122
  end
109
123
  ~~~
110
124
 
125
+ If you initialized the mixin with an alias mapping, missing values will
126
+ be resolved by their alias.
127
+
111
128
  NOTE: `ecs_select` should only be used during plugin initialization and
112
129
  not during event-by-event processing.
113
130
 
@@ -101,6 +101,10 @@ module LogStash
101
101
  end
102
102
  end
103
103
 
104
+ ##
105
+ # @override ECSCompatibilitySupport(*supported_versions, alias_map={})
106
+ # @param supported_versions [Array[Symbol]]: the supported ECS versions
107
+ # @param alias_map [Hash{Symbol=>Symbol}]: an optional mapping of aliases (keys) to supported version (values)
104
108
  def self.ECSCompatibilitySupport(*supported_versions)
105
109
  return ECSCompatibilitySupport if supported_versions.empty?
106
110
 
@@ -26,14 +26,31 @@ module LogStash
26
26
  base.include(ECSCompatibilitySupport)
27
27
  end
28
28
 
29
+ EMPTY_HASH = {}.freeze
30
+ private_constant :EMPTY_HASH
31
+
29
32
  ##
30
33
  # @api private
31
34
  # @see ECSCompatibilitySupport()
32
35
  # @param ecs_modes_supported
33
- def initialize(*ecs_modes_supported)
36
+ def initialize(*ecs_modes_and_optional_alias_map)
37
+ selector_module = self
38
+
39
+ alias_mapping = ecs_modes_and_optional_alias_map.last.kind_of?(Hash) ? ecs_modes_and_optional_alias_map.pop.dup.freeze : EMPTY_HASH
40
+ ecs_modes_supported = ecs_modes_and_optional_alias_map.dup
41
+
34
42
  fail(ArgumentError, "one or more ecs_modes_supported required") if ecs_modes_supported.empty?
35
43
  fail(ArgumentError, "ecs_modes_supported must only contain symbols") unless ecs_modes_supported.all? { |s| s.kind_of?(Symbol) }
36
44
 
45
+ fail(ArgumentError, "alias names must be symbols") unless alias_mapping.keys.all? { |v| v.kind_of?(Symbol) }
46
+ fail(ArgumentError, "alias targets must be symbols") unless alias_mapping.values.all? { |v| v.kind_of?(Symbol) }
47
+ fail(ArgumentError, "alias must not redefine") if alias_mapping.keys.any? {|v| ecs_modes_supported.include?(v) }
48
+ fail(ArgumentError, "alias map must not have circular references") if circular_references_present?(alias_mapping)
49
+
50
+ ecs_modes_supported |= alias_mapping.keys
51
+
52
+ fail(ArgumentError, "alias target doesn't exist") if alias_mapping.values.any? { |v| !ecs_modes_supported.include?(v) }
53
+
37
54
  ecs_modes_supported.freeze
38
55
 
39
56
  ##
@@ -49,7 +66,7 @@ module LogStash
49
66
  "Supported modes are: #{ecs_modes_supported}"
50
67
  fail(LogStash::ConfigurationError, message)
51
68
  end
52
- @_ecs_select = State.new(ecs_modes_supported, effective_ecs_mode)
69
+ @_ecs_select = selector_module.state_for(effective_ecs_mode)
53
70
  end
54
71
 
55
72
  ##
@@ -58,6 +75,13 @@ module LogStash
58
75
  define_method(:ecs_select) { @_ecs_select }
59
76
 
60
77
  define_singleton_method(:ecs_modes_supported) { ecs_modes_supported }
78
+
79
+ define_singleton_method(:state_for) do |selected_value|
80
+ unless ecs_modes_supported.include?(selected_value)
81
+ fail(NotImplementedError, "Unsupported state `#{selected_value}` (expected one of #{ecs_modes_supported})")
82
+ end
83
+ State.new(ecs_modes_supported, selected_value, alias_mapping)
84
+ end
61
85
  end
62
86
 
63
87
  ##
@@ -66,6 +90,21 @@ module LogStash
66
90
  "#{Selector}(#{ecs_modes_supported.join(',')})"
67
91
  end
68
92
 
93
+ private
94
+
95
+ def circular_references_present?(alias_candidates)
96
+ alias_candidates.each do |candidate, target|
97
+ current = target
98
+ (alias_candidates.size + 1).times do
99
+ return true if current == candidate
100
+ break unless alias_candidates.include?(current)
101
+ current = alias_candidates.fetch(current)
102
+ end
103
+ end
104
+
105
+ false
106
+ end
107
+
69
108
  ##
70
109
  # A `State` contains the active mode and a list of all supported modes.
71
110
  #
@@ -77,12 +116,15 @@ module LogStash
77
116
  # @api private
78
117
  class State
79
118
  ##
80
- # @api private
119
+ # @api private -- Use Selector#state_for(current_value)
81
120
  # @param supported_modes [Array<Symbol>]
82
121
  # @param active_mode [Symbol]
83
- def initialize(supported_modes, active_mode)
122
+ def initialize(supported_modes, active_mode, alias_map=EMPTY_HASH)
123
+ fail(ArgumentError, "invalid alias mapping") unless alias_map.flatten.all? {|v| supported_modes.include?(v) }
124
+
84
125
  @supported_modes = supported_modes
85
126
  @active_mode = active_mode
127
+ @alias_map = alias_map
86
128
  end
87
129
 
88
130
  attr_reader :active_mode
@@ -102,13 +144,20 @@ module LogStash
102
144
 
103
145
  fail(ArgumentError, "at least one choice must be defined") if defined_choices.empty?
104
146
 
105
- missing = @supported_modes - defined_choices.keys
147
+ missing = @supported_modes - (defined_choices.keys + @alias_map.keys)
106
148
  fail(ArgumentError, "missing one or more required choice definition #{missing}") if missing.any?
107
149
 
108
150
  unknown = defined_choices.keys - @supported_modes
109
151
  fail(ArgumentError, "unknown choices #{unknown}; valid choices are #{@supported_modes}") if unknown.any?
110
152
 
111
- defined_choices.fetch(@active_mode)
153
+ # resolve aliases of missing choices
154
+ effective_mode = @active_mode
155
+ @alias_map.size.times do # theoretical upper limit of alias chain
156
+ break if defined_choices.include?(effective_mode)
157
+ effective_mode = @alias_map.fetch(effective_mode)
158
+ end
159
+
160
+ defined_choices.fetch(effective_mode)
112
161
  end
113
162
  alias_method :[], :value_from
114
163
  end
@@ -28,15 +28,13 @@ module LogStash
28
28
  # ~~~
29
29
  module SpecHelper
30
30
  def ecs_compatibility_matrix(*supported_modes,&block)
31
- if supported_modes.empty? || supported_modes.any? { |mode| !mode.kind_of?(Symbol) }
32
- fail(ArgumentError, "ecs_compatibility_matrix(*supported_modes) requires one or more symbol supported_modes")
33
- end
31
+ ecs_selector = Selector.new(*supported_modes)
34
32
 
35
- supported_modes.each do |active_mode|
33
+ ecs_selector.ecs_modes_supported.each do |active_mode|
36
34
  context "`ecs_compatibility => #{active_mode}`" do
37
35
  let(:ecs_compatibility) { active_mode }
38
36
 
39
- ecs_select = Selector::State.new(supported_modes, active_mode)
37
+ ecs_select = ecs_selector.state_for(active_mode)
40
38
  instance_exec(ecs_select, &block)
41
39
  end
42
40
  end
@@ -34,6 +34,27 @@ describe LogStash::PluginMixins::ECSCompatibilitySupport::Selector do
34
34
  expect(selector_mod.name).to include('v1')
35
35
  end
36
36
  end
37
+ it 'accepts an alias list' do
38
+ selector_mod = described_class.new(:disabled, :v1, :v8 => :v1)
39
+ aggregate_failures do
40
+ expect(selector_mod.ecs_modes_supported).to contain_exactly(:disabled, :v1, :v8)
41
+ expect(selector_mod.name).to include('disabled')
42
+ expect(selector_mod.name).to include('v1')
43
+ expect(selector_mod.name).to include('v8')
44
+ end
45
+ end
46
+ it 'rejects alias list that doesnt resolve' do
47
+ expect { described_class.new(:disabled, :v1, :v8 => :v2) }.to raise_error(ArgumentError, /alias target/)
48
+ end
49
+ it 'rejects tight circular aliases' do
50
+ expect { described_class.new(:disabled, :v1, :v8 => :v8) }.to raise_error(ArgumentError, /circular/)
51
+ end
52
+ it 'rejects loose circular aliases' do
53
+ expect { described_class.new(:disabled, :v1, :v8 => :v7, :v7 => :v6, :v6 => :v8) }.to raise_error(ArgumentError, /circular/)
54
+ end
55
+ it 'rejects alias that redefines concrete definition' do
56
+ expect { described_class.new(:disabled, :v1, :v1 => :disabled) }.to raise_error(ArgumentError, /redefine/)
57
+ end
37
58
  end
38
59
  context 'included into a class' do
39
60
  let(:ecs_compatibility_support) { LogStash::PluginMixins::ECSCompatibilitySupport }
@@ -111,6 +132,19 @@ describe LogStash::PluginMixins::ECSCompatibilitySupport::Selector do
111
132
  it 'selects the correct effective value' do
112
133
  expect(ecs_select[disabled: "nope", v1: "winner"]).to eq("winner")
113
134
  end
135
+ context 'when aliases are given' do
136
+ let(:ecs_supported_modes) { [:disabled, :v1, :v8 => :v2, :v2 => :v1 ] }
137
+ let(:ecs_effective_mode) { :v8 }
138
+ it 'selects a given value' do
139
+ expect(ecs_select[disabled: "nope", v1: "nah", v8: "hooray"]).to eq("hooray")
140
+ end
141
+ it 'resolves the deepest alias of a missing value' do
142
+ expect(ecs_select[disabled: "sad-trombone", v1: "wahoo"]).to eq("wahoo")
143
+ end
144
+ it 'resolves the intermediate alias of a missing value' do
145
+ expect(ecs_select[disabled: "sad-trombone", v1: "oh-no", v2: "nice"]).to eq("nice")
146
+ end
147
+ end
114
148
  end
115
149
  end
116
150
  end
@@ -6,14 +6,14 @@ require "logstash/plugin_mixins/ecs_compatibility_support/spec_helper"
6
6
 
7
7
  describe LogStash::PluginMixins::ECSCompatibilitySupport::SpecHelper, :ecs_compatibility_support do
8
8
  context '::ecs_compatibility_matrix(*modes)' do
9
- ecs_compatibility_matrix(:disabled,:v1) do |ecs_select|
9
+ ecs_compatibility_matrix(:disabled,:v1,:v8 => :v1) do |ecs_select|
10
10
  it("sets `ecs_compatibility` with the current active mode `#{ecs_select.active_mode}`") do
11
11
  expect(ecs_compatibility).to eq(ecs_select.active_mode)
12
12
  end
13
13
  context 'the yielded value' do
14
14
  subject { ecs_select }
15
15
  it { is_expected.to be_a_kind_of LogStash::PluginMixins::ECSCompatibilitySupport::Selector::State }
16
- its(:supported_modes) { is_expected.to contain_exactly(:disabled,:v1) }
16
+ its(:supported_modes) { is_expected.to contain_exactly(:disabled,:v1,:v8) }
17
17
  its(:active_mode) { is_expected.to be ecs_compatibility }
18
18
  end
19
19
  end
@@ -155,5 +155,12 @@ describe 'LogStash::PluginMixins::ECSCompatibilitySupport()' do
155
155
  expect(selector_module.ecs_modes_supported).to contain_exactly(:disabled,:v1)
156
156
  end
157
157
  end
158
+ context 'with symbol arguments and alias mapping' do
159
+ it 'returns a properly-configured LogStash::PluginMixins::ECSCompatibilitySupport::Selector' do
160
+ selector_module = LogStash::PluginMixins::ECSCompatibilitySupport(:disabled,:v1,:v8 => :v1)
161
+ expect(selector_module).to be_a_kind_of(LogStash::PluginMixins::ECSCompatibilitySupport::Selector)
162
+ expect(selector_module.ecs_modes_supported).to contain_exactly(:disabled,:v1,:v8)
163
+ end
164
+ end
158
165
  end
159
166
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-mixin-ecs_compatibility_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: java
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-25 00:00:00.000000000 Z
11
+ date: 2021-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement