rdkafka 0.12.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -0
  3. data/.github/FUNDING.yml +1 -0
  4. data/.github/workflows/ci.yml +58 -0
  5. data/.gitignore +4 -0
  6. data/.rspec +1 -0
  7. data/.ruby-gemset +1 -0
  8. data/.ruby-version +1 -0
  9. data/CHANGELOG.md +141 -93
  10. data/Gemfile +2 -0
  11. data/{LICENSE → MIT-LICENSE} +2 -1
  12. data/README.md +64 -29
  13. data/Rakefile +2 -0
  14. data/certs/cert_chain.pem +26 -0
  15. data/docker-compose.yml +18 -15
  16. data/ext/README.md +1 -1
  17. data/ext/Rakefile +3 -1
  18. data/lib/rdkafka/abstract_handle.rb +41 -25
  19. data/lib/rdkafka/admin/acl_binding_result.rb +37 -0
  20. data/lib/rdkafka/admin/create_acl_handle.rb +28 -0
  21. data/lib/rdkafka/admin/create_acl_report.rb +24 -0
  22. data/lib/rdkafka/admin/create_partitions_handle.rb +27 -0
  23. data/lib/rdkafka/admin/create_partitions_report.rb +6 -0
  24. data/lib/rdkafka/admin/create_topic_handle.rb +2 -0
  25. data/lib/rdkafka/admin/create_topic_report.rb +2 -0
  26. data/lib/rdkafka/admin/delete_acl_handle.rb +30 -0
  27. data/lib/rdkafka/admin/delete_acl_report.rb +23 -0
  28. data/lib/rdkafka/admin/delete_groups_handle.rb +28 -0
  29. data/lib/rdkafka/admin/delete_groups_report.rb +24 -0
  30. data/lib/rdkafka/admin/delete_topic_handle.rb +2 -0
  31. data/lib/rdkafka/admin/delete_topic_report.rb +2 -0
  32. data/lib/rdkafka/admin/describe_acl_handle.rb +30 -0
  33. data/lib/rdkafka/admin/describe_acl_report.rb +23 -0
  34. data/lib/rdkafka/admin.rb +494 -35
  35. data/lib/rdkafka/bindings.rb +175 -40
  36. data/lib/rdkafka/callbacks.rb +194 -1
  37. data/lib/rdkafka/config.rb +62 -25
  38. data/lib/rdkafka/consumer/headers.rb +24 -9
  39. data/lib/rdkafka/consumer/message.rb +3 -1
  40. data/lib/rdkafka/consumer/partition.rb +2 -0
  41. data/lib/rdkafka/consumer/topic_partition_list.rb +13 -8
  42. data/lib/rdkafka/consumer.rb +219 -102
  43. data/lib/rdkafka/error.rb +15 -0
  44. data/lib/rdkafka/helpers/time.rb +14 -0
  45. data/lib/rdkafka/metadata.rb +25 -2
  46. data/lib/rdkafka/native_kafka.rb +120 -0
  47. data/lib/rdkafka/producer/delivery_handle.rb +5 -2
  48. data/lib/rdkafka/producer/delivery_report.rb +9 -2
  49. data/lib/rdkafka/producer.rb +117 -17
  50. data/lib/rdkafka/version.rb +5 -3
  51. data/lib/rdkafka.rb +24 -2
  52. data/rdkafka.gemspec +19 -3
  53. data/renovate.json +6 -0
  54. data/spec/rdkafka/abstract_handle_spec.rb +1 -1
  55. data/spec/rdkafka/admin/create_acl_handle_spec.rb +56 -0
  56. data/spec/rdkafka/admin/create_acl_report_spec.rb +18 -0
  57. data/spec/rdkafka/admin/create_topic_handle_spec.rb +1 -1
  58. data/spec/rdkafka/admin/create_topic_report_spec.rb +1 -1
  59. data/spec/rdkafka/admin/delete_acl_handle_spec.rb +85 -0
  60. data/spec/rdkafka/admin/delete_acl_report_spec.rb +71 -0
  61. data/spec/rdkafka/admin/delete_topic_handle_spec.rb +1 -1
  62. data/spec/rdkafka/admin/delete_topic_report_spec.rb +1 -1
  63. data/spec/rdkafka/admin/describe_acl_handle_spec.rb +85 -0
  64. data/spec/rdkafka/admin/describe_acl_report_spec.rb +72 -0
  65. data/spec/rdkafka/admin_spec.rb +209 -5
  66. data/spec/rdkafka/bindings_spec.rb +2 -1
  67. data/spec/rdkafka/callbacks_spec.rb +1 -1
  68. data/spec/rdkafka/config_spec.rb +24 -3
  69. data/spec/rdkafka/consumer/headers_spec.rb +60 -0
  70. data/spec/rdkafka/consumer/message_spec.rb +1 -1
  71. data/spec/rdkafka/consumer/partition_spec.rb +1 -1
  72. data/spec/rdkafka/consumer/topic_partition_list_spec.rb +20 -1
  73. data/spec/rdkafka/consumer_spec.rb +332 -61
  74. data/spec/rdkafka/error_spec.rb +1 -1
  75. data/spec/rdkafka/metadata_spec.rb +4 -3
  76. data/spec/rdkafka/{producer/client_spec.rb → native_kafka_spec.rb} +13 -35
  77. data/spec/rdkafka/producer/delivery_handle_spec.rb +4 -1
  78. data/spec/rdkafka/producer/delivery_report_spec.rb +7 -3
  79. data/spec/rdkafka/producer_spec.rb +208 -20
  80. data/spec/spec_helper.rb +20 -2
  81. data.tar.gz.sig +3 -0
  82. metadata +79 -16
  83. metadata.gz.sig +3 -0
  84. data/.semaphore/semaphore.yml +0 -23
  85. data/bin/console +0 -11
  86. data/lib/rdkafka/producer/client.rb +0 -47
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Rdkafka::Admin::DeleteAclReport do
6
+
7
+ let(:resource_name) {"acl-test-topic"}
8
+ let(:resource_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC}
9
+ let(:resource_pattern_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL}
10
+ let(:principal) {"User:anonymous"}
11
+ let(:host) {"*"}
12
+ let(:operation) {Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ}
13
+ let(:permission_type) {Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW}
14
+ let(:delete_acl_ptr) {FFI::Pointer::NULL}
15
+
16
+ subject do
17
+ error_buffer = FFI::MemoryPointer.from_string(" " * 256)
18
+ delete_acl_ptr = Rdkafka::Bindings.rd_kafka_AclBinding_new(
19
+ resource_type,
20
+ FFI::MemoryPointer.from_string(resource_name),
21
+ resource_pattern_type,
22
+ FFI::MemoryPointer.from_string(principal),
23
+ FFI::MemoryPointer.from_string(host),
24
+ operation,
25
+ permission_type,
26
+ error_buffer,
27
+ 256
28
+ )
29
+ if delete_acl_ptr.null?
30
+ raise Rdkafka::Config::ConfigError.new(error_buffer.read_string)
31
+ end
32
+ pointer_array = [delete_acl_ptr]
33
+ delete_acls_array_ptr = FFI::MemoryPointer.new(:pointer)
34
+ delete_acls_array_ptr.write_array_of_pointer(pointer_array)
35
+ Rdkafka::Admin::DeleteAclReport.new(matching_acls: delete_acls_array_ptr, matching_acls_count: 1)
36
+ end
37
+
38
+ after do
39
+ if delete_acl_ptr != FFI::Pointer::NULL
40
+ Rdkafka::Bindings.rd_kafka_AclBinding_destroy(delete_acl_ptr)
41
+ end
42
+ end
43
+
44
+ it "should get deleted acl resource type as Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC" do
45
+ expect(subject.deleted_acls[0].matching_acl_resource_type).to eq(Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC)
46
+ end
47
+
48
+ it "should get deleted acl resource name as acl-test-topic" do
49
+ expect(subject.deleted_acls[0].matching_acl_resource_name).to eq(resource_name)
50
+ end
51
+
52
+ it "should get deleted acl resource pattern type as Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL" do
53
+ expect(subject.deleted_acls[0].matching_acl_pattern_type).to eq(Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL)
54
+ end
55
+
56
+ it "should get deleted acl principal as User:anonymous" do
57
+ expect(subject.deleted_acls[0].matching_acl_principal).to eq("User:anonymous")
58
+ end
59
+
60
+ it "should get deleted acl host as * " do
61
+ expect(subject.deleted_acls[0].matching_acl_host).to eq("*")
62
+ end
63
+
64
+ it "should get deleted acl operation as Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ" do
65
+ expect(subject.deleted_acls[0].matching_acl_operation).to eq(Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ)
66
+ end
67
+
68
+ it "should get deleted acl permission_type as Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW" do
69
+ expect(subject.deleted_acls[0].matching_acl_permission_type).to eq(Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW)
70
+ end
71
+ end
@@ -1,4 +1,4 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Rdkafka::Admin::DeleteTopicHandle do
4
4
  let(:response) { 0 }
