logstash-integration-kafka 10.9.0-java → 11.3.2-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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -2
  3. data/DEVELOPER.md +1 -1
  4. data/docs/index.asciidoc +1 -1
  5. data/docs/input-kafka.asciidoc +96 -8
  6. data/docs/output-kafka.asciidoc +20 -5
  7. data/lib/logstash/inputs/kafka.rb +42 -21
  8. data/lib/logstash/outputs/kafka.rb +7 -12
  9. data/lib/logstash/plugin_mixins/kafka/avro_schema_registry.rb +139 -0
  10. data/lib/logstash/plugin_mixins/kafka/common.rb +55 -0
  11. data/lib/logstash-integration-kafka_jars.rb +9 -14
  12. data/logstash-integration-kafka.gemspec +2 -2
  13. data/spec/integration/inputs/kafka_spec.rb +184 -20
  14. data/spec/integration/outputs/kafka_spec.rb +21 -1
  15. data/spec/unit/inputs/kafka_spec.rb +28 -5
  16. data/spec/unit/outputs/kafka_spec.rb +8 -0
  17. data/vendor/jar-dependencies/com/github/luben/zstd-jni/1.5.5-4/zstd-jni-1.5.5-4.jar +0 -0
  18. data/vendor/jar-dependencies/io/confluent/kafka-avro-serializer/7.4.0/kafka-avro-serializer-7.4.0.jar +0 -0
  19. data/vendor/jar-dependencies/io/confluent/kafka-schema-registry-client/7.4.0/kafka-schema-registry-client-7.4.0.jar +0 -0
  20. data/vendor/jar-dependencies/io/confluent/kafka-schema-serializer/7.4.0/kafka-schema-serializer-7.4.0.jar +0 -0
  21. data/vendor/jar-dependencies/org/apache/avro/avro/1.11.3/avro-1.11.3.jar +0 -0
  22. data/vendor/jar-dependencies/org/apache/kafka/kafka-clients/3.4.1/kafka-clients-3.4.1.jar +0 -0
  23. data/vendor/jar-dependencies/org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar +0 -0
  24. data/vendor/jar-dependencies/org/slf4j/slf4j-api/{1.7.30/slf4j-api-1.7.30.jar → 1.7.36/slf4j-api-1.7.36.jar} +0 -0
  25. data/vendor/jar-dependencies/org/xerial/snappy/snappy-java/1.1.10.5/snappy-java-1.1.10.5.jar +0 -0
  26. metadata +16 -21
  27. data/lib/logstash/plugin_mixins/common.rb +0 -107
  28. data/lib/logstash/plugin_mixins/kafka_support.rb +0 -29
  29. data/vendor/jar-dependencies/com/github/luben/zstd-jni/1.4.4-7/zstd-jni-1.4.4-7.jar +0 -0
  30. data/vendor/jar-dependencies/io/confluent/common-config/5.5.1/common-config-5.5.1.jar +0 -0
  31. data/vendor/jar-dependencies/io/confluent/common-utils/5.5.1/common-utils-5.5.1.jar +0 -0
  32. data/vendor/jar-dependencies/io/confluent/kafka-avro-serializer/5.5.1/kafka-avro-serializer-5.5.1.jar +0 -0
  33. data/vendor/jar-dependencies/io/confluent/kafka-schema-registry-client/5.5.1/kafka-schema-registry-client-5.5.1.jar +0 -0
  34. data/vendor/jar-dependencies/io/confluent/kafka-schema-serializer/5.5.1/kafka-schema-serializer-5.5.1.jar +0 -0
  35. data/vendor/jar-dependencies/javax/ws/rs/javax.ws.rs-api/2.1.1/javax.ws.rs-api-2.1.1.jar +0 -0
  36. data/vendor/jar-dependencies/org/apache/avro/avro/1.9.2/avro-1.9.2.jar +0 -0
  37. data/vendor/jar-dependencies/org/apache/kafka/kafka-clients/2.5.1/kafka-clients-2.5.1.jar +0 -0
  38. data/vendor/jar-dependencies/org/apache/kafka/kafka_2.12/2.5.1/kafka_2.12-2.5.1.jar +0 -0
  39. data/vendor/jar-dependencies/org/glassfish/jersey/core/jersey-common/2.33/jersey-common-2.33.jar +0 -0
  40. data/vendor/jar-dependencies/org/lz4/lz4-java/1.7.1/lz4-java-1.7.1.jar +0 -0
  41. data/vendor/jar-dependencies/org/xerial/snappy/snappy-java/1.1.7.3/snappy-java-1.1.7.3.jar +0 -0
