karafka 2.0.23 → 2.0.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/workflows/ci.yml +2 -0
  4. data/CHANGELOG.md +32 -1
  5. data/Gemfile.lock +8 -7
  6. data/README.md +3 -3
  7. data/config/{errors.yml → locales/errors.yml} +1 -1
  8. data/config/locales/pro_errors.yml +18 -0
  9. data/docker-compose.yml +3 -0
  10. data/karafka.gemspec +2 -2
  11. data/lib/karafka/active_job/job_options_contract.rb +1 -1
  12. data/lib/karafka/admin.rb +2 -4
  13. data/lib/karafka/app.rb +15 -4
  14. data/lib/karafka/base_consumer.rb +36 -0
  15. data/lib/karafka/connection/listener.rb +1 -1
  16. data/lib/karafka/contracts/config.rb +1 -1
  17. data/lib/karafka/contracts/consumer_group.rb +1 -1
  18. data/lib/karafka/contracts/server_cli_options.rb +1 -1
  19. data/lib/karafka/contracts/topic.rb +1 -1
  20. data/lib/karafka/instrumentation/logger_listener.rb +32 -0
  21. data/lib/karafka/instrumentation/notifications.rb +3 -0
  22. data/lib/karafka/messages/message.rb +14 -2
  23. data/lib/karafka/messages/parser.rb +14 -0
  24. data/lib/karafka/pro/active_job/job_options_contract.rb +1 -1
  25. data/lib/karafka/pro/encryption/cipher.rb +58 -0
  26. data/lib/karafka/pro/encryption/contracts/config.rb +79 -0
  27. data/lib/karafka/pro/encryption/errors.rb +24 -0
  28. data/lib/karafka/pro/encryption/messages/middleware.rb +46 -0
  29. data/lib/karafka/pro/encryption/messages/parser.rb +56 -0
  30. data/lib/karafka/pro/encryption/setup/config.rb +48 -0
  31. data/lib/karafka/pro/encryption.rb +47 -0
  32. data/lib/karafka/pro/loader.rb +22 -1
  33. data/lib/karafka/pro/processing/strategies/aj_dlq_mom.rb +1 -1
  34. data/lib/karafka/pro/processing/strategies/aj_lrj_mom_vp.rb +1 -1
  35. data/lib/karafka/pro/processing/strategies/aj_mom_vp.rb +1 -1
  36. data/lib/karafka/pro/processing/strategies/default.rb +1 -1
  37. data/lib/karafka/pro/processing/strategies/dlq.rb +1 -1
  38. data/lib/karafka/pro/processing/strategies/dlq_lrj.rb +1 -1
  39. data/lib/karafka/pro/processing/strategies/dlq_lrj_mom.rb +1 -1
  40. data/lib/karafka/pro/processing/strategies/dlq_mom.rb +1 -1
  41. data/lib/karafka/pro/processing/strategies/lrj.rb +1 -1
  42. data/lib/karafka/pro/processing/strategies/lrj_mom.rb +1 -1
  43. data/lib/karafka/pro/processing/strategies/mom.rb +1 -1
  44. data/lib/karafka/pro/routing/features/dead_letter_queue/contract.rb +2 -2
  45. data/lib/karafka/pro/routing/features/long_running_job/contract.rb +2 -2
  46. data/lib/karafka/pro/routing/features/virtual_partitions/contract.rb +2 -2
  47. data/lib/karafka/processing/executor.rb +1 -1
  48. data/lib/karafka/processing/strategies/aj_dlq_mom.rb +1 -1
  49. data/lib/karafka/processing/strategies/default.rb +1 -1
  50. data/lib/karafka/processing/strategies/dlq.rb +1 -1
  51. data/lib/karafka/processing/strategies/dlq_mom.rb +1 -1
  52. data/lib/karafka/processing/strategies/mom.rb +1 -1
  53. data/lib/karafka/processing/worker.rb +1 -1
  54. data/lib/karafka/railtie.rb +3 -0
  55. data/lib/karafka/routing/builder.rb +1 -1
  56. data/lib/karafka/routing/consumer_group.rb +3 -3
  57. data/lib/karafka/routing/features/active_job/contract.rb +1 -1
  58. data/lib/karafka/routing/features/dead_letter_queue/contract.rb +1 -1
  59. data/lib/karafka/routing/features/manual_offset_management/contract.rb +1 -1
  60. data/lib/karafka/server.rb +14 -14
  61. data/lib/karafka/setup/config.rb +15 -2
  62. data/lib/karafka/status.rb +27 -9
  63. data/lib/karafka/templates/karafka.rb.erb +1 -2
  64. data/lib/karafka/time_trackers/pause.rb +3 -1
  65. data/lib/karafka/version.rb +1 -1
  66. data.tar.gz.sig +0 -0
  67. metadata +16 -7
  68. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84d8130c528081b283889f9f1ebe89b8829b800a2c5d1f4ca99f6d6ce9b4c9df