@@ -1,4 +1,4 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Rdkafka::Admin::DeleteTopicReport do
4
4
  subject { Rdkafka::Admin::DeleteTopicReport.new(
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Rdkafka::Admin::DescribeAclHandle do
6
+ let(:response) { Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR }
7
+ let(:resource_name) {"acl-test-topic"}
8
+ let(:resource_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC}
9
+ let(:resource_pattern_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL}
10
+ let(:principal) {"User:anonymous"}
11
+ let(:host) {"*"}
12
+ let(:operation) {Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ}
13
+ let(:permission_type) {Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW}
14
+ let(:describe_acl_ptr) {FFI::Pointer::NULL}
15
+
16
+ subject do
17
+ error_buffer = FFI::MemoryPointer.from_string(" " * 256)
18
+ describe_acl_ptr = Rdkafka::Bindings.rd_kafka_AclBinding_new(
19
+ resource_type,
20
+ FFI::MemoryPointer.from_string(resource_name),
21
+ resource_pattern_type,
22
+ FFI::MemoryPointer.from_string(principal),
23
+ FFI::MemoryPointer.from_string(host),
24
+ operation,
25
+ permission_type,
26
+ error_buffer,
27
+ 256
28
+ )
29
+ if describe_acl_ptr.null?
30
+ raise Rdkafka::Config::ConfigError.new(error_buffer.read_string)
31
+ end
32
+ pointer_array = [describe_acl_ptr]
33
+ describe_acls_array_ptr = FFI::MemoryPointer.new(:pointer)
34
+ describe_acls_array_ptr.write_array_of_pointer(pointer_array)
35
+ Rdkafka::Admin::DescribeAclHandle.new.tap do |handle|
36
+ handle[:pending] = pending_handle
37
+ handle[:response] = response
38
+ handle[:response_string] = FFI::MemoryPointer.from_string("")
39
+ handle[:acls] = describe_acls_array_ptr
40
+ handle[:acls_count] = 1
41
+ end
42
+ end
43
+
44
+ after do
45
+ if describe_acl_ptr != FFI::Pointer::NULL
46
+ Rdkafka::Bindings.rd_kafka_AclBinding_destroy(describe_acl_ptr)
47
+ end
48
+ end
49
+
50
+ describe "#wait" do
51
+ let(:pending_handle) { true }
52
+
53
+ it "should wait until the timeout and then raise an error" do
54
+ expect {
55
+ subject.wait(max_wait_timeout: 0.1)
56
+ }.to raise_error Rdkafka::Admin::DescribeAclHandle::WaitTimeoutError, /describe acl/
57
+ end
58
+
59
+ context "when not pending anymore and no error" do
60
+ let(:pending_handle) { false }
61
+
62
+ it "should return a describe acl report" do
63
+ report = subject.wait
64
+
65
+ expect(report.acls.length).to eq(1)
66
+ end
67
+
68
+ it "should wait without a timeout" do
69
+ report = subject.wait(max_wait_timeout: nil)
70
+
71
+ expect(report.acls[0].matching_acl_resource_name).to eq("acl-test-topic")
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#raise_error" do
77
+ let(:pending_handle) { false }
78
+
79
+ it "should raise the appropriate error" do
80
+ expect {
81
+ subject.raise_error
82
+ }.to raise_exception(Rdkafka::RdkafkaError, /Success \(no_error\)/)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ describe Rdkafka::Admin::DescribeAclReport do
6
+
7
+ let(:resource_name) {"acl-test-topic"}
8
+ let(:resource_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC}
9
+ let(:resource_pattern_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL}
10
+ let(:principal) {"User:anonymous"}
11
+ let(:host) {"*"}
12
+ let(:operation) {Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ}
13
+ let(:permission_type) {Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW}
14
+ let(:describe_acl_ptr) {FFI::Pointer::NULL}
15
+
16
+ subject do
17
+ error_buffer = FFI::MemoryPointer.from_string(" " * 256)
18
+ describe_acl_ptr = Rdkafka::Bindings.rd_kafka_AclBinding_new(
19
+ resource_type,
20
+ FFI::MemoryPointer.from_string(resource_name),
21
+ resource_pattern_type,
22
+ FFI::MemoryPointer.from_string(principal),
23
+ FFI::MemoryPointer.from_string(host),
24
+ operation,
25
+ permission_type,
26
+ error_buffer,
27
+ 256
28
+ )
29
+ if describe_acl_ptr.null?
30
+ raise Rdkafka::Config::ConfigError.new(error_buffer.read_string)
31
+ end
32
+ pointer_array = [describe_acl_ptr]
33
+ describe_acls_array_ptr = FFI::MemoryPointer.new(:pointer)
34
+ describe_acls_array_ptr.write_array_of_pointer(pointer_array)
35
+ Rdkafka::Admin::DescribeAclReport.new(acls: describe_acls_array_ptr, acls_count: 1)
36
+ end
37
+
38
+ after do
39
+ if describe_acl_ptr != FFI::Pointer::NULL
40
+ Rdkafka::Bindings.rd_kafka_AclBinding_destroy(describe_acl_ptr)
41
+ end
42
+ end
43
+
44
+
45
+ it "should get matching acl resource type as Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC" do
46
+ expect(subject.acls[0].matching_acl_resource_type).to eq(Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC)
47
+ end
48
+
49
+ it "should get matching acl resource name as acl-test-topic" do
50
+ expect(subject.acls[0].matching_acl_resource_name).to eq(resource_name)
51
+ end
52
+
53
+ it "should get matching acl resource pattern type as Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL" do
54
+ expect(subject.acls[0].matching_acl_pattern_type).to eq(Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL)
55
+ end
56
+
57
+ it "should get matching acl principal as User:anonymous" do
58
+ expect(subject.acls[0].matching_acl_principal).to eq("User:anonymous")
59
+ end
60
+
61
+ it "should get matching acl host as * " do
62
+ expect(subject.acls[0].matching_acl_host).to eq("*")
63
+ end
64
+
65
+ it "should get matching acl operation as Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ" do
66
+ expect(subject.acls[0].matching_acl_operation).to eq(Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ)
67
+ end
68
+
69
+ it "should get matching acl permission_type as Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW" do
70
+ expect(subject.acls[0].matching_acl_permission_type).to eq(Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW)
71
+ end
72
+ end
@@ -1,13 +1,18 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
+
2
3
  require "ostruct"
3
4
 
4
5
  describe Rdkafka::Admin do
5
- let(:config) { rdkafka_config }
6
- let(:admin) { config.admin }
6
+ let(:config) { rdkafka_config }
7
+ let(:admin) { config.admin }
7
8
 
8
9
  after do
9
10
  # Registry should always end up being empty
10
11
  expect(Rdkafka::Admin::CreateTopicHandle::REGISTRY).to be_empty
12
+ expect(Rdkafka::Admin::CreatePartitionsHandle::REGISTRY).to be_empty
13
+ expect(Rdkafka::Admin::DescribeAclHandle::REGISTRY).to be_empty
14
+ expect(Rdkafka::Admin::CreateAclHandle::REGISTRY).to be_empty
15
+ expect(Rdkafka::Admin::DeleteAclHandle::REGISTRY).to be_empty
11
16
  admin.close
12
17
  end
13
18
 
@@ -16,6 +21,15 @@ describe Rdkafka::Admin do
16
21
  let(:topic_replication_factor) { 1 }
17
22
  let(:topic_config) { {"cleanup.policy" => "compact", "min.cleanable.dirty.ratio" => 0.8} }
18
23
  let(:invalid_topic_config) { {"cleeeeenup.policee" => "campact"} }
24
+ let(:group_name) { "test-group-#{Random.new.rand(0..1_000_000)}" }
25
+
26
+ let(:resource_name) {"acl-test-topic"}
27
+ let(:resource_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_TOPIC}
28
+ let(:resource_pattern_type) {Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_LITERAL}
29
+ let(:principal) {"User:anonymous"}
30
+ let(:host) {"*"}
31
+ let(:operation) {Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_READ}
32
+ let(:permission_type) {Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ALLOW}
19
33
 
20
34
  describe "#create_topic" do
21
35
  describe "called with invalid input" do
@@ -31,7 +45,7 @@ describe Rdkafka::Admin do
31
45
  }.to raise_exception { |ex|
32
46
  expect(ex).to be_a(Rdkafka::RdkafkaError)
33
47
  expect(ex.message).to match(/Broker: Invalid topic \(topic_exception\)/)
34
- expect(ex.broker_message).to match(/Topic name.*is illegal, it contains a character other than ASCII alphanumerics/)
48
+ expect(ex.broker_message).to match(/Topic name.*is invalid: .* contains one or more characters other than ASCII alphanumerics, '.', '_' and '-'/)
35
49
  }
36
50
  end
37
51
  end
@@ -174,7 +188,6 @@ describe Rdkafka::Admin do
174
188
  end
175
189
  end
176
190
 
177
-
178
191
  it "deletes a topic that was newly created" do
179
192
  create_topic_handle = admin.create_topic(topic_name, topic_partition_count, topic_replication_factor)
180
193
  create_topic_report = create_topic_handle.wait(max_wait_timeout: 15.0)
@@ -200,4 +213,195 @@ describe Rdkafka::Admin do
200
213
  expect(delete_topic_report.result_name).to eq(topic_name)
201
214
  end
202
215
  end
216
+
217
+ describe "#ACL tests" do
218
+ let(:non_existing_resource_name) {"non-existing-topic"}
219
+ before do
220
+ #create topic for testing acl
221
+ create_topic_handle = admin.create_topic(resource_name, topic_partition_count, topic_replication_factor)
222
+ create_topic_report = create_topic_handle.wait(max_wait_timeout: 15.0)
223
+ end
224
+
225
+ after do
226
+ #delete acl
227
+ delete_acl_handle = admin.delete_acl(resource_type: resource_type, resource_name: resource_name, resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
228
+ delete_acl_report = delete_acl_handle.wait(max_wait_timeout: 15.0)
229
+
230
+ #delete topic that was created for testing acl
231
+ delete_topic_handle = admin.delete_topic(resource_name)
232
+ delete_topic_report = delete_topic_handle.wait(max_wait_timeout: 15.0)
233
+ end
234
+
235
+ describe "#create_acl" do
236
+ it "create acl for a topic that does not exist" do
237
+ # acl creation for resources that does not exist will still get created successfully.
238
+ create_acl_handle = admin.create_acl(resource_type: resource_type, resource_name: non_existing_resource_name, resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
239
+ create_acl_report = create_acl_handle.wait(max_wait_timeout: 15.0)
240
+ expect(create_acl_report.rdkafka_response).to eq(0)
241
+ expect(create_acl_report.rdkafka_response_string).to eq("")
242
+
243
+ # delete the acl that was created for a non existing topic"
244
+ delete_acl_handle = admin.delete_acl(resource_type: resource_type, resource_name: non_existing_resource_name, resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
245
+ delete_acl_report = delete_acl_handle.wait(max_wait_timeout: 15.0)
246
+ expect(delete_acl_handle[:response]).to eq(0)
247
+ expect(delete_acl_report.deleted_acls.size).to eq(1)
248
+ end
249
+
250
+ it "creates a acl for topic that was newly created" do
251
+ create_acl_handle = admin.create_acl(resource_type: resource_type, resource_name: resource_name, resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
252
+ create_acl_report = create_acl_handle.wait(max_wait_timeout: 15.0)
253
+ expect(create_acl_report.rdkafka_response).to eq(0)
254
+ expect(create_acl_report.rdkafka_response_string).to eq("")
255
+ end
256
+ end
257
+
258
+ describe "#describe_acl" do
259
+ it "describe acl of a topic that does not exist" do
260
+ describe_acl_handle = admin.describe_acl(resource_type: resource_type, resource_name: non_existing_resource_name, resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
261
+ describe_acl_report = describe_acl_handle.wait(max_wait_timeout: 15.0)
262
+ expect(describe_acl_handle[:response]).to eq(0)
263
+ expect(describe_acl_report.acls.size).to eq(0)
264
+ end
265
+
266
+ it "create acls and describe the newly created acls" do
267
+ #create_acl
268
+ create_acl_handle = admin.create_acl(resource_type: resource_type, resource_name: "test_acl_topic_1", resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
269
+ create_acl_report = create_acl_handle.wait(max_wait_timeout: 15.0)
270
+ expect(create_acl_report.rdkafka_response).to eq(0)
271
+ expect(create_acl_report.rdkafka_response_string).to eq("")
272
+
273
+ create_acl_handle = admin.create_acl(resource_type: resource_type, resource_name: "test_acl_topic_2", resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
274
+ create_acl_report = create_acl_handle.wait(max_wait_timeout: 15.0)
275
+ expect(create_acl_report.rdkafka_response).to eq(0)
276
+ expect(create_acl_report.rdkafka_response_string).to eq("")
277
+
278
+ #describe_acl
279
+ describe_acl_handle = admin.describe_acl(resource_type: Rdkafka::Bindings::RD_KAFKA_RESOURCE_ANY, resource_name: nil, resource_pattern_type: Rdkafka::Bindings::RD_KAFKA_RESOURCE_PATTERN_ANY, principal: nil, host: nil, operation: Rdkafka::Bindings::RD_KAFKA_ACL_OPERATION_ANY, permission_type: Rdkafka::Bindings::RD_KAFKA_ACL_PERMISSION_TYPE_ANY)
280
+ describe_acl_report = describe_acl_handle.wait(max_wait_timeout: 15.0)
281
+ expect(describe_acl_handle[:response]).to eq(0)
282
+ expect(describe_acl_report.acls.length).to eq(2)
283
+ end
284
+ end
285
+
286
+ describe "#delete_acl" do
287
+ it "delete acl of a topic that does not exist" do
288
+ delete_acl_handle = admin.delete_acl(resource_type: resource_type, resource_name: non_existing_resource_name, resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
289
+ delete_acl_report = delete_acl_handle.wait(max_wait_timeout: 15.0)
290
+ expect(delete_acl_handle[:response]).to eq(0)
291
+ expect(delete_acl_report.deleted_acls.size).to eq(0)
292
+ end
293
+
294
+ it "create an acl and delete the newly created acl" do
295
+ #create_acl
296
+ create_acl_handle = admin.create_acl(resource_type: resource_type, resource_name: "test_acl_topic_1", resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
297
+ create_acl_report = create_acl_handle.wait(max_wait_timeout: 15.0)
298
+ expect(create_acl_report.rdkafka_response).to eq(0)
299
+ expect(create_acl_report.rdkafka_response_string).to eq("")
300
+
301
+ create_acl_handle = admin.create_acl(resource_type: resource_type, resource_name: "test_acl_topic_2", resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
302
+ create_acl_report = create_acl_handle.wait(max_wait_timeout: 15.0)
303
+ expect(create_acl_report.rdkafka_response).to eq(0)
304
+ expect(create_acl_report.rdkafka_response_string).to eq("")
305
+
306
+ #delete_acl - resource_name nil - to delete all acls with any resource name and matching all other filters.
307
+ delete_acl_handle = admin.delete_acl(resource_type: resource_type, resource_name: nil, resource_pattern_type: resource_pattern_type, principal: principal, host: host, operation: operation, permission_type: permission_type)
308
+ delete_acl_report = delete_acl_handle.wait(max_wait_timeout: 15.0)
309
+ expect(delete_acl_handle[:response]).to eq(0)
310
+ expect(delete_acl_report.deleted_acls.length).to eq(2)
311
+
312
+ end
313
+ end
314
+ end
315
+
316
+ describe('Group tests') do
317
+ describe "#delete_group" do
318
+ describe("with an existing group") do
319
+ let(:consumer_config) { rdkafka_consumer_config('group.id': group_name) }
320
+ let(:producer_config) { rdkafka_producer_config }
321
+ let(:producer) { producer_config.producer }
322
+ let(:consumer) { consumer_config.consumer }
323
+
324
+ before do
325
+ # Create a topic, post a message to it, consume it and commit offsets, this will create a group that we can then delete.
326
+ admin.create_topic(topic_name, topic_partition_count, topic_replication_factor).wait(max_wait_timeout: 15.0)
327
+
328
+ producer.produce(topic: topic_name, payload: "test", key: "test").wait(max_wait_timeout: 15.0)
329
+
330
+ consumer.subscribe(topic_name)
331
+ wait_for_assignment(consumer)
332
+ message = consumer.poll(100)
333
+
334
+ expect(message).to_not be_nil
335
+
336
+ consumer.commit
337
+ consumer.close
338
+ end
339
+
340
+ after do
341
+ producer.close
342
+ consumer.close
343
+ end
344
+
345
+ it "deletes the group" do
346
+ delete_group_handle = admin.delete_group(group_name)
347
+ report = delete_group_handle.wait(max_wait_timeout: 15.0)
348
+
349
+ expect(report.result_name).to eql(group_name)
350
+ end
351
+ end
352
+
353
+ describe "called with invalid input" do
354
+ describe "with the name of a group that does not exist" do
355
+ it "raises an exception" do
356
+ delete_group_handle = admin.delete_group(group_name)
357
+
358
+ expect {
359
+ delete_group_handle.wait(max_wait_timeout: 15.0)
360
+ }.to raise_exception { |ex|
361
+ expect(ex).to be_a(Rdkafka::RdkafkaError)
362
+ expect(ex.message).to match(/Broker: The group id does not exist \(group_id_not_found\)/)
363
+ }
364
+ end
365
+ end
366
+ end
367
+
368
+ end
369
+ end
370
+
371
+ describe '#create_partitions' do
372
+ let(:metadata) { admin.metadata(topic_name).topics.first }
373
+
374
+ context 'when topic does not exist' do
375
+ it 'expect to fail due to unknown partition' do
376
+ expect { admin.create_partitions(topic_name, 10).wait }.to raise_error(Rdkafka::RdkafkaError, /unknown_topic_or_part/)
377
+ end
378
+ end
379
+
380
+ context 'when topic already has the desired number of partitions' do
381
+ before { admin.create_topic(topic_name, 2, 1).wait }
382
+
383
+ it 'expect not to change number of partitions' do
384
+ expect { admin.create_partitions(topic_name, 2).wait }.to raise_error(Rdkafka::RdkafkaError, /invalid_partitions/)
385
+ expect(metadata[:partition_count]).to eq(2)
386
+ end
387
+ end
388
+
389
+ context 'when topic has more than the requested number of partitions' do
390
+ before { admin.create_topic(topic_name, 5, 1).wait }
391
+
392
+ it 'expect not to change number of partitions' do
393
+ expect { admin.create_partitions(topic_name, 2).wait }.to raise_error(Rdkafka::RdkafkaError, /invalid_partitions/)
394
+ expect(metadata[:partition_count]).to eq(5)
395
+ end
396
+ end
397
+
398
+ context 'when topic has less then desired number of partitions' do
399
+ before { admin.create_topic(topic_name, 1, 1).wait }
400
+
401
+ it 'expect to change number of partitions' do
402
+ admin.create_partitions(topic_name, 10).wait
403
+ expect(metadata[:partition_count]).to eq(10)
404
+ end
405
+ end
406
+ end
203
407
  end
@@ -1,4 +1,5 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
+
2
3
  require 'zlib'
3
4
 
4
5
  describe Rdkafka::Bindings do
@@ -1,4 +1,4 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Rdkafka::Callbacks do
4
4
 
@@ -1,4 +1,4 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Rdkafka::Config do
4
4
  context "logger" do
@@ -113,6 +113,14 @@ describe Rdkafka::Config do
113
113
  consumer.close
114
114
  end
115
115
 
116
+ it "should create a consumer with consumer_poll_set set to false" do
117
+ config = rdkafka_consumer_config
118
+ config.consumer_poll_set = false
119
+ consumer = config.consumer
120
+ expect(consumer).to be_a Rdkafka::Consumer
121
+ consumer.close
122
+ end
123
+
116
124
  it "should raise an error when creating a consumer with invalid config" do
117
125
  config = Rdkafka::Config.new('invalid.key' => 'value')
118
126
  expect {
@@ -148,11 +156,24 @@ describe Rdkafka::Config do
148
156
  }.to raise_error(Rdkafka::Config::ConfigError, "No such configuration property: \"invalid.key\"")
149
157
  end
150
158
 
159
+ it "allows string partitioner key" do
160
+ expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2").and_call_original
161
+ config = Rdkafka::Config.new("partitioner" => "murmur2")
162
+ config.producer.close
163
+ end
164
+
165
+ it "allows symbol partitioner key" do
166
+ expect(Rdkafka::Producer).to receive(:new).with(kind_of(Rdkafka::NativeKafka), "murmur2").and_call_original
167
+ config = Rdkafka::Config.new(:partitioner => "murmur2")
168
+ config.producer.close
169
+ end
170
+
151
171
  it "should allow configuring zstd compression" do
152
172
  config = Rdkafka::Config.new('compression.codec' => 'zstd')
153
173
  begin
154
- expect(config.producer).to be_a Rdkafka::Producer
155
- config.producer.close
174
+ producer = config.producer
175
+ expect(producer).to be_a Rdkafka::Producer
176
+ producer.close
156
177
  rescue Rdkafka::Config::ConfigError => ex
157
178
  pending "Zstd compression not supported on this machine"
158
179
  raise ex
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Rdkafka::Consumer::Headers do
4
+ let(:headers) do
5
+ { # Note String keys!
6
+ "version" => "2.1.3",
7
+ "type" => "String"
8
+ }
9
+ end
10
+ let(:native_message) { double('native message') }
11
+ let(:headers_ptr) { double('headers pointer') }
12
+
13
+ describe '.from_native' do
14
+ before do
15
+ expect(Rdkafka::Bindings).to receive(:rd_kafka_message_headers).with(native_message, anything) do |_, headers_ptrptr|
16
+ expect(headers_ptrptr).to receive(:read_pointer).and_return(headers_ptr)
17
+ Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
18
+ end
19
+
20
+ expect(Rdkafka::Bindings).to \
21
+ receive(:rd_kafka_header_get_all)
22
+ .with(headers_ptr, 0, anything, anything, anything) do |_, _, name_ptrptr, value_ptrptr, size_ptr|
23
+ expect(name_ptrptr).to receive(:read_pointer).and_return(double("pointer 0", read_string_to_null: headers.keys[0]))
24
+ expect(size_ptr).to receive(:[]).with(:value).and_return(headers.keys[0].size)
25
+ expect(value_ptrptr).to receive(:read_pointer).and_return(double("value pointer 0", read_string: headers.values[0]))
26
+ Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
27
+ end
28
+
29
+ expect(Rdkafka::Bindings).to \
30
+ receive(:rd_kafka_header_get_all)
31
+ .with(headers_ptr, 1, anything, anything, anything) do |_, _, name_ptrptr, value_ptrptr, size_ptr|
32
+ expect(name_ptrptr).to receive(:read_pointer).and_return(double("pointer 1", read_string_to_null: headers.keys[1]))
33
+ expect(size_ptr).to receive(:[]).with(:value).and_return(headers.keys[1].size)
34
+ expect(value_ptrptr).to receive(:read_pointer).and_return(double("value pointer 1", read_string: headers.values[1]))
35
+ Rdkafka::Bindings::RD_KAFKA_RESP_ERR_NO_ERROR
36
+ end
37
+
38
+ expect(Rdkafka::Bindings).to \
39
+ receive(:rd_kafka_header_get_all)
40
+ .with(headers_ptr, 2, anything, anything, anything)
41
+ .and_return(Rdkafka::Bindings::RD_KAFKA_RESP_ERR__NOENT)
42
+ end
43
+
44
+ subject { described_class.from_native(native_message) }
45
+
46
+ it { is_expected.to eq(headers) }
47
+ it { is_expected.to be_frozen }
48
+
49
+ it 'allows String key' do
50
+ expect(subject['version']).to eq("2.1.3")
51
+ end
52
+
53
+ it 'allows Symbol key, but warns' do
54
+ expect(Kernel).to \
55
+ receive(:warn).with("rdkafka deprecation warning: header access with Symbol key :version treated as a String. " \
56
+ "Please change your code to use String keys to avoid this warning. Symbol keys will break in version 1.")
57
+ expect(subject[:version]).to eq("2.1.3")
58
+ end
59
+ end
60
+ end
@@ -1,4 +1,4 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Rdkafka::Consumer::Message do
4
4
  let(:native_client) { new_native_client }
@@ -1,4 +1,4 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Rdkafka::Consumer::Partition do
4
4
  let(:offset) { 100 }
@@ -1,4 +1,4 @@
1
- require "spec_helper"
1
+ # frozen_string_literal: true
2
2
 
3
3
  describe Rdkafka::Consumer::TopicPartitionList do
4
4
  it "should create a new list and add unassigned topics" do
@@ -219,5 +219,24 @@ describe Rdkafka::Consumer::TopicPartitionList do
219
219
 
220
220
  expect(list).to eq other
221
221
  end
222
+
223
+ it "should create a native list with timetamp offsets if offsets are Time" do
224
+ list = Rdkafka::Consumer::TopicPartitionList.new.tap do |list|
225
+ list.add_topic_and_partitions_with_offsets("topic", 0 => Time.at(1505069646, 250_000))
226
+ end
227
+
228
+ tpl = list.to_native_tpl
229
+
230
+ compare_list = Rdkafka::Consumer::TopicPartitionList.new.tap do |list|
231
+ list.add_topic_and_partitions_with_offsets(
232
+ "topic",
233
+ 0 => (Time.at(1505069646, 250_000).to_f * 1000).floor
234
+ )
235
+ end
236
+
237
+ native_list = Rdkafka::Consumer::TopicPartitionList.from_native_tpl(tpl)
238
+
239
+ expect(native_list).to eq compare_list
240
+ end
222
241
  end
223
242
  end