brown 2.2.2.25.g85ddf08 → 2.2.2.27.gbc378e8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37896a92c6f9b1be0a84ed693b67fb57108bd2f1b97b27676f5f0912babe435b
|
4
|
+
data.tar.gz: 9613d3ee171cacd4eb004c82acfe837d2e3ce757d8bb2d63fba3c45cd9237664
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af6720e99c528fc3d57d03f3c208853ec6edb177f29940235811f6713ff8f50585c4f745e6d6a206d7e75750c74c5ad7a8e79ee21b1bce887cc020d462b3f335
|
7
|
+
data.tar.gz: f7f2eee6381f9046e7f5e8ff2d0f5d71725eb62ceca37580830d590ac2b4790f4b3aa5fa173b5d9dbe28a18ad4f488411f73bb57a2dd1a2048218e41cf0a9b58
|
@@ -109,6 +109,12 @@ module Brown::Agent::AMQP::ClassMethods
|
|
109
109
|
# bindings are already in place, and thus no declarations should be
|
110
110
|
# made to the AMQP broker.
|
111
111
|
#
|
112
|
+
# @param reject_on_error [Boolean] by default, if an uncaught exception
|
113
|
+
# is raised during message handling, an error will be logged and the
|
114
|
+
# listener will not process any new messages. However, if
|
115
|
+
# `reject_on_error: true`, then instead the message will be rejected, and
|
116
|
+
# processing will continue
|
117
|
+
#
|
112
118
|
# @param blk [Proc] is called every time a message is received from
|
113
119
|
# the queue, and an instance of {Brown::Agent::AMQPMessage} will
|
114
120
|
# be passed as the sole argument.
|
@@ -123,6 +129,7 @@ module Brown::Agent::AMQP::ClassMethods
|
|
123
129
|
allowed_classes: nil,
|
124
130
|
routing_key: nil,
|
125
131
|
predeclared: false,
|
132
|
+
reject_on_error: false,
|
126
133
|
&blk
|
127
134
|
)
|
128
135
|
exchange_list = (Array === exchange_name ? exchange_name : [exchange_name]).map(&:to_s)
|
@@ -142,6 +149,7 @@ module Brown::Agent::AMQP::ClassMethods
|
|
142
149
|
routing_key: routing_key,
|
143
150
|
callback: blk,
|
144
151
|
predeclared: predeclared,
|
152
|
+
reject_on_error: reject_on_error,
|
145
153
|
}
|
146
154
|
end
|
147
155
|
end
|
@@ -58,13 +58,26 @@ module Brown::Agent::AMQP::Initializer
|
|
58
58
|
def initialize_listeners
|
59
59
|
(self.class.amqp_listeners || []).each do |listener|
|
60
60
|
logger.debug(logloc) { "Initializing AMQP listener #{listener}" }
|
61
|
-
worker_method
|
61
|
+
worker_method = "amqp_listener_worker_#{SecureRandom.uuid}".to_sym
|
62
|
+
wrapper_method = "amqp_listener_wrapper_#{SecureRandom.uuid}".to_sym
|
63
|
+
|
62
64
|
define_singleton_method(worker_method, listener[:callback])
|
65
|
+
define_singleton_method(wrapper_method) do |msg|
|
66
|
+
begin
|
67
|
+
__send__(worker_method, msg)
|
68
|
+
rescue StandardError => ex
|
69
|
+
log_exception(ex) { "Exception while processing message #{msg.payload.inspect}" }
|
70
|
+
if listener[:reject_on_error]
|
71
|
+
logger.info(logloc) { "Rejecting message which caused exception" }
|
72
|
+
msg.reject
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
63
76
|
|
64
77
|
@stimuli ||= []
|
65
78
|
@stimuli << {
|
66
79
|
name: "amqp_listener_#{listener[:exchange_list].join("_").gsub(/[^A-Za-z0-9_]/, '_').gsub(/__+/, "_")}",
|
67
|
-
method: method(
|
80
|
+
method: method(wrapper_method),
|
68
81
|
stimuli_proc: proc do |worker|
|
69
82
|
consumer = queue(listener).subscribe(manual_ack: true) do |di, prop, payload|
|
70
83
|
if listener[:autoparse]
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'rb-inotify'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
# Class-level inotify support for Brown agents.
|
6
|
+
#
|
7
|
+
# These methods are intended to be applied to a `Brown::Agent` subclass, so you
|
8
|
+
# can use them to define new file watchers in your agent.
|
9
|
+
# You should not attempt to extend your classes directly with this module; the
|
10
|
+
# {Brown::Agent::FileWatch} module should handle that for you automatically.
|
11
|
+
#
|
12
|
+
module Brown::Agent::FileWatch::ClassMethods
|
13
|
+
attr_reader :file_watchers
|
14
|
+
|
15
|
+
# Watch one or more files or directories for modification.
|
16
|
+
#
|
17
|
+
# If a change is detected on any of the paths you list, then the associated
|
18
|
+
# block of code will be executed. If a path is a directory, then the entire
|
19
|
+
# directory hierarchy will be watched recursively for any file or directory
|
20
|
+
# creation, deletion, renaming, ormodification. If the path is a file, it
|
21
|
+
# will be watched for modification. In either case, the path being
|
22
|
+
# specified must already exist.
|
23
|
+
#
|
24
|
+
# @yieldparam event [INotify::Event] the raw `INotify::Event` that
|
25
|
+
# caused the block to be called.
|
26
|
+
#
|
27
|
+
def watch(*paths, &blk)
|
28
|
+
@watchers ||= []
|
29
|
+
@watchers << {
|
30
|
+
paths: paths,
|
31
|
+
callback: blk,
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
require "json"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
# Methods that have to be prepended in order to work properly.
|
6
|
+
#
|
7
|
+
module Brown::Agent::AMQP::Initializer
|
8
|
+
def initialize(*_)
|
9
|
+
begin
|
10
|
+
super
|
11
|
+
rescue ArgumentError => ex
|
12
|
+
if ex.message =~ /wrong number of arguments.*expected 0/
|
13
|
+
super()
|
14
|
+
else
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
initialize_publishers
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
initialize_listeners
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def shutdown
|
29
|
+
amqp_session.close
|
30
|
+
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def amqp_session
|
37
|
+
@amqp_session ||= begin
|
38
|
+
logger.debug(logloc) { "Initializing AMQP session" }
|
39
|
+
Bunny.new(config.amqp_url, recover_from_connection_close: true, logger: config.logger).tap do |session|
|
40
|
+
session.on_blocked { |blocked| logger.warn(logloc) { "AMQP connection has become blocked: #{blocked.reason}" } }
|
41
|
+
session.on_unblocked { logger.info(logloc) { "AMQP connection has unblocked" } }
|
42
|
+
session.start
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize_publishers
|
48
|
+
(self.class.amqp_publishers || []).each do |publisher|
|
49
|
+
logger.debug(logloc) { "Initializing AMQP publisher #{publisher}" }
|
50
|
+
opts = { exchange_name: publisher[:name] }.merge(publisher[:opts])
|
51
|
+
|
52
|
+
amqp_publisher = Brown::Agent::AMQPPublisher.new(amqp_session: amqp_session, **opts)
|
53
|
+
|
54
|
+
define_singleton_method(publisher[:name]) { amqp_publisher }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize_listeners
|
59
|
+
(self.class.amqp_listeners || []).each do |listener|
|
60
|
+
logger.debug(logloc) { "Initializing AMQP listener #{listener}" }
|
61
|
+
worker_method = "amqp_listener_worker_#{SecureRandom.uuid}".to_sym
|
62
|
+
define_singleton_method(worker_method, listener[:callback])
|
63
|
+
|
64
|
+
@stimuli ||= []
|
65
|
+
@stimuli << {
|
66
|
+
name: "amqp_listener_#{listener[:exchange_list].join("_").gsub(/[^A-Za-z0-9_]/, '_').gsub(/__+/, "_")}",
|
67
|
+
method: method(worker_method),
|
68
|
+
stimuli_proc: proc do |worker|
|
69
|
+
consumer = queue(listener).subscribe(manual_ack: true) do |di, prop, payload|
|
70
|
+
if listener[:autoparse]
|
71
|
+
logger.debug(logloc) { "Attempting to autoparse against Content-Type: #{prop.content_type.inspect}" }
|
72
|
+
case prop.content_type
|
73
|
+
when "application/json"
|
74
|
+
logger.debug(logloc) { "Parsing as JSON" }
|
75
|
+
payload = JSON.parse(payload)
|
76
|
+
when "application/x.yaml"
|
77
|
+
logger.debug(logloc) { "Parsing as YAML" }
|
78
|
+
payload = YAML.load(payload)
|
79
|
+
when "application/vnd.brown.object.v1"
|
80
|
+
logger.debug(logloc) { "Parsing as Brown object, allowed classes: #{listener[:allowed_classes]}" }
|
81
|
+
begin
|
82
|
+
payload = YAML.safe_load(payload, listener[:allowed_classes])
|
83
|
+
rescue Psych::DisallowedClass => ex
|
84
|
+
logger.error(logloc) { "message rejected: #{ex.message}" }
|
85
|
+
di.channel.nack(di.delivery_tag, false, false)
|
86
|
+
next
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
worker.call Brown::Agent::AMQPMessage.new(di, prop, payload)
|
92
|
+
end
|
93
|
+
|
94
|
+
while consumer&.channel&.status == :open do
|
95
|
+
logger.debug(logloc) { "stimuli_proc for #{listener[:queue_name]} having a snooze" }
|
96
|
+
sleep
|
97
|
+
end
|
98
|
+
end
|
99
|
+
}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def queue(listener)
|
104
|
+
@queue_cache ||= {}
|
105
|
+
@queue_cache[listener] ||= begin
|
106
|
+
bind_queue(
|
107
|
+
queue_name: listener[:queue_name],
|
108
|
+
exchange_list: listener[:exchange_list].map(&:to_s),
|
109
|
+
concurrency: listener[:concurrency],
|
110
|
+
)
|
111
|
+
rescue StandardError => ex
|
112
|
+
log_exception(ex) { "Unknown error while binding queue #{listener[:queue_name].inspect} to exchange list #{listener[:exchange_list].inspect}" }
|
113
|
+
sleep 5
|
114
|
+
retry
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def bind_queue(queue_name:, exchange_list:, concurrency:)
|
119
|
+
ch = amqp_session.create_channel
|
120
|
+
ch.prefetch(concurrency)
|
121
|
+
|
122
|
+
ch.queue(queue_name, durable: true).tap do |q|
|
123
|
+
exchange_list.each do |exchange_name|
|
124
|
+
if exchange_name != ""
|
125
|
+
begin
|
126
|
+
q.bind(exchange_name)
|
127
|
+
rescue Bunny::NotFound => ex
|
128
|
+
logger.error { "bind failed: #{ex.message}" }
|
129
|
+
sleep 5
|
130
|
+
return bind_queue(
|
131
|
+
queue_name: queue_name,
|
132
|
+
exchange_list: exchange_list,
|
133
|
+
concurrency: concurrency
|
134
|
+
)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.2.
|
4
|
+
version: 2.2.2.27.gbc378e8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Palmer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-04-
|
11
|
+
date: 2020-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
@@ -203,6 +203,8 @@ files:
|
|
203
203
|
- lib/brown/agent/amqp_message_mock.rb
|
204
204
|
- lib/brown/agent/amqp_publisher.rb
|
205
205
|
- lib/brown/agent/class_methods.rb
|
206
|
+
- lib/brown/agent/inotify/class_methods.rb
|
207
|
+
- lib/brown/agent/inotify/initializer.rb
|
206
208
|
- lib/brown/agent/memo.rb
|
207
209
|
- lib/brown/agent/stimulus.rb
|
208
210
|
- lib/brown/agent/stimulus/metrics.rb
|