logstash-output-elasticsearch-test 11.16.0-x86_64-linux
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +649 -0
- data/CONTRIBUTORS +34 -0
- data/Gemfile +16 -0
- data/LICENSE +202 -0
- data/NOTICE.TXT +5 -0
- data/README.md +106 -0
- data/docs/index.asciidoc +1369 -0
- data/lib/logstash/outputs/elasticsearch/data_stream_support.rb +282 -0
- data/lib/logstash/outputs/elasticsearch/default-ilm-policy.json +14 -0
- data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +155 -0
- data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +534 -0
- data/lib/logstash/outputs/elasticsearch/http_client.rb +497 -0
- data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +201 -0
- data/lib/logstash/outputs/elasticsearch/ilm.rb +92 -0
- data/lib/logstash/outputs/elasticsearch/license_checker.rb +52 -0
- data/lib/logstash/outputs/elasticsearch/template_manager.rb +131 -0
- data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-6x.json +45 -0
- data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-7x.json +44 -0
- data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-8x.json +50 -0
- data/lib/logstash/outputs/elasticsearch.rb +699 -0
- data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +237 -0
- data/lib/logstash/plugin_mixins/elasticsearch/common.rb +409 -0
- data/lib/logstash/plugin_mixins/elasticsearch/noop_license_checker.rb +9 -0
- data/logstash-output-elasticsearch.gemspec +40 -0
- data/spec/es_spec_helper.rb +225 -0
- data/spec/fixtures/_nodes/6x.json +81 -0
- data/spec/fixtures/_nodes/7x.json +92 -0
- data/spec/fixtures/htpasswd +2 -0
- data/spec/fixtures/license_check/active.json +16 -0
- data/spec/fixtures/license_check/inactive.json +5 -0
- data/spec/fixtures/nginx_reverse_proxy.conf +22 -0
- data/spec/fixtures/scripts/painless/scripted_update.painless +2 -0
- data/spec/fixtures/scripts/painless/scripted_update_nested.painless +1 -0
- data/spec/fixtures/scripts/painless/scripted_upsert.painless +1 -0
- data/spec/fixtures/template-with-policy-es6x.json +48 -0
- data/spec/fixtures/template-with-policy-es7x.json +45 -0
- data/spec/fixtures/template-with-policy-es8x.json +50 -0
- data/spec/fixtures/test_certs/ca.crt +29 -0
- data/spec/fixtures/test_certs/ca.der.sha256 +1 -0
- data/spec/fixtures/test_certs/ca.key +51 -0
- data/spec/fixtures/test_certs/renew.sh +13 -0
- data/spec/fixtures/test_certs/test.crt +30 -0
- data/spec/fixtures/test_certs/test.der.sha256 +1 -0
- data/spec/fixtures/test_certs/test.key +51 -0
- data/spec/fixtures/test_certs/test.p12 +0 -0
- data/spec/fixtures/test_certs/test_invalid.crt +36 -0
- data/spec/fixtures/test_certs/test_invalid.key +51 -0
- data/spec/fixtures/test_certs/test_invalid.p12 +0 -0
- data/spec/fixtures/test_certs/test_self_signed.crt +32 -0
- data/spec/fixtures/test_certs/test_self_signed.key +54 -0
- data/spec/fixtures/test_certs/test_self_signed.p12 +0 -0
- data/spec/integration/outputs/compressed_indexing_spec.rb +70 -0
- data/spec/integration/outputs/create_spec.rb +67 -0
- data/spec/integration/outputs/data_stream_spec.rb +68 -0
- data/spec/integration/outputs/delete_spec.rb +63 -0
- data/spec/integration/outputs/ilm_spec.rb +534 -0
- data/spec/integration/outputs/index_spec.rb +421 -0
- data/spec/integration/outputs/index_version_spec.rb +98 -0
- data/spec/integration/outputs/ingest_pipeline_spec.rb +75 -0
- data/spec/integration/outputs/metrics_spec.rb +66 -0
- data/spec/integration/outputs/no_es_on_startup_spec.rb +78 -0
- data/spec/integration/outputs/painless_update_spec.rb +99 -0
- data/spec/integration/outputs/parent_spec.rb +94 -0
- data/spec/integration/outputs/retry_spec.rb +182 -0
- data/spec/integration/outputs/routing_spec.rb +61 -0
- data/spec/integration/outputs/sniffer_spec.rb +94 -0
- data/spec/integration/outputs/templates_spec.rb +133 -0
- data/spec/integration/outputs/unsupported_actions_spec.rb +75 -0
- data/spec/integration/outputs/update_spec.rb +114 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/elasticsearch/api/actions/delete_ilm_policy.rb +19 -0
- data/spec/support/elasticsearch/api/actions/get_alias.rb +18 -0
- data/spec/support/elasticsearch/api/actions/get_ilm_policy.rb +18 -0
- data/spec/support/elasticsearch/api/actions/put_alias.rb +24 -0
- data/spec/support/elasticsearch/api/actions/put_ilm_policy.rb +25 -0
- data/spec/unit/http_client_builder_spec.rb +185 -0
- data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +612 -0
- data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +151 -0
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +501 -0
- data/spec/unit/outputs/elasticsearch/http_client_spec.rb +339 -0
- data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +189 -0
- data/spec/unit/outputs/elasticsearch_proxy_spec.rb +103 -0
- data/spec/unit/outputs/elasticsearch_spec.rb +1573 -0
- data/spec/unit/outputs/elasticsearch_ssl_spec.rb +197 -0
- data/spec/unit/outputs/error_whitelist_spec.rb +56 -0
- data/spec/unit/outputs/license_check_spec.rb +57 -0
- metadata +423 -0
@@ -0,0 +1,282 @@
|
|
1
|
+
module LogStash module Outputs class ElasticSearch
|
2
|
+
# DS specific behavior/configuration.
|
3
|
+
module DataStreamSupport
|
4
|
+
|
5
|
+
# @api private
|
6
|
+
ENABLING_ECS_GUIDANCE = <<~END.tr("\n", " ")
|
7
|
+
Elasticsearch data streams require that events adhere to the Elastic Common Schema.
|
8
|
+
While `ecs_compatibility` can be set for this individual Elasticsearch output plugin, doing so will not fix schema conflicts caused by upstream plugins in your pipeline.
|
9
|
+
To avoid mapping conflicts, you will need to use ECS-compatible field names and datatypes throughout your pipeline.
|
10
|
+
Many plugins support an `ecs_compatibility` mode, and the `pipeline.ecs_compatibility` setting can be used to opt-in for all plugins in a pipeline.
|
11
|
+
END
|
12
|
+
private_constant :ENABLING_ECS_GUIDANCE
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
# Defines whether data will be indexed into an Elasticsearch data stream,
|
16
|
+
# `data_stream_*` settings will only be used if this setting is enabled!
|
17
|
+
# This setting supports values `true`, `false`, and `auto`.
|
18
|
+
# Defaults to `false` in Logstash 7.x and `auto` starting in Logstash 8.0.
|
19
|
+
base.config :data_stream, :validate => ['true', 'false', 'auto']
|
20
|
+
|
21
|
+
base.config :data_stream_type, :validate => ['logs', 'metrics', 'synthetics', 'traces'], :default => 'logs'
|
22
|
+
base.config :data_stream_dataset, :validate => :dataset_identifier, :default => 'generic'
|
23
|
+
base.config :data_stream_namespace, :validate => :namespace_identifier, :default => 'default'
|
24
|
+
|
25
|
+
base.config :data_stream_sync_fields, :validate => :boolean, :default => true
|
26
|
+
base.config :data_stream_auto_routing, :validate => :boolean, :default => true
|
27
|
+
|
28
|
+
base.extend(Validator)
|
29
|
+
end
|
30
|
+
|
31
|
+
def data_stream_config?
|
32
|
+
@data_stream_config.nil? ? @data_stream_config = check_data_stream_config! : @data_stream_config
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def data_stream_name(event)
|
38
|
+
data_stream = event.get('data_stream')
|
39
|
+
return @index if !data_stream_auto_routing || !data_stream.is_a?(Hash)
|
40
|
+
|
41
|
+
type = data_stream['type'] || data_stream_type
|
42
|
+
dataset = data_stream['dataset'] || data_stream_dataset
|
43
|
+
namespace = data_stream['namespace'] || data_stream_namespace
|
44
|
+
"#{type}-#{dataset}-#{namespace}"
|
45
|
+
end
|
46
|
+
|
47
|
+
DATA_STREAMS_AND_ECS_ENABLED_BY_DEFAULT_LS_VERSION = '8.0.0'
|
48
|
+
|
49
|
+
# @param params the user configuration for the ES output
|
50
|
+
# @note LS initialized configuration (with filled defaults) won't detect as data-stream
|
51
|
+
# compatible, only explicit (`original_params`) config should be tested.
|
52
|
+
# @return [Boolean] whether given configuration is data-stream compatible
|
53
|
+
def check_data_stream_config!(params = original_params)
|
54
|
+
case data_stream_explicit_value
|
55
|
+
when false
|
56
|
+
check_disabled_data_stream_config!(params)
|
57
|
+
return false
|
58
|
+
when true
|
59
|
+
check_enabled_data_stream_config!(params)
|
60
|
+
return true
|
61
|
+
else # data_stream => auto or not set
|
62
|
+
use_data_stream = data_stream_default(params)
|
63
|
+
|
64
|
+
check_disabled_data_stream_config!(params) unless use_data_stream
|
65
|
+
|
66
|
+
@logger.info("Data streams auto configuration (`data_stream => auto` or unset) resolved to `#{use_data_stream}`")
|
67
|
+
return use_data_stream
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def check_enabled_data_stream_config!(params)
|
72
|
+
invalid_data_stream_params = invalid_data_stream_params(params)
|
73
|
+
|
74
|
+
if invalid_data_stream_params.any?
|
75
|
+
@logger.error "Invalid data stream configuration, the following parameters are not supported:", invalid_data_stream_params
|
76
|
+
raise LogStash::ConfigurationError, "Invalid data stream configuration: #{invalid_data_stream_params.keys}"
|
77
|
+
end
|
78
|
+
|
79
|
+
if ecs_compatibility == :disabled
|
80
|
+
if ecs_compatibility_required?
|
81
|
+
@logger.error "Invalid data stream configuration; `ecs_compatibility` must not be `disabled`. " + ENABLING_ECS_GUIDANCE
|
82
|
+
raise LogStash::ConfigurationError, "Invalid data stream configuration: `ecs_compatibility => disabled`"
|
83
|
+
end
|
84
|
+
|
85
|
+
@deprecation_logger.deprecated "In a future release of Logstash, the Elasticsearch output plugin's `data_stream => true` will require the plugin to be run in ECS compatibility mode. " + ENABLING_ECS_GUIDANCE
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_disabled_data_stream_config!(params)
|
90
|
+
data_stream_params = data_stream_params(params)
|
91
|
+
|
92
|
+
if data_stream_params.any?
|
93
|
+
@logger.error "Ambiguous configuration; data stream settings must not be present when data streams are disabled (caused by `data_stream => false`, `data_stream => auto` or unset resolved to false). " \
|
94
|
+
"You can either manually set `data_stream => true` or remove the following specific data stream settings: ", data_stream_params
|
95
|
+
|
96
|
+
raise LogStash::ConfigurationError,
|
97
|
+
"Ambiguous configuration; data stream settings must not be present when data streams are disabled: #{data_stream_params.keys}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def data_stream_params(params)
|
102
|
+
params.select { |name, _| name.start_with?('data_stream_') }
|
103
|
+
end
|
104
|
+
|
105
|
+
def data_stream_explicit_value
|
106
|
+
case @data_stream
|
107
|
+
when 'true'
|
108
|
+
return true
|
109
|
+
when 'false'
|
110
|
+
return false
|
111
|
+
else
|
112
|
+
return nil # 'auto' or not set by user
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def invalid_data_stream_params(params)
|
117
|
+
shared_params = LogStash::PluginMixins::ElasticSearch::APIConfigs::CONFIG_PARAMS.keys.map(&:to_s)
|
118
|
+
params.reject do |name, value|
|
119
|
+
# NOTE: intentionally do not support explicit DS configuration like:
|
120
|
+
# - `index => ...` identifier provided by data_stream_xxx settings
|
121
|
+
case name
|
122
|
+
when 'action'
|
123
|
+
value == 'create'
|
124
|
+
when 'routing', 'pipeline'
|
125
|
+
true
|
126
|
+
when 'data_stream'
|
127
|
+
value.to_s == 'true'
|
128
|
+
when 'manage_template'
|
129
|
+
value.to_s == 'false'
|
130
|
+
when 'ecs_compatibility' then true # required for LS <= 6.x
|
131
|
+
else
|
132
|
+
name.start_with?('data_stream_') ||
|
133
|
+
shared_params.include?(name) ||
|
134
|
+
inherited_internal_config_param?(name) # 'id', 'enabled_metric' etc
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def inherited_internal_config_param?(name)
|
140
|
+
self.class.superclass.get_config.key?(name.to_s) # superclass -> LogStash::Outputs::Base
|
141
|
+
end
|
142
|
+
|
143
|
+
DATA_STREAMS_ORIGIN_ES_VERSION = '7.9.0'
|
144
|
+
|
145
|
+
# @note assumes to be running AFTER {after_successful_connection} completed, due ES version checks
|
146
|
+
# @return [Gem::Version] if ES supports DS nil (or raise) otherwise
|
147
|
+
def assert_es_version_supports_data_streams
|
148
|
+
fail 'no last_es_version' unless last_es_version # assert - should not happen
|
149
|
+
es_version = ::Gem::Version.create(last_es_version)
|
150
|
+
if es_version < ::Gem::Version.create(DATA_STREAMS_ORIGIN_ES_VERSION)
|
151
|
+
@logger.error "Elasticsearch version does not support data streams, Logstash might end up writing to an index", es_version: es_version.version
|
152
|
+
# NOTE: when switching to synchronous check from register, this should be a ConfigurationError
|
153
|
+
raise LogStash::Error, "A data_stream configuration is only supported since Elasticsearch #{DATA_STREAMS_ORIGIN_ES_VERSION} " +
|
154
|
+
"(detected version #{es_version.version}), please upgrade your cluster"
|
155
|
+
end
|
156
|
+
es_version # return truthy
|
157
|
+
end
|
158
|
+
|
159
|
+
# when data_stream => is either 'auto' or not set
|
160
|
+
def data_stream_default(params)
|
161
|
+
if ecs_compatibility == :disabled
|
162
|
+
@logger.info("Not eligible for data streams because ecs_compatibility is not enabled. " + ENABLING_ECS_GUIDANCE)
|
163
|
+
return false
|
164
|
+
end
|
165
|
+
|
166
|
+
invalid_data_stream_params = invalid_data_stream_params(params)
|
167
|
+
|
168
|
+
if data_stream_and_ecs_enabled_by_default?
|
169
|
+
if invalid_data_stream_params.any?
|
170
|
+
@logger.info("Not eligible for data streams because config contains one or more settings that are not compatible with data streams: #{invalid_data_stream_params.inspect}")
|
171
|
+
return false
|
172
|
+
end
|
173
|
+
|
174
|
+
return true
|
175
|
+
end
|
176
|
+
|
177
|
+
# LS 7.x
|
178
|
+
if !invalid_data_stream_params.any? && !data_stream_params(params).any?
|
179
|
+
@logger.warn "Configuration is data stream compliant but due backwards compatibility Logstash 7.x will not assume " +
|
180
|
+
"writing to a data-stream, default behavior will change on Logstash 8.0 " +
|
181
|
+
"(set `data_stream => true/false` to disable this warning)"
|
182
|
+
end
|
183
|
+
false
|
184
|
+
end
|
185
|
+
|
186
|
+
def ecs_compatibility_required?
|
187
|
+
data_stream_and_ecs_enabled_by_default?
|
188
|
+
end
|
189
|
+
|
190
|
+
def data_stream_and_ecs_enabled_by_default?
|
191
|
+
::Gem::Version.create(LOGSTASH_VERSION) >= ::Gem::Version.create(DATA_STREAMS_AND_ECS_ENABLED_BY_DEFAULT_LS_VERSION)
|
192
|
+
end
|
193
|
+
|
194
|
+
# an {event_action_tuple} replacement when a data-stream configuration is detected
|
195
|
+
def data_stream_event_action_tuple(event)
|
196
|
+
event_data = event.to_hash
|
197
|
+
data_stream_event_sync(event_data) if data_stream_sync_fields
|
198
|
+
EventActionTuple.new('create', common_event_params(event), event, event_data)
|
199
|
+
end
|
200
|
+
|
201
|
+
DATA_STREAM_SYNC_FIELDS = [ 'type', 'dataset', 'namespace' ].freeze
|
202
|
+
|
203
|
+
def data_stream_event_sync(event_data)
|
204
|
+
data_stream = event_data['data_stream']
|
205
|
+
if data_stream.is_a?(Hash)
|
206
|
+
unless data_stream_auto_routing
|
207
|
+
sync_fields = DATA_STREAM_SYNC_FIELDS.select { |name| data_stream.key?(name) && data_stream[name] != send(:"data_stream_#{name}") }
|
208
|
+
if sync_fields.any? # these fields will need to be overwritten
|
209
|
+
info = sync_fields.inject({}) { |info, name| info[name] = data_stream[name]; info }
|
210
|
+
info[:event] = event_data
|
211
|
+
@logger.warn "Some data_stream fields are out of sync, these will be updated to reflect data-stream name", info
|
212
|
+
|
213
|
+
# NOTE: we work directly with event.to_hash data thus fine to mutate the 'data_stream' hash
|
214
|
+
sync_fields.each { |name| data_stream[name] = nil } # fallback to ||= bellow
|
215
|
+
end
|
216
|
+
end
|
217
|
+
else
|
218
|
+
unless data_stream.nil?
|
219
|
+
@logger.warn "Invalid 'data_stream' field type, due fields sync will overwrite", value: data_stream, event: event_data
|
220
|
+
end
|
221
|
+
event_data['data_stream'] = data_stream = Hash.new
|
222
|
+
end
|
223
|
+
|
224
|
+
data_stream['type'] ||= data_stream_type
|
225
|
+
data_stream['dataset'] ||= data_stream_dataset
|
226
|
+
data_stream['namespace'] ||= data_stream_namespace
|
227
|
+
|
228
|
+
event_data
|
229
|
+
end
|
230
|
+
|
231
|
+
module Validator
|
232
|
+
|
233
|
+
# @override {LogStash::Config::Mixin::validate_value} to handle custom validators
|
234
|
+
# @param value [Array<Object>]
|
235
|
+
# @param validator [nil,Array,Symbol]
|
236
|
+
# @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
|
237
|
+
# @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
|
238
|
+
def validate_value(value, validator)
|
239
|
+
case validator
|
240
|
+
when :dataset_identifier then validate_dataset_identifier(value)
|
241
|
+
when :namespace_identifier then validate_namespace_identifier(value)
|
242
|
+
else super
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
private
|
247
|
+
|
248
|
+
def validate_dataset_identifier(value)
|
249
|
+
valid, value = validate_value(value, :string)
|
250
|
+
return false, value unless valid
|
251
|
+
|
252
|
+
validate_identifier(value)
|
253
|
+
end
|
254
|
+
|
255
|
+
def validate_namespace_identifier(value)
|
256
|
+
valid, value = validate_value(value, :string)
|
257
|
+
return false, value unless valid
|
258
|
+
|
259
|
+
validate_identifier(value)
|
260
|
+
end
|
261
|
+
|
262
|
+
def validate_identifier(value, max_size = 100)
|
263
|
+
if value.empty?
|
264
|
+
return false, "Invalid identifier - empty string"
|
265
|
+
end
|
266
|
+
if value.bytesize > max_size
|
267
|
+
return false, "Invalid identifier - too long (#{value.bytesize} bytes)"
|
268
|
+
end
|
269
|
+
# cannot include \, /, *, ?, ", <, >, |, ' ' (space char), ',', #, :
|
270
|
+
if value.match? Regexp.union(INVALID_IDENTIFIER_CHARS)
|
271
|
+
return false, "Invalid characters detected #{INVALID_IDENTIFIER_CHARS.inspect} are not allowed"
|
272
|
+
end
|
273
|
+
return true, value
|
274
|
+
end
|
275
|
+
|
276
|
+
INVALID_IDENTIFIER_CHARS = [ '\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',', '#', ':' ]
|
277
|
+
private_constant :INVALID_IDENTIFIER_CHARS
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
end end end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'manticore'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module LogStash; module Outputs; class ElasticSearch; class HttpClient;
|
5
|
+
DEFAULT_HEADERS = { "Content-Type" => "application/json" }
|
6
|
+
|
7
|
+
class ManticoreAdapter
|
8
|
+
attr_reader :manticore, :logger
|
9
|
+
|
10
|
+
def initialize(logger, options)
|
11
|
+
@logger = logger
|
12
|
+
options = options.dup
|
13
|
+
options[:ssl] = options[:ssl] || {}
|
14
|
+
|
15
|
+
# We manage our own retries directly, so let's disable them here
|
16
|
+
options[:automatic_retries] = 0
|
17
|
+
# We definitely don't need cookies
|
18
|
+
options[:cookies] = false
|
19
|
+
|
20
|
+
@client_params = {:headers => DEFAULT_HEADERS.merge(options[:headers] || {})}
|
21
|
+
|
22
|
+
if options[:proxy]
|
23
|
+
options[:proxy] = manticore_proxy_hash(options[:proxy])
|
24
|
+
end
|
25
|
+
|
26
|
+
@manticore = ::Manticore::Client.new(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Transform the proxy option to a hash. Manticore's support for non-hash
|
30
|
+
# proxy options is broken. This was fixed in https://github.com/cheald/manticore/commit/34a00cee57a56148629ed0a47c329181e7319af5
|
31
|
+
# but this is not yet released
|
32
|
+
def manticore_proxy_hash(proxy_uri)
|
33
|
+
[:scheme, :port, :user, :password, :path].reduce(:host => proxy_uri.host) do |acc,opt|
|
34
|
+
value = proxy_uri.send(opt)
|
35
|
+
acc[opt] = value unless value.nil? || (value.is_a?(String) && value.empty?)
|
36
|
+
acc
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def client
|
41
|
+
@manticore
|
42
|
+
end
|
43
|
+
|
44
|
+
# Performs the request by invoking {Transport::Base#perform_request} with a block.
|
45
|
+
#
|
46
|
+
# @return [Response]
|
47
|
+
# @see Transport::Base#perform_request
|
48
|
+
#
|
49
|
+
def perform_request(url, method, path, params={}, body=nil)
|
50
|
+
# Perform 2-level deep merge on the params, so if the passed params and client params will both have hashes stored on a key they
|
51
|
+
# will be merged as well, instead of choosing just one of the values
|
52
|
+
params = (params || {}).merge(@client_params) { |key, oldval, newval|
|
53
|
+
(oldval.is_a?(Hash) && newval.is_a?(Hash)) ? oldval.merge(newval) : newval
|
54
|
+
}
|
55
|
+
params[:body] = body if body
|
56
|
+
|
57
|
+
if url.user
|
58
|
+
params[:auth] = {
|
59
|
+
:user => CGI.unescape(url.user),
|
60
|
+
# We have to unescape the password here since manticore won't do it
|
61
|
+
# for us unless its part of the URL
|
62
|
+
:password => CGI.unescape(url.password),
|
63
|
+
:eager => true
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
request_uri = format_url(url, path)
|
68
|
+
request_uri_as_string = remove_double_escaping(request_uri.to_s)
|
69
|
+
begin
|
70
|
+
resp = @manticore.send(method.downcase, request_uri_as_string, params)
|
71
|
+
# Manticore returns lazy responses by default
|
72
|
+
# We want to block for our usage, this will wait for the response to finish
|
73
|
+
resp.call
|
74
|
+
rescue ::Manticore::ManticoreException => e
|
75
|
+
log_request_error(e)
|
76
|
+
raise ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new(e, request_uri_as_string)
|
77
|
+
end
|
78
|
+
|
79
|
+
# 404s are excluded because they are valid codes in the case of
|
80
|
+
# template installation. We might need a better story around this later
|
81
|
+
# but for our current purposes this is correct
|
82
|
+
code = resp.code
|
83
|
+
if code < 200 || code > 299 && code != 404
|
84
|
+
raise ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError.new(code, request_uri, body, resp.body)
|
85
|
+
end
|
86
|
+
|
87
|
+
resp
|
88
|
+
end
|
89
|
+
|
90
|
+
def log_request_error(e)
|
91
|
+
details = { message: e.message, exception: e.class }
|
92
|
+
details[:cause] = e.cause if e.respond_to?(:cause)
|
93
|
+
details[:backtrace] = e.backtrace if @logger.debug?
|
94
|
+
|
95
|
+
level = case e
|
96
|
+
when ::Manticore::Timeout
|
97
|
+
:debug
|
98
|
+
when ::Manticore::UnknownException
|
99
|
+
:warn
|
100
|
+
else
|
101
|
+
:info
|
102
|
+
end
|
103
|
+
|
104
|
+
@logger.send level, "Failed to perform request", details
|
105
|
+
log_java_exception(details[:cause], :debug) if details[:cause] && @logger.debug?
|
106
|
+
end
|
107
|
+
|
108
|
+
def log_java_exception(e, level = :debug)
|
109
|
+
return unless e.is_a?(java.lang.Exception)
|
110
|
+
# @logger.name using the same convention as LS does
|
111
|
+
logger = self.class.name.gsub('::', '.').downcase
|
112
|
+
logger = org.apache.logging.log4j.LogManager.getLogger(logger)
|
113
|
+
logger.send(level, '', e) # logger.error('', e) - prints nested causes
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returned urls from this method should be checked for double escaping.
|
117
|
+
def format_url(url, path_and_query=nil)
|
118
|
+
request_uri = url.clone
|
119
|
+
|
120
|
+
# We excise auth info from the URL in case manticore itself tries to stick
|
121
|
+
# sensitive data in a thrown exception or log data
|
122
|
+
request_uri.user = nil
|
123
|
+
request_uri.password = nil
|
124
|
+
|
125
|
+
return request_uri.to_s if path_and_query.nil?
|
126
|
+
|
127
|
+
parsed_path_and_query = java.net.URI.new(path_and_query)
|
128
|
+
|
129
|
+
new_query_parts = [request_uri.query, parsed_path_and_query.query].select do |part|
|
130
|
+
part && !part.empty? # Skip empty nil and ""
|
131
|
+
end
|
132
|
+
|
133
|
+
request_uri.query = new_query_parts.join("&") unless new_query_parts.empty?
|
134
|
+
|
135
|
+
# use `raw_path`` as `path` will unescape any escaped '/' in the path
|
136
|
+
request_uri.path = "#{request_uri.path}/#{parsed_path_and_query.raw_path}".gsub(/\/{2,}/, "/")
|
137
|
+
request_uri
|
138
|
+
end
|
139
|
+
|
140
|
+
# Later versions of SafeURI will also escape the '%' sign in an already escaped URI.
|
141
|
+
# (If the path variable is used, it constructs a new java.net.URI object using the multi-arg constructor,
|
142
|
+
# which will escape any '%' characters in the path, as opposed to the single-arg constructor which requires illegal
|
143
|
+
# characters to be already escaped, and will throw otherwise)
|
144
|
+
# The URI needs to have been previously escaped, as it does not play nice with an escaped '/' in the
|
145
|
+
# middle of a URI, as required by date math, treating it as a path separator
|
146
|
+
def remove_double_escaping(url)
|
147
|
+
url.gsub(/%25([0-9A-F]{2})/i, '%\1')
|
148
|
+
end
|
149
|
+
|
150
|
+
def close
|
151
|
+
@manticore.close
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end; end; end; end
|