@@ -0,0 +1,139 @@
1
+ require 'manticore'
2
+
3
+ module LogStash module PluginMixins module Kafka
4
+ module AvroSchemaRegistry
5
+
6
+ def self.included(base)
7
+ base.extend(self)
8
+ base.setup_schema_registry_config
9
+ end
10
+
11
+ def setup_schema_registry_config
12
+ # Option to set key to access Schema Registry.
13
+ config :schema_registry_key, :validate => :string
14
+
15
+ # Option to set secret to access Schema Registry.
16
+ config :schema_registry_secret, :validate => :password
17
+
18
+ # Option to set the endpoint of the Schema Registry.
19
+ # This option permit the usage of Avro Kafka deserializer which retrieve the schema of the Avro message from an
20
+ # instance of schema registry. If this option has value `value_deserializer_class` nor `topics_pattern` could be valued
21
+ config :schema_registry_url, :validate => :uri
22
+
23
+ # Option to set the proxy of the Schema Registry.
24
+ # This option permits to define a proxy to be used to reach the schema registry service instance.
25
+ config :schema_registry_proxy, :validate => :uri
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
+
45
+ # Option to skip validating the schema registry during registration. This can be useful when using
46
+ # certificate based auth
47
+ config :schema_registry_validation, :validate => ['auto', 'skip'], :default => 'auto'
48
+ end
49
+
50
+ def check_schema_registry_parameters
51
+ if @schema_registry_url
52
+ check_for_schema_registry_conflicts
53
+ @schema_registry_proxy_host, @schema_registry_proxy_port = split_proxy_into_host_and_port(schema_registry_proxy)
54
+ check_for_key_and_secret
55
+ check_for_schema_registry_connectivity_and_subjects if schema_registry_validation?
56
+ end
57
+ end
58
+
59
+ def schema_registry_validation?
60
+ return false if schema_registry_validation.to_s == 'skip'
61
+ return false if using_kerberos? # pre-validation doesn't support kerberos
62
+
63
+ true
64
+ end
65
+
66
+ def using_kerberos?
67
+ security_protocol == "SASL_PLAINTEXT" || security_protocol == "SASL_SSL"
68
+ end
69
+
70
+ private
71
+ def check_for_schema_registry_conflicts
72
+ if @value_deserializer_class != LogStash::Inputs::Kafka::DEFAULT_DESERIALIZER_CLASS
73
+ raise LogStash::ConfigurationError, 'Option schema_registry_url prohibit the customization of value_deserializer_class'
74
+ end
75
+ if @topics_pattern && !@topics_pattern.empty?
76
+ raise LogStash::ConfigurationError, 'Option schema_registry_url prohibit the customization of topics_pattern'
77
+ end
78
+ end
79
+
80
+ private
81
+ def check_for_schema_registry_connectivity_and_subjects
82
+ options = {}
83
+ if schema_registry_proxy && !schema_registry_proxy.empty?
84
+ options[:proxy] = schema_registry_proxy.to_s
85
+ end
86
+ if schema_registry_key and !schema_registry_key.empty?
87
+ options[:auth] = {:user => schema_registry_key, :password => schema_registry_secret.value}
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
+
102
+ client = Manticore::Client.new(options)
103
+ begin
104
+ response = client.get(@schema_registry_url.uri.to_s + '/subjects').body
105
+ rescue Manticore::ManticoreException => e
106
+ raise LogStash::ConfigurationError.new("Schema registry service doesn't respond, error: #{e.message}")
107
+ end
108
+ registered_subjects = JSON.parse response
109
+ expected_subjects = @topics.map { |t| "#{t}-value"}
110
+ if (expected_subjects & registered_subjects).size != expected_subjects.size
111
+ undefined_topic_subjects = expected_subjects - registered_subjects
112
+ raise LogStash::ConfigurationError, "The schema registry does not contain definitions for required topic subjects: #{undefined_topic_subjects}"
113
+ end
114
+ end
115
+
116
+ def split_proxy_into_host_and_port(proxy_uri)
117
+ return nil unless proxy_uri && !proxy_uri.empty?
118
+
119
+ port = proxy_uri.port
120
+
121
+ host_spec = ""
122
+ host_spec << proxy_uri.scheme || "http"
123
+ host_spec << "://"
124
+ host_spec << "#{proxy_uri.userinfo}@" if proxy_uri.userinfo
125
+ host_spec << proxy_uri.host
126
+
127
+ [host_spec, port]
128
+ end
129
+
130
+ def check_for_key_and_secret
131
+ if schema_registry_key and !schema_registry_key.empty?
132
+ if !schema_registry_secret or schema_registry_secret.value.empty?
133
+ raise LogStash::ConfigurationError, "Setting `schema_registry_secret` is required when `schema_registry_key` is provided."
134
+ end
135
+ end
136
+ end
137
+
138
+ end
139
+ end end end
@@ -0,0 +1,55 @@
1
+ module LogStash module PluginMixins module Kafka
2
+ module Common
3
+
4
+ def self.included(base)
5
+ # COMMON CONFIGURATION SUPPORTED BY BOTH PRODUCER/CONSUMER
6
+
7
+ # Close idle connections after the number of milliseconds specified by this config.
8
+ base.config :connections_max_idle_ms, :validate => :number, :default => 540_000 # (9m) Kafka default
9
+
10
+ # The period of time in milliseconds after which we force a refresh of metadata even if
11
+ # we haven't seen any partition leadership changes to proactively discover any new brokers or partitions
12
+ base.config :metadata_max_age_ms, :validate => :number, :default => 300_000 # (5m) Kafka default
13
+
14
+ # The configuration controls the maximum amount of time the client will wait for the response of a request.
15
+ # If the response is not received before the timeout elapses the client will resend the request if necessary
16
+ # or fail the request if retries are exhausted.
17
+ base.config :request_timeout_ms, :validate => :number, :default => 40_000 # Kafka default
18
+ end
19
+
20
+ def set_trustore_keystore_config(props)
21
+ props.put("ssl.truststore.type", ssl_truststore_type) unless ssl_truststore_type.nil?
22
+ props.put("ssl.truststore.location", ssl_truststore_location) unless ssl_truststore_location.nil?
23
+ props.put("ssl.truststore.password", ssl_truststore_password.value) unless ssl_truststore_password.nil?
24
+
25
+ # Client auth stuff
26
+ props.put("ssl.keystore.type", ssl_keystore_type) unless ssl_keystore_type.nil?
27
+ props.put("ssl.key.password", ssl_key_password.value) unless ssl_key_password.nil?
28
+ props.put("ssl.keystore.location", ssl_keystore_location) unless ssl_keystore_location.nil?
29
+ props.put("ssl.keystore.password", ssl_keystore_password.value) unless ssl_keystore_password.nil?
30
+ props.put("ssl.endpoint.identification.algorithm", ssl_endpoint_identification_algorithm) unless ssl_endpoint_identification_algorithm.nil?
31
+ end
32
+
33
+ def set_sasl_config(props)
34
+ java.lang.System.setProperty("java.security.auth.login.config", jaas_path) unless jaas_path.nil?
35
+ java.lang.System.setProperty("java.security.krb5.conf", kerberos_config) unless kerberos_config.nil?
36
+
37
+ props.put("sasl.mechanism", sasl_mechanism)
38
+ if sasl_mechanism == "GSSAPI" && sasl_kerberos_service_name.nil?
39
+ raise LogStash::ConfigurationError, "sasl_kerberos_service_name must be specified when SASL mechanism is GSSAPI"
40
+ end
41
+
42
+ props.put("sasl.kerberos.service.name", sasl_kerberos_service_name) unless sasl_kerberos_service_name.nil?
43
+ props.put("sasl.jaas.config", sasl_jaas_config) unless sasl_jaas_config.nil?
44
+ end
45
+
46
+ def reassign_dns_lookup
47
+ if @client_dns_lookup == "default"
48
+ @client_dns_lookup = "use_all_dns_ips"
49
+ logger.warn("client_dns_lookup setting 'default' value is deprecated, forced to 'use_all_dns_ips', please update your configuration")
50
+ deprecation_logger.deprecated("Deprecated value `default` for `client_dns_lookup` option; use `use_all_dns_ips` instead.")
51
+ end
52
+ end
53
+
54
+ end
55
+ end end end
@@ -1,17 +1,12 @@
1
1
  # AUTOGENERATED BY THE GRADLE SCRIPT. DO NOT EDIT.
