manageiq-messaging 0.1.3 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8859fa0a68ffe54c38283145348077bcbc78e004636e38960cc0d2ff70923f7a
4
- data.tar.gz: '088f6d9d2dc80e753b16a0bbdce58dc789aec1d5eada50c0aa3b74fd78109006'
3
+ metadata.gz: 503935968b3f9866a7c638f6f5902f4a4fff7bcacab799b12cce939f2e9922c8
4
+ data.tar.gz: d1681ca75ff3c5500a3f8d0a68dd622d8dff60ae7e7bdc19f1a54dbe51a01e24
5
5
  SHA512:
6
- metadata.gz: 457f22d4a96c0ec19a05cb2a0ebe3a9f665f62583d7337ede95ddf786e899b78dcd86173c219988d34ef8d4deafc188d8bcd184728e8362e4b66c772875370f5
7
- data.tar.gz: b496df16b18b87754bf22107fe0d7f30a2dee767f6673cdd51330880ea290539967397b32f629cb4ff24fb24e428390ba9478cc0c2bbc359c404e0a5cd358976
6
+ metadata.gz: 4717a96bfdb879318756f037baaa00030e9c1f76a33a04bbe276a0296c2490a9b2fd5abfba6c5fa21ea15fc623659c5987767b01c4e1a95f363f6b307f421829
7
+ data.tar.gz: e26d52f01f2e871cdd99b377e49b6d5914584506f65df11bedd325b56ba794607ffaac033fb837a75ffb6b40722723e144458da6842777c0d152311826cf7d10
@@ -34,6 +34,7 @@ engines:
34
34
  rubocop:
35
35
  enabled: true
36
36
  config: '.rubocop_cc.yml'
37
+ channel: 'rubocop-0-69'
37
38
  prepare:
38
39
  fetch:
39
40
  - url: "https://raw.githubusercontent.com/ManageIQ/guides/master/.rubocop_base.yml"
@@ -1,4 +1,5 @@
1
+ inherit_gem:
2
+ manageiq-style: .rubocop_base.yml
1
3
  inherit_from:
2
- - https://raw.githubusercontent.com/ManageIQ/guides/master/.rubocop_base.yml
3
4
  # put all local rubocop config into .rubocop_local.yml as it will be loaded by .rubocop_cc.yml as well
4
5
  - .rubocop_local.yml
@@ -1,9 +1,8 @@
1
- sudo: false
2
1
  language: ruby
3
2
  rvm:
4
- - 2.3.1
5
3
  - 2.4.5
4
+ - 2.5.7
5
+ - 2.6.5
6
6
  before_install: gem install bundler -v 1.13.0
7
7
  after_script: bundle exec codeclimate-test-reporter
8
- sudo: false
9
8
  cache: bundler
data/CHANGES CHANGED
@@ -7,3 +7,21 @@
7
7
 
8
8
  = 0.1.2 - 11-Dec-2018
9
9
  * Allow to set max_bytes to each_batch when subscribe to a Kafka topic.
