logstash-mixin-ecs_compatibility_support 1.0.0-java → 1.3.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +74 -3
- data/lib/logstash/plugin_mixins/ecs_compatibility_support/selector.rb +167 -0
- data/lib/logstash/plugin_mixins/ecs_compatibility_support/spec_helper.rb +47 -0
- data/lib/logstash/plugin_mixins/ecs_compatibility_support/target_check.rb +58 -0
- data/lib/logstash/plugin_mixins/ecs_compatibility_support.rb +14 -3
- data/spec/logstash/plugin_mixins/ecs_compatibility_support/selector_spec.rb +153 -0
- data/spec/logstash/plugin_mixins/ecs_compatibility_support/spec_helper_spec.rb +21 -0
- data/spec/logstash/plugin_mixins/ecs_compatibility_support/target_check_spec.rb +67 -0
- data/spec/logstash/plugin_mixins/ecs_compatibility_support_spec.rb +22 -0
- metadata +28 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a64fc499677faca675d99eccc08d8d397f6d1330b6073ce31ab1570a0e750d1
|
|
4
|
+
data.tar.gz: 6a4c3df642e4dc252d545069b4a7b301ecf246c2655b893409f1898643507353
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b84794d3a79993ecb23464e8293ee9f44d2430f6901c5f92f5dfcaa46e45fe09b5ee6f353afe8a8b36afa976f1bcc8b4ee69e123d2b665e5cb2b1228c9aea445
|
|
7
|
+
data.tar.gz: bb9d04088b8dcdd81a1ffefeb9ef1e5eec02e4fd24491684630e5c7e5c9a007121604c5b3435433f6a971b8e42eb2b0e926b1355fc0021e22f53a8de899cd433
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
# 1.
|
|
1
|
+
# 1.3.0
|
|
2
|
+
- Feat: introduce a target check helper [#6](https://github.com/logstash-plugins/logstash-mixin-ecs_compatibility_support/pull/6)
|
|
3
|
+
|
|
4
|
+
# 1.2.0
|
|
5
|
+
- Added support for resolution aliases, allowing a plugin that uses `ecs_select` to support multiple ECS versions with a single declaration.
|
|
2
6
|
|
|
7
|
+
# 1.1.0
|
|
8
|
+
- Added support for `ecs_select` helper, allowing plugins to declare mappings that are selected during plugin instantiation.
|
|
9
|
+
|
|
10
|
+
# 1.0.0
|
|
3
11
|
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ECS Compatibility Support Mixin
|
|
2
2
|
|
|
3
|
-
[](https://travis-ci.com/logstash-plugins/logstash-mixin-ecs_compatibility_support)
|
|
4
4
|
|
|
5
5
|
This gem provides an API-compatible implementation of ECS-compatiblity mode,
|
|
6
6
|
allowing plugins to be explicitly configured with `ecs_compatibility` in a way
|
|
@@ -8,7 +8,7 @@ that respects pipeline- and process-level settings where they are available.
|
|
|
8
8
|
It can be added as a dependency of any plugin that wishes to implement one or
|
|
9
9
|
more ECS-compatibility modes while still supporting older Logstash versions.
|
|
10
10
|
|
|
11
|
-
## Usage
|
|
11
|
+
## Usage (simple)
|
|
12
12
|
|
|
13
13
|
1. Add version `~>1.0` of this gem as a runtime dependency of your Logstash plugin's `gemspec`:
|
|
14
14
|
|
|
@@ -57,8 +57,79 @@ more ECS-compatibility modes while still supporting older Logstash versions.
|
|
|
57
57
|
end
|
|
58
58
|
~~~
|
|
59
59
|
|
|
60
|
+
## Usage (advanced)
|
|
61
|
+
|
|
62
|
+
Release 1.1 of this support gem includes support for constraining a plugin
|
|
63
|
+
to only operate in specified ECS-Compatibility modes, and advanced support for
|
|
64
|
+
runtime selectors that provide developers a way to provide alternate _values_
|
|
65
|
+
during initialization based on the instantiated plugin's effective
|
|
66
|
+
`ecs_compatibility` mode. This is helpful in plugins that define large field
|
|
67
|
+
mappings, because it allows those mappings to be side-by-side where they are
|
|
68
|
+
unlikely to diverge and introduce bugs.
|
|
69
|
+
|
|
70
|
+
1. Add version `~>1.2` of this gem as a runtime dependency of your Logstash plugin's `gemspec`:
|
|
71
|
+
|
|
72
|
+
~~~ ruby
|
|
73
|
+
Gem::Specification.new do |s|
|
|
74
|
+
# ...
|
|
75
|
+
|
|
76
|
+
s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.2'
|
|
77
|
+
end
|
|
78
|
+
~~~
|
|
79
|
+
|
|
80
|
+
2. In your plugin code, require this library and include it into your plugin class
|
|
81
|
+
that already inherits `LogStash::Plugin`, but this time specify which versions
|
|
82
|
+
of ECS your plugin supports:
|
|
83
|
+
|
|
84
|
+
~~~ ruby
|
|
85
|
+
require 'logstash/plugin_mixins/ecs_compatibility_support'
|
|
86
|
+
|
|
87
|
+
class LogStash::Inputs::Foo < Logstash::Inputs::Base
|
|
88
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled,:v1)
|
|
89
|
+
|
|
90
|
+
# ...
|
|
91
|
+
end
|
|
92
|
+
~~~
|
|
93
|
+
|
|
94
|
+
This prevents the plugin from being instantiated with an unsupported mode,
|
|
95
|
+
whether that mode was explicitly defined for the plugin instance or implictly
|
|
96
|
+
defined by the pipeline in which the plugin is run.
|
|
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
|
+
|
|
112
|
+
3. As in the simple usage example, you can use the `ecs_compatibility` method.
|
|
113
|
+
|
|
114
|
+
But when supported versions are specified, you can also use the `ecs_select`
|
|
115
|
+
method to define alternates in-line. At runtime, the correct value will be
|
|
116
|
+
selected based on the current effective `ecs_compatibility` mode.
|
|
117
|
+
|
|
118
|
+
~~~ ruby
|
|
119
|
+
def register
|
|
120
|
+
@field_hostname = ecs_select[disabled: "hostname", v1: "[host][name]"]
|
|
121
|
+
@field_hostip = ecs_select[disabled: "ip", v1: "[host][ip]" ]
|
|
122
|
+
end
|
|
123
|
+
~~~
|
|
124
|
+
|
|
125
|
+
If you initialized the mixin with an alias mapping, missing values will
|
|
126
|
+
be resolved by their alias.
|
|
127
|
+
|
|
128
|
+
NOTE: `ecs_select` should only be used during plugin initialization and
|
|
129
|
+
not during event-by-event processing.
|
|
130
|
+
|
|
60
131
|
## Development
|
|
61
132
|
|
|
62
133
|
This gem:
|
|
63
134
|
- *MUST* remain API-stable at 1.x
|
|
64
|
-
- *MUST NOT* introduce additional runtime dependencies
|
|
135
|
+
- *MUST NOT* introduce additional runtime dependencies
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../ecs_compatibility_support'
|
|
4
|
+
|
|
5
|
+
module LogStash
|
|
6
|
+
module PluginMixins
|
|
7
|
+
module ECSCompatibilitySupport
|
|
8
|
+
# A `ECSCompatibilitySupport::Selector` is a `Module` that can be included into any `LogStash::Plugin`
|
|
9
|
+
# to constrain instances to a specific set of supported `ecs_compatibility` modes.
|
|
10
|
+
#
|
|
11
|
+
# It also provides an `ecs_select` that allows plugin developers to specify ECS alternatives
|
|
12
|
+
# in-line with their existing code.
|
|
13
|
+
#
|
|
14
|
+
# @api private
|
|
15
|
+
# @see ECSCompatibilitySupport()
|
|
16
|
+
class Selector < Module
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# @api internal (use: `LogStash::Plugin::include`)
|
|
20
|
+
# @param base [Class]: a class that inherits `LogStash::Plugin`, typically one
|
|
21
|
+
# descending from one of the four plugin base classes
|
|
22
|
+
# (e.g., `LogStash::Inputs::Base`)
|
|
23
|
+
# @return [void]
|
|
24
|
+
def included(base)
|
|
25
|
+
fail(ArgumentError, "`#{base}` must inherit LogStash::Plugin") unless base < LogStash::Plugin
|
|
26
|
+
base.include(ECSCompatibilitySupport)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
EMPTY_HASH = {}.freeze
|
|
30
|
+
private_constant :EMPTY_HASH
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# @api private
|
|
34
|
+
# @see ECSCompatibilitySupport()
|
|
35
|
+
# @param 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
|
+
|
|
42
|
+
fail(ArgumentError, "one or more ecs_modes_supported required") if ecs_modes_supported.empty?
|
|
43
|
+
fail(ArgumentError, "ecs_modes_supported must only contain symbols") unless ecs_modes_supported.all? { |s| s.kind_of?(Symbol) }
|
|
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
|
+
|
|
54
|
+
ecs_modes_supported.freeze
|
|
55
|
+
|
|
56
|
+
##
|
|
57
|
+
# Hooks initialization to throw a configuration error if plugin is initialized with
|
|
58
|
+
# an unsupported `ecs_compatibility` mode.
|
|
59
|
+
# @method initialize
|
|
60
|
+
define_method(:initialize) do |*args|
|
|
61
|
+
super(*args) # Plugin#initialize
|
|
62
|
+
|
|
63
|
+
effective_ecs_mode = ecs_compatibility
|
|
64
|
+
if !ecs_modes_supported.include?(effective_ecs_mode)
|
|
65
|
+
message = "#{config_name} #{@plugin_type} plugin does not support `ecs_compatibility => #{effective_ecs_mode}`. "+
|
|
66
|
+
"Supported modes are: #{ecs_modes_supported}"
|
|
67
|
+
fail(LogStash::ConfigurationError, message)
|
|
68
|
+
end
|
|
69
|
+
@_ecs_select = selector_module.state_for(effective_ecs_mode)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# @method ecs_select
|
|
74
|
+
# @return [State]
|
|
75
|
+
define_method(:ecs_select) { @_ecs_select }
|
|
76
|
+
|
|
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
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
##
|
|
88
|
+
# @return [String]
|
|
89
|
+
def name
|
|
90
|
+
"#{Selector}(#{ecs_modes_supported.join(',')})"
|
|
91
|
+
end
|
|
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
|
+
|
|
108
|
+
##
|
|
109
|
+
# A `State` contains the active mode and a list of all supported modes.
|
|
110
|
+
#
|
|
111
|
+
# It allows a developer to safely define mappings of alternative values, exactly
|
|
112
|
+
# one of which will be selected based on the effective mode.
|
|
113
|
+
#
|
|
114
|
+
# It is _NOT_ designed for performance, but may be helpful during instantiation.
|
|
115
|
+
#
|
|
116
|
+
# @api private
|
|
117
|
+
class State
|
|
118
|
+
##
|
|
119
|
+
# @api private -- Use Selector#state_for(current_value)
|
|
120
|
+
# @param supported_modes [Array<Symbol>]
|
|
121
|
+
# @param active_mode [Symbol]
|
|
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
|
+
|
|
125
|
+
@supported_modes = supported_modes
|
|
126
|
+
@active_mode = active_mode
|
|
127
|
+
@alias_map = alias_map
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
attr_reader :active_mode
|
|
131
|
+
attr_reader :supported_modes
|
|
132
|
+
|
|
133
|
+
# With the active mode, select one of the provided options.
|
|
134
|
+
# @param defined_choices [Hash{Symbol=>Object}]: the options to chose between.
|
|
135
|
+
# it is an `ArgumentError` to provide a different set of
|
|
136
|
+
# options than those this `State` was initialized with.
|
|
137
|
+
# This ensures that all reachable code implements all
|
|
138
|
+
# supported options.
|
|
139
|
+
# @return [Object]
|
|
140
|
+
def value_from(defined_choices)
|
|
141
|
+
fail(ArgumentError, "defined_choices must be a Hash") unless defined_choices.kind_of?(Hash)
|
|
142
|
+
fail(ArgumentError, "defined_choices cannot be empty") if defined_choices.empty?
|
|
143
|
+
fail(ArgumentError, "defined_choices must have Symbol keys") unless defined_choices.keys.all? { |k| k.kind_of?(Symbol) }
|
|
144
|
+
|
|
145
|
+
fail(ArgumentError, "at least one choice must be defined") if defined_choices.empty?
|
|
146
|
+
|
|
147
|
+
missing = @supported_modes - (defined_choices.keys + @alias_map.keys)
|
|
148
|
+
fail(ArgumentError, "missing one or more required choice definition #{missing}") if missing.any?
|
|
149
|
+
|
|
150
|
+
unknown = defined_choices.keys - @supported_modes
|
|
151
|
+
fail(ArgumentError, "unknown choices #{unknown}; valid choices are #{@supported_modes}") if unknown.any?
|
|
152
|
+
|
|
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)
|
|
161
|
+
end
|
|
162
|
+
alias_method :[], :value_from
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative 'selector'
|
|
4
|
+
|
|
5
|
+
module LogStash
|
|
6
|
+
module PluginMixins
|
|
7
|
+
module ECSCompatibilitySupport
|
|
8
|
+
# The `SpecHelper` is meant to be extended into an rspec context to provide it with
|
|
9
|
+
# tools for vaidating the ecs_compatibility matrix.
|
|
10
|
+
#
|
|
11
|
+
# It creates one sub-context per provided mode, and yields a Selector::State
|
|
12
|
+
# to the provided block primed with the list of supported supported_modes and the active mode.
|
|
13
|
+
#
|
|
14
|
+
# It also memoizes an `ecs_compatibility` set to the active mode. It is up to the user
|
|
15
|
+
# to plumb this into the initialization of your plugin.
|
|
16
|
+
#
|
|
17
|
+
# ~~~ ruby
|
|
18
|
+
# require 'logstash/plugin_mixins/ecs_compatibility_support/spec_helper'
|
|
19
|
+
#
|
|
20
|
+
# describe YourClass, :ecs_compatibility_support
|
|
21
|
+
# ecs_compatibility_matrix(:disabled, :v1) do |ecs_select|
|
|
22
|
+
# context "#description" do
|
|
23
|
+
# subject(:result) { instance.description }
|
|
24
|
+
# it { is_expected.to eq ecs_select[disabled: "legacy", v1: "novel"] }
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
# ~~~
|
|
29
|
+
module SpecHelper
|
|
30
|
+
def ecs_compatibility_matrix(*supported_modes,&block)
|
|
31
|
+
ecs_selector = Selector.new(*supported_modes)
|
|
32
|
+
|
|
33
|
+
ecs_selector.ecs_modes_supported.each do |active_mode|
|
|
34
|
+
context "`ecs_compatibility => #{active_mode}`" do
|
|
35
|
+
let(:ecs_compatibility) { active_mode }
|
|
36
|
+
|
|
37
|
+
ecs_select = ecs_selector.state_for(active_mode)
|
|
38
|
+
instance_exec(ecs_select, &block)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
::RSpec.configure { |c| c.extend(SpecHelper, :ecs_compatibility_support) } if defined?(::RSpec)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../ecs_compatibility_support'
|
|
4
|
+
|
|
5
|
+
module LogStash
|
|
6
|
+
module PluginMixins
|
|
7
|
+
module ECSCompatibilitySupport
|
|
8
|
+
# A target option check that can be included into any `LogStash::Plugin`.
|
|
9
|
+
#
|
|
10
|
+
# @see ECSCompatibilitySupport()
|
|
11
|
+
module TargetCheck
|
|
12
|
+
##
|
|
13
|
+
# @api internal (use: `LogStash::Plugin::include`)
|
|
14
|
+
# @param base [Class]: a class that inherits `LogStash::Plugin`, typically one
|
|
15
|
+
# descending from one of the four plugin base classes
|
|
16
|
+
# (e.g., `LogStash::Inputs::Base`)
|
|
17
|
+
# @return [void]
|
|
18
|
+
def self.included(base)
|
|
19
|
+
fail(ArgumentError, "`#{base}` must inherit LogStash::Plugin") unless base < LogStash::Plugin
|
|
20
|
+
fail(ArgumentError, "`#{base}` must include #{ECSCompatibilitySupport}") unless base < ECSCompatibilitySupport
|
|
21
|
+
base.prepend(RegisterDecorator)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
TARGET_NOT_SET_MESSAGE = ("ECS compatibility is enabled but `target` option was not specified. " +
|
|
25
|
+
"This may cause fields to be set at the top-level of the event where they are likely to clash with the Elastic Common Schema. " +
|
|
26
|
+
"It is recommended to set the `target` option to avoid potential schema conflicts (if your data is ECS compliant " +
|
|
27
|
+
"or non-conflicting, feel free to ignore this message)").freeze
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Check whether `target` is specified.
|
|
33
|
+
#
|
|
34
|
+
# @return [nil] if target is unspecified and ECS compatibility is disabled
|
|
35
|
+
# @return [true, false]
|
|
36
|
+
def target_set?
|
|
37
|
+
return true unless target.nil?
|
|
38
|
+
return nil if ecs_compatibility == :disabled
|
|
39
|
+
false # target isn't set
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module RegisterDecorator
|
|
43
|
+
|
|
44
|
+
##
|
|
45
|
+
# Logs an info message when ecs_compatibility is enabled but plugin has no `target` configuration specified.
|
|
46
|
+
# @override
|
|
47
|
+
def register
|
|
48
|
+
super.tap do
|
|
49
|
+
logger.info(TARGET_NOT_SET_MESSAGE) if target_set? == false
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
private_constant :RegisterDecorator
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -17,9 +17,9 @@ module LogStash
|
|
|
17
17
|
module ECSCompatibilitySupport
|
|
18
18
|
##
|
|
19
19
|
# @api internal (use: `LogStash::Plugin::include`)
|
|
20
|
-
# @param: a class that inherits `LogStash::Plugin`, typically one
|
|
21
|
-
#
|
|
22
|
-
#
|
|
20
|
+
# @param base [Class]: a class that inherits `LogStash::Plugin`, typically one
|
|
21
|
+
# descending from one of the four plugin base classes
|
|
22
|
+
# (e.g., `LogStash::Inputs::Base`)
|
|
23
23
|
# @return [void]
|
|
24
24
|
def self.included(base)
|
|
25
25
|
fail(ArgumentError, "`#{base}` must inherit LogStash::Plugin") unless base < LogStash::Plugin
|
|
@@ -100,5 +100,16 @@ module LogStash
|
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
end
|
|
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)
|
|
108
|
+
def self.ECSCompatibilitySupport(*supported_versions)
|
|
109
|
+
return ECSCompatibilitySupport if supported_versions.empty?
|
|
110
|
+
|
|
111
|
+
require_relative "ecs_compatibility_support/selector"
|
|
112
|
+
ECSCompatibilitySupport::Selector.new(*supported_versions)
|
|
113
|
+
end
|
|
103
114
|
end
|
|
104
115
|
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'rspec/its'
|
|
4
|
+
|
|
5
|
+
require "logstash-core"
|
|
6
|
+
|
|
7
|
+
require 'logstash/inputs/base'
|
|
8
|
+
require 'logstash/filters/base'
|
|
9
|
+
require 'logstash/codecs/base'
|
|
10
|
+
require 'logstash/outputs/base'
|
|
11
|
+
|
|
12
|
+
require "logstash/plugin_mixins/ecs_compatibility_support/selector"
|
|
13
|
+
|
|
14
|
+
describe LogStash::PluginMixins::ECSCompatibilitySupport::Selector do
|
|
15
|
+
context 'initialize' do
|
|
16
|
+
it 'rejects zero arguments' do
|
|
17
|
+
expect { described_class.new }.to raise_error(ArgumentError, /one or more/)
|
|
18
|
+
end
|
|
19
|
+
it 'rejects non-symbol arguments' do
|
|
20
|
+
expect { described_class.new("v1") }.to raise_error(ArgumentError, /symbol/)
|
|
21
|
+
end
|
|
22
|
+
it 'accepts one symbol argument'do
|
|
23
|
+
selector_mod = described_class.new(:disabled)
|
|
24
|
+
aggregate_failures do
|
|
25
|
+
expect(selector_mod.ecs_modes_supported).to contain_exactly(:disabled)
|
|
26
|
+
expect(selector_mod.name).to include('disabled')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
it 'accepts many symbol arguments'do
|
|
30
|
+
selector_mod = described_class.new(:disabled, :v1)
|
|
31
|
+
aggregate_failures do
|
|
32
|
+
expect(selector_mod.ecs_modes_supported).to contain_exactly(:disabled, :v1)
|
|
33
|
+
expect(selector_mod.name).to include('disabled')
|
|
34
|
+
expect(selector_mod.name).to include('v1')
|
|
35
|
+
end
|
|
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
|
|
58
|
+
end
|
|
59
|
+
context 'included into a class' do
|
|
60
|
+
let(:ecs_compatibility_support) { LogStash::PluginMixins::ECSCompatibilitySupport }
|
|
61
|
+
let(:selector_module) { described_class.new(:disabled,:v1) }
|
|
62
|
+
context 'that does not inherit from LogStash::Plugin' do
|
|
63
|
+
let(:plugin_class) { Class.new }
|
|
64
|
+
it 'fails with an ArgumentError' do
|
|
65
|
+
expect do
|
|
66
|
+
plugin_class.send(:include, selector_module)
|
|
67
|
+
end.to raise_error(ArgumentError, /LogStash::Plugin/)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
[
|
|
72
|
+
LogStash::Inputs::Base,
|
|
73
|
+
LogStash::Filters::Base,
|
|
74
|
+
LogStash::Codecs::Base,
|
|
75
|
+
LogStash::Outputs::Base
|
|
76
|
+
].each do |base_class|
|
|
77
|
+
context "that inherits from `#{base_class}`" do
|
|
78
|
+
let(:ecs_supported_modes) { [:disabled, :v1] }
|
|
79
|
+
let(:selector_module) { described_class.new(*ecs_supported_modes) }
|
|
80
|
+
let(:plugin_base_class) { base_class }
|
|
81
|
+
subject(:plugin_class) do
|
|
82
|
+
klass = Class.new(plugin_base_class) do
|
|
83
|
+
config_name 'test'
|
|
84
|
+
end
|
|
85
|
+
klass.send(:include, selector_module)
|
|
86
|
+
klass
|
|
87
|
+
end
|
|
88
|
+
context 'the result' do
|
|
89
|
+
its(:ancestors) { is_expected.to include(ecs_compatibility_support) }
|
|
90
|
+
its(:instance_methods) { is_expected.to include(:ecs_select) }
|
|
91
|
+
end
|
|
92
|
+
context '#ecs_select' do
|
|
93
|
+
let(:plugin_options) { Hash.new }
|
|
94
|
+
let(:plugin_instance) { plugin_class.new(plugin_options) }
|
|
95
|
+
let(:ecs_effective_mode) { :v1 }
|
|
96
|
+
|
|
97
|
+
before(:each) do
|
|
98
|
+
# occurs during initialization, before we can get a reference to the instance.
|
|
99
|
+
# our plugin_class is generated per-spec and not reused.
|
|
100
|
+
allow_any_instance_of(plugin_class).to receive(:ecs_compatibility).and_return(ecs_effective_mode)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
subject(:ecs_select) { plugin_instance.ecs_select }
|
|
104
|
+
|
|
105
|
+
it { is_expected.to be_a_kind_of described_class::State }
|
|
106
|
+
it { is_expected.to respond_to :[] }
|
|
107
|
+
|
|
108
|
+
context 'when effective ecs_compatibility is not supported' do
|
|
109
|
+
let(:ecs_supported_modes) { [:disabled,:v1,:v2] }
|
|
110
|
+
let(:ecs_effective_mode) { :v3 }
|
|
111
|
+
it 'raises a configuration error' do
|
|
112
|
+
expect { plugin_instance.ecs_select }.to raise_error(LogStash::ConfigurationError)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
context '#[]' do
|
|
117
|
+
it 'rejects empty options' do
|
|
118
|
+
expect { ecs_select[{}] }.to raise_error(ArgumentError, /empty/)
|
|
119
|
+
end
|
|
120
|
+
it 'rejects unknown options' do
|
|
121
|
+
expect { ecs_select[disabled: "nope", v1: "no", bananas: "monkey"] }.to raise_error(ArgumentError, /unknown/)
|
|
122
|
+
end
|
|
123
|
+
it 'rejects missing options' do
|
|
124
|
+
expect { ecs_select[disabled: "nope"] }.to raise_error(ArgumentError, /missing/)
|
|
125
|
+
end
|
|
126
|
+
it 'rejects non-hash options' do
|
|
127
|
+
expect { ecs_select["bananas"] }.to raise_error(ArgumentError, /Hash/)
|
|
128
|
+
end
|
|
129
|
+
it 'requires symbol keys' do
|
|
130
|
+
expect { ecs_select["bananas"=>"apes"] }.to raise_error(ArgumentError, /Symbol keys/)
|
|
131
|
+
end
|
|
132
|
+
it 'selects the correct effective value' do
|
|
133
|
+
expect(ecs_select[disabled: "nope", v1: "winner"]).to eq("winner")
|
|
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
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'rspec/its'
|
|
4
|
+
|
|
5
|
+
require "logstash/plugin_mixins/ecs_compatibility_support/spec_helper"
|
|
6
|
+
|
|
7
|
+
describe LogStash::PluginMixins::ECSCompatibilitySupport::SpecHelper, :ecs_compatibility_support do
|
|
8
|
+
context '::ecs_compatibility_matrix(*modes)' do
|
|
9
|
+
ecs_compatibility_matrix(:disabled,:v1,:v8 => :v1) do |ecs_select|
|
|
10
|
+
it("sets `ecs_compatibility` with the current active mode `#{ecs_select.active_mode}`") do
|
|
11
|
+
expect(ecs_compatibility).to eq(ecs_select.active_mode)
|
|
12
|
+
end
|
|
13
|
+
context 'the yielded value' do
|
|
14
|
+
subject { ecs_select }
|
|
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,:v8) }
|
|
17
|
+
its(:active_mode) { is_expected.to be ecs_compatibility }
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require "logstash-core"
|
|
4
|
+
|
|
5
|
+
require 'logstash/inputs/base'
|
|
6
|
+
require 'logstash/filters/base'
|
|
7
|
+
require 'logstash/codecs/base'
|
|
8
|
+
require 'logstash/outputs/base'
|
|
9
|
+
|
|
10
|
+
require "logstash/plugin_mixins/ecs_compatibility_support/target_check"
|
|
11
|
+
|
|
12
|
+
describe LogStash::PluginMixins::ECSCompatibilitySupport::TargetCheck do
|
|
13
|
+
|
|
14
|
+
describe "check_target_set_in_ecs_mode" do
|
|
15
|
+
|
|
16
|
+
context 'with a plugin' do
|
|
17
|
+
|
|
18
|
+
subject(:plugin_class) do
|
|
19
|
+
Class.new(LogStash::Filters::Base) do
|
|
20
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport
|
|
21
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport::TargetCheck
|
|
22
|
+
|
|
23
|
+
config :target, :validate => :string
|
|
24
|
+
|
|
25
|
+
def register; 42 end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'skips check when ECS disabled' do
|
|
31
|
+
plugin = plugin_class.new('ecs_compatibility' => 'disabled')
|
|
32
|
+
allow( plugin.logger ).to receive(:info)
|
|
33
|
+
expect( plugin.register ).to eql 42
|
|
34
|
+
expect( plugin.logger ).to_not have_received(:info).with(a_string_including "`target` option")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'warns when target is not set in ECS mode' do
|
|
38
|
+
plugin = plugin_class.new('ecs_compatibility' => 'v1')
|
|
39
|
+
allow( plugin.logger ).to receive(:info)
|
|
40
|
+
expect( plugin.register ).to eql 42
|
|
41
|
+
expect( plugin.logger ).to have_received(:info).with(a_string_including "ECS compatibility is enabled but `target` option was not specified.")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'does not warn when target is set' do
|
|
45
|
+
plugin = plugin_class.new('ecs_compatibility' => 'v1', 'target' => 'foo')
|
|
46
|
+
allow( plugin.logger ).to receive(:info)
|
|
47
|
+
expect( plugin.register ).to eql 42
|
|
48
|
+
expect( plugin.logger ).to_not have_received(:info).with(a_string_including "`target` option")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'fails check when no target config' do
|
|
54
|
+
plugin_class = Class.new(LogStash::Filters::Base) do
|
|
55
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport
|
|
56
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport::TargetCheck
|
|
57
|
+
|
|
58
|
+
def register; end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
plugin = plugin_class.new('ecs_compatibility' => 'v1')
|
|
62
|
+
expect { plugin.register }.to raise_error NameError, /\btarget\b/
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
@@ -142,3 +142,25 @@ describe LogStash::PluginMixins::ECSCompatibilitySupport do
|
|
|
142
142
|
end
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
+
describe 'LogStash::PluginMixins::ECSCompatibilitySupport()' do
|
|
146
|
+
context 'with no arguments' do
|
|
147
|
+
it 'returns LogStash::PluginMixins::ECSCompatibilitySupport' do
|
|
148
|
+
expect(LogStash::PluginMixins::ECSCompatibilitySupport()).to eq(LogStash::PluginMixins::ECSCompatibilitySupport)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
context 'with symbol arguments' do
|
|
152
|
+
it 'returns a properly-configured LogStash::PluginMixins::ECSCompatibilitySupport::Selector' do
|
|
153
|
+
selector_module = LogStash::PluginMixins::ECSCompatibilitySupport(:disabled,:v1)
|
|
154
|
+
expect(selector_module).to be_a_kind_of(LogStash::PluginMixins::ECSCompatibilitySupport::Selector)
|
|
155
|
+
expect(selector_module.ecs_modes_supported).to contain_exactly(:disabled,:v1)
|
|
156
|
+
end
|
|
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
|
|
165
|
+
end
|
|
166
|
+
|
metadata
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: logstash-mixin-ecs_compatibility_support
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Elastic
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-06-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 6.0.0
|
|
19
19
|
name: logstash-core
|
|
20
20
|
prerelease: false
|
|
21
21
|
type: :runtime
|
|
@@ -23,7 +23,21 @@ dependencies:
|
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
26
|
+
version: 6.0.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
name: logstash-devutils
|
|
34
|
+
prerelease: false
|
|
35
|
+
type: :development
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
29
43
|
requirements:
|
|
@@ -80,6 +94,12 @@ files:
|
|
|
80
94
|
- LICENSE
|
|
81
95
|
- README.md
|
|
82
96
|
- lib/logstash/plugin_mixins/ecs_compatibility_support.rb
|
|
97
|
+
- lib/logstash/plugin_mixins/ecs_compatibility_support/selector.rb
|
|
98
|
+
- lib/logstash/plugin_mixins/ecs_compatibility_support/spec_helper.rb
|
|
99
|
+
- lib/logstash/plugin_mixins/ecs_compatibility_support/target_check.rb
|
|
100
|
+
- spec/logstash/plugin_mixins/ecs_compatibility_support/selector_spec.rb
|
|
101
|
+
- spec/logstash/plugin_mixins/ecs_compatibility_support/spec_helper_spec.rb
|
|
102
|
+
- spec/logstash/plugin_mixins/ecs_compatibility_support/target_check_spec.rb
|
|
83
103
|
- spec/logstash/plugin_mixins/ecs_compatibility_support_spec.rb
|
|
84
104
|
homepage: https://github.com/logstash-plugins/logstash-mixin-ecs_compatibility_support
|
|
85
105
|
licenses:
|
|
@@ -101,10 +121,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
101
121
|
version: '0'
|
|
102
122
|
requirements: []
|
|
103
123
|
rubyforge_project:
|
|
104
|
-
rubygems_version: 2.6.
|
|
124
|
+
rubygems_version: 2.6.13
|
|
105
125
|
signing_key:
|
|
106
126
|
specification_version: 4
|
|
107
127
|
summary: Support for the ECS-Compatibility mode introduced in Logstash 7.x, for plugins
|
|
108
128
|
wishing to use this API on older Logstashes
|
|
109
129
|
test_files:
|
|
130
|
+
- spec/logstash/plugin_mixins/ecs_compatibility_support/selector_spec.rb
|
|
131
|
+
- spec/logstash/plugin_mixins/ecs_compatibility_support/spec_helper_spec.rb
|
|
132
|
+
- spec/logstash/plugin_mixins/ecs_compatibility_support/target_check_spec.rb
|
|
110
133
|
- spec/logstash/plugin_mixins/ecs_compatibility_support_spec.rb
|