2
2
 
3
3
  require 'jar_dependencies'
4
- require_jar('io.confluent', 'kafka-avro-serializer', '5.5.1')
5
- require_jar('io.confluent', 'kafka-schema-serializer', '5.5.1')
6
- require_jar('io.confluent', 'common-config', '5.5.1')
7
- require_jar('org.apache.avro', 'avro', '1.9.2')
8
- require_jar('io.confluent', 'kafka-schema-registry-client', '5.5.1')
9
- require_jar('org.apache.kafka', 'kafka_2.12', '2.5.1')
10
- require_jar('io.confluent', 'common-utils', '5.5.1')
11
- require_jar('javax.ws.rs', 'javax.ws.rs-api', '2.1.1')
12
- require_jar('org.glassfish.jersey.core', 'jersey-common', '2.33')
13
- require_jar('org.apache.kafka', 'kafka-clients', '2.5.1')
14
- require_jar('com.github.luben', 'zstd-jni', '1.4.4-7')
15
- require_jar('org.slf4j', 'slf4j-api', '1.7.30')
16
- require_jar('org.lz4', 'lz4-java', '1.7.1')
17
- require_jar('org.xerial.snappy', 'snappy-java', '1.1.7.3')
4
+ require_jar('io.confluent', 'kafka-avro-serializer', '7.4.0')
5
+ require_jar('io.confluent', 'kafka-schema-serializer', '7.4.0')
6
+ require_jar('org.apache.avro', 'avro', '1.11.3')
7
+ require_jar('io.confluent', 'kafka-schema-registry-client', '7.4.0')
8
+ require_jar('org.apache.kafka', 'kafka-clients', '3.4.1')
9
+ require_jar('org.slf4j', 'slf4j-api', '1.7.36')
10
+ require_jar('com.github.luben', 'zstd-jni', '1.5.5-4')
11
+ require_jar('org.lz4', 'lz4-java', '1.8.0')
12
+ require_jar('org.xerial.snappy', 'snappy-java', '1.1.10.5')
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-integration-kafka'
3
- s.version = '10.9.0'
3
+ s.version = '11.3.2'
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 "+
@@ -41,7 +41,7 @@ Gem::Specification.new do |s|
41
41
 