4
- data.tar.gz: 3cc30f65586226bcb6d8ed4fdac912a72c1a4a7eb5691862b378629d9de1347f
3
+ metadata.gz: 25eb837923f78f1bf35402bf24469228d8f5af12090b2d406718a0077ce42598
4
+ data.tar.gz: 23520ecab430080061046e683c0dcb47316d1216d1ad9843894caefff76e24cc
5
5
  SHA512:
6
- metadata.gz: 8e1f3fc0a3c73035fdb38093eb0594c999d0a715752f41ed95079b1d8f2e89a5a0fa0abdf054272891e89ffa4dd8d010dd2225540d4480067d8bc0359bf2b7b8
7
- data.tar.gz: 7c1f3f958ef52682e46efb11e8a3ddcbe641ba85c62956fefb84ec6eacadbe9d28ee509bdfd61fcd73ea8f52b1ab867de4909990503024cc5c603788931264ca
6
+ metadata.gz: 3e84c4c4dedd7a160ebcbd6cff53e0dfcf601a31eb19469d1a86eabb3f33507f3b1871c8f4489b743651118ed47e52925f12dba4fc67e462bd8be04d71dfa3bb
7
+ data.tar.gz: 8b9754f5566c5ef213c4803cfba2a4263553a5112e23fd12e42a963fd493f8f20c1409b12303628519ba7ef5bcf07aa09fabc6dd4ffc2a2114a6bad8f94cbc72
checksums.yaml.gz.sig CHANGED
Binary file
@@ -46,6 +46,7 @@ jobs:
46
46
  run: \curl -sSL https://api.coditsu.io/run/ci | bash
47
47
 
48
48
  specs:
49
+ timeout-minutes: 30
49
50
  runs-on: ubuntu-latest
50
51
  needs: diffend
51
52
  strategy:
@@ -84,6 +85,7 @@ jobs:
84
85
  run: bin/rspecs
85
86
 
86
87
  integrations:
88
+ timeout-minutes: 30
87
89
  runs-on: ubuntu-latest
88
90
  needs: diffend
89
91
  strategy:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Karafka framework changelog
2
2
 
