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 +4 -4
- data/CHANGELOG.md +22 -0
- data/CONTRIBUTORS +1 -0
- data/docs/index.asciidoc +96 -5
- data/lib/logstash/inputs/elasticsearch.rb +129 -47
- data/lib/logstash/inputs/patch.rb +48 -0
- data/logstash-input-elasticsearch.gemspec +2 -1
- data/spec/inputs/elasticsearch_spec.rb +155 -43
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4091feeb0b3bf292cfb9afcde7496a72f95168eb570b21d29064ade174bb1352
|
4
|
+
data.tar.gz: cfd02af050bb495dceea16b4ffe5daa6828088d2bd7994639ee08374f87da69d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 873680bea22204e65d519d310f3952f26fd672ce336504e45e99b13988e2da2794b7c05f83b3e1b7a87f317eb51c079759f9df515dc99236577417ecfd379aa1
|
7
|
+
data.tar.gz: 85c66a665eb7a3b503ab7cec64ea2f24b0e16829c4b78a75560d69c6272b1861583dad41e04ea1157ce58435714a86eaa6f4d71b5a4a724edfb9604f77150b99
|
data/CHANGELOG.md
CHANGED
@@ -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
|
|
data/CONTRIBUTORS
CHANGED
data/docs/index.asciidoc
CHANGED
@@ -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
|
|
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
|
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
|
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
|
279
|
-
https://www.elastic.co/guide/en/elasticsearch/reference/current/search-
|
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
|
-
|
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
|
-
|
190
|
-
|
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 =
|
193
|
-
|
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
|
-
|
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
|
355
|
-
|
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
|
358
|
-
raise LogStash::ConfigurationError, '
|
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
|
-
|
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.
|
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
|
-
|
44
|
-
config
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
87
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
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, /
|
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.
|
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-
|
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.
|
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
|