42
42
  # Gem dependencies
43
43
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
44
- s.add_runtime_dependency "logstash-core", ">= 6.5.0"
44
+ s.add_runtime_dependency "logstash-core", ">= 8.3.0"
45
45
 
46
46
  s.add_runtime_dependency 'logstash-codec-json'
47
47
  s.add_runtime_dependency 'logstash-codec-plain'
@@ -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
- response = Manticore.post("http://localhost:8081/subjects/#{subject_name}/versions",
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
- describe "Schema registry API", :integration => true do
286
- schema_registry = Manticore::Client.new
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('http://localhost:8081/subjects').body
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('http://localhost:8081/subjects/schema_test_1?permanent=false').code ).to be(200)
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('http://localhost:8081/subjects').body
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
- schema_registry = Manticore::Client.new
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
- context 'with an unauthed schema registry' do
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) { "http://localhost:8081/subjects" }
430
- let(:plain_config) { base_config.merge!({'schema_registry_url' => "http://localhost:8081"}) }
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 authed schema registry' do
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) { "http://#{user}:#{password}@localhost:8081/subjects" }
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
- base_config.merge!({
445
- 'schema_registry_url' => "http://localhost:8081",
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
- base_config.merge!({
457
- 'schema_registry_url' => "http://#{user}:#{password}@localhost:8081"
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
@@ -28,7 +28,8 @@ describe "outputs/kafka", :integration => true do
28
28
  let(:num_events) { 3 }
29
29
 
30
30
  before :each do
31
- config = base_config.merge({"topic_id" => test_topic})
31
+ # NOTE: the connections_max_idle_ms is irrelevant just testing that configuration works ...
32
+ config = base_config.merge({"topic_id" => test_topic, "connections_max_idle_ms" => 540_000})
32
33
  load_kafka_data(config)
33
34
  end
34
35
 
@@ -139,6 +140,25 @@ describe "outputs/kafka", :integration => true do
139
140
  # end
140
141
  end
141
142
 
143
+ context 'when using zstd compression' do
144
+ let(:test_topic) { 'logstash_integration_zstd_topic' }
145
+
146
+ before :each do
147
+ config = base_config.merge({"topic_id" => test_topic, "compression_type" => "zstd"})
148
+ load_kafka_data(config)
149
+ end
150
+
151
+ # NOTE: depends on zstd-ruby gem which is using a C-extension
152
+ # it 'should have data integrity' do
153
+ # messages = fetch_messages(test_topic)
154
+ #
155
+ # expect(messages.size).to eq(num_events)
156
+ # messages.each do |m|
157
+ # expect(m.value).to eq(event.to_s)
158
+ # end
159
+ # end
160
+ end
161
+
142
162
  context 'when using multi partition topic' do
143
163
  let(:num_events) { 100 } # ~ more than (batch.size) 16,384 bytes
144
164
  let(:test_topic) { 'logstash_integration_topic3' }
@@ -83,6 +83,16 @@ describe LogStash::Inputs::Kafka do
83
83
  it "should register" do
84
84
  expect { subject.register }.to_not raise_error
85
85
  end
86
+
87
+ context "when the deprecated `default` is specified" do
88
+ let(:config) { common_config.merge('client_dns_lookup' => 'default') }
89
+
90
+ it 'should fallback `client_dns_lookup` to `use_all_dns_ips`' do
91
+ subject.register
92
+
93
+ expect(subject.client_dns_lookup).to eq('use_all_dns_ips')
94
+ end
95
+ end
86
96
  end
87
97
 
88
98
  describe '#running' do
@@ -277,6 +287,19 @@ describe LogStash::Inputs::Kafka do
277
287
  subject.register
278
288
  expect(subject.metadata_mode).to include(:record_props)
279
289
  end
290
+
291
+ context "guards against nil header" do
292
+ let(:header) { double(:value => nil, :key => "k") }
293
+ let(:headers) { [ header ] }
294
+ let(:record) { double(:headers => headers, :topic => "topic", :partition => 0,
295
+ :offset => 123456789, :key => "someId", :timestamp => nil ) }
296
+
297
+ it "does not raise error when key is nil" do
298
+ subject.register
299
+ evt = LogStash::Event.new('message' => 'Hello')
300
+ expect { subject.maybe_set_metadata(evt, record) }.not_to raise_error
301
+ end
302
+ end
280
303
  end
281
304
 
282
305
  context 'with client_rack' do
@@ -287,7 +310,7 @@ describe LogStash::Inputs::Kafka do
287
310
  to receive(:new).with(hash_including('client.rack' => 'EU-R1')).
288
311
  and_return kafka_client = double('kafka-consumer')
289
312
 
290
- expect( subject.send(:create_consumer, 'sample_client-0') ).to be kafka_client
313
+ expect( subject.send(:create_consumer, 'sample_client-0', 'group_instance_id') ).to be kafka_client
291
314
  end
292
315
  end
293
316
 
@@ -299,7 +322,7 @@ describe LogStash::Inputs::Kafka do
299
322
  to receive(:new).with(hash_including('session.timeout.ms' => '25000', 'max.poll.interval.ms' => '345000')).
300
323
  and_return kafka_client = double('kafka-consumer')
301
324
 
302
- expect( subject.send(:create_consumer, 'sample_client-1') ).to be kafka_client
325
+ expect( subject.send(:create_consumer, 'sample_client-1', 'group_instance_id') ).to be kafka_client
303
326
  end
304
327
  end
305
328
 
@@ -311,7 +334,7 @@ describe LogStash::Inputs::Kafka do
311
334
  to receive(:new).with(hash_including('session.timeout.ms' => '25200', 'max.poll.interval.ms' => '123000')).
312
335
  and_return kafka_client = double('kafka-consumer')
313
336
 
314
- expect( subject.send(:create_consumer, 'sample_client-2') ).to be kafka_client
337
+ expect( subject.send(:create_consumer, 'sample_client-2', 'group_instance_id') ).to be kafka_client
315
338
  end
316
339
  end
317
340
 
@@ -323,7 +346,7 @@ describe LogStash::Inputs::Kafka do
323
346
  to receive(:new).with(hash_including('enable.auto.commit' => 'false', 'check.crcs' => 'true')).
324
347
  and_return kafka_client = double('kafka-consumer')
325
348
 
326
- expect( subject.send(:create_consumer, 'sample_client-3') ).to be kafka_client
349
+ expect( subject.send(:create_consumer, 'sample_client-3', 'group_instance_id') ).to be kafka_client
327
350
  expect( subject.enable_auto_commit ).to be false
328
351
  end
329
352
  end
@@ -336,7 +359,7 @@ describe LogStash::Inputs::Kafka do
336
359
  to receive(:new).with(hash_including('enable.auto.commit' => 'true', 'check.crcs' => 'false')).
337
360
  and_return kafka_client = double('kafka-consumer')
338
361
 
339
- expect( subject.send(:create_consumer, 'sample_client-4') ).to be kafka_client
362
+ expect( subject.send(:create_consumer, 'sample_client-4', 'group_instance_id') ).to be kafka_client
340
363
  expect( subject.enable_auto_commit ).to be true
341
364
  end
342
365
  end