3
+ ## 2.0.24 (2022-12-19)
4
+ - **[Feature]** Provide out of the box encryption support for Pro.
5
+ - [Improvement] Add instrumentation upon `#pause`.
6
+ - [Improvement] Add instrumentation upon retries.
7
+ - [Improvement] Assign `#id` to consumers similar to other entities for ease of debugging.
8
+ - [Improvement] Add retries and pausing to the default `LoggerListener`.
9
+ - [Improvement] Introduce a new final `terminated` state that will kick in prior to exit but after all the instrumentation and other things are done.
10
+ - [Improvement] Ensure that state transitions are thread-safe and ensure state transitions can occur in one direction.
11
+ - [Improvement] Optimize status methods proxying to `Karafka::App`.
12
+ - [Improvement] Allow for easier state usage by introducing explicit `#to_s` for reporting.
13
+ - [Improvement] Change auto-generated id from `SecureRandom#uuid` to `SecureRandom#hex(6)`
14
+ - [Improvement] Emit statistic every 5 seconds by default.
15
+ - [Improvement] Introduce general messages parser that can be swapped when needed.
16
+ - [Fix] Do not trigger code reloading when `consumer_persistence` is enabled.
17
+ - [Fix] Shutdown producer after all the consumer components are down and the status is stopped. This will ensure, that any instrumentation related Kafka messaging can still operate.
18
+
19
+ ### Upgrade notes
20
+
21
+ If you want to disable `librdkafka` statistics because you do not use them at all, update the `kafka` `statistics.interval.ms` setting and set it to `0`:
22
+
23
+ ```ruby
24
+ class KarafkaApp < Karafka::App
25
+ setup do |config|
26
+ # Other settings...
27
+ config.kafka = {
28
+ 'statistics.interval.ms': 0
29
+ }
30
+ end
31
+ end
32
+ ```
33
+
3
34
  ## 2.0.23 (2022-12-07)
4
35
  - [Maintenance] Align with `waterdrop` and `karafka-core`
5
36
  - [Improvement] Provide `Admin#read_topic` API to get topic data without subscribing.
@@ -425,7 +456,7 @@ There are several things in the plan already for 2.1 and beyond, including a web
425
456
  - Small integration specs refactoring + specs for pausing scenarios
426
457
 
427
458
  ## 2.0.0-alpha6 (2022-04-17)
