logstash-filter-elasticsearch 3.9.0 → 3.10.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: 18eb25a6d3942e75e5092fc2316ba4aaecc77cc272258b20db0580404757e104
4
- data.tar.gz: 62de9b40a41014e62f287ee7d8001d82f032661320ecb1bb941a2fe8a2e8c380
3
+ metadata.gz: 8e04ff9ca7407b8404764623b42f7ce8e754fe9ceafc6bde7cc064bc13c30723
4
+ data.tar.gz: 3b317df7d5bc1b11c912e8f9d6ec14c2ef260fa975412f2ebbf2baf84da547ba
5
5
  SHA512:
6
- metadata.gz: c5c01abd1acc96a76ab3c9e5d49e32f2f7a43c9bad12495e436b1467c6a8d21c987671d70018394f2cf7917c51c7b116b46e2878374a9f865d562b4fa77341c8
7
- data.tar.gz: 87dff1c1d7c7a0ea48b3e3ab6c00229f000d546ec154a792c281a2e302a85c501b8609e78d4e889b9fb9092dc2ab19aec1572f7b8ad692964023e7d44979f3c6
6
+ metadata.gz: 6dd245e044d1c06882885e776ba64463c1dbfc93d3c80ad03ad4009b6a3b6246e5a166dc11c09cafb58f2b05b3a6e485741aef6a2137edc227081b1e06bf9e3c
7
+ data.tar.gz: b9b211e1bf50bcb18d028e5322e78eaa3e113a034550eaae4ee3550b941e74235b5ed672417759eb35709e46693f7f602b8106ce13beb17bc6937eacbd991514
data/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 3.10.0
2
+ - Feat: add user-agent header passed to the Elasticsearch HTTP connection [#152](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/152)
3
+
4
+ ## 3.9.5
5
+ - Fixed SSL handshake hang indefinitely with proxy setup [#151](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/151)
6
+
7
+ ## 3.9.4
8
+ - Fix: a regression (in LS 7.14.0) where due the elasticsearch client update (from 5.0.5 to 7.5.0) the `Authorization`
9
+ header isn't passed, this leads to the plugin not being able to leverage `user`/`password` credentials set by the user.
10
+ [#148](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/148)
11
+ - Fix: default setting for `hosts` not working (since 3.7.0) GH-147
12
+ - Fix: mutating @hosts variable which leads to issues with multiple worker threads GH-129
13
+
14
+ ## 3.9.3
15
+ - [DOC] Update links to use shared attributes [#144](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/144)
16
+
17
+ ## 3.9.2
18
+ - [DOC] Fixed links to restructured Logstash-to-cloud docs [#142](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/142)
19
+
20
+ ## 3.9.1
21
+ - [DOC] Document the permissions required in secured clusters [#140](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/140)
22
+
1
23
  ## 3.9.0
2
24
  - Add support to define a proxy with the proxy config option [#134](https://github.com/logstash-plugins/logstash-filter-elasticsearch/pull/134)
3
25
 
data/CONTRIBUTORS CHANGED
@@ -11,6 +11,7 @@ Contributors:
11
11
  * Adrian Solom (addrians)
12
12
  * Colin Surprenant (colinsurprenant)
13
13
  * Andres Rodriguez (roaksoax)
14
+ * Luca Belluccini (lucabelluccini)
14
15
 
15
16
  Note: If you've sent us patches, bug reports, or otherwise contributed to
16
17
  Logstash, and you aren't on the list above and want to be, please let us know
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Logstash Plugin
2
2
 
3
- [![Travis Build Status](https://travis-ci.org/logstash-plugins/logstash-filter-elasticsearch.svg)](https://travis-ci.org/logstash-plugins/logstash-filter-elasticsearch)
3
+ [![Travis Build Status](https://travis-ci.com/logstash-plugins/logstash-filter-elasticsearch.svg)](https://travis-ci.com/logstash-plugins/logstash-filter-elasticsearch)
4
4
 
5
5
  This is a plugin for [Logstash](https://github.com/elastic/logstash).
6
6
 
data/docs/index.asciidoc CHANGED
@@ -24,7 +24,8 @@ Search Elasticsearch for a previous log event and copy some fields from it
24
24
  into the current event. Below are two complete examples of how this filter might
25
25
  be used.
26
26
 
27
- The first example uses the legacy 'query' parameter where the user is limited to an Elasticsearch query_string.
27
+ The first example uses the legacy 'query' parameter where the user is limited to
28
+ an Elasticsearch query_string.
28
29
  Whenever logstash receives an "end" event, it uses this elasticsearch
29
30
  filter to find the matching "start" event based on some operation identifier.
30
31
  Then it copies the `@timestamp` field from the "start" event into a new field on
@@ -110,6 +111,12 @@ Authentication to a secure Elasticsearch cluster is possible using _one_ of the
110
111
  * <<plugins-{type}s-{plugin}-cloud_auth>>
111
112
  * <<plugins-{type}s-{plugin}-api_key>>
112
113
 
114
+ [id="plugins-{type}s-{plugin}-autz"]
115
+ ==== Authorization
116
+
117
+ Authorization to a secure Elasticsearch cluster requires `read` permission at index level and `monitoring` permissions at cluster level.
118
+ The `monitoring` permission at cluster level is necessary to perform periodic connectivity checks.
119
+
113
120
  [id="plugins-{type}s-{plugin}-options"]
114
121
  ==== Elasticsearch Filter Configuration Options
115
122
 
@@ -168,19 +175,11 @@ Example:
168
175
  * Value type is <<password,password>>
169
176
  * There is no default value for this setting.
170
177
 
171
- Authenticate using Elasticsearch API key. Note that this option also requires enabling the `ssl` option.
172
-
173
- Format is `id:api_key` where `id` and `api_key` are as returned by the Elasticsearch https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html[Create API key API].
174
-
175
- [id="plugins-{type}s-{plugin}-proxy"]
176
- ===== `proxy`
178
+ Authenticate using Elasticsearch API key. Note that this option also requires
179
+ enabling the `ssl` option.
177
180
 
178
- * Value type is <<uri,uri>>
179
- * There is no default value for this setting.
180
-
181
- Set the address of a forward HTTP proxy.
182
- An empty string is treated as if proxy was not set, and is useful when using
183
- environment variables e.g. `proxy => '${LS_PROXY:}'`.
181
+ Format is `id:api_key` where `id` and `api_key` are as returned by the
182
+ Elasticsearch {ref}/security-api-create-api-key.html[Create API key API].
184
183
 
185
184
  [id="plugins-{type}s-{plugin}-ca_file"]
186
185
  ===== `ca_file`
@@ -190,6 +189,28 @@ environment variables e.g. `proxy => '${LS_PROXY:}'`.
190
189
 
191
190
  SSL Certificate Authority file
192
191
 
192
+ [id="plugins-{type}s-{plugin}-cloud_auth"]
193
+ ===== `cloud_auth`
194
+
195
+ * Value type is <<password,password>>
196
+ * There is no default value for this setting.
197
+
198
+ Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` pair.
199
+
200
+ For more info, check out the
201
+ {logstash-ref}/connecting-to-cloud.html[Logstash-to-Cloud documentation].
202
+
203
+ [id="plugins-{type}s-{plugin}-cloud_id"]
204
+ ===== `cloud_id`
205
+
206
+ * Value type is <<string,string>>
207
+ * There is no default value for this setting.
208
+
209
+ Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
210
+
211
+ For more info, check out the
212
+ {logstash-ref}/connecting-to-cloud.html[Logstash-to-Cloud documentation].
213
+
193
214
  [id="plugins-{type}s-{plugin}-docinfo_fields"]
194
215
  ===== `docinfo_fields`
195
216
 
@@ -263,14 +284,26 @@ Field substitution (e.g. `index-name-%{date_field}`) is available
263
284
 
264
285
  Basic Auth - password
265
286
 
287
+ [id="plugins-{type}s-{plugin}-proxy"]
288
+ ===== `proxy`
289
+
290
+ * Value type is <<uri,uri>>
291
+ * There is no default value for this setting.
292
+
293
+ Set the address of a forward HTTP proxy.
294
+ An empty string is treated as if proxy was not set, and is useful when using
295
+ environment variables e.g. `proxy => '${LS_PROXY:}'`.
296
+
266
297
  [id="plugins-{type}s-{plugin}-query"]
267
298
  ===== `query`
268
299
 
269
300
  * Value type is <<string,string>>
270
301
  * There is no default value for this setting.
271
302
 
272
- Elasticsearch query string. Read the Elasticsearch query string documentation.
273
- for more info at: https://www.elastic.co/guide/en/elasticsearch/reference/master/query-dsl-query-string-query.html#query-string-syntax
303
+ Elasticsearch query string. More information is available in the
304
+ {ref}/query-dsl-query-string-query.html#query-string-syntax[Elasticsearch query
305
+ string documentation].
306
+
274
307
 
275
308
  [id="plugins-{type}s-{plugin}-query_template"]
276
309
  ===== `query_template`
@@ -278,8 +311,8 @@ for more info at: https://www.elastic.co/guide/en/elasticsearch/reference/master
278
311
  * Value type is <<string,string>>
279
312
  * There is no default value for this setting.
280
313
 
281
- File path to elasticsearch query in DSL format. Read the Elasticsearch query documentation
282
- for more info at: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html
314
+ File path to elasticsearch query in DSL format. More information is available in
315
+ the {ref}/query-dsl.html[Elasticsearch query documentation].
283
316
 
284
317
  [id="plugins-{type}s-{plugin}-result_size"]
285
318
  ===== `result_size`
@@ -321,27 +354,6 @@ Tags the event on failure to look up previous log event information. This can be
321
354
 
322
355
  Basic Auth - username
323
356
 
324
- [id="plugins-{type}s-{plugin}-cloud_auth"]
325
- ===== `cloud_auth`
326
-
327
- * Value type is <<password,password>>
328
- * There is no default value for this setting.
329
-
330
- Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` pair.
331
-
332
- For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_auth[Logstash-to-Cloud documentation]
333
-
334
- [id="plugins-{type}s-{plugin}-cloud_id"]
335
- ===== `cloud_id`
336
-
337
- * Value type is <<string,string>>
338
- * There is no default value for this setting.
339
-
340
- Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
341
-
342
- For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
343
-
344
-
345
357
 
346
358
  [id="plugins-{type}s-{plugin}-common-options"]
347
359
  include::{include_path}/{type}.asciidoc[]
@@ -16,15 +16,17 @@ module LogStash
16
16
  password = options.fetch(:password, nil)
17
17
  api_key = options.fetch(:api_key, nil)
18
18
  proxy = options.fetch(:proxy, nil)
19
+ user_agent = options[:user_agent]
19
20
 
20
21
  transport_options = {:headers => {}}
21
22
  transport_options[:headers].merge!(setup_basic_auth(user, password))
22
23
  transport_options[:headers].merge!(setup_api_key(api_key))
24
+ transport_options[:headers].merge!({ 'user-agent' => "#{user_agent}" })
23
25
 
24
26
  logger.warn "Supplied proxy setting (proxy => '') has no effect" if @proxy.eql?('')
25
27
  transport_options[:proxy] = proxy.to_s if proxy && !proxy.eql?('')
26
28
 
27
- hosts.map! {|h| { host: h, scheme: 'https' } } if ssl
29
+ hosts = hosts.map { |host| { host: host, scheme: 'https' } } if ssl
28
30
  # set ca_file even if ssl isn't on, since the host can be an https url
29
31
  ssl_options = { ssl: true, ca_file: options[:ca_file] } if options[:ca_file]
30
32
  ssl_options ||= {}
@@ -43,14 +45,14 @@ module LogStash
43
45
  return {} unless user && password && password.value
44
46
 
45
47
  token = ::Base64.strict_encode64("#{user}:#{password.value}")
46
- { Authorization: "Basic #{token}" }
48
+ { 'Authorization' => "Basic #{token}" }
47
49
  end
48
50
 
49
51
  def setup_api_key(api_key)
50
52
  return {} unless (api_key && api_key.value)
51
53
 
52
54
  token = ::Base64.strict_encode64(api_key.value)
53
- { Authorization: "ApiKey #{token}" }
55
+ { 'Authorization' => "ApiKey #{token}" }
54
56
  end
55
57
  end
56
58
  end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ require "elasticsearch"
3
+ require "elasticsearch/transport/transport/http/manticore"
4
+
5
+ es_client_version = Gem.loaded_specs['elasticsearch-transport'].version
6
+ if es_client_version >= Gem::Version.new('7.2') && es_client_version < Gem::Version.new('7.16')
7
+ # elasticsearch-transport 7.2.0 - 7.14.0 had a bug where setting http headers
8
+ # ES::Client.new ..., transport_options: { headers: { 'Authorization' => ... } }
9
+ # would be lost https://github.com/elastic/elasticsearch-ruby/issues/1428
10
+ #
11
+ # NOTE: needs to be idempotent as input ES plugin might apply the same patch!
12
+ #
13
+ # @private
14
+ module Elasticsearch
15
+ module Transport
16
+ module Transport
17
+ module HTTP
18
+ class Manticore
19
+
20
+ def apply_headers(request_options, options)
21
+ headers = (options && options[:headers]) || {}
22
+ headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE
23
+
24
+ # this code is necessary to grab the correct user-agent header
25
+ # when this method is invoked with apply_headers(@request_options, options)
26
+ # from https://github.com/elastic/elasticsearch-ruby/blob/v7.14.0/elasticsearch-transport/lib/elasticsearch/transport/transport/http/manticore.rb#L113-L114
27
+ transport_user_agent = nil
28
+ if (options && options[:transport_options] && options[:transport_options][:headers])
29
+ transport_headers = {}
30
+ transport_headers = options[:transport_options][:headers]
31
+ transport_user_agent = find_value(transport_headers, USER_AGENT_REGEX)
32
+ end
33
+
34
+ headers[USER_AGENT_STR] = transport_user_agent || find_value(headers, USER_AGENT_REGEX) || user_agent_header
35
+ headers[ACCEPT_ENCODING] = GZIP if use_compression?
36
+ (request_options[:headers] ||= {}).merge!(headers) # this line was changed
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,19 +1,15 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/filters/base"
3
3
  require "logstash/namespace"
4
- require_relative "elasticsearch/client"
5
4
  require "logstash/json"
6
- require "logstash/util/safe_uri"
7
- java_import "java.util.concurrent.ConcurrentHashMap"
8
-
5
+ require_relative "elasticsearch/client"
6
+ require_relative "elasticsearch/patches/_elasticsearch_transport_http_manticore"
9
7
 
10
8
  class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
11
9
  config_name "elasticsearch"
12
10
 
13
- DEFAULT_HOST = ::LogStash::Util::SafeURI.new("//localhost:9200")
14
-
15
11
  # List of elasticsearch hosts to use for querying.
16
- config :hosts, :validate => :array, :default => [ DEFAULT_HOST ]
12
+ config :hosts, :validate => :array, :default => [ 'localhost:9200' ]
17
13
 
18
14
  # Comma-delimited list of index names to search; use `_all` or empty string to perform the operation on all indices.
19
15
  # Field substitution (e.g. `index-name-%{date_field}`) is available
@@ -112,7 +108,7 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
112
108
  fill_user_password_from_cloud_auth
113
109
  fill_hosts_from_cloud_id
114
110
 
115
- @hosts = Array(@hosts).map { |host| host.to_s } # for ES client URI#to_s
111
+ @hosts = Array(@hosts).map { |host| host.to_s } # potential SafeURI#to_s
116
112
 
117
113
  test_connection!
118
114
  end # def register
@@ -120,7 +116,7 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
120
116
  def filter(event)
121
117
  matched = false
122
118
  begin
123
- params = {:index => event.sprintf(@index) }
119
+ params = { :index => event.sprintf(@index) }
124
120
 
125
121
  if @query_dsl
126
122
  query = LogStash::Json.load(event.sprintf(@query_dsl))
@@ -180,6 +176,19 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
180
176
  end
181
177
  end # def filter
182
178
 
179
+ # public only to be reuse in testing
180
+ def prepare_user_agent
181
+ os_name = java.lang.System.getProperty('os.name')
182
+ os_version = java.lang.System.getProperty('os.version')
183
+ os_arch = java.lang.System.getProperty('os.arch')
184
+ jvm_vendor = java.lang.System.getProperty('java.vendor')
185
+ jvm_version = java.lang.System.getProperty('java.version')
186
+
187
+ plugin_version = Gem.loaded_specs['logstash-filter-elasticsearch'].version
188
+ # example: logstash/7.14.1 (OS=Linux-5.4.0-84-generic-amd64; JVM=AdoptOpenJDK-11.0.11) logstash-output-elasticsearch/11.0.1
189
+ "logstash/#{LOGSTASH_VERSION} (OS=#{os_name}-#{os_version}-#{os_arch}; JVM=#{jvm_vendor}-#{jvm_version}) logstash-#{@plugin_type}-#{config_name}/#{plugin_version}"
190
+ end
191
+
183
192
  private
184
193
 
185
194
  def client_options
@@ -196,7 +205,11 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
196
205
  def new_client
197
206
  # NOTE: could pass cloud-id/cloud-auth to client but than we would need to be stricter on ES version requirement
198
207
  # and also LS parsing might differ from ES client's parsing so for consistency we do not pass cloud options ...
199
- LogStash::Filters::ElasticsearchClient.new(@logger, @hosts, client_options)
208
+ opts = client_options
209
+
210
+ opts[:user_agent] = prepare_user_agent
211
+
212
+ LogStash::Filters::ElasticsearchClient.new(@logger, @hosts, opts)
200
213
  end
201
214
 
202
215
  def get_client
@@ -237,8 +250,7 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
237
250
  end
238
251
 
239
252
  def hosts_default?(hosts)
240
- # NOTE: would be nice if pipeline allowed us a clean way to detect a config default :
241
- hosts.is_a?(Array) && hosts.size == 1 && hosts.first.equal?(DEFAULT_HOST)
253
+ hosts.is_a?(Array) && hosts.size == 1 && !original_params.key?('hosts')
242
254
  end
243
255
 
244
256
  def validate_authentication
@@ -273,6 +285,7 @@ class LogStash::Filters::Elasticsearch < LogStash::Filters::Base
273
285
  end
274
286
 
275
287
  def parse_host_uri_from_cloud_id(cloud_id)
288
+ require 'logstash/util/safe_uri'
276
289
  begin # might not be available on older LS
277
290
  require 'logstash/util/cloud_setting_id'
278
291
  rescue LoadError
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-filter-elasticsearch'
4
- s.version = '3.9.0'
4
+ s.version = '3.10.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Copies fields from previous log events in Elasticsearch to current events "
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"
@@ -21,8 +21,10 @@ Gem::Specification.new do |s|
21
21
 
22
22
  # Gem dependencies
23
23
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
- s.add_runtime_dependency 'elasticsearch', ">= 5.0.3"
25
- s.add_runtime_dependency 'manticore', "~> 0.6"
24
+ s.add_runtime_dependency 'elasticsearch', ">= 5.0.5" # LS >= 6.7 and < 7.14 all used version 5.0.5
25
+ s.add_runtime_dependency 'manticore', ">= 0.7.1"
26
+ s.add_development_dependency 'cabin', ['~> 0.6']
27
+ s.add_development_dependency 'webrick'
26
28
 
27
29
  s.add_development_dependency 'logstash-devutils'
28
30
  end
data/spec/es_helper.rb CHANGED
@@ -7,8 +7,12 @@ module ESHelper
7
7
  end
8
8
  end
9
9
 
10
- def self.get_client
11
- Elasticsearch::Client.new(:hosts => [get_host_port])
10
+ def self.get_client(credentials)
11
+ require 'elasticsearch/transport/transport/http/faraday' # supports user/password options
12
+ host, port = get_host_port.split(':')
13
+ host_opts = credentials.inject({}) { |h, (k, v)| h[k.to_sym] = v; h } # user: _, password: _
14
+ host_opts.merge! host: host, port: port, scheme: 'http'
15
+ Elasticsearch::Client.new(hosts: [host_opts], transport_class: Elasticsearch::Transport::Transport::HTTP::Faraday)
12
16
  end
13
17
 
14
18
  def self.doc_type
@@ -3,6 +3,9 @@ require "logstash/devutils/rspec/spec_helper"
3
3
  require "logstash/plugin"
4
4
  require "logstash/filters/elasticsearch"
5
5
  require "logstash/json"
6
+ require "cabin"
7
+ require "webrick"
8
+ require "uri"
6
9
 
7
10
  describe LogStash::Filters::Elasticsearch do
8
11
 
@@ -291,6 +294,112 @@ describe LogStash::Filters::Elasticsearch do
291
294
  end
292
295
  end
293
296
 
297
+ class StoppableServer
298
+
299
+ attr_reader :port
300
+
301
+ def initialize()
302
+ queue = Queue.new
303
+ @first_req_waiter = java.util.concurrent.CountDownLatch.new(1)
304
+ @first_request = nil
305
+
306
+ @t = java.lang.Thread.new(
307
+ proc do
308
+ begin
309
+ @server = WEBrick::HTTPServer.new :Port => 0, :DocumentRoot => ".",
310
+ :Logger => Cabin::Channel.get, # silence WEBrick logging
311
+ :StartCallback => Proc.new {
312
+ queue.push("started")
313
+ }
314
+ @port = @server.config[:Port]
315
+ @server.mount_proc '/' do |req, res|
316
+ res.body = '''
317
+ {
318
+ "name": "ce7ccfb438e8",
319
+ "cluster_name": "docker-cluster",
320
+ "cluster_uuid": "DyR1hN03QvuCWXRy3jtb0g",
321
+ "version": {
322
+ "number": "7.13.1",
323
+ "build_flavor": "default",
324
+ "build_type": "docker",
325
+ "build_hash": "9a7758028e4ea59bcab41c12004603c5a7dd84a9",
326
+ "build_date": "2021-05-28T17:40:59.346932922Z",
327
+ "build_snapshot": false,
328
+ "lucene_version": "8.8.2",
329
+ "minimum_wire_compatibility_version": "6.8.0",
330
+ "minimum_index_compatibility_version": "6.0.0-beta1"
331
+ },
332
+ "tagline": "You Know, for Search"
333
+ }
334
+ '''
335
+ res.status = 200
336
+ res['Content-Type'] = 'application/json'
337
+ @first_request = req
338
+ @first_req_waiter.countDown()
339
+ end
340
+
341
+ @server.start
342
+ rescue => e
343
+ puts "Error in webserver thread #{e}"
344
+ # ignore
345
+ end
346
+ end
347
+ )
348
+ @t.daemon = true
349
+ @t.start
350
+ queue.pop # blocks until the server is up
351
+ end
352
+
353
+ def stop
354
+ @server.shutdown
355
+ end
356
+
357
+ def wait_receive_request
358
+ @first_req_waiter.await(2, java.util.concurrent.TimeUnit::SECONDS)
359
+ @first_request
360
+ end
361
+ end
362
+
363
+ describe "user-agent header" do
364
+ let!(:webserver) { StoppableServer.new } # webserver must be started before the call, so no lazy "let"
365
+
366
+ after :each do
367
+ webserver.stop
368
+ end
369
+
370
+ it "server should be started" do
371
+ require 'net/http'
372
+ response = nil
373
+ Net::HTTP.start('localhost', webserver.port) {|http|
374
+ response = http.request_get('/')
375
+ }
376
+ expect(response.code.to_i).to eq(200)
377
+ end
378
+
379
+ context "used by plugin" do
380
+ let(:config) do
381
+ {
382
+ "hosts" => ["localhost:#{webserver.port}"],
383
+ "query" => "response: 404",
384
+ "fields" => { "response" => "code" },
385
+ "docinfo_fields" => { "_index" => "es_index" },
386
+ "aggregation_fields" => { "bytes_avg" => "bytes_avg_ls_field" }
387
+ }
388
+ end
389
+ let(:plugin) { described_class.new(config) }
390
+ let(:event) { LogStash::Event.new({}) }
391
+
392
+ it "client should sent the expect user-agent" do
393
+ plugin.register
394
+
395
+ request = webserver.wait_receive_request
396
+
397
+ expect(request.header['user-agent'].size).to eq(1)
398
+ expect(request.header['user-agent'][0]).to match(/logstash\/\d*\.\d*\.\d* \(OS=.*; JVM=.*\) logstash-filter-elasticsearch\/\d*\.\d*\.\d*/)
399
+ end
400
+ end
401
+ end
402
+
294
403
  describe "client" do
295
404
  let(:config) do
296
405
  {
@@ -313,12 +422,12 @@ describe LogStash::Filters::Elasticsearch do
313
422
  'sample:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlvJGFjMzFlYmI5MDI0MTc3MzE1NzA0M2MzNGZkMjZmZDQ2OjkyNDMkYTRjMDYyMzBlNDhjOGZjZTdiZTg4YTA3NGEzYmIzZTA6OTI0NA=='
314
423
  end
315
424
 
316
- let(:config) { super.merge({ 'cloud_id' => valid_cloud_id }) }
425
+ let(:config) { super().merge({ 'cloud_id' => valid_cloud_id }) }
317
426
 
318
427
  it "should set host(s)" do
319
428
  plugin.register
320
429
  client = plugin.send(:get_client).client
321
- expect( client.transport.hosts ).to eql [{
430
+ expect( extract_transport(client).hosts ).to eql [{
322
431
  :scheme => "https",
323
432
  :host => "ac31ebb90241773157043c34fd26fd46.us-central1.gcp.cloud.es.io",
324
433
  :port => 9243,
@@ -328,7 +437,7 @@ describe LogStash::Filters::Elasticsearch do
328
437
  end
329
438
 
330
439
  context 'invalid' do
331
- let(:config) { super.merge({ 'cloud_id' => 'invalid:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlv' }) }
440
+ let(:config) { super().merge({ 'cloud_id' => 'invalid:dXMtY2VudHJhbDEuZ2NwLmNsb3VkLmVzLmlv' }) }
332
441
 
333
442
  it "should fail" do
334
443
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_id.*? is invalid/
@@ -336,7 +445,7 @@ describe LogStash::Filters::Elasticsearch do
336
445
  end
337
446
 
338
447
  context 'hosts also set' do
339
- let(:config) { super.merge({ 'cloud_id' => valid_cloud_id, 'hosts' => [ 'localhost:9200' ] }) }
448
+ let(:config) { super().merge({ 'cloud_id' => valid_cloud_id, 'hosts' => [ 'localhost:9200' ] }) }
340
449
 
341
450
  it "should fail" do
342
451
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_id and hosts/
@@ -345,18 +454,18 @@ describe LogStash::Filters::Elasticsearch do
345
454
  end if LOGSTASH_VERSION > '6.0'
346
455
 
347
456
  describe "cloud.auth" do
348
- let(:config) { super.merge({ 'cloud_auth' => LogStash::Util::Password.new('elastic:my-passwd-00') }) }
457
+ let(:config) { super().merge({ 'cloud_auth' => LogStash::Util::Password.new('elastic:my-passwd-00') }) }
349
458
 
350
459
  it "should set authorization" do
351
460
  plugin.register
352
461
  client = plugin.send(:get_client).client
353
- auth_header = client.transport.options[:transport_options][:headers][:Authorization]
462
+ auth_header = extract_transport(client).options[:transport_options][:headers]['Authorization']
354
463
 
355
464
  expect( auth_header ).to eql "Basic #{Base64.encode64('elastic:my-passwd-00').rstrip}"
356
465
  end
357
466
 
358
467
  context 'invalid' do
359
- let(:config) { super.merge({ 'cloud_auth' => 'invalid-format' }) }
468
+ let(:config) { super().merge({ 'cloud_auth' => 'invalid-format' }) }
360
469
 
361
470
  it "should fail" do
362
471
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_auth.*? format/
@@ -364,7 +473,7 @@ describe LogStash::Filters::Elasticsearch do
364
473
  end
365
474
 
366
475
  context 'user also set' do
367
- let(:config) { super.merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
476
+ let(:config) { super().merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
368
477
 
369
478
  it "should fail" do
370
479
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
@@ -374,7 +483,7 @@ describe LogStash::Filters::Elasticsearch do
374
483
 
375
484
  describe "api_key" do
376
485
  context "without ssl" do
377
- let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar') }) }
486
+ let(:config) { super().merge({ 'api_key' => LogStash::Util::Password.new('foo:bar') }) }
378
487
 
379
488
  it "should fail" do
380
489
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /api_key authentication requires SSL\/TLS/
@@ -382,18 +491,18 @@ describe LogStash::Filters::Elasticsearch do
382
491
  end
383
492
 
384
493
  context "with ssl" do
385
- let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar'), "ssl" => true }) }
494
+ let(:config) { super().merge({ 'api_key' => LogStash::Util::Password.new('foo:bar'), "ssl" => true }) }
386
495
 
387
496
  it "should set authorization" do
388
497
  plugin.register
389
498
  client = plugin.send(:get_client).client
390
- auth_header = client.transport.options[:transport_options][:headers][:Authorization]
499
+ auth_header = extract_transport(client).options[:transport_options][:headers]['Authorization']
391
500
 
392
501
  expect( auth_header ).to eql "ApiKey #{Base64.strict_encode64('foo:bar')}"
393
502
  end
394
503
 
395
504
  context 'user also set' do
396
- let(:config) { super.merge({ 'api_key' => 'foo:bar', 'user' => 'another' }) }
505
+ let(:config) { super().merge({ 'api_key' => 'foo:bar', 'user' => 'another' }) }
397
506
 
398
507
  it "should fail" do
399
508
  expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
@@ -404,30 +513,44 @@ describe LogStash::Filters::Elasticsearch do
404
513
 
405
514
  describe "proxy" do
406
515
  context 'valid' do
407
- let(:config) { super.merge({ 'proxy' => 'http://localhost:1234' }) }
516
+ let(:config) { super().merge({ 'proxy' => 'http://localhost:1234' }) }
408
517
 
409
518
  it "should set proxy" do
410
519
  plugin.register
411
520
  client = plugin.send(:get_client).client
412
- proxy = client.transport.options[:transport_options][:proxy]
521
+ proxy = extract_transport(client).options[:transport_options][:proxy]
413
522
 
414
523
  expect( proxy ).to eql "http://localhost:1234"
415
524
  end
416
525
  end
417
526
 
418
527
  context 'invalid' do
419
- let(:config) { super.merge({ 'proxy' => '${A_MISSING_ENV_VAR:}' }) }
528
+ let(:config) { super().merge({ 'proxy' => '${A_MISSING_ENV_VAR:}' }) }
420
529
 
421
530
  it "should not set proxy" do
422
531
  plugin.register
423
532
  client = plugin.send(:get_client).client
424
533
 
425
- expect( client.transport.options[:transport_options] ).to_not include(:proxy)
534
+ expect( extract_transport(client).options[:transport_options] ).to_not include(:proxy)
426
535
  end
427
536
  end
428
537
  end
429
538
  end
430
539
 
540
+ describe "defaults" do
541
+
542
+ let(:config) { Hash.new }
543
+ let(:plugin) { described_class.new(config) }
544
+
545
+ before { allow(plugin).to receive(:test_connection!) }
546
+
547
+ it "should set localhost:9200 as hosts" do
548
+ plugin.register
549
+ client = plugin.send(:get_client).client
550
+ expect( extract_transport(client).hosts ).to eql [{ :host => "localhost", :port => 9200, :protocol => "http"}]
551
+ end
552
+ end
553
+
431
554
  describe "query template" do
432
555
  let(:config) do
433
556
  {
@@ -453,4 +576,10 @@ describe LogStash::Filters::Elasticsearch do
453
576
  plugin.filter(LogStash::Event.new)
454
577
  end
455
578
  end
579
+
580
+ # @note can be removed once gem depends on elasticsearch >= 6.x
581
+ def extract_transport(client) # on 7.x client.transport is a ES::Transport::Client
582
+ client.transport.respond_to?(:transport) ? client.transport.transport : client.transport
583
+ end
584
+
456
585
  end
@@ -6,21 +6,31 @@ require_relative "../../../spec/es_helper"
6
6
 
7
7
  describe LogStash::Filters::Elasticsearch, :integration => true do
8
8
 
9
+ ELASTIC_SECURITY_ENABLED = ENV['ELASTIC_SECURITY_ENABLED'].eql? 'true'
9
10
 
10
- let(:config) do
11
+ let(:base_config) do
11
12
  {
12
- "index" => 'logs',
13
- "hosts" => [ESHelper.get_host_port],
14
- "query" => "response: 404",
15
- "sort" => "response",
16
- "fields" => [ ["response", "code"] ],
13
+ "index" => 'logs',
14
+ "hosts" => [ESHelper.get_host_port],
15
+ "query" => "response: 404",
16
+ "sort" => "response",
17
+ "fields" => [ ["response", "code"] ],
17
18
  }
18
19
  end
20
+
21
+ let(:credentials) do
22
+ { 'user' => 'elastic', 'password' => ENV['ELASTIC_PASSWORD'] }
23
+ end
24
+
25
+ let(:config) do
26
+ ELASTIC_SECURITY_ENABLED ? base_config.merge(credentials) : base_config
27
+ end
28
+
19
29
  let(:plugin) { described_class.new(config) }
20
30
  let(:event) { LogStash::Event.new({}) }
21
31
 
22
32
  before(:each) do
23
- @es = ESHelper.get_client
33
+ @es = ESHelper.get_client(ELASTIC_SECURITY_ENABLED ? credentials : {})
24
34
  # Delete all templates first.
25
35
  # Clean ES of data before we start.
26
36
  @es.indices.delete_template(:name => "*")
@@ -30,11 +40,10 @@ describe LogStash::Filters::Elasticsearch, :integration => true do
30
40
  ESHelper.index_doc(@es, :index => 'logs', :body => { :response => 404, :this => 'that'})
31
41
  end
32
42
  @es.indices.refresh
33
-
34
- plugin.register
35
43
  end
36
44
 
37
45
  it "should enhance the current event with new data" do
46
+ plugin.register
38
47
  plugin.filter(event)
39
48
  expect(event.get('code')).to eq(404)
40
49
  end
@@ -42,20 +51,28 @@ describe LogStash::Filters::Elasticsearch, :integration => true do
42
51
  context "when retrieving a list of elements" do
43
52
 
44
53
  let(:config) do
45
- {
46
- "index" => 'logs',
47
- "hosts" => [ESHelper.get_host_port],
48
- "query" => "response: 404",
49
- "fields" => [ ["response", "code"] ],
50
- "sort" => "response",
51
- "result_size" => 10
52
- }
54
+ super().merge("fields" => [ ["response", "code"] ], "result_size" => 10)
53
55
  end
54
56
 
57
+ before { plugin.register }
58
+
55
59
  it "should enhance the current event with new data" do
56
60
  plugin.filter(event)
57
61
  expect(event.get("code")).to eq([404]*10)
58
62
  end
59
63
 
60
64
  end
65
+
66
+ context "incorrect auth credentials" do
67
+
68
+ let(:config) do
69
+ super().reject { |key, _| key == 'password' }
70
+ end
71
+
72
+ it "should enhance the current event with new data" do
73
+ expect { plugin.register }.to raise_error Elasticsearch::Transport::Transport::Errors::Unauthorized
74
+ end
75
+
76
+ end if ELASTIC_SECURITY_ENABLED
77
+
61
78
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.0
4
+ version: 3.10.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-25 00:00:00.000000000 Z
11
+ date: 2021-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -35,7 +35,7 @@ dependencies:
35
35
  requirements:
36
36
  - - ">="
37
37
  - !ruby/object:Gem::Version
38
- version: 5.0.3
38
+ version: 5.0.5
39
39
  name: elasticsearch
40
40
  prerelease: false
41
41
  type: :runtime
@@ -43,21 +43,49 @@ dependencies:
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: 5.0.3
46
+ version: 5.0.5
47
47
  - !ruby/object:Gem::Dependency
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
50
- - - "~>"
50
+ - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: '0.6'
52
+ version: 0.7.1
53
53
  name: manticore
54
54
  prerelease: false
55
55
  type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 0.7.1
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '0.6'
67
+ name: cabin
68
+ prerelease: false
69
+ type: :development
56
70
  version_requirements: !ruby/object:Gem::Requirement
57
71
  requirements:
58
72
  - - "~>"
59
73
  - !ruby/object:Gem::Version
60
74
  version: '0.6'
75
+ - !ruby/object:Gem::Dependency
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ name: webrick
82
+ prerelease: false
83
+ type: :development
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
61
89
  - !ruby/object:Gem::Dependency
62
90
  requirement: !ruby/object:Gem::Requirement
63
91
  requirements:
@@ -89,6 +117,7 @@ files:
89
117
  - docs/index.asciidoc
90
118
  - lib/logstash/filters/elasticsearch.rb
91
119
  - lib/logstash/filters/elasticsearch/client.rb
120
+ - lib/logstash/filters/elasticsearch/patches/_elasticsearch_transport_http_manticore.rb
92
121
  - logstash-filter-elasticsearch.gemspec
93
122
  - spec/es_helper.rb
94
123
  - spec/filters/elasticsearch_spec.rb
@@ -121,8 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
150
  - !ruby/object:Gem::Version
122
151
  version: '0'
123
152
  requirements: []
124
- rubyforge_project:
125
- rubygems_version: 2.6.13
153
+ rubygems_version: 3.1.6
126
154
  signing_key:
127
155
  specification_version: 4
128
156
  summary: Copies fields from previous log events in Elasticsearch to current events