10
+
11
+ = 0.1.4 - 3-Apr-2019
12
+ * Add an #ack method to a ReceivedMessage to simplify manual acknowledgements
13
+ * Allow caller to provide a session_timeout to kafka consumers, default of 30sec
14
+
15
+ = 0.1.5 - 6-Jun-2019
16
+ * Allow caller to provide extra headers to the message
17
+
18
+ = 0.1.6 - 6-July-2020
19
+ * Rescue message body decoding errors. Re-raise errors raised by users code of processing received messages.
20
+
21
+ = 1.0.0 - 28-Sep-2020
22
+ * Switch to use rdkafka client
23
+
24
+ = 1.0.1 - 25-Dec-2020
25
+ * Allow all kafka options to be passed
26
+ * remove sudo:false from .travis.yml
27
+ * Use manageiq-style
data/README.md CHANGED
@@ -1,10 +1,9 @@
1
1
  # ManageIQ Messaging Client
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/manageiq-messaging.svg)](http://badge.fury.io/rb/manageiq-messaging)
4
- [![Build Status](https://travis-ci.org/ManageIQ/manageiq-messaging.svg)](https://travis-ci.org/ManageIQ/manageiq-messaging)
4
+ [![Build Status](https://travis-ci.com/ManageIQ/manageiq-messaging.svg)](https://travis-ci.com/ManageIQ/manageiq-messaging)
5
5
  [![Code Climate](https://codeclimate.com/github/ManageIQ/manageiq-messaging.svg)](https://codeclimate.com/github/ManageIQ/manageiq-messaging)
6
6
  [![Test Coverage](https://codeclimate.com/github/ManageIQ/manageiq-messaging/badges/coverage.svg)](https://codeclimate.com/github/ManageIQ/manageiq-messaging/coverage)
7
- [![Dependency Status](https://gemnasium.com/ManageIQ/manageiq-messaging.svg)](https://gemnasium.com/ManageIQ/manageiq-messaging)
8
7
  [![Security](https://hakiri.io/github/ManageIQ/manageiq-messaging/master.svg)](https://hakiri.io/github/ManageIQ/manageiq-messaging/master)
9
8
 
10
9
  Client library for ManageIQ components to exchange messages through its internal message bus.
@@ -83,7 +82,7 @@ This is the one-to-one publish/subscribe pattern. Multiple subscribers can subsc
83
82
  client.subscribe_messages(:service => 'ems_operation', :affinity => 'ems_amazon1', :auto_ack => false) do |messages|
84
83
  messages.each do |msg|
85
84
  # do stuff with msg.message and msg.payload
86
- client.ack(msg.ack_ref)
85
+ msg.ack
87
86
  end
88
87
  end
89
88
 
@@ -156,6 +155,24 @@ This is the one-to-many publish/subscribe pattern. Multiple subscribers can subs
156
155
 
157
156
  By default, events are delivered to live subscribers only. Some messaging systems support persistence with options.
158
157
 
158
+ ### Add your own headers to a message (Queue or Topic)
159
+
160
+ If you want you can add in your own headers to the send message
161
+
162
+ ```
163
+ client.publish_topic(
164
+ :service => 'provider_events',
165
+ :event => 'powered_on',
166
+ :headers => {:request_id => "12345"},
167
+ :payload => {:ems_ref => 'uid987'}
168
+
169
+ )
170
+
171
+ client.subscribe_topic(:service => 'provider_events', :persist_ref => 'automate_1') do |msg|
172
+ puts "Received event #{msg.message} with request-id: #{msg.headers['request_id']}"
173
+ end
174
+ ```
175
+
159
176
  ## Development
160
177
 
161
178
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -26,7 +26,7 @@ class ProducerConsumer < Common
26
26
  client.subscribe_messages(:service => 'ems_operation', :affinity => 'ems_amazon1', :auto_ack => false) do |messages|
27
27
  messages.each do |msg|
28
28
  do_stuff(msg)
29
- client.ack(msg.ack_ref)
29
+ msg.ack
30
30
  end
31
31
  end
32
32
  sleep(5)
@@ -40,6 +40,7 @@ module ManageIQ
40
40
  def self.open(options)
41
41
  protocol = options[:protocol] || :Stomp
42
42
  client = Object.const_get("ManageIQ::Messaging::#{protocol}::Client").new(options)
43
+
43
44
  return client unless block_given?
44
45
 
45
46
  begin
@@ -58,6 +59,7 @@ module ManageIQ
58
59
  # * :message (e.g. method name or message type)
59
60
  # * :payload (message body, a string or an user object that can be serialized)
60
61
  # * :sender (optional, identify the sender)
62
+ # * :headers (optional, additional headers to add to the message)
61
63
  # Other options are underlying messaging system specific.
62
64
  #
63
65
  # Optionally a call back block can be provided to wait on the consumer to send
@@ -106,9 +108,9 @@ module ManageIQ
106
108
  # msg.sender
107
109
  # msg.message
108
110
  # msg.payload
109
- # msg.ack_ref #used to ack the message
111
+ # msg.ack_ref
110
112
  #
111
- # client.ack(msg.ack_ref) # needed only when options[:auto_ack] is false
113
+ # msg.ack # needed only when options[:auto_ack] is false
112
114
  # # process the message
113
115
  # end
114
116
  # end
@@ -122,7 +124,7 @@ module ManageIQ
122
124
  # message is proccessed. Any un-acked message will be redelivered to next subscriber
123
125
  # AFTER the current subscriber disconnects normally or abnormally (e.g. crashed).
124
126
  #
125
- # To ack a message call +ack+(+msg.ack_ref+)
127
+ # To ack a message call +msg.ack+
126
128
  def subscribe_messages(options, &block)
127
129
  raise "A block is required" unless block_given?
128
130
  assert_options(options, [:service])
@@ -164,6 +166,7 @@ module ManageIQ
164
166
  # * :event (event name)
165
167
  # * :payload (message body, a string or an user object that can be serialized)
166
168
  # * :sender (optional, identify the sender)
169
+ # * :headers (optional, additional headers to add to the message)
167
170
  # Other options are underlying messaging system specific.
168
171
  #
169
172
  def publish_topic(options)
@@ -189,9 +192,9 @@ module ManageIQ
189
192
  # msg.sender
190
193
  # msg.message
191
194
  # msg.payload
192
- # msg.ack_ref #used to ack the message
195
+ # msg.ack_ref
193
196
  #
194
- # client.ack(msg.ack_ref) # needed only when options[:auto_ack] is false
197
+ # msg.ack # needed only when options[:auto_ack] is false
195
198
  # # process the message
196
199
  # end
197
200
  #
@@ -204,7 +207,7 @@ module ManageIQ
204
207
  # message is proccessed. Any un-acked message will be redelivered to next subscriber
205
208
  # AFTER the current subscriber disconnects normally or abnormally (e.g. crashed).
206
209
  #
207
- # To ack a message call +ack+(+msg.ack_ref+)
210
+ # To ack a message call +msg.ack+
208
211
  def subscribe_topic(options, &block)
209
212
  raise "A block is required" unless block_given?
210
213
  assert_options(options, [:service])
@@ -18,7 +18,7 @@ module ManageIQ
18
18
 
19
19
  def decode_body(headers, raw_body)
20
20
  return raw_body unless headers.kind_of?(Hash)
21
- case headers["encoding"]
21
+ case headers["encoding"] || headers[:encoding]
22
22
  when "json"
23
23
  JSON.parse(raw_body)
24
24
  when "yaml"
@@ -26,6 +26,10 @@ module ManageIQ
26
26
  else
27
27
  raw_body
28
28
  end
29
+ rescue => e # JSON or YAML parsing error
30
+ logger.error("Error decoding message body: #{e.message}")
31
+ logger.error(e.backtrace.join("\n"))
32
+ raw_body
29
33
  end
30
34
 
31
35
  def payload_log(payload)
@@ -10,25 +10,22 @@ module ManageIQ
10
10
  # * :hosts (Array of Kafka cluster hosts, or)
11
11
  # * :host (Single host name)
12
12
  # * :port (host port number)
13
- # * :ssl_ca_cert (security options)
14
- # * :ssl_client_cert
15
- # * :ssl_client_cert_key
16
- # * :sasl_gssapi_principal
17
- # * :sasl_gssapi_keytab
18
- # * :sasl_plain_username
19
- # * :sasl_plain_password
20
- # * :sasl_scram_username
21
- # * :sasl_scram_password
22
- # * :sasl_scram_mechanism
13
+ #
14
+ # For additional security options, please refer to
15
+ # https://github.com/edenhill/librdkafka/wiki/Using-SSL-with-librdkafka and
16
+ # https://github.com/edenhill/librdkafka/wiki/Using-SASL-with-librdkafka
17
+ #
23
18
  #
24
19
  # Kafka specific +publish_message+ options:
25
20
  # * :group_name (Used as Kafka partition_key)
26
21
  #
27
22
  # Kafka specific +subscribe_topic+ options:
28
23
  # * :persist_ref (Used as Kafka group_id)
24
+ # * :session_timeout (Max time in seconds allowed to process a message, default is 30)
29
25
  #
30
26
  # Kafka specific +subscribe_messages+ options:
31
27
  # * :max_bytes (Max batch size to read, default is 10Mb)
28
+ # * :session_timeout (Max time in seconds allowed to process a message, default is 30)
32
29
  #
33
30
  # Without +:persist_ref+ every topic subscriber receives a copy of each message
34
31
  # only when they are active. If multiple topic subscribers join with the same
@@ -40,7 +37,7 @@ module ManageIQ
40
37
  #
41
38
  # +subscribe_background_job+ is currently not implemented.
42
39
  class Client < ManageIQ::Messaging::Client
43
- require 'kafka'
40
+ require 'rdkafka'
44
41
  require 'manageiq/messaging/kafka/common'
45
42
  require 'manageiq/messaging/kafka/queue'
46
43
  require 'manageiq/messaging/kafka/background_job'
@@ -51,27 +48,27 @@ module ManageIQ
51
48
  include BackgroundJob
52
49
  include Topic
53
50
 
54
- private *delegate(:subscribe, :unsubscribe, :publish, :to => :kafka_client)
55
- delegate :close, :to => :kafka_client
56
-
57
51
  attr_accessor :encoding
58
52
 
59
53
  def ack(ack_ref)
60
- @queue_consumer.try(:mark_message_as_processed, ack_ref)
61
- @topic_consumer.try(:mark_message_as_processed, ack_ref)
54
+ ack_ref.commit
55
+ rescue Rdkafka::RdkafkaError => e
56
+ logger.warn("ack failed with error #{e.message}")
57
+ raise unless e.message =~ /no_offset/
62
58
  end
63
59
 
64
60
  def close
65
- @topic_consumer.try(:stop)
66
- @topic_consumer = nil
67
- @queue_consumer.try(:stop)
68
- @queue_consumer = nil
69
-
70
- @producer.try(:shutdown)
61
+ @producer&.close
71
62
  @producer = nil
72
63
 
73
- kafka_client.close
74
- @kafka_client = nil
64
+ @consumer&.close
65
+ @consumer = nil
66
+ end
67
+
68
+ # list all topics
69
+ def topics
70
+ native_kafka = producer.instance_variable_get(:@native_kafka)
71
+ Rdkafka::Metadata.new(native_kafka).topics.collect { |topic| topic[:topic_name] }
75
72
  end
76
73
 
77
74
  private
@@ -85,12 +82,12 @@ module ManageIQ
85
82
  @encoding = options[:encoding] || 'yaml'
86
83
  require "json" if @encoding == "json"
87
84
 
88
- connection_opts = {}
89
- connection_opts[:client_id] = options[:client_ref] if options[:client_ref]
90
-
91
- connection_opts.merge!(options.slice(:ssl_ca_cert, :ssl_client_cert, :ssl_client_cert_key, :sasl_gssapi_principal, :sasl_gssapi_keytab, :sasl_plain_username, :sasl_plain_password, :sasl_scram_username, :sasl_scram_password, :sasl_scram_mechanism))
85
+ connection_opts = {:"bootstrap.servers" => hosts.join(',')}
86
+ connection_opts[:"client.id"] = options[:client_ref] if options[:client_ref]
87
+ connection_opts.merge!(options.except(:port, :host, :hosts, :encoding, :protocol, :client_ref))
92
88
 
93
- @kafka_client = ::Kafka.new(hosts, connection_opts)
89
+ ::Rdkafka::Config.logger = logger
90
+ @kafka_client = ::Rdkafka::Config.new(connection_opts)
94
91
  end
95
92
  end
96
93
  end
@@ -5,46 +5,35 @@ module ManageIQ
5
5
  require 'manageiq/messaging/common'
6
6
  include ManageIQ::Messaging::Common
7
7
 
8
- GROUP_FOR_QUEUE_MESSAGES = 'manageiq_messaging_queue_group_'.freeze
9
-
10
8
  private
11
9
 
12
10
  def producer
13
11
  @producer ||= kafka_client.producer
14
12
  end
15
13
 
16
- def topic_consumer(persist_ref)
17
- # persist_ref enables consumer to receive messages sent when consumer is temporarily offline
18
- # it also enables consumers to do load balancing when multiple consumers join the with the same ref.
19
- @topic_consumer.try(:stop) unless @persist_ref == persist_ref
20
- @persist_ref = persist_ref
21
- @topic_consumer ||= kafka_client.consumer(:group_id => persist_ref)
22
- end
23
-
24
- def queue_consumer(topic)
25
- # all queue consumers join the same group so that each message can be processed by one and only one consumer
26
- @queue_consumer.try(:stop) unless @queue_topic == topic
27
- @queue_topic = topic
28
- @queue_consumer ||= kafka_client.consumer(:group_id => GROUP_FOR_QUEUE_MESSAGES + topic)
29
- end
30
-
31
- trap("TERM") do
32
- @topic_consumer.try(:stop)
33
- @topic_consumer = nil
34
- @queue_consumer.try(:stop)
35
- @queue_consumer = nil
14
+ def consumer(beginning, options)
15
+ @consumer&.close
16
+ kafka_client[:"group.id"] = options[:persist_ref]
17
+ kafka_client[:"auto.offset.reset"] = beginning ? 'smallest' : 'largest'
18
+ kafka_client[:"enable.auto.commit"] = !!auto_ack?(options)
19
+ kafka_client[:"session.timeout.ms"] = options[:session_timeout] * 1000 if options[:session_timeout].present?
20
+ kafka_client[:"group.instance.id"] = options[:group_instance_id] if options[:group_instance_id].present?
21
+ @consumer = kafka_client.consumer
36
22
  end
37
23
 
38
- def raw_publish(commit, body, options)
39
- producer.produce(encode_body(options[:headers], body), options)
40
- producer.deliver_messages if commit
41
- logger.info("Published to topic(#{options[:topic]}), msg(#{payload_log(body.inspect)})")
24
+ def raw_publish(wait, body, options)
25
+ options[:payload] = encode_body(options[:headers], body)
26
+ producer.produce(options).tap do |handle|
27
+ handle.wait if wait
28
+ logger.info("Published to topic(#{options[:topic]}), msg(#{payload_log(body.inspect)})")
29
+ end
42
30
  end
43
31
 
44
32
  def queue_for_publish(options)
45
33
  body, kafka_opts = for_publish(options)
46
34
  kafka_opts[:headers][:message_type] = options[:message] if options[:message]
47
35
  kafka_opts[:headers][:class_name] = options[:class_name] if options[:class_name]
36
+ kafka_opts[:headers].merge!(options[:headers].except(*message_header_keys)) if options.key?(:headers)
48
37
 
49
38
  [body, kafka_opts]
50
39
  end
@@ -52,13 +41,15 @@ module ManageIQ
52
41
  def topic_for_publish(options)
53
42
  body, kafka_opts = for_publish(options)
54
43
  kafka_opts[:headers][:event_type] = options[:event] if options[:event]
44
+ kafka_opts[:headers].merge!(options[:headers].except(*event_header_keys)) if options.key?(:headers)
55
45
 
56
46
  [body, kafka_opts]
57
47
  end
58
48
 
59
49
  def for_publish(options)
60
- kafka_opts = {:topic => address(options), :headers => {}}
50
+ kafka_opts = {:topic => address(options)}
61
51
  kafka_opts[:partition_key] = options[:group_name] if options[:group_name]
52
+ kafka_opts[:headers] = {}
62
53
  kafka_opts[:headers][:sender] = options[:sender] if options[:sender]
63
54
 
64
55
  body = options[:payload] || ''
@@ -74,34 +65,54 @@ module ManageIQ
74
65
  end
75
66
  end
76
67
 
77
- def process_queue_message(queue, message)
78
- payload = decode_body(message.headers, message.value)
79
- sender, message_type, class_name = parse_message_headers(message.headers)
80
- logger.info("Message received: queue(#{queue}), message(#{payload_log(payload)}), sender(#{sender}), type(#{message_type})")
81
- [sender, message_type, class_name, payload]
68
+ def process_queue_message(queue_consumer, queue, message)
69
+ begin
70
+ payload = decode_body(message.headers, message.payload)
71
+ sender, message_type, _class_name = parse_message_headers(message.headers)
72
+ client_headers = message.headers.except(*message_header_keys).with_indifferent_access
73
+
74
+ logger.info("Message received: queue(#{queue}), message(#{payload_log(payload)}), sender(#{sender}), type(#{message_type})")
75
+ yield [ManageIQ::Messaging::ReceivedMessage.new(sender, message_type, payload, client_headers, queue_consumer, self)]
76
+ logger.info("Messsage processed")
77
+ rescue StandardError => e
78
+ logger.error("Message processing error: #{e.message}")
79
+ logger.error(e.backtrace.join("\n"))
80
+ raise
81
+ end
82
82
  end
83
83
 
84
- def process_topic_message(topic, message)
84
+ def process_topic_message(topic_consumer, topic, message)
85
85
  begin
86
- payload = decode_body(message.headers, message.value)
86
+ payload = decode_body(message.headers, message.payload)
87
87
  sender, event_type = parse_event_headers(message.headers)
88
+ client_headers = message.headers.except(*event_header_keys).with_indifferent_access
89
+
88
90
  logger.info("Event received: topic(#{topic}), event(#{payload_log(payload)}), sender(#{sender}), type(#{event_type})")
89
- yield ManageIQ::Messaging::ReceivedMessage.new(sender, event_type, payload, message)
91
+ yield ManageIQ::Messaging::ReceivedMessage.new(sender, event_type, payload, client_headers, topic_consumer, self)
90
92
  logger.info("Event processed")
91
93
  rescue StandardError => e
92
94
  logger.error("Event processing error: #{e.message}")
93
95
  logger.error(e.backtrace.join("\n"))
96
+ raise
94
97
  end
95
98
  end
96
99
 
100
+ def message_header_keys
101
+ [:sender, :message_type, :class_name]
102
+ end
103
+
97
104
  def parse_message_headers(headers)
98
105
  return [nil, nil, nil] unless headers.kind_of?(Hash)
99
- headers.values_at('sender', 'message_type', 'class_name')
106
+ headers.values_at(*message_header_keys)
107
+ end
108
+
109
+ def event_header_keys
110
+ [:sender, :event_type]
100
111
  end
101
112
 
102
113
  def parse_event_headers(headers)
103
114
  return [nil, nil] unless headers.kind_of?(Hash)
104
- headers.values_at('sender', 'event_type')
115
+ headers.values_at(*event_header_keys)
105
116
  end
106
117
  end
107
118
  end
@@ -2,6 +2,8 @@ module ManageIQ
2
2
  module Messaging
3
3
  module Kafka
4
4
  module Queue
5
+ GROUP_FOR_QUEUE_MESSAGES = ENV['QUEUE_MESSAGES_GROUP_PREFIX'].freeze || 'manageiq_messaging_queue_group_'.freeze
6
+
5
7
  private
6
8
 
7
9
  def publish_message_impl(options)
@@ -10,33 +12,18 @@ module ManageIQ
10
12
  end
11
13
 
12
14
  def publish_messages_impl(messages)
13
- messages.each { |msg_options| raw_publish(false, *queue_for_publish(msg_options)) }
14
- producer.deliver_messages
15
+ handles = messages.collect { |msg_options| raw_publish(false, *queue_for_publish(msg_options)) }
16
+ handles.each(&:wait)
15
17
  end
16
18
 
17
- def subscribe_messages_impl(options)
19
+ def subscribe_messages_impl(options, &block)
18
20
  topic = address(options)
21
+ options[:persist_ref] = GROUP_FOR_QUEUE_MESSAGES + topic
19
22
 
20
- batch_options = {}
21
- batch_options[:automatically_mark_as_processed] = auto_ack?(options)
22
- batch_options[:max_bytes] = options[:max_bytes] if options.key?(:max_bytes)
23
-
24
- consumer = queue_consumer(topic)
25
- consumer.subscribe(topic)
26
- consumer.each_batch(batch_options) do |batch|
27
- logger.info("Batch message received: queue(#{topic})")
28
- begin
29
- messages = batch.messages.collect do |message|
30
- sender, message_type, _class_name, payload = process_queue_message(topic, message)
31
- ManageIQ::Messaging::ReceivedMessage.new(sender, message_type, payload, message)
32
- end
33
-
34
- yield messages
35
- rescue StandardError => e
36
- logger.error("Event processing error: #{e.message}")
37
- logger.error(e.backtrace.join("\n"))
38
- end
39
- logger.info("Batch message processed")
23
+ queue_consumer = consumer(true, options)
24
+ queue_consumer.subscribe(topic)
25
+ queue_consumer.each do |message|
26
+ process_queue_message(queue_consumer, topic, message, &block)
40
27
  end
41
28
  end
42
29
  end
@@ -1,7 +1,11 @@
1
+ require 'socket'
2
+
1
3
  module ManageIQ
2
4
  module Messaging
3
5
  module Kafka
4
6
  module Topic
7
+ GROUP_FOR_ADHOC_LISTENERS = Socket.gethostname.freeze
8
+
5
9
  private
6
10
 
7
11
  def publish_topic_impl(options)
@@ -10,18 +14,12 @@ module ManageIQ
10
14
 
11
15
  def subscribe_topic_impl(options, &block)
12
16
  topic = address(options)
13
- persist_ref = options[:persist_ref]
14
17
 
15
- if persist_ref
16
- consumer = topic_consumer(persist_ref)
17
- consumer.subscribe(topic, :start_from_beginning => false)
18
- consumer.each_message(:automatically_mark_as_processed => auto_ack?(options)) do |message|
19
- process_topic_message(topic, message, &block)
20
- end
21
- else
22
- kafka_client.each_message(:topic => topic, :start_from_beginning => false) do |message|
23
- process_topic_message(topic, message, &block)
24
- end
18
+ options[:persist_ref] = "#{GROUP_FOR_ADHOC_LISTENERS}_#{Time.now.to_i}" unless options[:persist_ref]
19
+ topic_consumer = consumer(false, options)
20
+ topic_consumer.subscribe(topic)
21
+ topic_consumer.each do |message|
22
+ process_topic_message(topic_consumer, topic, message, &block)
25
23
  end
26
24
  end
27
25
  end
@@ -1,10 +1,14 @@
1
1
  module ManageIQ
2
2
  module Messaging
3
3
  class ReceivedMessage
4
- attr_accessor :sender, :message, :payload, :ack_ref
4
+ attr_accessor :sender, :message, :payload, :headers, :ack_ref, :client
5
5
 
6
- def initialize(sender, message, payload, ack_ref)
7
- @sender, @message, @payload, @ack_ref = sender, message, payload, ack_ref
6
+ def initialize(sender, message, payload, headers, ack_ref, client)
7
+ @sender, @message, @payload, @headers, @ack_ref, @client = sender, message, payload, headers, ack_ref, client
8
+ end
9
+
10
+ def ack
11
+ client.ack(ack_ref)
8
12
  end
9
13
  end
10
14
  end
@@ -17,6 +17,8 @@ module ManageIQ
17
17
  address = "queue/#{options[:service]}.#{affinity}"
18
18
 
19
19
  headers = {:"destination-type" => 'ANYCAST', :persistent => true}
20
+ headers.merge!(options[:headers].except(*internal_header_keys)) if options.key?(:headers)
21
+
20
22
  headers[:expires] = options[:expires_on].to_i * 1000 if options[:expires_on]
21
23
  headers[:AMQ_SCHEDULED_TIME] = options[:deliver_on].to_i * 1000 if options[:deliver_on]
22
24
  headers[:priority] = options[:priority] if options[:priority]
@@ -38,6 +40,8 @@ module ManageIQ
38
40
  address = "topic/#{options[:service]}"
39
41
 
40
42
  headers = {:"destination-type" => 'MULTICAST', :persistent => true}
43
+ headers.merge!(options[:headers].except(*internal_header_keys)) if options.key?(:headers)
44
+
41
45
  headers[:expires] = options[:expires_on].to_i * 1000 if options[:expires_on]
42
46
  headers[:AMQ_SCHEDULED_TIME] = options[:deliver_on].to_i * 1000 if options[:deliver_on]
43
47
  headers[:priority] = options[:priority] if options[:priority]
@@ -63,6 +67,10 @@ module ManageIQ
63
67
  raw_publish(address, result || '', response_headers.merge(:correlation_id => correlation_ref))
64
68
  end
65
69
 
70
+ def internal_header_keys
71
+ [:"destination-type", :persistent, :expires, :AMQ_SCHEDULED_TIME, :priority, :_AMQ_GROUP_ID]
72
+ end
73
+
66
74
  def receive_response(service, correlation_ref)
67
75
  response_options = {
68
76
  :service => "#{service}.response",
@@ -33,9 +33,15 @@ module ManageIQ
33
33
  sender = msg.headers['sender']
34
34
  message_type = msg.headers['message_type']
35
35
  message_body = decode_body(msg.headers, msg.body)
36
+ client_headers = msg.headers.except(*internal_header_keys)
37
+
36
38
  logger.info("Message received: queue(#{queue_name}), msg(#{payload_log(message_body)}), headers(#{msg.headers})")
37
39
 
38
- result = yield [ManageIQ::Messaging::ReceivedMessage.new(sender, message_type, message_body, msg)]
40
+ messages = [
41
+ ManageIQ::Messaging::ReceivedMessage.new(sender, message_type, message_body, client_headers, msg, self)
42
+ ]
43
+
44
+ result = yield messages
39
45
  logger.info("Message processed")
40
46
 
41
47
  correlation_ref = msg.headers['correlation_id']
@@ -46,6 +52,7 @@ module ManageIQ
46
52
  rescue => e
47
53
  logger.error("Message processing error: #{e.message}")
48
54
  logger.error(e.backtrace.join("\n"))
55
+ raise
49
56
  end
50
57
  end
51
58
  end
@@ -21,13 +21,16 @@ module ManageIQ
21
21
 
22
22
  sender = event.headers['sender']
23
23
  event_type = event.headers['event_type']
24
+ client_headers = event.headers.except(*internal_header_keys)
25
+
24
26
  event_body = decode_body(event.headers, event.body)
25
27
  logger.info("Event received: queue(#{queue_name}), event(#{event_body}), headers(#{event.headers})")
26
- yield ManageIQ::Messaging::ReceivedMessage.new(sender, event_type, event_body, event)
28
+ yield ManageIQ::Messaging::ReceivedMessage.new(sender, event_type, event_body, client_headers, event, self)
27
29
  logger.info("Event processed")
28
30
  rescue => e
29
31
  logger.error("Event processing error: #{e.message}")
30
32
  logger.error(e.backtrace.join("\n"))
33
+ raise
31
34
  end
32
35
  end
33
36
  end
@@ -1,5 +1,5 @@
1
1
  module ManageIQ
2
2
  module Messaging
3
- VERSION = "0.1.3"
3
+ VERSION = "1.0.1"
4
4
  end
5
5
  end
@@ -4,9 +4,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'manageiq/messaging/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "manageiq-messaging"
8
- spec.version = ManageIQ::Messaging::VERSION
9
- spec.authors = ["ManageIQ Authors"]
7
+ spec.name = "manageiq-messaging"
8
+ spec.version = ManageIQ::Messaging::VERSION
9
+ spec.required_ruby_version = '>= 2.4'
10
+ spec.authors = ["ManageIQ Authors"]
10
11
 
11
12
  spec.summary = 'Client library for ManageIQ components to exchange messages through its internal message bus.'
12
13
  spec.description = 'Client library for ManageIQ components to exchange messages through its internal message bus.'
@@ -20,14 +21,14 @@ Gem::Specification.new do |spec|
20
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
22
  spec.require_paths = ["lib"]
22
23
 
23
- spec.add_dependency 'activesupport', '>= 4.2.2'
24
- spec.add_dependency 'ruby-kafka', '~> 0.7.0'
24
+ spec.add_dependency 'activesupport', '~> 5.2.4.3'
25
+ spec.add_dependency 'rdkafka', '~> 0.8'
25
26
  spec.add_dependency 'stomp', '~> 1.4.4'
26
27
 
27
- spec.add_development_dependency "bundler", "~> 1.13"
28
+ spec.add_development_dependency "bundler"
28
29
  spec.add_development_dependency "codeclimate-test-reporter", "~> 1.0.0"
29
- spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "manageiq-style"
31
+ spec.add_development_dependency "rake", ">= 12.3.3"
30
32
  spec.add_development_dependency "rspec", "~> 3.0"
31
- spec.add_development_dependency "rubocop"
32
33
  spec.add_development_dependency "simplecov"
33
34
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: manageiq-messaging
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ManageIQ Authors
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-02-25 00:00:00.000000000 Z
11
+ date: 2020-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 4.2.2
19
+ version: 5.2.4.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 4.2.2
26
+ version: 5.2.4.3
27
27
  - !ruby/object:Gem::Dependency
28
- name: ruby-kafka
28
+ name: rdkafka
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.7.0
33
+ version: '0.8'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.7.0
40
+ version: '0.8'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: stomp
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,16 +56,16 @@ dependencies:
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '1.13'
61
+ version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '1.13'
68
+ version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: codeclimate-test-reporter
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -81,47 +81,47 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: 1.0.0
83
83
  - !ruby/object:Gem::Dependency
84
- name: rake
84
+ name: manageiq-style
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '10.0'
89
+ version: '0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '10.0'
96
+ version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: rspec
98
+ name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - "~>"
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: '3.0'
103
+ version: 12.3.3
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - "~>"
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
- version: '3.0'
110
+ version: 12.3.3
111
111
  - !ruby/object:Gem::Dependency
112
- name: rubocop
112
+ name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ">="
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: '3.0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">="
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: '3.0'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: simplecov
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -138,7 +138,7 @@ dependencies:
138
138
  version: '0'
139
139
  description: Client library for ManageIQ components to exchange messages through its
140
140
  internal message bus.
141
- email:
141
+ email:
142
142
  executables: []
143
143
  extensions: []
144
144
  extra_rdoc_files: []
@@ -186,7 +186,7 @@ homepage: http://github.com/ManageIQ/manageiq-messaging
186
186
  licenses:
187
187
  - MIT
188
188
  metadata: {}
189
- post_install_message:
189
+ post_install_message:
190
190
  rdoc_options: []
191
191
  require_paths:
192
192
  - lib
@@ -194,16 +194,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
194
194
  requirements:
195
195
  - - ">="
196
196
  - !ruby/object:Gem::Version
197
- version: '0'
197
+ version: '2.4'
198
198
  required_rubygems_version: !ruby/object:Gem::Requirement
199
199
  requirements:
200
200
  - - ">="
201
201
  - !ruby/object:Gem::Version
202
202
  version: '0'
203
203
  requirements: []
204
- rubyforge_project:
205
- rubygems_version: 2.7.6
206
- signing_key:
204
+ rubygems_version: 3.2.0.rc.2
205
+ signing_key:
207
206
  specification_version: 4
208
207
  summary: Client library for ManageIQ components to exchange messages through its internal
209
208
  message bus.