logstash-integration-kafka 11.0.0-java → 11.2.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/docs/input-kafka.asciidoc +77 -0
- data/lib/logstash/inputs/kafka.rb +26 -3
- data/lib/logstash/plugin_mixins/kafka/avro_schema_registry.rb +31 -0
- data/logstash-integration-kafka.gemspec +1 -1
- data/spec/integration/inputs/kafka_spec.rb +184 -20
- data/spec/unit/inputs/kafka_spec.rb +5 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f4d4eeff4c13ac6151467cb7998742151a2829a18b05d6a74377edd7acab7fc
|
4
|
+
data.tar.gz: 22fc71adaf29c11b9f0090436dc2e330a1a21eefc8fceb3aa4b4063f2adad626
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ae9306a90795f0d877e896ee79e2c7ecd46b1c7033bca2d3182a5b0b4dbc66b50502d23c39a70451b5a7b83013763232e8e7a8a5dba8958d237831e2808ce6a
|
7
|
+
data.tar.gz: 5e4e48213df61f86cabf5e3535fc4f2c424714b904e12b0076812c832793b243ebbf54376edd90f9490e67f0a8268f290296be9570fa71704b626acd51ee92de
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 11.2.0
|
2
|
+
- Added TLS truststore and keystore settings specifically to access the schema registry [#137](https://github.com/logstash-plugins/logstash-integration-kafka/pull/137)
|
3
|
+
|
4
|
+
## 11.1.0
|
5
|
+
- Added config `group_instance_id` to use the Kafka's consumer static membership feature [#135](https://github.com/logstash-plugins/logstash-integration-kafka/pull/135)
|
6
|
+
|
1
7
|
## 11.0.0
|
2
8
|
- Changed Kafka client to 3.3.1, requires Logstash >= 8.3.0.
|
3
9
|
- Deprecated `default` value for setting `client_dns_lookup` forcing to `use_all_dns_ips` when explicitly used [#130](https://github.com/logstash-plugins/logstash-integration-kafka/pull/130)
|
data/docs/input-kafka.asciidoc
CHANGED
@@ -113,6 +113,7 @@ See the https://kafka.apache.org/{kafka_client_doc}/documentation for more detai
|
|
113
113
|
| <<plugins-{type}s-{plugin}-fetch_max_wait_ms>> |<<number,number>>|No
|
114
114
|
| <<plugins-{type}s-{plugin}-fetch_min_bytes>> |<<number,number>>|No
|
115
115
|
| <<plugins-{type}s-{plugin}-group_id>> |<<string,string>>|No
|
116
|
+
| <<plugins-{type}s-{plugin}-group_instance_id>> |<<string,string>>|No
|
116
117
|
| <<plugins-{type}s-{plugin}-heartbeat_interval_ms>> |<<number,number>>|No
|
117
118
|
| <<plugins-{type}s-{plugin}-isolation_level>> |<<string,string>>|No
|
118
119
|
| <<plugins-{type}s-{plugin}-jaas_path>> |a valid filesystem path|No
|
@@ -134,6 +135,12 @@ See the https://kafka.apache.org/{kafka_client_doc}/documentation for more detai
|
|
134
135
|
| <<plugins-{type}s-{plugin}-schema_registry_key>> |<<string,string>>|No
|
135
136
|
| <<plugins-{type}s-{plugin}-schema_registry_proxy>> |<<uri,uri>>|No
|
136
137
|
| <<plugins-{type}s-{plugin}-schema_registry_secret>> |<<string,string>>|No
|
138
|
+
| <<plugins-{type}s-{plugin}-schema_registry_ssl_keystore_location>> |a valid filesystem path|No
|
139
|
+
| <<plugins-{type}s-{plugin}-schema_registry_ssl_keystore_password>> |<<password,password>>|No
|
140
|
+
| <<plugins-{type}s-{plugin}-schema_registry_ssl_keystore_type>> |<<string,string>>, one of `["jks", "PKCS12"]`|No
|
141
|
+
| <<plugins-{type}s-{plugin}-schema_registry_ssl_truststore_location>> |a valid filesystem path|No
|
142
|
+
| <<plugins-{type}s-{plugin}-schema_registry_ssl_truststore_password>> |<<password,password>>|No
|
143
|
+
| <<plugins-{type}s-{plugin}-schema_registry_ssl_truststore_type>> |<<string,string>>, one of `["jks", "PKCS12"]`|No
|
137
144
|
| <<plugins-{type}s-{plugin}-schema_registry_url>> |<<uri,uri>>|No
|
138
145
|
| <<plugins-{type}s-{plugin}-schema_registry_validation>> |<<string,string>>|No
|
139
146
|
| <<plugins-{type}s-{plugin}-security_protocol>> |<<string,string>>, one of `["PLAINTEXT", "SSL", "SASL_PLAINTEXT", "SASL_SSL"]`|No
|
@@ -344,6 +351,28 @@ NOTE: In cases when multiple inputs are being used in a single pipeline, reading
|
|
344
351
|
it's essential to set a different `group_id => ...` for each input. Setting a unique `client_id => ...`
|
345
352
|
is also recommended.
|
346
353
|
|
354
|
+
[id="plugins-{type}s-{plugin}-group_instance_id"]
|
355
|
+
===== `group_instance_id`
|
356
|
+
|
357
|
+
* Value type is <<string,string>>
|
358
|
+
* There is no default value for this setting.
|
359
|
+
|
360
|
+
The static membership identifier for this Logstash Kafka consumer. Static membership feature was introduced in
|
361
|
+
https://cwiki.apache.org/confluence/display/KAFKA/KIP-345%3A+Introduce+static+membership+protocol+to+reduce+consumer+rebalances[KIP-345],
|
362
|
+
available under Kafka property `group.instance.id`.
|
363
|
+
Its purpose is to avoid rebalances in situations in which a lot of data
|
364
|
+
has to be forwarded after a consumer goes offline.
|
365
|
+
This feature mitigates cases where the service state is heavy and the rebalance of one topic partition from instance
|
366
|
+
A to B would cause a huge amount of data to be transferred.
|
367
|
+
A client that goes offline/online frequently can avoid frequent and heavy rebalances by using this option.
|
368
|
+
|
369
|
+
NOTE: The `group_instance_id` setting must be unique across all the clients belonging to the same <<plugins-{type}s-{plugin}-group_id>>.
|
370
|
+
Otherwise, another client connecting with same `group.instance.id` value would cause the oldest instance to be disconnected.
|
371
|
+
You can set this value to use information such as a hostname, an IP, or anything that uniquely identifies the client application.
|
372
|
+
|
373
|
+
NOTE: In cases when multiple threads are configured and `consumer_threads` is greater than one, a suffix is appended to
|
374
|
+
the `group_instance_id` to avoid collisions.
|
375
|
+
|
347
376
|
[id="plugins-{type}s-{plugin}-heartbeat_interval_ms"]
|
348
377
|
===== `heartbeat_interval_ms`
|
349
378
|
|
@@ -575,6 +604,54 @@ Set the address of a forward HTTP proxy. An empty string is treated as if proxy
|
|
575
604
|
|
576
605
|
Set the password for basic authorization to access remote Schema Registry.
|
577
606
|
|
607
|
+
[id="plugins-{type}s-{plugin}-schema_registry_ssl_keystore_location"]
|
608
|
+
===== `schema_registry_ssl_keystore_location`
|
609
|
+
|
610
|
+
* Value type is <<path,path>>
|
611
|
+
* There is no default value for this setting.
|
612
|
+
|
613
|
+
If schema registry client authentication is required, this setting stores the keystore path.
|
614
|
+
|
615
|
+
[id="plugins-{type}s-{plugin}-schema_registry_ssl_keystore_password"]
|
616
|
+
===== `schema_registry_ssl_keystore_password`
|
617
|
+
|
618
|
+
* Value type is <<password,password>>
|
619
|
+
* There is no default value for this setting.
|
620
|
+
|
621
|
+
If schema registry authentication is required, this setting stores the keystore password.
|
622
|
+
|
623
|
+
[id="plugins-{type}s-{plugin}-schema_registry_ssl_keystore_type"]
|
624
|
+
===== `schema_registry_ssl_keystore_type`
|
625
|
+
|
626
|
+
* Value type is <<string,string>>
|
627
|
+
* There is no default value for this setting.
|
628
|
+
|
629
|
+
The format of the keystore file. It must be either `jks` or `PKCS12`.
|
630
|
+
|
631
|
+
[id="plugins-{type}s-{plugin}-schema_registry_ssl_truststore_location"]
|
632
|
+
===== `schema_registry_ssl_truststore_location`
|
633
|
+
|
634
|
+
* Value type is <<path,path>>
|
635
|
+
* There is no default value for this setting.
|
636
|
+
|
637
|
+
The truststore path to validate the schema registry's certificate.
|
638
|
+
|
639
|
+
[id="plugins-{type}s-{plugin}-schema_registry_ssl_truststore_password"]
|
640
|
+
===== `schema_registry_ssl_truststore_password`
|
641
|
+
|
642
|
+
* Value type is <<password,password>>
|
643
|
+
* There is no default value for this setting.
|
644
|
+
|
645
|
+
The schema registry truststore password.
|
646
|
+
|
647
|
+
[id="plugins-{type}s-{plugin}-schema_registry_ssl_truststore_type"]
|
648
|
+
===== `schema_registry_ssl_truststore_type`
|
649
|
+
|
650
|
+
* Value type is <<string,string>>
|
651
|
+
* There is no default value for this setting.
|
652
|
+
|
653
|
+
The format of the schema registry's truststore file. It must be either `jks` or `PKCS12`.
|
654
|
+
|
578
655
|
[id="plugins-{type}s-{plugin}-schema_registry_url"]
|
579
656
|
===== `schema_registry_url`
|
580
657
|
|
@@ -124,6 +124,11 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
124
124
|
# that happens to be made up of multiple processors. Messages in a topic will be distributed to all
|
125
125
|
# Logstash instances with the same `group_id`
|
126
126
|
config :group_id, :validate => :string, :default => "logstash"
|
127
|
+
# Set a static group instance id used in static membership feature to avoid rebalancing when a
|
128
|
+
# consumer goes offline. If set and `consumer_threads` is greater than 1 then for each
|
129
|
+
# consumer crated by each thread an artificial suffix is appended to the user provided `group_instance_id`
|
130
|
+
# to avoid clashing.
|
131
|
+
config :group_instance_id, :validate => :string
|
127
132
|
# The expected time between heartbeats to the consumer coordinator. Heartbeats are used to ensure
|
128
133
|
# that the consumer's session stays active and to facilitate rebalancing when new
|
129
134
|
# consumers join or leave the group. The value must be set lower than
|
@@ -136,7 +141,7 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
136
141
|
# been aborted. Non-transactional messages will be returned unconditionally in either mode.
|
137
142
|
config :isolation_level, :validate => ["read_uncommitted", "read_committed"], :default => "read_uncommitted" # Kafka default
|
138
143
|
# Java Class used to deserialize the record's key
|
139
|
-
config :key_deserializer_class, :validate => :string, :default =>
|
144
|
+
config :key_deserializer_class, :validate => :string, :default => DEFAULT_DESERIALIZER_CLASS
|
140
145
|
# The maximum delay between invocations of poll() when using consumer group management. This places
|
141
146
|
# an upper bound on the amount of time that the consumer can be idle before fetching more records.
|
142
147
|
# If poll() is not called before expiration of this timeout, then the consumer is considered failed and
|
@@ -287,7 +292,10 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
287
292
|
|
288
293
|
public
|
289
294
|
def run(logstash_queue)
|
290
|
-
@runner_consumers = consumer_threads.times.map
|
295
|
+
@runner_consumers = consumer_threads.times.map do |i|
|
296
|
+
thread_group_instance_id = consumer_threads > 1 && group_instance_id ? "#{group_instance_id}-#{i}" : group_instance_id
|
297
|
+
subscribe(create_consumer("#{client_id}-#{i}", thread_group_instance_id))
|
298
|
+
end
|
291
299
|
@runner_threads = @runner_consumers.map.with_index { |consumer, i| thread_runner(logstash_queue, consumer,
|
292
300
|
"kafka-input-worker-#{client_id}-#{i}") }
|
293
301
|
@runner_threads.each(&:start)
|
@@ -335,6 +343,9 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
335
343
|
rescue org.apache.kafka.common.errors.WakeupException => e
|
336
344
|
logger.debug("Wake up from poll", :kafka_error_message => e)
|
337
345
|
raise e unless stop?
|
346
|
+
rescue org.apache.kafka.common.errors.FencedInstanceIdException => e
|
347
|
+
logger.error("Another consumer with same group.instance.id has connected", :original_error_message => e.message)
|
348
|
+
raise e unless stop?
|
338
349
|
rescue => e
|
339
350
|
logger.error("Unable to poll Kafka consumer",
|
340
351
|
:kafka_error_message => e,
|
@@ -389,7 +400,7 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
389
400
|
end
|
390
401
|
|
391
402
|
private
|
392
|
-
def create_consumer(client_id)
|
403
|
+
def create_consumer(client_id, group_instance_id)
|
393
404
|
begin
|
394
405
|
props = java.util.Properties.new
|
395
406
|
kafka = org.apache.kafka.clients.consumer.ConsumerConfig
|
@@ -407,6 +418,7 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
407
418
|
props.put(kafka::FETCH_MAX_WAIT_MS_CONFIG, fetch_max_wait_ms.to_s) unless fetch_max_wait_ms.nil?
|
408
419
|
props.put(kafka::FETCH_MIN_BYTES_CONFIG, fetch_min_bytes.to_s) unless fetch_min_bytes.nil?
|
409
420
|
props.put(kafka::GROUP_ID_CONFIG, group_id)
|
421
|
+
props.put(kafka::GROUP_INSTANCE_ID_CONFIG, group_instance_id) unless group_instance_id.nil?
|
410
422
|
props.put(kafka::HEARTBEAT_INTERVAL_MS_CONFIG, heartbeat_interval_ms.to_s) unless heartbeat_interval_ms.nil?
|
411
423
|
props.put(kafka::ISOLATION_LEVEL_CONFIG, isolation_level)
|
412
424
|
props.put(kafka::KEY_DESERIALIZER_CLASS_CONFIG, key_deserializer_class)
|
@@ -448,6 +460,17 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
448
460
|
set_trustore_keystore_config(props)
|
449
461
|
set_sasl_config(props)
|
450
462
|
end
|
463
|
+
if schema_registry_ssl_truststore_location
|
464
|
+
props.put('schema.registry.ssl.truststore.location', schema_registry_ssl_truststore_location)
|
465
|
+
props.put('schema.registry.ssl.truststore.password', schema_registry_ssl_truststore_password.value)
|
466
|
+
props.put('schema.registry.ssl.truststore.type', schema_registry_ssl_truststore_type)
|
467
|
+
end
|
468
|
+
|
469
|
+
if schema_registry_ssl_keystore_location
|
470
|
+
props.put('schema.registry.ssl.keystore.location', schema_registry_ssl_keystore_location)
|
471
|
+
props.put('schema.registry.ssl.keystore.password', schema_registry_ssl_keystore_password.value)
|
472
|
+
props.put('schema.registry.ssl.keystore.type', schema_registry_ssl_keystore_type)
|
473
|
+
end
|
451
474
|
|
452
475
|
org.apache.kafka.clients.consumer.KafkaConsumer.new(props)
|
453
476
|
rescue => e
|
@@ -24,6 +24,24 @@ module LogStash module PluginMixins module Kafka
|
|
24
24
|
# This option permits to define a proxy to be used to reach the schema registry service instance.
|
25
25
|
config :schema_registry_proxy, :validate => :uri
|
26
26
|
|
27
|
+
# If schema registry client authentication is required, this setting stores the keystore path.
|
28
|
+
config :schema_registry_ssl_keystore_location, :validate => :string
|
29
|
+
|
30
|
+
# The keystore password.
|
31
|
+
config :schema_registry_ssl_keystore_password, :validate => :password
|
32
|
+
|
33
|
+
# The keystore type
|
34
|
+
config :schema_registry_ssl_keystore_type, :validate => ['jks', 'PKCS12'], :default => "jks"
|
35
|
+
|
36
|
+
# The JKS truststore path to validate the Schema Registry's certificate.
|
37
|
+
config :schema_registry_ssl_truststore_location, :validate => :string
|
38
|
+
|
39
|
+
# The truststore password.
|
40
|
+
config :schema_registry_ssl_truststore_password, :validate => :password
|
41
|
+
|
42
|
+
# The truststore type
|
43
|
+
config :schema_registry_ssl_truststore_type, :validate => ['jks', 'PKCS12'], :default => "jks"
|
44
|
+
|
27
45
|
# Option to skip validating the schema registry during registration. This can be useful when using
|
28
46
|
# certificate based auth
|
29
47
|
config :schema_registry_validation, :validate => ['auto', 'skip'], :default => 'auto'
|
@@ -68,6 +86,19 @@ module LogStash module PluginMixins module Kafka
|
|
68
86
|
if schema_registry_key and !schema_registry_key.empty?
|
69
87
|
options[:auth] = {:user => schema_registry_key, :password => schema_registry_secret.value}
|
70
88
|
end
|
89
|
+
if schema_registry_ssl_truststore_location and !schema_registry_ssl_truststore_location.empty?
|
90
|
+
options[:ssl] = {} unless options.key?(:ssl)
|
91
|
+
options[:ssl][:truststore] = schema_registry_ssl_truststore_location unless schema_registry_ssl_truststore_location.nil?
|
92
|
+
options[:ssl][:truststore_password] = schema_registry_ssl_truststore_password.value unless schema_registry_ssl_truststore_password.nil?
|
93
|
+
options[:ssl][:truststore_type] = schema_registry_ssl_truststore_type unless schema_registry_ssl_truststore_type.nil?
|
94
|
+
end
|
95
|
+
if schema_registry_ssl_keystore_location and !schema_registry_ssl_keystore_location.empty?
|
96
|
+
options[:ssl] = {} unless options.key? :ssl
|
97
|
+
options[:ssl][:keystore] = schema_registry_ssl_keystore_location unless schema_registry_ssl_keystore_location.nil?
|
98
|
+
options[:ssl][:keystore_password] = schema_registry_ssl_keystore_password.value unless schema_registry_ssl_keystore_password.nil?
|
99
|
+
options[:ssl][:keystore_type] = schema_registry_ssl_keystore_type unless schema_registry_ssl_keystore_type.nil?
|
100
|
+
end
|
101
|
+
|
71
102
|
client = Manticore::Client.new(options)
|
72
103
|
begin
|
73
104
|
response = client.get(@schema_registry_url.uri.to_s + '/subjects').body
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-integration-kafka'
|
3
|
-
s.version = '11.
|
3
|
+
s.version = '11.2.0'
|
4
4
|
s.licenses = ['Apache-2.0']
|
5
5
|
s.summary = "Integration with Kafka - input and output plugins"
|
6
6
|
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline "+
|
@@ -79,6 +79,7 @@ describe "inputs/kafka", :integration => true do
|
|
79
79
|
producer = org.apache.kafka.clients.producer.KafkaProducer.new(props)
|
80
80
|
|
81
81
|
producer.send(record)
|
82
|
+
producer.flush
|
82
83
|
producer.close
|
83
84
|
end
|
84
85
|
|
@@ -185,10 +186,105 @@ describe "inputs/kafka", :integration => true do
|
|
185
186
|
end
|
186
187
|
end
|
187
188
|
end
|
189
|
+
|
190
|
+
context "static membership 'group.instance.id' setting" do
|
191
|
+
let(:base_config) do
|
192
|
+
{
|
193
|
+
"topics" => ["logstash_integration_static_membership_topic"],
|
194
|
+
"group_id" => "logstash",
|
195
|
+
"consumer_threads" => 1,
|
196
|
+
# this is needed because the worker thread could be executed little after the producer sent the "up" message
|
197
|
+
"auto_offset_reset" => "earliest",
|
198
|
+
"group_instance_id" => "test_static_group_id"
|
199
|
+
}
|
200
|
+
end
|
201
|
+
let(:consumer_config) { base_config }
|
202
|
+
let(:logger) { double("logger") }
|
203
|
+
let(:queue) { java.util.concurrent.ArrayBlockingQueue.new(10) }
|
204
|
+
let(:kafka_input) { LogStash::Inputs::Kafka.new(consumer_config) }
|
205
|
+
before :each do
|
206
|
+
allow(LogStash::Inputs::Kafka).to receive(:logger).and_return(logger)
|
207
|
+
[:error, :warn, :info, :debug].each do |level|
|
208
|
+
allow(logger).to receive(level)
|
209
|
+
end
|
210
|
+
|
211
|
+
kafka_input.register
|
212
|
+
end
|
213
|
+
|
214
|
+
it "input plugin disconnects from the broker when another client with same static membership connects" do
|
215
|
+
expect(logger).to receive(:error).with("Another consumer with same group.instance.id has connected", anything)
|
216
|
+
|
217
|
+
input_worker = java.lang.Thread.new { kafka_input.run(queue) }
|
218
|
+
begin
|
219
|
+
input_worker.start
|
220
|
+
wait_kafka_input_is_ready("logstash_integration_static_membership_topic", queue)
|
221
|
+
saboteur_kafka_consumer = create_consumer_and_start_consuming("test_static_group_id")
|
222
|
+
saboteur_kafka_consumer.run # ask to be scheduled
|
223
|
+
saboteur_kafka_consumer.join
|
224
|
+
|
225
|
+
expect(saboteur_kafka_consumer.value).to eq("saboteur exited")
|
226
|
+
ensure
|
227
|
+
input_worker.join(30_000)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "when the plugin is configured with multiple consumer threads" do
|
232
|
+
let(:consumer_config) { base_config.merge({"consumer_threads" => 2}) }
|
233
|
+
|
234
|
+
it "should avoid to connect with same 'group.instance.id'" do
|
235
|
+
expect(logger).to_not receive(:error).with("Another consumer with same group.instance.id has connected", anything)
|
236
|
+
|
237
|
+
input_worker = java.lang.Thread.new { kafka_input.run(queue) }
|
238
|
+
begin
|
239
|
+
input_worker.start
|
240
|
+
wait_kafka_input_is_ready("logstash_integration_static_membership_topic", queue)
|
241
|
+
ensure
|
242
|
+
kafka_input.stop
|
243
|
+
input_worker.join(1_000)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# return consumer Ruby Thread
|
251
|
+
def create_consumer_and_start_consuming(static_group_id)
|
252
|
+
props = java.util.Properties.new
|
253
|
+
kafka = org.apache.kafka.clients.consumer.ConsumerConfig
|
254
|
+
props.put(kafka::BOOTSTRAP_SERVERS_CONFIG, "localhost:9092")
|
255
|
+
props.put(kafka::KEY_DESERIALIZER_CLASS_CONFIG, LogStash::Inputs::Kafka::DEFAULT_DESERIALIZER_CLASS)
|
256
|
+
props.put(kafka::VALUE_DESERIALIZER_CLASS_CONFIG, LogStash::Inputs::Kafka::DEFAULT_DESERIALIZER_CLASS)
|
257
|
+
props.put(kafka::GROUP_ID_CONFIG, "logstash")
|
258
|
+
props.put(kafka::GROUP_INSTANCE_ID_CONFIG, static_group_id)
|
259
|
+
consumer = org.apache.kafka.clients.consumer.KafkaConsumer.new(props)
|
260
|
+
|
261
|
+
Thread.new do
|
262
|
+
LogStash::Util::set_thread_name("integration_test_simple_consumer")
|
263
|
+
begin
|
264
|
+
consumer.subscribe(["logstash_integration_static_membership_topic"])
|
265
|
+
records = consumer.poll(java.time.Duration.ofSeconds(3))
|
266
|
+
"saboteur exited"
|
267
|
+
rescue => e
|
268
|
+
e # return the exception reached in thread.value
|
269
|
+
ensure
|
270
|
+
consumer.close
|
271
|
+
end
|
272
|
+
end
|
188
273
|
end
|
189
274
|
|
190
275
|
private
|
191
276
|
|
277
|
+
def wait_kafka_input_is_ready(topic, queue)
|
278
|
+
# this is needed to give time to the kafka input to be up and running
|
279
|
+
header = org.apache.kafka.common.header.internals.RecordHeader.new("name", "Ping Up".to_java_bytes)
|
280
|
+
record = org.apache.kafka.clients.producer.ProducerRecord.new(topic, 0, "key", "value", [header])
|
281
|
+
send_message(record)
|
282
|
+
|
283
|
+
# Wait the message is processed
|
284
|
+
message = queue.poll(1, java.util.concurrent.TimeUnit::MINUTES)
|
285
|
+
expect(message).to_not eq(nil)
|
286
|
+
end
|
287
|
+
|
192
288
|
def consume_messages(config, queue: Queue.new, timeout:, event_count:)
|
193
289
|
kafka_input = LogStash::Inputs::Kafka.new(config)
|
194
290
|
kafka_input.register
|
@@ -257,10 +353,13 @@ describe "schema registry connection options" do
|
|
257
353
|
end
|
258
354
|
end
|
259
355
|
|
260
|
-
def save_avro_schema_to_schema_registry(schema_file, subject_name)
|
356
|
+
def save_avro_schema_to_schema_registry(schema_file, subject_name, proto = 'http', port = 8081, manticore_options = {})
|
261
357
|
raw_schema = File.readlines(schema_file).map(&:chomp).join
|
262
358
|
raw_schema_quoted = raw_schema.gsub('"', '\"')
|
263
|
-
|
359
|
+
|
360
|
+
client = Manticore::Client.new(manticore_options)
|
361
|
+
|
362
|
+
response = client.post("#{proto}://localhost:#{port}/subjects/#{subject_name}/versions",
|
264
363
|
body: '{"schema": "' + raw_schema_quoted + '"}',
|
265
364
|
headers: {"Content-Type" => "application/vnd.schemaregistry.v1+json"})
|
266
365
|
response
|
@@ -282,8 +381,17 @@ def startup_schema_registry(schema_registry, auth=false)
|
|
282
381
|
end
|
283
382
|
end
|
284
383
|
|
285
|
-
|
286
|
-
|
384
|
+
shared_examples 'it has endpoints available to' do |tls|
|
385
|
+
let(:port) { tls ? 8083 : 8081 }
|
386
|
+
let(:proto) { tls ? 'https' : 'http' }
|
387
|
+
|
388
|
+
manticore_options = {
|
389
|
+
:ssl => {
|
390
|
+
:truststore => File.join(Dir.pwd, "tls_repository/clienttruststore.jks"),
|
391
|
+
:truststore_password => "changeit"
|
392
|
+
}
|
393
|
+
}
|
394
|
+
schema_registry = Manticore::Client.new(manticore_options)
|
287
395
|
|
288
396
|
before(:all) do
|
289
397
|
startup_schema_registry(schema_registry)
|
@@ -295,36 +403,53 @@ describe "Schema registry API", :integration => true do
|
|
295
403
|
|
296
404
|
context 'listing subject on clean instance' do
|
297
405
|
it "should return an empty set" do
|
298
|
-
subjects = JSON.parse schema_registry.get(
|
406
|
+
subjects = JSON.parse schema_registry.get("#{proto}://localhost:#{port}/subjects").body
|
299
407
|
expect( subjects ).to be_empty
|
300
408
|
end
|
301
409
|
end
|
302
410
|
|
303
411
|
context 'send a schema definition' do
|
304
412
|
it "save the definition" do
|
305
|
-
response = save_avro_schema_to_schema_registry(File.join(Dir.pwd, "spec", "unit", "inputs", "avro_schema_fixture_payment.asvc"), "schema_test_1")
|
413
|
+
response = save_avro_schema_to_schema_registry(File.join(Dir.pwd, "spec", "unit", "inputs", "avro_schema_fixture_payment.asvc"), "schema_test_1", proto, port, manticore_options)
|
306
414
|
expect( response.code ).to be(200)
|
307
415
|
delete_remote_schema(schema_registry, "schema_test_1")
|
308
416
|
end
|
309
417
|
|
310
418
|
it "delete the schema just added" do
|
311
|
-
response = save_avro_schema_to_schema_registry(File.join(Dir.pwd, "spec", "unit", "inputs", "avro_schema_fixture_payment.asvc"), "schema_test_1")
|
419
|
+
response = save_avro_schema_to_schema_registry(File.join(Dir.pwd, "spec", "unit", "inputs", "avro_schema_fixture_payment.asvc"), "schema_test_1", proto, port, manticore_options)
|
312
420
|
expect( response.code ).to be(200)
|
313
421
|
|
314
|
-
expect( schema_registry.delete(
|
422
|
+
expect( schema_registry.delete("#{proto}://localhost:#{port}/subjects/schema_test_1?permanent=false").code ).to be(200)
|
315
423
|
sleep(1)
|
316
|
-
subjects = JSON.parse schema_registry.get(
|
424
|
+
subjects = JSON.parse schema_registry.get("#{proto}://localhost:#{port}/subjects").body
|
317
425
|
expect( subjects ).to be_empty
|
318
426
|
end
|
319
427
|
end
|
320
428
|
end
|
321
429
|
|
430
|
+
describe "Schema registry API", :integration => true do
|
431
|
+
|
432
|
+
context "when exposed with HTTPS" do
|
433
|
+
it_behaves_like 'it has endpoints available to', true
|
434
|
+
end
|
435
|
+
|
436
|
+
context "when exposed with plain HTTP" do
|
437
|
+
it_behaves_like 'it has endpoints available to', false
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
322
441
|
def shutdown_schema_registry
|
323
442
|
system('./stop_schema_registry.sh')
|
324
443
|
end
|
325
444
|
|
326
445
|
describe "Deserializing with the schema registry", :integration => true do
|
327
|
-
|
446
|
+
manticore_options = {
|
447
|
+
:ssl => {
|
448
|
+
:truststore => File.join(Dir.pwd, "tls_repository/clienttruststore.jks"),
|
449
|
+
:truststore_password => "changeit"
|
450
|
+
}
|
451
|
+
}
|
452
|
+
schema_registry = Manticore::Client.new(manticore_options)
|
328
453
|
|
329
454
|
shared_examples 'it reads from a topic using a schema registry' do |with_auth|
|
330
455
|
|
@@ -423,28 +548,57 @@ describe "Deserializing with the schema registry", :integration => true do
|
|
423
548
|
end
|
424
549
|
end
|
425
550
|
|
426
|
-
|
551
|
+
shared_examples 'with an unauthed schema registry' do |tls|
|
552
|
+
let(:port) { tls ? 8083 : 8081 }
|
553
|
+
let(:proto) { tls ? 'https' : 'http' }
|
554
|
+
|
427
555
|
let(:auth) { false }
|
428
556
|
let(:avro_topic_name) { "topic_avro" }
|
429
|
-
let(:subject_url) { "
|
430
|
-
let(:plain_config) { base_config.merge!({
|
557
|
+
let(:subject_url) { "#{proto}://localhost:#{port}/subjects" }
|
558
|
+
let(:plain_config) { base_config.merge!({
|
559
|
+
'schema_registry_url' => "#{proto}://localhost:#{port}",
|
560
|
+
'schema_registry_ssl_truststore_location' => File.join(Dir.pwd, "tls_repository/clienttruststore.jks"),
|
561
|
+
'schema_registry_ssl_truststore_password' => 'changeit',
|
562
|
+
}) }
|
431
563
|
|
432
564
|
it_behaves_like 'it reads from a topic using a schema registry', false
|
433
565
|
end
|
434
566
|
|
435
|
-
context 'with an
|
567
|
+
context 'with an unauthed schema registry' do
|
568
|
+
context "accessed through HTTPS" do
|
569
|
+
it_behaves_like 'with an unauthed schema registry', true
|
570
|
+
end
|
571
|
+
|
572
|
+
context "accessed through HTTPS" do
|
573
|
+
it_behaves_like 'with an unauthed schema registry', false
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
shared_examples 'with an authed schema registry' do |tls|
|
578
|
+
let(:port) { tls ? 8083 : 8081 }
|
579
|
+
let(:proto) { tls ? 'https' : 'http' }
|
436
580
|
let(:auth) { true }
|
437
581
|
let(:user) { "barney" }
|
438
582
|
let(:password) { "changeme" }
|
439
583
|
let(:avro_topic_name) { "topic_avro_auth" }
|
440
|
-
let(:subject_url) { "
|
584
|
+
let(:subject_url) { "#{proto}://#{user}:#{password}@localhost:#{port}/subjects" }
|
585
|
+
let(:tls_base_config) do
|
586
|
+
if tls
|
587
|
+
base_config.merge({
|
588
|
+
'schema_registry_ssl_truststore_location' => ::File.join(Dir.pwd, "tls_repository/clienttruststore.jks"),
|
589
|
+
'schema_registry_ssl_truststore_password' => 'changeit',
|
590
|
+
})
|
591
|
+
else
|
592
|
+
base_config
|
593
|
+
end
|
594
|
+
end
|
441
595
|
|
442
596
|
context 'using schema_registry_key' do
|
443
597
|
let(:plain_config) do
|
444
|
-
|
445
|
-
'schema_registry_url' => "
|
598
|
+
tls_base_config.merge!({
|
599
|
+
'schema_registry_url' => "#{proto}://localhost:#{port}",
|
446
600
|
'schema_registry_key' => user,
|
447
|
-
'schema_registry_secret' => password
|
601
|
+
'schema_registry_secret' => password,
|
448
602
|
})
|
449
603
|
end
|
450
604
|
|
@@ -453,12 +607,22 @@ describe "Deserializing with the schema registry", :integration => true do
|
|
453
607
|
|
454
608
|
context 'using schema_registry_url' do
|
455
609
|
let(:plain_config) do
|
456
|
-
|
457
|
-
'schema_registry_url' => "
|
610
|
+
tls_base_config.merge!({
|
611
|
+
'schema_registry_url' => "#{proto}://#{user}:#{password}@localhost:#{port}",
|
458
612
|
})
|
459
613
|
end
|
460
614
|
|
461
615
|
it_behaves_like 'it reads from a topic using a schema registry', true
|
462
616
|
end
|
463
617
|
end
|
618
|
+
|
619
|
+
context 'with an authed schema registry' do
|
620
|
+
context "accessed through HTTPS" do
|
621
|
+
it_behaves_like 'with an authed schema registry', true
|
622
|
+
end
|
623
|
+
|
624
|
+
context "accessed through HTTPS" do
|
625
|
+
it_behaves_like 'with an authed schema registry', false
|
626
|
+
end
|
627
|
+
end
|
464
628
|
end
|
@@ -297,7 +297,7 @@ describe LogStash::Inputs::Kafka do
|
|
297
297
|
to receive(:new).with(hash_including('client.rack' => 'EU-R1')).
|
298
298
|
and_return kafka_client = double('kafka-consumer')
|
299
299
|
|
300
|
-
expect( subject.send(:create_consumer, 'sample_client-0') ).to be kafka_client
|
300
|
+
expect( subject.send(:create_consumer, 'sample_client-0', 'group_instance_id') ).to be kafka_client
|
301
301
|
end
|
302
302
|
end
|
303
303
|
|
@@ -309,7 +309,7 @@ describe LogStash::Inputs::Kafka do
|
|
309
309
|
to receive(:new).with(hash_including('session.timeout.ms' => '25000', 'max.poll.interval.ms' => '345000')).
|
310
310
|
and_return kafka_client = double('kafka-consumer')
|
311
311
|
|
312
|
-
expect( subject.send(:create_consumer, 'sample_client-1') ).to be kafka_client
|
312
|
+
expect( subject.send(:create_consumer, 'sample_client-1', 'group_instance_id') ).to be kafka_client
|
313
313
|
end
|
314
314
|
end
|
315
315
|
|
@@ -321,7 +321,7 @@ describe LogStash::Inputs::Kafka do
|
|
321
321
|
to receive(:new).with(hash_including('session.timeout.ms' => '25200', 'max.poll.interval.ms' => '123000')).
|
322
322
|
and_return kafka_client = double('kafka-consumer')
|
323
323
|
|
324
|
-
expect( subject.send(:create_consumer, 'sample_client-2') ).to be kafka_client
|
324
|
+
expect( subject.send(:create_consumer, 'sample_client-2', 'group_instance_id') ).to be kafka_client
|
325
325
|
end
|
326
326
|
end
|
327
327
|
|
@@ -333,7 +333,7 @@ describe LogStash::Inputs::Kafka do
|
|
333
333
|
to receive(:new).with(hash_including('enable.auto.commit' => 'false', 'check.crcs' => 'true')).
|
334
334
|
and_return kafka_client = double('kafka-consumer')
|
335
335
|
|
336
|
-
expect( subject.send(:create_consumer, 'sample_client-3') ).to be kafka_client
|
336
|
+
expect( subject.send(:create_consumer, 'sample_client-3', 'group_instance_id') ).to be kafka_client
|
337
337
|
expect( subject.enable_auto_commit ).to be false
|
338
338
|
end
|
339
339
|
end
|
@@ -346,7 +346,7 @@ describe LogStash::Inputs::Kafka do
|
|
346
346
|
to receive(:new).with(hash_including('enable.auto.commit' => 'true', 'check.crcs' => 'false')).
|
347
347
|
and_return kafka_client = double('kafka-consumer')
|
348
348
|
|
349
|
-
expect( subject.send(:create_consumer, 'sample_client-4') ).to be kafka_client
|
349
|
+
expect( subject.send(:create_consumer, 'sample_client-4', 'group_instance_id') ).to be kafka_client
|
350
350
|
expect( subject.enable_auto_commit ).to be true
|
351
351
|
end
|
352
352
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-integration-kafka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 11.
|
4
|
+
version: 11.2.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|