logstash-input-elasticsearch 4.6.2 → 4.9.0

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: 92c58eb709a67922d73d44a13cca7fc933968747feebb0c7afa81c04ea3543d2
4
- data.tar.gz: 4354af2982b8af23492a8c1f4c063c21ce6e2d1bfc1e082a9de243ab1c575248
3
+ metadata.gz: 4091feeb0b3bf292cfb9afcde7496a72f95168eb570b21d29064ade174bb1352
4
+ data.tar.gz: cfd02af050bb495dceea16b4ffe5daa6828088d2bd7994639ee08374f87da69d
5
5
  SHA512:
6
- metadata.gz: ba09a7c97593fb2b32cbd4c1f5c4448af2d6ed10c32908ea98b5d4bd54ec0bf11321ae30c48afa745be27bc6280c8fbac6c3f7e62dfa8d5887142dce04e3d0b3
7
- data.tar.gz: 86e4b75f7937c4e66a23b5724902d35db23303d297d7d46cfb55092efc14d0141e708b73b2a72ee1f459f27bc4a9ee0f46d337de2b31b9f38675e25286b58778
6
+ metadata.gz: 873680bea22204e65d519d310f3952f26fd672ce336504e45e99b13988e2da2794b7c05f83b3e1b7a87f317eb51c079759f9df515dc99236577417ecfd379aa1
7
+ data.tar.gz: 85c66a665eb7a3b503ab7cec64ea2f24b0e16829c4b78a75560d69c6272b1861583dad41e04ea1157ce58435714a86eaa6f4d71b5a4a724edfb9604f77150b99
@@ -1,3 +1,25 @@
1
+ ## 4.9.0
2
+ - Added `target` option, allowing the hit's source to target a specific field instead of being expanded at the root of the event. This allows the input to play nicer with the Elastic Common Schema when the input does not follow the schema. [#117](https://github.com/logstash-plugins/logstash-input-elasticsearch/issues/117)
3
+
4
+ ## 4.8.3
5
+ - [DOC] Fixed links to restructured Logstash-to-cloud docs [#139](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/139)
6
+
7
+ ## 4.8.2
8
+ - [DOC] Document the permissions required in secured clusters [#137](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/137)
9
+
10
+ ## 4.8.1
11
+ - Fixed connection error when using multiple `slices`. [#133](https://github.com/logstash-plugins/logstash-input-elasticsearch/issues/133)
12
+
13
+ ## 4.8.0
14
+ - Added the ability to configure connection-, request-, and socket-timeouts with `connect_timeout_seconds`, `request_timeout_seconds`, and `socket_timeout_seconds` [#121](https://github.com/logstash-plugins/logstash-input-elasticsearch/issues/121)
15
+
16
+ ## 4.7.1
17
+ - [DOC] Updated sliced scroll link to resolve to correct location after doc structure change [#135](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/135)
18
+ - [DOC] Added usage example of docinfo metadata [#98](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/98)
19
+
20
+ ## 4.7.0
21
+ - Added api_key support [#131](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/131)
22
+
1
23
  ## 4.6.2
2
24
  - Added scroll clearing and better handling of scroll expiration [#128](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/128)
3
25
 
@@ -7,6 +7,7 @@ Contributors:
7
7
  * Jordan Sissel (jordansissel)
8
8
  * João Duarte (jsvd)
9
9
  * Kurt Hurtado (kurtado)
10
+ * Luca Belluccini (lucabelluccini)
10
11
  * Pier-Hugues Pellerin (ph)
11
12
  * Richard Pijnenburg (electrical)
12
13
  * Suyog Rao (suyograo)
@@ -68,6 +68,21 @@ Further documentation describing this syntax can be found
68
68
  https://github.com/jmettraux/rufus-scheduler#parsing-cronlines-and-time-strings[here].
69
69
 
70
70
 
71
+ [id="plugins-{type}s-{plugin}-auth"]
72
+ ==== Authentication
73
+
74
+ Authentication to a secure Elasticsearch cluster is possible using _one_ of the following options:
75
+
76
+ * <<plugins-{type}s-{plugin}-user>> AND <<plugins-{type}s-{plugin}-password>>
77
+ * <<plugins-{type}s-{plugin}-cloud_auth>>
78
+ * <<plugins-{type}s-{plugin}-api_key>>
79
+
80
+ [id="plugins-{type}s-{plugin}-autz"]
81
+ ==== Authorization
82
+
83
+ Authorization to a secure Elasticsearch cluster requires `read` permission at index level and `monitoring` permissions at cluster level.
84
+ The `monitoring` permission at cluster level is necessary to perform periodic connectivity checks.
85
+
71
86
  [id="plugins-{type}s-{plugin}-options"]
72
87
  ==== Elasticsearch Input Configuration Options
73
88
 
@@ -76,9 +91,11 @@ This plugin supports the following configuration options plus the <<plugins-{typ
76
91
  [cols="<,<,<",options="header",]
77
92
  |=======================================================================
78
93
  |Setting |Input type|Required
94
+ | <<plugins-{type}s-{plugin}-api_key>> |<<password,password>>|No
79
95
  | <<plugins-{type}s-{plugin}-ca_file>> |a valid filesystem path|No
80
96
  | <<plugins-{type}s-{plugin}-cloud_auth>> |<<password,password>>|No
81
97
  | <<plugins-{type}s-{plugin}-cloud_id>> |<<string,string>>|No
98
+ | <<plugins-{type}s-{plugin}-connect_timeout_seconds>> | <<number,number>>|No
82
99
  | <<plugins-{type}s-{plugin}-docinfo>> |<<boolean,boolean>>|No
83
100
  | <<plugins-{type}s-{plugin}-docinfo_fields>> |<<array,array>>|No
84
101
  | <<plugins-{type}s-{plugin}-docinfo_target>> |<<string,string>>|No
@@ -87,11 +104,14 @@ This plugin supports the following configuration options plus the <<plugins-{typ
87
104
  | <<plugins-{type}s-{plugin}-password>> |<<password,password>>|No
88
105
  | <<plugins-{type}s-{plugin}-proxy>> |<<uri,uri>>|No
89
106
  | <<plugins-{type}s-{plugin}-query>> |<<string,string>>|No
107
+ | <<plugins-{type}s-{plugin}-request_timeout_seconds>> | <<number,number>>|No
90
108
  | <<plugins-{type}s-{plugin}-schedule>> |<<string,string>>|No
91
109
  | <<plugins-{type}s-{plugin}-scroll>> |<<string,string>>|No
92
110
  | <<plugins-{type}s-{plugin}-size>> |<<number,number>>|No
93
111
  | <<plugins-{type}s-{plugin}-slices>> |<<number,number>>|No
94
112
  | <<plugins-{type}s-{plugin}-ssl>> |<<boolean,boolean>>|No
113
+ | <<plugins-{type}s-{plugin}-socket_timeout_seconds>> | <<number,number>>|No
114
+ | <<plugins-{type}s-{plugin}-target>> | https://www.elastic.co/guide/en/logstash/master/field-references-deepdive.html[field reference] | No
95
115
  | <<plugins-{type}s-{plugin}-user>> |<<string,string>>|No
96
116
  |=======================================================================
97
117
 
@@ -100,6 +120,19 @@ input plugins.
100
120
 
101
121
  &nbsp;
102
122
 
123
+ [id="plugins-{type}s-{plugin}-api_key"]
124
+ ===== `api_key`
125
+
126
+ * Value type is <<password,password>>
127
+ * There is no default value for this setting.
128
+
129
+ Authenticate using Elasticsearch API key. Note that this option also requires enabling the `ssl` option.
130
+
131
+ Format is `id:api_key` where `id` and `api_key` are as returned by the
132
+ Elasticsearch
133
+ https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create
134
+ API key API].
135
+
103
136
  [id="plugins-{type}s-{plugin}-ca_file"]
104
137
  ===== `ca_file`
105
138
 
@@ -116,7 +149,9 @@ SSL Certificate Authority file in PEM encoded format, must also include any chai
116
149
 
117
150
  Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` pair.
118
151
 
119
- For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_auth[Logstash-to-Cloud documentation]
152
+ For more info, check out the
153
+ https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html[Logstash-to-Cloud
154
+ documentation]
120
155
 
121
156
  [id="plugins-{type}s-{plugin}-cloud_id"]
122
157
  ===== `cloud_id`
@@ -126,7 +161,18 @@ For more info, check out the https://www.elastic.co/guide/en/logstash/current/co
126
161
 
127
162
  Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
128
163
 
129
- For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
164
+ For more info, check out the
165
+ https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html[Logstash-to-Cloud
166
+ documentation]
167
+
168
+ [id="plugins-{type}s-{plugin}-connect_timeout_seconds"]
169
+ ===== `connect_timeout_seconds`
170
+
171
+ * Value type is <<number,number>>
172
+ * Default value is `10`
173
+
174
+ The maximum amount of time, in seconds, to wait while establishing a connection to Elasticsearch.
175
+ Connect timeouts tend to occur when Elasticsearch or an intermediate proxy is overloaded with requests and has exhausted its connection pool.
130
176
 
131
177
  [id="plugins-{type}s-{plugin}-docinfo"]
132
178
  ===== `docinfo`
@@ -163,6 +209,19 @@ Example
163
209
  }
164
210
  }
165
211
 
212
+ If set, you can use metadata information in the <<plugins-{type}s-{plugin}-add_field>> common option.
213
+
214
+ Example
215
+ [source, ruby]
216
+ input {
217
+ elasticsearch {
218
+ docinfo => true
219
+ add_field => {
220
+ identifier => %{[@metadata][_index]}:%{[@metadata][_type]}:%{[@metadata][_id]}"
221
+ }
222
+ }
223
+ }
224
+
166
225
 
167
226
  [id="plugins-{type}s-{plugin}-docinfo_fields"]
168
227
  ===== `docinfo_fields`
@@ -237,6 +296,16 @@ The query to be executed. Read the
237
296
  https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html[Elasticsearch query DSL documentation]
238
297
  for more information.
239
298
 
299
+ [id="plugins-{type}s-{plugin}-request_timeout_seconds"]
300
+ ===== `request_timeout_seconds`
301
+
302
+ * Value type is <<number,number>>
303
+ * Default value is `60`
304
+
305
+ The maximum amount of time, in seconds, for a single request to Elasticsearch.
306
+ Request timeouts tend to occur when an individual page of data is very large, such as when it contains large-payload
307
+ documents and/or the <<plugins-{type}s-{plugin}-size>> has been specified as a large value.
308
+
240
309
  [id="plugins-{type}s-{plugin}-schedule"]
241
310
  ===== `schedule`
242
311
 
@@ -275,8 +344,8 @@ This allows you to set the maximum number of hits returned per scroll.
275
344
  * Sensible values range from 2 to about 8.
276
345
 
277
346
  In some cases, it is possible to improve overall throughput by consuming multiple
278
- distinct slices of a query simultaneously using the
279
- https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html#sliced-scroll[Sliced Scroll API],
347
+ distinct slices of a query simultaneously using
348
+ https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#slice-scroll[sliced scrolls],
280
349
  especially if the pipeline is spending significant time waiting on Elasticsearch
281
350
  to provide results.
282
351
 
@@ -300,6 +369,28 @@ instructions into the query.
300
369
  If enabled, SSL will be used when communicating with the Elasticsearch
301
370
  server (i.e. HTTPS will be used instead of plain HTTP).
302
371
 
372
+ [id="plugins-{type}s-{plugin}-socket_timeout_seconds"]
373
+ ===== `socket_timeout_seconds`
374
+
375
+ * Value type is <<number,number>>
376
+ * Default value is `60`
377
+
378
+ The maximum amount of time, in seconds, to wait on an incomplete response from Elasticsearch while no additional data has been appended.
379
+ Socket timeouts usually occur while waiting for the first byte of a response, such as when executing a particularly complex query.
380
+
381
+
382
+ [id="plugins-{type}s-{plugin}-target"]
383
+ ===== `target`
384
+
385
+ * Value type is https://www.elastic.co/guide/en/logstash/master/field-references-deepdive.html[field reference]
386
+ * There is no default value for this setting.
387
+
388
+ Without a `target`, events are created from each hit's `_source` at the root level.
389
+ When the `target` is set to a field reference, the `_source` of the hit is placed in the target field instead.
390
+
391
+ This option can be useful to avoid populating unknown fields when a downstream schema such as ECS is enforced.
392
+ It is also possible to target an entry in the event's metadata, which will be available during event processing but not exported to your outputs (e.g., `target \=> "[@metadata][_source]"`).
393
+
303
394
  [id="plugins-{type}s-{plugin}-user"]
304
395
  ===== `user`
305
396
 
@@ -315,4 +406,4 @@ empty string authentication will be disabled.
315
406
  [id="plugins-{type}s-{plugin}-common-options"]
316
407
  include::{include_path}/{type}.asciidoc[]
317
408
 
318
- :default_codec!:
409
+ :default_codec!:
@@ -3,7 +3,9 @@ require "logstash/inputs/base"
3
3
  require "logstash/namespace"
4
4
  require "logstash/json"
5
5
  require "logstash/util/safe_uri"
6
+ require 'logstash/plugin_mixins/validator_support/field_reference_validation_adapter'
6
7
  require "base64"
8
+ require_relative "patch"
7
9
 
8
10
 
9
11
  # .Compatibility Note
@@ -61,6 +63,8 @@ require "base64"
61
63
  #
62
64
  #
63
65
  class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
66
+ extend LogStash::PluginMixins::ValidatorSupport::FieldReferenceValidationAdapter
67
+
64
68
  config_name "elasticsearch"
65
69
 
66
70
  default :codec, "json"
@@ -70,11 +74,6 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
70
74
  # Port defaults to 9200
71
75
  config :hosts, :validate => :array
72
76
 
73
- # Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
74
- #
75
- # For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
76
- config :cloud_id, :validate => :string
77
-
78
77
  # The index or alias to search.
79
78
  config :index, :validate => :string, :default => "logstash-*"
80
79
 
@@ -140,11 +139,29 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
140
139
  # Basic Auth - password
141
140
  config :password, :validate => :password
142
141
 
142
+ # Connection Timeout, in Seconds
143
+ config :connect_timeout_seconds, :validate => :positive_whole_number, :default => 10
144
+
145
+ # Request Timeout, in Seconds
146
+ config :request_timeout_seconds, :validate => :positive_whole_number, :default => 60
147
+
148
+ # Socket Timeout, in Seconds
149
+ config :socket_timeout_seconds, :validate => :positive_whole_number, :default => 60
150
+
151
+ # Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
152
+ #
153
+ # For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
154
+ config :cloud_id, :validate => :string
155
+
143
156
  # Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` configuration.
144
157
  #
145
158
  # For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_auth[Logstash-to-Cloud documentation]
146
159
  config :cloud_auth, :validate => :password
147
160
 
161
+ # Authenticate using Elasticsearch API key.
162
+ # format is id:api_key (as returned by https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create API key])
163
+ config :api_key, :validate => :password
164
+
148
165
  # Set the address of a forward HTTP proxy.
149
166
  config :proxy, :validate => :uri_or_empty
150
167
 
@@ -161,6 +178,9 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
161
178
  # exactly once.
162
179
  config :schedule, :validate => :string
163
180
 
181
+ # If set, the _source of each hit will be added nested under the target instead of at the top-level
182
+ config :target, :validate => :field_reference
183
+
164
184
  def register
165
185
  require "elasticsearch"
166
186
  require "rufus/scheduler"
@@ -177,28 +197,20 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
177
197
  @slices < 1 && fail(LogStash::ConfigurationError, "Elasticsearch Input Plugin's `slices` option must be greater than zero, got `#{@slices}`")
178
198
  end
179
199
 
180
- transport_options = {}
181
-
200
+ validate_authentication
182
201
  fill_user_password_from_cloud_auth
202
+ fill_hosts_from_cloud_id
183
203
 
184
- if @user && @password
185
- token = Base64.strict_encode64("#{@user}:#{@password.value}")
186
- transport_options[:headers] = { :Authorization => "Basic #{token}" }
187
- end
188
204
 
189
- fill_hosts_from_cloud_id
190
- @hosts = Array(@hosts).map { |host| host.to_s } # potential SafeURI#to_s
205
+ transport_options = {:headers => {}}
206
+ transport_options[:headers].merge!(setup_basic_auth(user, password))
207
+ transport_options[:headers].merge!(setup_api_key(api_key))
208
+ transport_options[:request_timeout] = @request_timeout_seconds unless @request_timeout_seconds.nil?
209
+ transport_options[:connect_timeout] = @connect_timeout_seconds unless @connect_timeout_seconds.nil?
210
+ transport_options[:socket_timeout] = @socket_timeout_seconds unless @socket_timeout_seconds.nil?
191
211
 
192
- hosts = if @ssl
193
- @hosts.map do |h|
194
- host, port = h.split(":")
195
- { :host => host, :scheme => 'https', :port => port }
196
- end
197
- else
198
- @hosts
199
- end
200
- ssl_options = { :ssl => true, :ca_file => @ca_file } if @ssl && @ca_file
201
- ssl_options ||= {}
212
+ hosts = setup_hosts
213
+ ssl_options = setup_ssl
202
214
 
203
215
  @logger.warn "Supplied proxy setting (proxy => '') has no effect" if @proxy.eql?('')
204
216
 
@@ -212,22 +224,7 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
212
224
  )
213
225
  end
214
226
 
215
- ##
216
- # @override to handle proxy => '' as if none was set
217
- # @param value [Array<Object>]
218
- # @param validator [nil,Array,Symbol]
219
- # @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
220
- # @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
221
- def self.validate_value(value, validator)
222
- return super unless validator == :uri_or_empty
223
-
224
- value = deep_replace(value)
225
- value = hash_or_array(value)
226
227
 
227
- return true, value.first if value.size == 1 && value.first.empty?
228
-
229
- return super(value, :uri)
230
- end
231
228
 
232
229
  def run(output_queue)
233
230
  if @schedule
@@ -307,7 +304,12 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
307
304
  end
308
305
 
309
306
  def push_hit(hit, output_queue)
310
- event = LogStash::Event.new(hit['_source'])
307
+ if @target.nil?
308
+ event = LogStash::Event.new(hit['_source'])
309
+ else
310
+ event = LogStash::Event.new
311
+ event.set(@target, hit['_source'])
312
+ end
311
313
 
312
314
  if @docinfo
313
315
  # do not assume event[@docinfo_target] to be in-place updatable. first get it, update it, then at the end set it in the event.
@@ -351,25 +353,67 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
351
353
  hosts.nil? || ( hosts.is_a?(Array) && hosts.empty? )
352
354
  end
353
355
 
354
- def fill_hosts_from_cloud_id
355
- return unless @cloud_id
356
+ def validate_authentication
357
+ authn_options = 0
358
+ authn_options += 1 if @cloud_auth
359
+ authn_options += 1 if (@api_key && @api_key.value)
360
+ authn_options += 1 if (@user || (@password && @password.value))
356
361
 
357
- if @hosts && !hosts_default?(@hosts)
358
- raise LogStash::ConfigurationError, 'Both cloud_id and hosts specified, please only use one of those.'
362
+ if authn_options > 1
363
+ raise LogStash::ConfigurationError, 'Multiple authentication options are specified, please only use one of user/password, cloud_auth or api_key'
359
364
  end
360
- @hosts = parse_host_uri_from_cloud_id(@cloud_id)
365
+
366
+ if @api_key && @api_key.value && @ssl != true
367
+ raise(LogStash::ConfigurationError, "Using api_key authentication requires SSL/TLS secured communication using the `ssl => true` option")
368
+ end
369
+ end
370
+
371
+ def setup_ssl
372
+ @ssl && @ca_file ? { :ssl => true, :ca_file => @ca_file } : {}
373
+ end
374
+
375
+ def setup_hosts
376
+ @hosts = Array(@hosts).map { |host| host.to_s } # potential SafeURI#to_s
377
+ if @ssl
378
+ @hosts.map do |h|
379
+ host, port = h.split(":")
380
+ { :host => host, :scheme => 'https', :port => port }
381
+ end
382
+ else
383
+ @hosts
384
+ end
385
+ end
386
+
387
+ def setup_basic_auth(user, password)
388
+ return {} unless user && password && password.value
389
+
390
+ token = ::Base64.strict_encode64("#{user}:#{password.value}")
391
+ { Authorization: "Basic #{token}" }
392
+ end
393
+
394
+ def setup_api_key(api_key)
395
+ return {} unless (api_key && api_key.value)
396
+
397
+ token = ::Base64.strict_encode64(api_key.value)
398
+ { Authorization: "ApiKey #{token}" }
361
399
  end
362
400
 
363
401
  def fill_user_password_from_cloud_auth
364
402
  return unless @cloud_auth
365
403
 
366
- if @user || @password
367
- raise LogStash::ConfigurationError, 'Both cloud_auth and user/password specified, please only use one.'
368
- end
369
404
  @user, @password = parse_user_password_from_cloud_auth(@cloud_auth)
370
405
  params['user'], params['password'] = @user, @password
371
406
  end
372
407
 
408
+ def fill_hosts_from_cloud_id
409
+ return unless @cloud_id
410
+
411
+ if @hosts && !hosts_default?(@hosts)
412
+ raise LogStash::ConfigurationError, 'Both cloud_id and hosts specified, please only use one of those.'
413
+ end
414
+ @hosts = parse_host_uri_from_cloud_id(@cloud_id)
415
+ end
416
+
373
417
  def parse_host_uri_from_cloud_id(cloud_id)
374
418
  begin # might not be available on older LS
375
419
  require 'logstash/util/cloud_setting_id'
@@ -404,4 +448,42 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
404
448
  [ cloud_auth.username, cloud_auth.password ]
405
449
  end
406
450
 
451
+ module URIOrEmptyValidator
452
+ ##
453
+ # @override to provide :uri_or_empty validator
454
+ # @param value [Array<Object>]
455
+ # @param validator [nil,Array,Symbol]
456
+ # @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
457
+ # @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
458
+ def validate_value(value, validator)
459
+ return super unless validator == :uri_or_empty
460
+
461
+ value = deep_replace(value)
462
+ value = hash_or_array(value)
463
+
464
+ return true, value.first if value.size == 1 && value.first.empty?
465
+
466
+ return super(value, :uri)
467
+ end
468
+ end
469
+ extend(URIOrEmptyValidator)
470
+
471
+ module PositiveWholeNumberValidator
472
+ ##
473
+ # @override to provide :positive_whole_number validator
474
+ # @param value [Array<Object>]
475
+ # @param validator [nil,Array,Symbol]
476
+ # @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
477
+ # @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
478
+ def validate_value(value, validator)
479
+ return super unless validator == :positive_whole_number
480
+
481
+ is_number, coerced_number = super(value, :number)
482
+
483
+ return [true, coerced_number.to_i] if is_number && coerced_number.denominator == 1 && coerced_number > 0
484
+
485
+ return [false, "Expected positive whole number, got `#{value.inspect}`"]
486
+ end
487
+ end
488
+ extend(PositiveWholeNumberValidator)
407
489
  end
@@ -0,0 +1,48 @@
1
+ if Gem.loaded_specs['elasticsearch-transport'].version >= Gem::Version.new("7.2.0")
2
+ # elasticsearch-transport versions prior to 7.2.0 suffered of a race condition on accessing
3
+ # the connection pool. This issue was fixed with https://github.com/elastic/elasticsearch-ruby/commit/15f9d78591a6e8823948494d94b15b0ca38819d1
4
+ # This plugin, at the moment, is forced to use v5.x so we have to monkey patch the gem. When this requirement
5
+ # ceases, this patch could be removed.
6
+ puts "WARN remove the patch code into logstash-input-elasticsearch plugin"
7
+ else
8
+ module Elasticsearch
9
+ module Transport
10
+ module Transport
11
+ module Connections
12
+ module Selector
13
+
14
+ # "Round-robin" selector strategy (default).
15
+ #
16
+ class RoundRobin
17
+ include Base
18
+
19
+ # @option arguments [Connections::Collection] :connections Collection with connections.
20
+ #
21
+ def initialize(arguments = {})
22
+ super
23
+ @mutex = Mutex.new
24
+ @current = nil
25
+ end
26
+
27
+ # Returns the next connection from the collection, rotating them in round-robin fashion.
28
+ #
29
+ # @return [Connections::Connection]
30
+ #
31
+ def select(options={})
32
+ @mutex.synchronize do
33
+ conns = connections
34
+ if @current && (@current < conns.size-1)
35
+ @current += 1
36
+ else
37
+ @current = 0
38
+ end
39
+ conns[@current]
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-elasticsearch'
4
- s.version = '4.6.2'
4
+ s.version = '4.9.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Reads query results from an Elasticsearch cluster"
7
7
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
21
21
 
22
22
  # Gem dependencies
23
+ s.add_runtime_dependency "logstash-mixin-validator_support", '~> 1.0'
23
24
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
25
 
25
26
  s.add_runtime_dependency 'elasticsearch', '>= 5.0.3'
@@ -40,57 +40,91 @@ describe LogStash::Inputs::TestableElasticsearch do
40
40
  end
41
41
  end
42
42
 
43
- it "should retrieve json event from elasticseach" do
44
- config = %q[
45
- input {
46
- elasticsearch {
47
- hosts => ["localhost"]
48
- query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
43
+ context 'creating events from Elasticsearch' do
44
+ let(:config) do
45
+ %q[
46
+ input {
47
+ elasticsearch {
48
+ hosts => ["localhost"]
49
+ query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
50
+ }
49
51
  }
52
+ ]
53
+ end
54
+
55
+ let(:mock_response) do
56
+ {
57
+ "_scroll_id" => "cXVlcnlUaGVuRmV0Y2g",
58
+ "took" => 27,
59
+ "timed_out" => false,
60
+ "_shards" => {
61
+ "total" => 169,
62
+ "successful" => 169,
63
+ "failed" => 0
64
+ },
65
+ "hits" => {
66
+ "total" => 1,
67
+ "max_score" => 1.0,
68
+ "hits" => [ {
69
+ "_index" => "logstash-2014.10.12",
70
+ "_type" => "logs",
71
+ "_id" => "C5b2xLQwTZa76jBmHIbwHQ",
72
+ "_score" => 1.0,
73
+ "_source" => { "message" => ["ohayo"] }
74
+ } ]
75
+ }
50
76
  }
51
- ]
52
-
53
- response = {
54
- "_scroll_id" => "cXVlcnlUaGVuRmV0Y2g",
55
- "took" => 27,
56
- "timed_out" => false,
57
- "_shards" => {
58
- "total" => 169,
59
- "successful" => 169,
60
- "failed" => 0
61
- },
62
- "hits" => {
63
- "total" => 1,
64
- "max_score" => 1.0,
65
- "hits" => [ {
66
- "_index" => "logstash-2014.10.12",
67
- "_type" => "logs",
68
- "_id" => "C5b2xLQwTZa76jBmHIbwHQ",
69
- "_score" => 1.0,
70
- "_source" => { "message" => ["ohayo"] }
71
- } ]
77
+ end
78
+
79
+ let(:mock_scroll_response) do
80
+ {
81
+ "_scroll_id" => "r453Wc1jh0caLJhSDg",
82
+ "hits" => { "hits" => [] }
72
83
  }
73
- }
84
+ end
74
85
 
75
- scroll_reponse = {
76
- "_scroll_id" => "r453Wc1jh0caLJhSDg",
77
- "hits" => { "hits" => [] }
78
- }
86
+ before(:each) do
87
+ client = Elasticsearch::Client.new
88
+ expect(Elasticsearch::Client).to receive(:new).with(any_args).and_return(client)
89
+ expect(client).to receive(:search).with(any_args).and_return(mock_response)
90
+ expect(client).to receive(:scroll).with({ :body => { :scroll_id => "cXVlcnlUaGVuRmV0Y2g" }, :scroll=> "1m" }).and_return(mock_scroll_response)
91
+ expect(client).to receive(:clear_scroll).and_return(nil)
92
+ end
79
93
 
80
- client = Elasticsearch::Client.new
81
- expect(Elasticsearch::Client).to receive(:new).with(any_args).and_return(client)
82
- expect(client).to receive(:search).with(any_args).and_return(response)
83
- expect(client).to receive(:scroll).with({ :body => { :scroll_id => "cXVlcnlUaGVuRmV0Y2g" }, :scroll=> "1m" }).and_return(scroll_reponse)
84
- expect(client).to receive(:clear_scroll).and_return(nil)
94
+ it 'creates the events from the hits' do
95
+ event = input(config) do |pipeline, queue|
96
+ queue.pop
97
+ end
85
98
 
86
- event = input(config) do |pipeline, queue|
87
- queue.pop
99
+ expect(event).to be_a(LogStash::Event)
100
+ puts event.to_hash_with_metadata
101
+ expect(event.get("message")).to eql [ "ohayo" ]
88
102
  end
89
103
 
90
- expect(event).to be_a(LogStash::Event)
91
- expect(event.get("message")).to eql [ "ohayo" ]
92
- end
104
+ context 'when a target is set' do
105
+ let(:config) do
106
+ %q[
107
+ input {
108
+ elasticsearch {
109
+ hosts => ["localhost"]
110
+ query => '{ "query": { "match": { "city_name": "Okinawa" } }, "fields": ["message"] }'
111
+ target => "[@metadata][_source]"
112
+ }
113
+ }
114
+ ]
115
+ end
116
+
117
+ it 'creates the event using the target' do
118
+ event = input(config) do |pipeline, queue|
119
+ queue.pop
120
+ end
93
121
 
122
+ expect(event).to be_a(LogStash::Event)
123
+ puts event.to_hash_with_metadata
124
+ expect(event.get("[@metadata][_source][message]")).to eql [ "ohayo" ]
125
+ end
126
+ end
127
+ end
94
128
 
95
129
  # This spec is an adapter-spec, ensuring that we send the right sequence of messages to our Elasticsearch Client
96
130
  # to support sliced scrolling. The underlying implementation will spawn its own threads to consume, so we must be
@@ -583,7 +617,37 @@ describe LogStash::Inputs::TestableElasticsearch do
583
617
  let(:config) { super.merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
584
618
 
585
619
  it "should fail" do
586
- expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_auth and user/
620
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
621
+ end
622
+ end
623
+ end if LOGSTASH_VERSION > '6.0'
624
+
625
+ describe "api_key" do
626
+ context "without ssl" do
627
+ let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar') }) }
628
+
629
+ it "should fail" do
630
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /api_key authentication requires SSL\/TLS/
631
+ end
632
+ end
633
+
634
+ context "with ssl" do
635
+ let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar'), "ssl" => true }) }
636
+
637
+ it "should set authorization" do
638
+ plugin.register
639
+ client = plugin.send(:client)
640
+ auth_header = client.transport.options[:transport_options][:headers][:Authorization]
641
+
642
+ expect( auth_header ).to eql "ApiKey #{Base64.strict_encode64('foo:bar')}"
643
+ end
644
+
645
+ context 'user also set' do
646
+ let(:config) { super.merge({ 'api_key' => 'foo:bar', 'user' => 'another' }) }
647
+
648
+ it "should fail" do
649
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
650
+ end
587
651
  end
588
652
  end
589
653
  end if LOGSTASH_VERSION > '6.0'
@@ -610,6 +674,54 @@ describe LogStash::Inputs::TestableElasticsearch do
610
674
  end
611
675
  end
612
676
  end
677
+
678
+ shared_examples'configurable timeout' do |config_name, manticore_transport_option|
679
+ let(:config_value) { fail NotImplementedError }
680
+ let(:config) { super().merge(config_name => config_value) }
681
+ {
682
+ :string => 'banana',
683
+ :negative => -123,
684
+ :zero => 0,
685
+ }.each do |value_desc, value|
686
+ let(:config_value) { value }
687
+ context "with an invalid #{value_desc} value" do
688
+ it 'prevents instantiation with a helpful message' do
689
+ expect(described_class.logger).to receive(:error).with(/Expected positive whole number/)
690
+ expect { described_class.new(config) }.to raise_error(LogStash::ConfigurationError)
691
+ end
692
+ end
693
+ end
694
+
695
+ context 'with a valid value' do
696
+ let(:config_value) { 17 }
697
+
698
+ it "instantiates the elasticsearch client with the timeout value set via #{manticore_transport_option} in the transport options" do
699
+ expect(Elasticsearch::Client).to receive(:new) do |new_elasticsearch_client_params|
700
+ # We rely on Manticore-specific transport options, fail early if we are using a different
701
+ # transport or are allowing the client to determine its own transport class.
702
+ expect(new_elasticsearch_client_params).to include(:transport_class)
703
+ expect(new_elasticsearch_client_params[:transport_class].name).to match(/\bManticore\b/)
704
+
705
+ expect(new_elasticsearch_client_params).to include(:transport_options)
706
+ transport_options = new_elasticsearch_client_params[:transport_options]
707
+ expect(transport_options).to include(manticore_transport_option)
708
+ expect(transport_options[manticore_transport_option]).to eq(config_value.to_i)
709
+ end
710
+
711
+ plugin.register
712
+ end
713
+ end
714
+ end
715
+
716
+ context 'connect_timeout_seconds' do
717
+ include_examples('configurable timeout', 'connect_timeout_seconds', :connect_timeout)
718
+ end
719
+ context 'request_timeout_seconds' do
720
+ include_examples('configurable timeout', 'request_timeout_seconds', :request_timeout)
721
+ end
722
+ context 'socket_timeout_seconds' do
723
+ include_examples('configurable timeout', 'socket_timeout_seconds', :socket_timeout)
724
+ end
613
725
  end
614
726
 
615
727
  context "when scheduling" do
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.6.2
4
+ version: 4.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-02 00:00:00.000000000 Z
11
+ date: 2020-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ name: logstash-mixin-validator_support
20
+ prerelease: false
21
+ type: :runtime
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  requirement: !ruby/object:Gem::Requirement
15
29
  requirements:
@@ -200,6 +214,7 @@ files:
200
214
  - README.md
201
215
  - docs/index.asciidoc
202
216
  - lib/logstash/inputs/elasticsearch.rb
217
+ - lib/logstash/inputs/patch.rb
203
218
  - logstash-input-elasticsearch.gemspec
204
219
  - spec/es_helper.rb
205
220
  - spec/fixtures/test_certs/ca/ca.crt
@@ -230,7 +245,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
230
245
  version: '0'
231
246
  requirements: []
232
247
  rubyforge_project:
233
- rubygems_version: 2.6.13
248
+ rubygems_version: 2.7.10
234
249
  signing_key:
235
250
  specification_version: 4
236
251
  summary: Reads query results from an Elasticsearch cluster