logstash-input-elasticsearch 4.5.0 → 4.7.1

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: a5e1d79e9b4b6fdd1deaf7575f6fe3c5c91c578a327478d3dec2172ca3d12cc0
4
- data.tar.gz: 046771cceceb57a4a9f0ad395f55fa273dc80b224fa50eab410c3a3b0212ff2b
3
+ metadata.gz: ba97fe448682c96253a9809b3c8010e358d0044a0bb6888498653f05048369d5
4
+ data.tar.gz: f49d1231a4e82657a2128e27f522d8e93cdf48b955d57e5f8ed788d0e58babe7
5
5
  SHA512:
6
- metadata.gz: 24e767e7f20fe2320ad6e103762e0a5158d23d3c63ed781675d1c9fd648bea8fcbc654722bd2c792cedccdc5f534617ac78d552938e60a8b7c957e5b078e1d70
7
- data.tar.gz: 9133d597e944199974171430a9c6bbe1acc7c3709d5d6371176e26152d53b0256dc62cac045e614f8123e10b904aa5af9c31a1d6c58be47b83823c0762d95fda
6
+ metadata.gz: 24ec0b4f107494c0be8264769ae6ff5143e30e02c680f7158bc4a04d430b370a67963c82f3148fbf064e70e6f439e182fdbf9ec8e2cfe180fcdf0697d884f260
7
+ data.tar.gz: d0a0841f6b014fe6f0799c423095db4fc112469893b5ca628717db42edaeb271457d92fc79cd5e1c63399f06293bf9823e0911ed7cfa21c4b0c71f88279a09ec
@@ -1,3 +1,19 @@
1
+ ## 4.7.1
2
+ - [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)
3
+ - [DOC] Added usage example of docinfo metadata [#98](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/98)
4
+
5
+ ## 4.7.0
6
+ - Added api_key support [#131](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/131)
7
+
8
+ ## 4.6.2
9
+ - Added scroll clearing and better handling of scroll expiration [#128](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/128)
10
+
11
+ ## 4.6.1
12
+ - [DOC] Removed outdated compatibility notice [#124](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/124)
13
+
14
+ ## 4.6.0
15
+ - Feat: added option to specify proxy for ES [#114](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/114)
16
+
1
17
  ## 4.5.0
2
18
  - Feat: Added support for cloud_id / cloud_auth configuration [#112](https://github.com/logstash-plugins/logstash-input-elasticsearch/pull/112)
3
19
 
data/LICENSE CHANGED
@@ -1,13 +1,202 @@
1
- Copyright (c) 2012-2018 Elasticsearch <http://www.elastic.co>
2
1
 
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
6
5
 
7
- http://www.apache.org/licenses/LICENSE-2.0
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
7
 
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2020 Elastic and contributors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -21,16 +21,6 @@ include::{include_path}/plugin_header.asciidoc[]
21
21
 
22
22
  ==== Description
23
23
 
24
- .Compatibility Note
25
- [NOTE]
26
- ================================================================================
27
- Starting with Elasticsearch 5.3, there's an {ref}/modules-http.html[HTTP setting]
28
- called `http.content_type.required`. If this option is set to `true`, and you
29
- are using Logstash 2.4 through 5.2, you need to update the Elasticsearch input
30
- plugin to version 4.0.2 or higher.
31
-
32
- ================================================================================
33
-
34
24
  Read from an Elasticsearch cluster, based on search query results.
35
25
  This is useful for replaying test logs, reindexing, etc.
36
26
  You can periodically schedule ingestion using a cron syntax
@@ -78,6 +68,16 @@ Further documentation describing this syntax can be found
78
68
  https://github.com/jmettraux/rufus-scheduler#parsing-cronlines-and-time-strings[here].
79
69
 
80
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
+
81
81
  [id="plugins-{type}s-{plugin}-options"]
82
82
  ==== Elasticsearch Input Configuration Options
83
83
 
@@ -86,6 +86,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
86
86
  [cols="<,<,<",options="header",]
87
87
  |=======================================================================
88
88
  |Setting |Input type|Required
89
+ | <<plugins-{type}s-{plugin}-api_key>> |<<password,password>>|No
89
90
  | <<plugins-{type}s-{plugin}-ca_file>> |a valid filesystem path|No
90
91
  | <<plugins-{type}s-{plugin}-cloud_auth>> |<<password,password>>|No
91
92
  | <<plugins-{type}s-{plugin}-cloud_id>> |<<string,string>>|No
@@ -95,6 +96,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
95
96
  | <<plugins-{type}s-{plugin}-hosts>> |<<array,array>>|No
96
97
  | <<plugins-{type}s-{plugin}-index>> |<<string,string>>|No
97
98
  | <<plugins-{type}s-{plugin}-password>> |<<password,password>>|No
99
+ | <<plugins-{type}s-{plugin}-proxy>> |<<uri,uri>>|No
98
100
  | <<plugins-{type}s-{plugin}-query>> |<<string,string>>|No
99
101
  | <<plugins-{type}s-{plugin}-schedule>> |<<string,string>>|No
100
102
  | <<plugins-{type}s-{plugin}-scroll>> |<<string,string>>|No
@@ -109,6 +111,16 @@ input plugins.
109
111
 
110
112
  &nbsp;
111
113
 
114
+ [id="plugins-{type}s-{plugin}-api_key"]
115
+ ===== `api_key`
116
+
117
+ * Value type is <<password,password>>
118
+ * There is no default value for this setting.
119
+
120
+ Authenticate using Elasticsearch API key. Note that this option also requires enabling the `ssl` option.
121
+
122
+ 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].
123
+
112
124
  [id="plugins-{type}s-{plugin}-ca_file"]
113
125
  ===== `ca_file`
114
126
 
@@ -172,11 +184,19 @@ Example
172
184
  }
173
185
  }
174
186
 
187
+ If set, you can use metadata information in the <<plugins-{type}s-{plugin}-add_field>> common option.
188
+
189
+ Example
190
+ [source, ruby]
191
+ input {
192
+ elasticsearch {
193
+ docinfo => true
194
+ add_field => {
195
+ identifier => %{[@metadata][_index]}:%{[@metadata][_type]}:%{[@metadata][_id]}"
196
+ }
197
+ }
198
+ }
175
199
 
176
- NOTE: Starting with Logstash 6.0, the `document_type` option is
177
- deprecated due to the
178
- https://www.elastic.co/guide/en/elasticsearch/reference/6.0/removal-of-types.html[removal of types in Logstash 6.0].
179
- It will be removed in the next major version of Logstash.
180
200
 
181
201
  [id="plugins-{type}s-{plugin}-docinfo_fields"]
182
202
  ===== `docinfo_fields`
@@ -231,6 +251,16 @@ The password to use together with the username in the `user` option
231
251
  when authenticating to the Elasticsearch server. If set to an empty
232
252
  string authentication will be disabled.
233
253
 
254
+ [id="plugins-{type}s-{plugin}-proxy"]
255
+ ===== `proxy`
256
+
257
+ * Value type is <<uri,uri>>
258
+ * There is no default value for this setting.
259
+
260
+ Set the address of a forward HTTP proxy.
261
+ An empty string is treated as if proxy was not set, this is useful when using
262
+ environment variables e.g. `proxy => '${LS_PROXY:}'`.
263
+
234
264
  [id="plugins-{type}s-{plugin}-query"]
235
265
  ===== `query`
236
266
 
@@ -279,8 +309,8 @@ This allows you to set the maximum number of hits returned per scroll.
279
309
  * Sensible values range from 2 to about 8.
280
310
 
281
311
  In some cases, it is possible to improve overall throughput by consuming multiple
282
- distinct slices of a query simultaneously using the
283
- https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html#sliced-scroll[Sliced Scroll API],
312
+ distinct slices of a query simultaneously using
313
+ https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html#slice-scroll[sliced scrolls],
284
314
  especially if the pipeline is spending significant time waiting on Elasticsearch
285
315
  to provide results.
286
316
 
@@ -319,4 +349,4 @@ empty string authentication will be disabled.
319
349
  [id="plugins-{type}s-{plugin}-common-options"]
320
350
  include::{include_path}/{type}.asciidoc[]
321
351
 
322
- :default_codec!:
352
+ :default_codec!:
@@ -70,11 +70,6 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
70
70
  # Port defaults to 9200
71
71
  config :hosts, :validate => :array
72
72
 
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
73
  # The index or alias to search.
79
74
  config :index, :validate => :string, :default => "logstash-*"
80
75
 
@@ -140,11 +135,23 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
140
135
  # Basic Auth - password
141
136
  config :password, :validate => :password
142
137
 
138
+ # Cloud ID, from the Elastic Cloud web console. If set `hosts` should not be used.
139
+ #
140
+ # For more info, check out the https://www.elastic.co/guide/en/logstash/current/connecting-to-cloud.html#_cloud_id[Logstash-to-Cloud documentation]
141
+ config :cloud_id, :validate => :string
142
+
143
143
  # Cloud authentication string ("<username>:<password>" format) is an alternative for the `user`/`password` configuration.
144
144
  #
145
145
  # 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
146
  config :cloud_auth, :validate => :password
147
147
 
148
+ # Authenticate using Elasticsearch API key.
149
+ # 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])
150
+ config :api_key, :validate => :password
151
+
152
+ # Set the address of a forward HTTP proxy.
153
+ config :proxy, :validate => :uri_or_empty
154
+
148
155
  # SSL
149
156
  config :ssl, :validate => :boolean, :default => false
150
157
 
@@ -174,34 +181,46 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
174
181
  @slices < 1 && fail(LogStash::ConfigurationError, "Elasticsearch Input Plugin's `slices` option must be greater than zero, got `#{@slices}`")
175
182
  end
176
183
 
177
- transport_options = {}
178
-
184
+ validate_authentication
179
185
  fill_user_password_from_cloud_auth
186
+ fill_hosts_from_cloud_id
180
187
 
181
- if @user && @password
182
- token = Base64.strict_encode64("#{@user}:#{@password.value}")
183
- transport_options[:headers] = { :Authorization => "Basic #{token}" }
184
- end
185
188
 
186
- fill_hosts_from_cloud_id
187
- @hosts = Array(@hosts).map { |host| host.to_s } # potential SafeURI#to_s
189
+ transport_options = {:headers => {}}
190
+ transport_options[:headers].merge!(setup_basic_auth(user, password))
191
+ transport_options[:headers].merge!(setup_api_key(api_key))
188
192
 
189
- hosts = if @ssl
190
- @hosts.map do |h|
191
- host, port = h.split(":")
192
- { :host => host, :scheme => 'https', :port => port }
193
- end
194
- else
195
- @hosts
196
- end
197
- ssl_options = { :ssl => true, :ca_file => @ca_file } if @ssl && @ca_file
198
- ssl_options ||= {}
193
+ hosts = setup_hosts
194
+ ssl_options = setup_ssl
199
195
 
200
- @client = Elasticsearch::Client.new(:hosts => hosts, :transport_options => transport_options,
201
- :transport_class => ::Elasticsearch::Transport::Transport::HTTP::Manticore,
202
- :ssl => ssl_options)
196
+ @logger.warn "Supplied proxy setting (proxy => '') has no effect" if @proxy.eql?('')
197
+
198
+ transport_options[:proxy] = @proxy.to_s if @proxy && !@proxy.eql?('')
199
+
200
+ @client = Elasticsearch::Client.new(
201
+ :hosts => hosts,
202
+ :transport_options => transport_options,
203
+ :transport_class => ::Elasticsearch::Transport::Transport::HTTP::Manticore,
204
+ :ssl => ssl_options
205
+ )
203
206
  end
204
207
 
208
+ ##
209
+ # @override to handle proxy => '' as if none was set
210
+ # @param value [Array<Object>]
211
+ # @param validator [nil,Array,Symbol]
212
+ # @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
213
+ # @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
214
+ def self.validate_value(value, validator)
215
+ return super unless validator == :uri_or_empty
216
+
217
+ value = deep_replace(value)
218
+ value = hash_or_array(value)
219
+
220
+ return true, value.first if value.size == 1 && value.first.empty?
221
+
222
+ return super(value, :uri)
223
+ end
205
224
 
206
225
  def run(output_queue)
207
226
  if @schedule
@@ -243,25 +262,41 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
243
262
  slice_options = @options.merge(:body => LogStash::Json.dump(slice_query) )
244
263
 
245
264
  logger.info("Slice starting", slice_id: slice_id, slices: @slices) unless slice_id.nil?
246
- r = search_request(slice_options)
247
-
248
- r['hits']['hits'].each { |hit| push_hit(hit, output_queue) }
249
- logger.debug("Slice progress", slice_id: slice_id, slices: @slices) unless slice_id.nil?
250
265
 
251
- has_hits = r['hits']['hits'].any?
266
+ scroll_id = nil
267
+ begin
268
+ r = search_request(slice_options)
252
269
 
253
- while has_hits && r['_scroll_id'] && !stop?
254
- r = process_next_scroll(output_queue, r['_scroll_id'])
270
+ r['hits']['hits'].each { |hit| push_hit(hit, output_queue) }
255
271
  logger.debug("Slice progress", slice_id: slice_id, slices: @slices) unless slice_id.nil?
256
- has_hits = r['has_hits']
272
+
273
+ has_hits = r['hits']['hits'].any?
274
+ scroll_id = r['_scroll_id']
275
+
276
+ while has_hits && scroll_id && !stop?
277
+ has_hits, scroll_id = process_next_scroll(output_queue, scroll_id)
278
+ logger.debug("Slice progress", slice_id: slice_id, slices: @slices) if logger.debug? && slice_id
279
+ end
280
+ logger.info("Slice complete", slice_id: slice_id, slices: @slices) unless slice_id.nil?
281
+ ensure
282
+ clear_scroll(scroll_id)
257
283
  end
258
- logger.info("Slice complete", slice_id: slice_id, slices: @slices) unless slice_id.nil?
259
284
  end
260
285
 
286
+ ##
287
+ # @param output_queue [#<<]
288
+ # @param scroll_id [String]: a scroll id to resume
289
+ # @return [Array(Boolean,String)]: a tuple representing whether the response
290
+ #
261
291
  def process_next_scroll(output_queue, scroll_id)
262
292
  r = scroll_request(scroll_id)
263
293
  r['hits']['hits'].each { |hit| push_hit(hit, output_queue) }
264
- {'has_hits' => r['hits']['hits'].any?, '_scroll_id' => r['_scroll_id']}
294
+ [r['hits']['hits'].any?, r['_scroll_id']]
295
+ rescue => e
296
+ # this will typically be triggered by a scroll timeout
297
+ logger.error("Scroll request error, aborting scroll", error: e.inspect)
298
+ # return no hits and original scroll_id so we can try to clear it
299
+ [false, scroll_id]
265
300
  end
266
301
 
267
302
  def push_hit(hit, output_queue)
@@ -290,39 +325,86 @@ class LogStash::Inputs::Elasticsearch < LogStash::Inputs::Base
290
325
  output_queue << event
291
326
  end
292
327
 
328
+ def clear_scroll(scroll_id)
329
+ @client.clear_scroll(scroll_id: scroll_id) if scroll_id
330
+ rescue => e
331
+ # ignore & log any clear_scroll errors
332
+ logger.warn("Ignoring clear_scroll exception", message: e.message)
333
+ end
334
+
293
335
  def scroll_request scroll_id
294
- client.scroll(:body => { :scroll_id => scroll_id }, :scroll => @scroll)
336
+ @client.scroll(:body => { :scroll_id => scroll_id }, :scroll => @scroll)
295
337
  end
296
338
 
297
339
  def search_request(options)
298
- client.search(options)
340
+ @client.search(options)
299
341
  end
300
342
 
301
- attr_reader :client
302
-
303
343
  def hosts_default?(hosts)
304
344
  hosts.nil? || ( hosts.is_a?(Array) && hosts.empty? )
305
345
  end
306
346
 
307
- def fill_hosts_from_cloud_id
308
- return unless @cloud_id
347
+ def validate_authentication
348
+ authn_options = 0
349
+ authn_options += 1 if @cloud_auth
350
+ authn_options += 1 if (@api_key && @api_key.value)
351
+ authn_options += 1 if (@user || (@password && @password.value))
309
352
 
310
- if @hosts && !hosts_default?(@hosts)
311
- raise LogStash::ConfigurationError, 'Both cloud_id and hosts specified, please only use one of those.'
353
+ if authn_options > 1
354
+ raise LogStash::ConfigurationError, 'Multiple authentication options are specified, please only use one of user/password, cloud_auth or api_key'
355
+ end
356
+
357
+ if @api_key && @api_key.value && @ssl != true
358
+ raise(LogStash::ConfigurationError, "Using api_key authentication requires SSL/TLS secured communication using the `ssl => true` option")
312
359
  end
313
- @hosts = parse_host_uri_from_cloud_id(@cloud_id)
360
+ end
361
+
362
+ def setup_ssl
363
+ @ssl && @ca_file ? { :ssl => true, :ca_file => @ca_file } : {}
364
+ end
365
+
366
+ def setup_hosts
367
+ @hosts = Array(@hosts).map { |host| host.to_s } # potential SafeURI#to_s
368
+ if @ssl
369
+ @hosts.map do |h|
370
+ host, port = h.split(":")
371
+ { :host => host, :scheme => 'https', :port => port }
372
+ end
373
+ else
374
+ @hosts
375
+ end
376
+ end
377
+
378
+ def setup_basic_auth(user, password)
379
+ return {} unless user && password && password.value
380
+
381
+ token = ::Base64.strict_encode64("#{user}:#{password.value}")
382
+ { Authorization: "Basic #{token}" }
383
+ end
384
+
385
+ def setup_api_key(api_key)
386
+ return {} unless (api_key && api_key.value)
387
+
388
+ token = ::Base64.strict_encode64(api_key.value)
389
+ { Authorization: "ApiKey #{token}" }
314
390
  end
315
391
 
316
392
  def fill_user_password_from_cloud_auth
317
393
  return unless @cloud_auth
318
394
 
319
- if @user || @password
320
- raise LogStash::ConfigurationError, 'Both cloud_auth and user/password specified, please only use one.'
321
- end
322
395
  @user, @password = parse_user_password_from_cloud_auth(@cloud_auth)
323
396
  params['user'], params['password'] = @user, @password
324
397
  end
325
398
 
399
+ def fill_hosts_from_cloud_id
400
+ return unless @cloud_id
401
+
402
+ if @hosts && !hosts_default?(@hosts)
403
+ raise LogStash::ConfigurationError, 'Both cloud_id and hosts specified, please only use one of those.'
404
+ end
405
+ @hosts = parse_host_uri_from_cloud_id(@cloud_id)
406
+ end
407
+
326
408
  def parse_host_uri_from_cloud_id(cloud_id)
327
409
  begin # might not be available on older LS
328
410
  require 'logstash/util/cloud_setting_id'
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-input-elasticsearch'
4
- s.version = '4.5.0'
4
+ s.version = '4.7.1'
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"
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/devutils/rspec/shared_examples"
3
4
  require "logstash/inputs/elasticsearch"
4
5
  require "elasticsearch"
5
6
  require "timecop"
@@ -7,9 +8,13 @@ require "stud/temporary"
7
8
  require "time"
8
9
  require "date"
9
10
 
10
- describe LogStash::Inputs::Elasticsearch do
11
+ class LogStash::Inputs::TestableElasticsearch < LogStash::Inputs::Elasticsearch
12
+ attr_reader :client
13
+ end
14
+
15
+ describe LogStash::Inputs::TestableElasticsearch do
11
16
 
12
- let(:plugin) { LogStash::Inputs::Elasticsearch.new(config) }
17
+ let(:plugin) { LogStash::Inputs::TestableElasticsearch.new(config) }
13
18
  let(:queue) { Queue.new }
14
19
 
15
20
  it_behaves_like "an interruptible input plugin" do
@@ -31,6 +36,7 @@ describe LogStash::Inputs::Elasticsearch do
31
36
  }
32
37
  allow(esclient).to receive(:search) { { "hits" => { "hits" => [hit] } } }
33
38
  allow(esclient).to receive(:scroll) { { "hits" => { "hits" => [hit] } } }
39
+ allow(esclient).to receive(:clear_scroll).and_return(nil)
34
40
  end
35
41
  end
36
42
 
@@ -75,13 +81,14 @@ describe LogStash::Inputs::Elasticsearch do
75
81
  expect(Elasticsearch::Client).to receive(:new).with(any_args).and_return(client)
76
82
  expect(client).to receive(:search).with(any_args).and_return(response)
77
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)
78
85
 
79
86
  event = input(config) do |pipeline, queue|
80
87
  queue.pop
81
88
  end
82
89
 
83
- insist { event }.is_a?(LogStash::Event)
84
- insist { event.get("message") } == [ "ohayo" ]
90
+ expect(event).to be_a(LogStash::Event)
91
+ expect(event.get("message")).to eql [ "ohayo" ]
85
92
  end
86
93
 
87
94
 
@@ -256,6 +263,8 @@ describe LogStash::Inputs::Elasticsearch do
256
263
  expect(Elasticsearch::Client).to receive(:new).with(any_args).and_return(client)
257
264
  plugin.register
258
265
 
266
+ expect(client).to receive(:clear_scroll).and_return(nil)
267
+
259
268
  # SLICE0 is a three-page scroll in which the last page is empty
260
269
  slice0_query = LogStash::Json.dump(query.merge('slice' => { 'id' => 0, 'max' => 2}))
261
270
  expect(client).to receive(:search).with(hash_including(:body => slice0_query)).and_return(slice0_response0)
@@ -359,6 +368,7 @@ describe LogStash::Inputs::Elasticsearch do
359
368
  expect(Elasticsearch::Client).to receive(:new).with(any_args).and_return(client)
360
369
  expect(client).to receive(:search).with(any_args).and_return(response)
361
370
  allow(client).to receive(:scroll).with({ :body => {:scroll_id => "cXVlcnlUaGVuRmV0Y2g"}, :scroll => "1m" }).and_return(scroll_reponse)
371
+ allow(client).to receive(:clear_scroll).and_return(nil)
362
372
  end
363
373
 
364
374
  context 'when defining docinfo' do
@@ -404,6 +414,7 @@ describe LogStash::Inputs::Elasticsearch do
404
414
  "docinfo_target" => 'metadata_with_string'
405
415
  } }