428
- - Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
459
+ - Fix a bug, where upon missing boot file and Rails, railtie would fail with a generic exception (#818)
429
460
  - Fix an issue with parallel pristine specs colliding with each other during `bundle install` (#820)
430
461
  - Replace `consumer.consume` with `consumer.consumed` event to match the behaviour
431
462
  - Make sure, that offset committing happens before the `consumer.consumed` event is propagated
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- karafka (2.0.23)
5
- karafka-core (>= 2.0.6, < 3.0.0)
4
+ karafka (2.0.24)
5
+ karafka-core (>= 2.0.7, < 3.0.0)
6
6
  thor (>= 0.20)
7
- waterdrop (>= 2.4.3, < 3.0.0)
7
+ waterdrop (>= 2.4.7, < 3.0.0)
8
8
  zeitwerk (~> 2.3)
9
9
 
10
10
  GEM
@@ -29,7 +29,7 @@ GEM
29
29
  activesupport (>= 5.0)
30
30
  i18n (1.12.0)
31
31
  concurrent-ruby (~> 1.0)
32
- karafka-core (2.0.6)
32
+ karafka-core (2.0.7)
33
33
  concurrent-ruby (>= 1.1)
34
34
  rdkafka (>= 0.12)
35
35
  mini_portile2 (2.8.0)
@@ -48,7 +48,7 @@ GEM
48
48
  rspec-expectations (3.12.0)
49
49
  diff-lcs (>= 1.2.0, < 2.0)
50
50
  rspec-support (~> 3.12.0)
51
- rspec-mocks (3.12.0)
51
+ rspec-mocks (3.12.1)
52
52
  diff-lcs (>= 1.2.0, < 2.0)
53
53
  rspec-support (~> 3.12.0)
54
54
  rspec-support (3.12.0)
@@ -61,12 +61,13 @@ GEM
61
61
  thor (1.2.1)
62
62
  tzinfo (2.0.5)
63
63
  concurrent-ruby (~> 1.0)
64
- waterdrop (2.4.3)
65
- karafka-core (>= 2.0.6, < 3.0.0)
64
+ waterdrop (2.4.7)
65
+ karafka-core (>= 2.0.7, < 3.0.0)
66
66
  zeitwerk (~> 2.3)
67
67
  zeitwerk (2.6.6)
68
68
 
69
69
  PLATFORMS
70
+ x86_64-darwin-21
70
71
  x86_64-linux
71
72
 
72
73
  DEPENDENCIES
data/README.md CHANGED
@@ -72,10 +72,10 @@ Karafka.producer.produce_sync(topic: 'example', payload: { 'ping' => 'pong' }.to
72
72
  ```bash
73
73
  bundle exec karafka server
74
74
 
75
- [7616dc24-505a-417f-b87b-6bf8fc2d98c5] Polled 1 message in 1000ms
76
- [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example started
75
+ [86d47f0b92f7] Polled 1 message in 1000ms
76
+ [3732873c8a74] Consume job for ExampleConsumer on example started
77
77
  {"ping"=>"pong"}
78
- [dcf3a8d8-0bd9-433a-8f63-b70a0cdb0732] Consume job for ExampleConsumer on example finished in 0ms
78
+ [3732873c8a74] Consume job for ExampleConsumer on example finished in 0ms
79
79
  ```
80
80
 
81
81
  ## Want to Upgrade? LGPL is not for you? Want to help?
@@ -63,7 +63,7 @@ en:
63
63
 
64
64
  pro_topic:
65
65
  virtual_partitions.partitioner_respond_to_call: needs to be defined and needs to respond to `#call`
66
- virtual_partitions.max_partitions_format: needs to be equl or more than 1
66
+ virtual_partitions.max_partitions_format: needs to be equal or more than 1
67
67
  manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
68
68
  long_running_job.active_format: needs to be either true or false
69
69
  dead_letter_queue_not_with_virtual_partitions: cannot be used together with Virtual Partitions
@@ -0,0 +1,18 @@
1
+ en:
2
+ validations:
3
+ topic:
4
+ virtual_partitions.partitioner_respond_to_call: needs to be defined and needs to respond to `#call`
5
+ virtual_partitions.max_partitions_format: needs to be equal or more than 1
6
+ manual_offset_management_not_with_virtual_partitions: cannot be used together with Virtual Partitions
7
+ long_running_job.active_format: needs to be either true or false
8
+ dead_letter_queue_not_with_virtual_partitions: cannot be used together with Virtual Partitions
9
+
10
+ config:
11
+ encryption.active_format: 'needs to be either true or false'
12
+ encryption.public_key_invalid: 'is not a valid public RSA key'
13
+ encryption.public_key_needs_to_be_public: 'is a private RSA key not a public one'
14
+ encryption.private_keys_format: 'needs to be a hash of version and private key value'
15
+ encryption.private_keys_need_to_be_private: 'all keys need to be private'
16
+ encryption.version_format: must be a non-empty string
17
+ encryption.public_key_format: 'is not a valid public RSA key'
18
+ encryption.private_keys_invalid: 'contains an invalid private RSA key string'
data/docker-compose.yml CHANGED
@@ -3,8 +3,10 @@ services:
3
3
  zookeeper:
4
4
  container_name: karafka_20_zookeeper
5
5
  image: wurstmeister/zookeeper
6
+ restart: on-failure
6
7
  ports:
7
8
  - '2181:2181'
9
+
8
10
  kafka:
9
11
  container_name: karafka_20_kafka
10
12
  image: wurstmeister/kafka
@@ -22,3 +24,4 @@ services:
22
24
  benchmarks_00_10:10:1"
23
25
  volumes:
24
26
  - /var/run/docker.sock:/var/run/docker.sock
27
+ restart: on-failure
data/karafka.gemspec CHANGED
@@ -21,9 +21,9 @@ Gem::Specification.new do |spec|
21
21
  without having to focus on things that are not your business domain.
22
22
  DESC
23
23
 
24
- spec.add_dependency 'karafka-core', '>= 2.0.6', '< 3.0.0'
24
+ spec.add_dependency 'karafka-core', '>= 2.0.7', '< 3.0.0'
25
25
  spec.add_dependency 'thor', '>= 0.20'
26
- spec.add_dependency 'waterdrop', '>= 2.4.3', '< 3.0.0'
26
+ spec.add_dependency 'waterdrop', '>= 2.4.7', '< 3.0.0'
27
27
  spec.add_dependency 'zeitwerk', '~> 2.3'
28
28
 
29
29
  spec.required_ruby_version = '>= 2.7.0'
@@ -10,7 +10,7 @@ module Karafka
10
10
  configure do |config|
11
11
  config.error_messages = YAML.safe_load(
12
12
  File.read(
13
- File.join(Karafka.gem_root, 'config', 'errors.yml')
13
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
14
14
  )
15
15
  ).fetch('en').fetch('validations').fetch('job_options')
16
16
  end
data/lib/karafka/admin.rb CHANGED
@@ -116,8 +116,7 @@ module Karafka
116
116
  # Creates admin instance and yields it. After usage it closes the admin instance
117
117
  def with_admin
118
118
  admin = config(:producer).admin
119
- result = yield(admin)
120
- result
119
+ yield(admin)
121
120
  ensure
122
121
  admin&.close
123
122
  end
@@ -125,8 +124,7 @@ module Karafka
125
124
  # Creates consumer instance and yields it. After usage it closes the consumer instance
126
125
  def with_consumer
127
126
  consumer = config(:consumer).consumer
128
- result = yield(consumer)
129
- result
127
+ yield(consumer)
130
128
  ensure
131
129
  consumer&.close
132
130
  end
data/lib/karafka/app.rb CHANGED
@@ -35,10 +35,21 @@ module Karafka
35
35
  # Just a nicer name for the consumer groups
36
36
  alias routes consumer_groups
37
37
 
38
- Status.instance_methods(false).each do |delegated|
39
- define_method(delegated) do
40
- App.config.internal.status.send(delegated)
41
- end
38
+ # Allow for easier status management via `Karafka::App` by aliasing status methods here
39
+ Status::STATES.each do |state, transition|
40
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
41
+ def #{state}
42
+ App.config.internal.status.#{state}
43
+ end
44
+
45
+ def #{state}?
46
+ App.config.internal.status.#{state}?
47
+ end
48
+
49
+ def #{transition}
50
+ App.config.internal.status.#{transition}
51
+ end
52
+ RUBY
42
53
  end
43
54
 
44
55
  # Methods that should be delegated to Karafka module
@@ -4,6 +4,8 @@
4
4
  module Karafka
5
5
  # Base consumer from which all Karafka consumers should inherit
6
6
  class BaseConsumer
7
+ # @return [String] id of the current consumer
8
+ attr_reader :id
7
9
  # @return [Karafka::Routing::Topic] topic to which a given consumer is subscribed
8
10
  attr_accessor :topic
9
11
  # @return [Karafka::Messages::Messages] current messages batch
@@ -15,6 +17,11 @@ module Karafka
15
17
  # @return [Waterdrop::Producer] producer instance
16
18
  attr_accessor :producer
17
19
 
20
+ # Creates new consumer and assigns it an id
21
+ def initialize
22
+ @id = SecureRandom.hex(6)
23
+ end
24
+
18
25
  # Can be used to run preparation code prior to the job being enqueued
19
26
  #
20
27
  # @private
@@ -204,6 +211,17 @@ module Karafka
204
211
 
205
212
  # Indicate, that user took a manual action of pausing
206
213
  coordinator.manual_pause if manual_pause
214
+
215
+ Karafka.monitor.instrument(
216
+ 'consumer.consuming.pause',
217
+ caller: self,
218
+ manual: manual_pause,
219
+ topic: messages.metadata.topic,
220
+ partition: messages.metadata.partition,
221
+ offset: offset,
222
+ timeout: coordinator.pause_tracker.current_timeout,
223
+ attempt: coordinator.pause_tracker.attempt
224
+ )
207
225
  end
208
226
 
209
227
  # Resumes processing of the current topic partition
@@ -232,5 +250,23 @@ module Karafka
232
250
  def revoked?
233
251
  coordinator.revoked?
234
252
  end
253
+
254
+ # Pauses the processing from the last offset to retry on given message
255
+ # @private
256
+ def retry_after_pause
257
+ pause(coordinator.seek_offset, nil, false)
258
+
259
+ # Instrumentation needs to run **after** `#pause` invocation because we rely on the states
260
+ # set by `#pause`
261
+ Karafka.monitor.instrument(
262
+ 'consumer.consuming.retry',
263
+ caller: self,
264
+ topic: messages.metadata.topic,
265
+ partition: messages.metadata.partition,
266
+ offset: coordinator.seek_offset,
267
+ timeout: coordinator.pause_tracker.current_timeout,
268
+ attempt: coordinator.pause_tracker.attempt
269
+ )
270
+ end
235
271
  end
236
272
  end
@@ -21,7 +21,7 @@ module Karafka
21
21
  def initialize(consumer_group_coordinator, subscription_group, jobs_queue)
22
22
  proc_config = ::Karafka::App.config.internal.processing
23
23
 
24
- @id = SecureRandom.uuid
24
+ @id = SecureRandom.hex(6)
25
25
  @consumer_group_coordinator = consumer_group_coordinator
26
26
  @subscription_group = subscription_group
27
27
  @jobs_queue = jobs_queue
@@ -12,7 +12,7 @@ module Karafka
12
12
  configure do |config|
13
13
  config.error_messages = YAML.safe_load(
14
14
  File.read(
15
- File.join(Karafka.gem_root, 'config', 'errors.yml')
15
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
16
16
  )
17
17
  ).fetch('en').fetch('validations').fetch('config')
18
18
  end
@@ -7,7 +7,7 @@ module Karafka
7
7
  configure do |config|
8
8
  config.error_messages = YAML.safe_load(
9
9
  File.read(
10
- File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
11
11
  )
12
12
  ).fetch('en').fetch('validations').fetch('consumer_group')
13
13
  end
@@ -7,7 +7,7 @@ module Karafka
7
7
  configure do |config|
8
8
  config.error_messages = YAML.safe_load(
9
9
  File.read(
10
- File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
11
11
  )
12
12
  ).fetch('en').fetch('validations').fetch('server_cli_options')
13
13
  end
@@ -7,7 +7,7 @@ module Karafka
7
7
  configure do |config|
8
8
  config.error_messages = YAML.safe_load(
9
9
  File.read(
10
- File.join(Karafka.gem_root, 'config', 'errors.yml')
10
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
11
11
  )
12
12
  ).fetch('en').fetch('validations').fetch('topic')
13
13
  end
@@ -63,6 +63,38 @@ module Karafka
63
63
  info "[#{job.id}] #{job_type} job for #{consumer} on #{topic} finished in #{time}ms"
64
64
  end
65
65
 
66
+ # Prints info about a pause occurrence. Irrelevant if user or system initiated.
67
+ #
68
+ # @param event [Karafka::Core::Monitoring::Event] event details including payload
69
+ def on_consumer_consuming_pause(event)
70
+ topic = event[:topic]
71
+ partition = event[:partition]
72
+ offset = event[:offset]
73
+ consumer = event[:caller]
74
+ timeout = event[:timeout]
75
+
76
+ info <<~MSG.tr("\n", ' ').strip!
77
+ [#{consumer.id}] Pausing partition #{partition} of topic #{topic}
78
+ on offset #{offset} for #{timeout} ms.
79
+ MSG
80
+ end
81
+
82
+ # Prints info about retry of processing after an error
83
+ #
84
+ # @param event [Karafka::Core::Monitoring::Event] event details including payload
85
+ def on_consumer_consuming_retry(event)
86
+ topic = event[:topic]
87
+ partition = event[:partition]
88
+ offset = event[:offset]
89
+ consumer = event[:caller]
90
+ timeout = event[:timeout]
91
+
92
+ info <<~MSG.tr("\n", ' ').strip!
93
+ [#{consumer.id}] Retrying of #{consumer.class} after #{timeout} ms
94
+ on partition #{partition} of topic #{topic} from offset #{offset}
95
+ MSG
96
+ end
97
+
66
98
  # Logs info about system signals that Karafka received and prints backtrace for threads in
67
99
  # case of ttin
68
100
  #
@@ -22,8 +22,11 @@ module Karafka
22
22
  app.quieting
23
23
  app.stopping
24
24
  app.stopped
25
+ app.terminated
25
26
 
26
27
  consumer.consumed
28
+ consumer.consuming.pause
29
+ consumer.consuming.retry
27
30
  consumer.revoked
28
31
  consumer.shutdown
29
32
 
@@ -9,7 +9,19 @@ module Karafka
9
9
  class Message
10
10
  extend Forwardable
11
11
 
12
- attr_reader :raw_payload, :metadata
12
+ class << self
13
+ # @return [Object] general parser
14
+ # @note We cache it here for performance reasons. It is 2.5x times faster than getting it
15
+ # via the config chain.
16
+ def parser
17
+ @parser ||= App.config.internal.messages.parser
18
+ end
19
+ end
20
+
21
+ attr_reader :metadata
22
+ # raw payload needs to be mutable as we want to have option to change it in the parser
23
+ # prior to the final deserialization
24
+ attr_accessor :raw_payload
13
25
 
14
26
  def_delegators :metadata, *Metadata.members
15
27
 
@@ -42,7 +54,7 @@ module Karafka
42
54
 
43
55
  # @return [Object] deserialized data
44
56
  def deserialize
45
- metadata.deserializer.call(self)
57
+ self.class.parser.call(self)
46
58
  end
47
59
  end
48
60
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karafka
4
+ module Messages
5
+ # Default message parser. The only thing it does, is calling the deserializer
6
+ class Parser
7
+ # @param message [::Karafka::Messages::Message]
8
+ # @return [Object] deserialized payload
9
+ def call(message)
10
+ message.metadata.deserializer.call(message)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -20,7 +20,7 @@ module Karafka
20
20
  configure do |config|
21
21
  config.error_messages = YAML.safe_load(
22
22
  File.read(
23
- File.join(Karafka.gem_root, 'config', 'errors.yml')
23
+ File.join(Karafka.gem_root, 'config', 'locales', 'errors.yml')
24
24
  )
25
25
  ).fetch('en').fetch('validations').fetch('job_options')
26
26
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component under a commercial license.
4
+ # This Karafka component is NOT licensed under LGPL.
5
+ #
6
+ # All of the commercial components are present in the lib/karafka/pro directory of this
7
+ # repository and their usage requires commercial license agreement.
8
+ #
9
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
10
+ #
11
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
12
+ # your code to Maciej Mensfeld.
13
+
14
+ module Karafka
15
+ module Pro
16
+ module Encryption
17
+ # Cipher for encrypting and decrypting data
18
+ class Cipher
19
+ def initialize
20
+ @private_pems = {}
21
+ end
22
+
23
+ # Encrypts given string content with the public key
24
+ # @param content [String]
25
+ # @return [String]
26
+ def encrypt(content)
27
+ public_pem.public_encrypt(content)
28
+ end
29
+
30
+ # Decrypts provided content using `version` key
31
+ # @param version [String] encryption version
32
+ # @param content [String] encrypted content
33
+ # @return [String] decrypted content
34
+ def decrypt(version, content)
35
+ private_pem(version).private_decrypt(content)
36
+ end
37
+
38
+ private
39
+
40
+ # @return [::OpenSSL::PKey::RSA] rsa public key
41
+ def public_pem
42
+ @public_pem ||= ::OpenSSL::PKey::RSA.new(::Karafka::App.config.encryption.public_key)
43
+ end
44
+
45
+ # @param version [String] version for which we want to get the rsa key
46
+ # @return [::OpenSSL::PKey::RSA] rsa private key
47
+ def private_pem(version)
48
+ return @private_pems[version] if @private_pems.key?(version)
49
+
50
+ key_string = ::Karafka::App.config.encryption.private_keys[version]
51
+ key_string || raise(Errors::PrivateKeyNotFound, version)
52
+
53
+ @private_pems[version] = ::OpenSSL::PKey::RSA.new(key_string)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component under a commercial license.
4
+ # This Karafka component is NOT licensed under LGPL.
5
+ #
6
+ # All of the commercial components are present in the lib/karafka/pro directory of this
7
+ # repository and their usage requires commercial license agreement.
8
+ #
9
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
10
+ #
11
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
12
+ # your code to Maciej Mensfeld.
13
+
14
+ module Karafka
15
+ module Pro
16
+ module Encryption
17
+ # Encryption related contracts
18
+ module Contracts
19
+ # Makes sure, all the expected config is defined as it should be
20
+ class Config < ::Karafka::Contracts::Base
21
+ configure do |config|
22
+ config.error_messages = YAML.safe_load(
23
+ File.read(
24
+ File.join(Karafka.gem_root, 'config', 'locales', 'pro_errors.yml')
25
+ )
26
+ ).fetch('en').fetch('validations').fetch('config')
27
+ end
28
+
29
+ nested(:encryption) do
30
+ required(:active) { |val| [true, false].include?(val) }
31
+ required(:version) { |val| val.is_a?(String) && !val.empty? }
32
+ required(:public_key) { |val| val.is_a?(String) }
33
+
34
+ required(:private_keys) do |val|
35
+ val.is_a?(Hash) &&
36
+ val.keys.all? { |key| key.is_a?(String) } &&
37
+ val.values.all? { |key| key.is_a?(String) }
38
+ end
39
+ end
40
+
41
+ # Public key validation
42
+ virtual do |data, errors|
43
+ next unless errors.empty?
44
+ next unless data.fetch(:encryption).fetch(:active)
45
+
46
+ key = OpenSSL::PKey::RSA.new(data.fetch(:encryption).fetch(:public_key))
47
+
48
+ next unless key.private?
49
+
50
+ [[%i[encryption public_key], :needs_to_be_public]]
51
+ rescue OpenSSL::PKey::RSAError
52
+ [[%i[encryption public_key], :invalid]]
53
+ end
54
+
55
+ # Private keys validation
56
+ virtual do |data, errors|
57
+ next unless errors.empty?
58
+ next unless data.fetch(:encryption).fetch(:active)
59
+
60
+ private_keys = data.fetch(:encryption).fetch(:private_keys)
61
+
62
+ # Keys may be empty for production only envs
63
+ next if private_keys.empty?
64
+
65
+ keys = private_keys.each_value.map do |key|
66
+ OpenSSL::PKey::RSA.new(key)
67
+ end
68
+
69
+ next if keys.all?(&:private?)
70
+
71
+ [[%i[encryption private_keys], :need_to_be_private]]
72
+ rescue OpenSSL::PKey::RSAError
73
+ [[%i[encryption private_keys], :invalid]]
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This Karafka component is a Pro component under a commercial license.
4
+ # This Karafka component is NOT licensed under LGPL.
5
+ #
6
+ # All of the commercial components are present in the lib/karafka/pro directory of this
7
+ # repository and their usage requires commercial license agreement.
8
+ #
9
+ # Karafka has also commercial-friendly license, commercial support and commercial components.
10
+ #
11
+ # By sending a pull request to the pro components, you are agreeing to transfer the copyright of
12
+ # your code to Maciej Mensfeld.
13
+
14
+ module Karafka
15
+ module Pro
16
+ module Encryption
17
+ # Encryption related errors
18
+ module Errors
19
+ # Raised when we have encountered encryption key with version we do not have
20
+ PrivateKeyNotFound = Class.new(::Karafka::Errors::BaseError)
21
+ end
22
+ end
23
+ end
24
+ end