406
416
  it 'thows an exception if the `docinfo_target` exist but is not of type hash' do
417
+ expect(client).not_to receive(:clear_scroll)
407
418
  plugin.register
408
419
  expect { plugin.run([]) }.to raise_error(Exception, /incompatible event/)
409
420
  end
@@ -572,10 +583,63 @@ describe LogStash::Inputs::Elasticsearch do
572
583
  let(:config) { super.merge({ 'cloud_auth' => 'elastic:my-passwd-00', 'user' => 'another' }) }
573
584
 
574
585
  it "should fail" do
575
- expect { plugin.register }.to raise_error LogStash::ConfigurationError, /cloud_auth and user/
586
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
576
587
  end
577
588
  end
578
589
  end if LOGSTASH_VERSION > '6.0'
590
+
591
+ describe "api_key" do
592
+ context "without ssl" do
593
+ let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar') }) }
594
+
595
+ it "should fail" do
596
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /api_key authentication requires SSL\/TLS/
597
+ end
598
+ end
599
+
600
+ context "with ssl" do
601
+ let(:config) { super.merge({ 'api_key' => LogStash::Util::Password.new('foo:bar'), "ssl" => true }) }
602
+
603
+ it "should set authorization" do
604
+ plugin.register
605
+ client = plugin.send(:client)
606
+ auth_header = client.transport.options[:transport_options][:headers][:Authorization]
607
+
608
+ expect( auth_header ).to eql "ApiKey #{Base64.strict_encode64('foo:bar')}"
609
+ end
610
+
611
+ context 'user also set' do
612
+ let(:config) { super.merge({ 'api_key' => 'foo:bar', 'user' => 'another' }) }
613
+
614
+ it "should fail" do
615
+ expect { plugin.register }.to raise_error LogStash::ConfigurationError, /Multiple authentication options are specified/
616
+ end
617
+ end
618
+ end
619
+ end if LOGSTASH_VERSION > '6.0'
620
+
621
+ describe "proxy" do
622
+ let(:config) { super.merge({ 'proxy' => 'http://localhost:1234' }) }
623
+
624
+ it "should set proxy" do
625
+ plugin.register
626
+ client = plugin.send(:client)
627
+ proxy = client.transport.options[:transport_options][:proxy]
628
+
629
+ expect( proxy ).to eql "http://localhost:1234"
630
+ end
631
+
632
+ context 'invalid' do
633
+ let(:config) { super.merge({ 'proxy' => '${A_MISSING_ENV_VAR:}' }) }
634
+
635
+ it "should not set proxy" do
636
+ plugin.register
637
+ client = plugin.send(:client)
638
+
639
+ expect( client.transport.options[:transport_options] ).to_not include(:proxy)
640
+ end
641
+ end
642
+ end
579
643
  end
580
644
 
581
645
  context "when scheduling" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-elasticsearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.5.0
4
+ version: 4.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-08 00:00:00.000000000 Z
11
+ date: 2020-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement