rsmp 0.48.4 → 0.51.1
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 +4 -4
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +19 -20
- data/README.md +27 -3
- data/Rakefile +3 -1
- data/documentation/cli.md +79 -4
- data/documentation/collecting_message.md +90 -170
- data/documentation/configuration.md +8 -0
- data/documentation/message_distribution.md +17 -3
- data/documentation/tasks.md +65 -117
- data/lib/rsmp/cli.rb +93 -5
- data/lib/rsmp/collect/collector.rb +117 -134
- data/lib/rsmp/collect/distributor.rb +16 -3
- data/lib/rsmp/collect/queue.rb +14 -6
- data/lib/rsmp/collect/receiver.rb +1 -1
- data/lib/rsmp/collect/state_collector.rb +11 -2
- data/lib/rsmp/collection.rb +12 -0
- data/lib/rsmp/completion.rb +49 -0
- data/lib/rsmp/component/component_base.rb +3 -3
- data/lib/rsmp/convert/export/json_schema/index.rb +8 -4
- data/lib/rsmp/convert/import/yaml.rb +12 -8
- data/lib/rsmp/delivery.rb +18 -0
- data/lib/rsmp/event.rb +19 -0
- data/lib/rsmp/event_source.rb +36 -0
- data/lib/rsmp/exchange.rb +10 -0
- data/lib/rsmp/failure.rb +23 -0
- data/lib/rsmp/helpers/error.rb +14 -23
- data/lib/rsmp/message.rb +10 -9
- data/lib/rsmp/node/node.rb +3 -21
- data/lib/rsmp/node/site/connections.rb +4 -9
- data/lib/rsmp/node/site/site.rb +33 -17
- data/lib/rsmp/node/supervisor/modules/configuration.rb +24 -11
- data/lib/rsmp/node/supervisor/modules/connection.rb +19 -5
- data/lib/rsmp/node/supervisor/modules/sites.rb +16 -5
- data/lib/rsmp/node/supervisor/supervisor.rb +13 -14
- data/lib/rsmp/node/task.rb +59 -53
- data/lib/rsmp/proxy/modules/acknowledgements.rb +22 -9
- data/lib/rsmp/proxy/modules/lifecycle.rb +134 -0
- data/lib/rsmp/proxy/modules/receive.rb +109 -66
- data/lib/rsmp/proxy/modules/send.rb +109 -51
- data/lib/rsmp/proxy/modules/state.rb +15 -5
- data/lib/rsmp/proxy/modules/tasks.rb +96 -70
- data/lib/rsmp/proxy/modules/versions.rb +69 -19
- data/lib/rsmp/proxy/modules/watchdogs.rb +15 -2
- data/lib/rsmp/proxy/proxy.rb +7 -69
- data/lib/rsmp/proxy/site/connection.rb +27 -22
- data/lib/rsmp/proxy/site/modules/aggregated_status.rb +18 -8
- data/lib/rsmp/proxy/site/modules/alarms.rb +45 -38
- data/lib/rsmp/proxy/site/modules/commands.rb +18 -8
- data/lib/rsmp/proxy/site/modules/status.rb +44 -19
- data/lib/rsmp/proxy/site/site_proxy.rb +12 -10
- data/lib/rsmp/proxy/site/sxl_selection.rb +7 -2
- data/lib/rsmp/proxy/supervisor/modules/aggregated_status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/alarms.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/commands.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/message_buffer.rb +8 -9
- data/lib/rsmp/proxy/supervisor/modules/status.rb +1 -1
- data/lib/rsmp/proxy/supervisor/modules/status_updates.rb +1 -1
- data/lib/rsmp/proxy/supervisor/supervisor_proxy.rb +36 -31
- data/lib/rsmp/result.rb +95 -0
- data/lib/rsmp/schema/validation.rb +5 -3
- data/lib/rsmp/schema/version.rb +27 -0
- data/lib/rsmp/schema.rb +1 -0
- data/lib/rsmp/sxl/interface.rb +8 -1
- data/lib/rsmp/sxl/processing/catalogue.rb +61 -0
- data/lib/rsmp/sxl/processing/conflict_checker.rb +30 -0
- data/lib/rsmp/sxl/processing/dependency_graph.rb +37 -0
- data/lib/rsmp/sxl/processing/document.rb +158 -0
- data/lib/rsmp/sxl/processing/error.rb +8 -0
- data/lib/rsmp/sxl/processing/manifest.rb +144 -0
- data/lib/rsmp/sxl/processing/natural_sort.rb +22 -0
- data/lib/rsmp/sxl/processing/resolver.rb +94 -0
- data/lib/rsmp/sxl/processing/version_requirement.rb +86 -0
- data/lib/rsmp/sxl/processing.rb +18 -0
- data/lib/rsmp/sxl/supervisor_interface.rb +8 -1
- data/lib/rsmp/termination.rb +10 -0
- data/lib/rsmp/tlc/modules/plans.rb +0 -2
- data/lib/rsmp/tlc/modules/system.rb +2 -2
- data/lib/rsmp/tlc/proxy/control.rb +50 -16
- data/lib/rsmp/tlc/proxy/detectors.rb +15 -4
- data/lib/rsmp/tlc/proxy/io.rb +46 -31
- data/lib/rsmp/tlc/proxy/plans.rb +36 -19
- data/lib/rsmp/tlc/proxy/plans_bang.rb +44 -0
- data/lib/rsmp/tlc/proxy/status.rb +47 -23
- data/lib/rsmp/tlc/proxy/system.rb +16 -4
- data/lib/rsmp/tlc/supervisor_interface.rb +7 -1
- data/lib/rsmp/tlc/traffic_controller_site.rb +6 -9
- data/lib/rsmp/validation.rb +22 -0
- data/lib/rsmp/version.rb +1 -1
- data/lib/rsmp.rb +19 -4
- data/rsmp.gemspec +1 -1
- metadata +26 -4
- data/lib/rsmp/collect/collector/status.rb +0 -34
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
# Reactor-confined, ordered delivery of background lifecycle events.
|
|
3
|
+
module EventSource
|
|
4
|
+
attr_reader :event_receivers
|
|
5
|
+
|
|
6
|
+
def initialize_event_source
|
|
7
|
+
@event_receivers = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add_event_receiver(receiver)
|
|
11
|
+
raise ArgumentError, 'event receiver must respond to receive_event' unless receiver.respond_to?(:receive_event)
|
|
12
|
+
|
|
13
|
+
@event_receivers << receiver unless @event_receivers.include?(receiver)
|
|
14
|
+
receiver
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def remove_event_receiver(receiver)
|
|
18
|
+
@event_receivers.delete(receiver)
|
|
19
|
+
receiver
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def publish_event(event)
|
|
23
|
+
raise ArgumentError, 'event must be an RSMP::Event' unless event.is_a?(Event)
|
|
24
|
+
|
|
25
|
+
@event_receivers.dup.each do |receiver|
|
|
26
|
+
receiver.receive_event(event)
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
remove_event_receiver(receiver)
|
|
29
|
+
raise unless receiver.respond_to?(:crash)
|
|
30
|
+
|
|
31
|
+
receiver.crash(e)
|
|
32
|
+
end
|
|
33
|
+
event
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require 'forwardable'
|
|
2
|
+
|
|
3
|
+
module RSMP
|
|
4
|
+
# A sent request and the immutable collection produced in response.
|
|
5
|
+
Exchange = Data.define(:request, :collection) do
|
|
6
|
+
extend Forwardable
|
|
7
|
+
|
|
8
|
+
def_delegators :collection, :messages, :reached, :matcher_status
|
|
9
|
+
end
|
|
10
|
+
end
|
data/lib/rsmp/failure.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RSMP
|
|
2
|
+
# An expected operational failure. Failures are values: they describe why an
|
|
3
|
+
# operation could not complete without changing Async task control flow.
|
|
4
|
+
Failure = Data.define(:code, :message, :source, :context, :cause) do
|
|
5
|
+
def initialize(code:, message:, source:, context: {}, cause: nil)
|
|
6
|
+
raise ArgumentError, 'failure code must be a Symbol' unless code.is_a?(Symbol)
|
|
7
|
+
raise ArgumentError, 'failure source must be a Symbol' unless source.is_a?(Symbol)
|
|
8
|
+
raise ArgumentError, 'failure context must be a Hash' unless context.is_a?(Hash)
|
|
9
|
+
|
|
10
|
+
super(
|
|
11
|
+
code: code,
|
|
12
|
+
message: message.to_s.freeze,
|
|
13
|
+
source: source,
|
|
14
|
+
context: context.dup.freeze,
|
|
15
|
+
cause: cause
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_s
|
|
20
|
+
message.empty? ? code.to_s : message
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/rsmp/helpers/error.rb
CHANGED
|
@@ -8,24 +8,24 @@ module RSMP
|
|
|
8
8
|
class MalformedMessage < Error
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
# Semantic failures which may be raised while processing a schema-valid peer
|
|
12
|
+
# message. The receive boundary converts only this explicit family to a peer
|
|
13
|
+
# Failure; unrelated exceptions retain their original stack trace.
|
|
14
|
+
class PeerMessageError < Error
|
|
15
|
+
end
|
|
16
|
+
|
|
11
17
|
# Raised when schema validation fails.
|
|
12
18
|
class SchemaError < Error
|
|
13
19
|
attr_accessor :schemas
|
|
14
20
|
end
|
|
15
21
|
|
|
16
|
-
class InvalidMessage <
|
|
22
|
+
class InvalidMessage < PeerMessageError
|
|
17
23
|
end
|
|
18
24
|
|
|
19
25
|
class UnknownMessage < Error
|
|
20
26
|
end
|
|
21
27
|
|
|
22
|
-
class
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
class MissingWatchdog < Error
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
class MessageRejected < Error
|
|
28
|
+
class MessageRejected < PeerMessageError
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
class MissingAttribute < InvalidMessage
|
|
@@ -37,36 +37,27 @@ module RSMP
|
|
|
37
37
|
class HandshakeError < FatalError
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
class NotReady < Error
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
class TimeoutError < Error
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
class DisconnectError < Error
|
|
47
|
-
end
|
|
48
|
-
|
|
49
40
|
class ConnectionError < Error
|
|
50
41
|
end
|
|
51
42
|
|
|
52
|
-
class UnknownComponent <
|
|
43
|
+
class UnknownComponent < PeerMessageError
|
|
53
44
|
end
|
|
54
45
|
|
|
55
|
-
class UnknownCommand <
|
|
46
|
+
class UnknownCommand < PeerMessageError
|
|
56
47
|
end
|
|
57
48
|
|
|
58
|
-
class UnknownStatus <
|
|
49
|
+
class UnknownStatus < PeerMessageError
|
|
59
50
|
end
|
|
60
51
|
|
|
61
52
|
class ConfigurationError < Error
|
|
62
53
|
end
|
|
63
54
|
|
|
64
|
-
class RepeatedAlarmError <
|
|
55
|
+
class RepeatedAlarmError < PeerMessageError
|
|
65
56
|
end
|
|
66
57
|
|
|
67
|
-
class RepeatedStatusError <
|
|
58
|
+
class RepeatedStatusError < PeerMessageError
|
|
68
59
|
end
|
|
69
60
|
|
|
70
|
-
class TimestampError <
|
|
61
|
+
class TimestampError < PeerMessageError
|
|
71
62
|
end
|
|
72
63
|
end
|
data/lib/rsmp/message.rb
CHANGED
|
@@ -164,11 +164,14 @@ module RSMP
|
|
|
164
164
|
end
|
|
165
165
|
|
|
166
166
|
def validate(schemas)
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
RSMP::Schema.validate attributes, schemas
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def validate!(schemas)
|
|
171
|
+
validation = validate(schemas)
|
|
172
|
+
return self if validation.valid?
|
|
169
173
|
|
|
170
|
-
|
|
171
|
-
err = SchemaError.new error_string.to_s
|
|
174
|
+
err = SchemaError.new validation.message
|
|
172
175
|
err.schemas = schemas
|
|
173
176
|
raise err
|
|
174
177
|
end
|
|
@@ -199,13 +202,11 @@ module RSMP
|
|
|
199
202
|
|
|
200
203
|
# Represents a malformed message with invalid attributes.
|
|
201
204
|
class Malformed < Message
|
|
202
|
-
# rubocop:disable Lint/MissingSuper
|
|
203
205
|
def initialize(attributes = {})
|
|
204
|
-
|
|
205
|
-
@attributes
|
|
206
|
+
super({})
|
|
207
|
+
@attributes.clear
|
|
206
208
|
@invalid_attributes = attributes
|
|
207
209
|
end
|
|
208
|
-
# rubocop:enable Lint/MissingSuper
|
|
209
210
|
end
|
|
210
211
|
|
|
211
212
|
# Version message, lists supported versions and SXL information.
|
|
@@ -391,7 +392,7 @@ module RSMP
|
|
|
391
392
|
end
|
|
392
393
|
|
|
393
394
|
def original=(message)
|
|
394
|
-
raise
|
|
395
|
+
raise ArgumentError, 'original message is required' unless message
|
|
395
396
|
|
|
396
397
|
@original = message
|
|
397
398
|
end
|
data/lib/rsmp/node/node.rb
CHANGED
|
@@ -3,16 +3,16 @@ module RSMP
|
|
|
3
3
|
class Node
|
|
4
4
|
include Logging
|
|
5
5
|
include Task
|
|
6
|
+
include EventSource
|
|
6
7
|
|
|
7
|
-
attr_reader :archive, :logger, :task, :deferred, :
|
|
8
|
+
attr_reader :archive, :logger, :task, :deferred, :clock, :collector
|
|
8
9
|
|
|
9
10
|
def initialize(options = {})
|
|
10
11
|
initialize_logging options
|
|
11
12
|
initialize_task
|
|
13
|
+
initialize_event_source
|
|
12
14
|
@deferred = []
|
|
13
15
|
@clock = Clock.new
|
|
14
|
-
@error_queue = Async::Queue.new
|
|
15
|
-
@ignore_errors = []
|
|
16
16
|
@collect = options[:collect]
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -31,24 +31,6 @@ module RSMP
|
|
|
31
31
|
super
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def ignore_errors(classes)
|
|
35
|
-
was = @ignore_errors
|
|
36
|
-
@ignore_errors = [classes].flatten
|
|
37
|
-
yield
|
|
38
|
-
ensure
|
|
39
|
-
@ignore_errors = was
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def distribute_error(error, options = {})
|
|
43
|
-
return if @ignore_errors.find { |klass| error.is_a? klass }
|
|
44
|
-
|
|
45
|
-
if options[:level] == :internal
|
|
46
|
-
log ["#{error} in task: #{Async::Task.current}", error.backtrace].flatten.join("\n"),
|
|
47
|
-
level: :error
|
|
48
|
-
end
|
|
49
|
-
@error_queue.enqueue error
|
|
50
|
-
end
|
|
51
|
-
|
|
52
34
|
def defer(key, item = nil)
|
|
53
35
|
@deferred << [key, item]
|
|
54
36
|
end
|
|
@@ -9,30 +9,25 @@ module RSMP
|
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def listen_for_supervisors
|
|
12
|
+
def listen_for_supervisors(parent:)
|
|
13
13
|
ip = @site_settings['ip'] || '0.0.0.0'
|
|
14
14
|
port = @site_settings['port']
|
|
15
15
|
log "Starting #{site_type_name} listener on #{ip}:#{port}", level: :info, timestamp: @clock.now
|
|
16
16
|
@endpoint = IO::Endpoint.tcp(ip, port)
|
|
17
|
-
@accept_task =
|
|
17
|
+
@accept_task = parent.async do |task|
|
|
18
18
|
task.annotate 'site accept loop'
|
|
19
19
|
accept_supervisor_connections
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
@ready_condition.signal
|
|
23
|
-
@accept_task
|
|
23
|
+
@accept_task
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def accept_supervisor_connections
|
|
27
27
|
@endpoint.accept do |socket|
|
|
28
28
|
accept_supervisor_connection socket
|
|
29
|
-
rescue StandardError => e
|
|
30
|
-
distribute_error e, level: :internal
|
|
31
29
|
end
|
|
32
|
-
|
|
33
|
-
# Expected during shutdown - no action needed
|
|
34
|
-
rescue StandardError => e
|
|
35
|
-
distribute_error e, level: :internal
|
|
30
|
+
Async::Task.current.wait_all
|
|
36
31
|
end
|
|
37
32
|
|
|
38
33
|
def accept_supervisor_connection(socket)
|
data/lib/rsmp/node/site/site.rb
CHANGED
|
@@ -81,8 +81,9 @@ module RSMP
|
|
|
81
81
|
def check_core_versions
|
|
82
82
|
version = @site_settings['core_version']
|
|
83
83
|
return unless version
|
|
84
|
+
return if %w[all latest].include? version
|
|
84
85
|
|
|
85
|
-
return if RSMP::Schema.
|
|
86
|
+
return if RSMP::Schema.normalize_core_version version
|
|
86
87
|
|
|
87
88
|
error_str = "Unknown core version: #{version}"
|
|
88
89
|
raise RSMP::ConfigurationError, error_str
|
|
@@ -106,13 +107,24 @@ module RSMP
|
|
|
106
107
|
|
|
107
108
|
def run
|
|
108
109
|
log_site_starting
|
|
109
|
-
|
|
110
|
+
barrier = Async::Barrier.new(parent: @task)
|
|
111
|
+
termination_task = barrier.async { wait_for_termination }
|
|
112
|
+
start_auxiliary_tasks(barrier)
|
|
110
113
|
if server_role?
|
|
111
|
-
listen_for_supervisors
|
|
114
|
+
listen_for_supervisors(parent: barrier)
|
|
112
115
|
else
|
|
113
|
-
@proxies.each(
|
|
114
|
-
@proxies.each(&:wait)
|
|
116
|
+
@proxies.each { |proxy| proxy.start(parent: barrier) }
|
|
115
117
|
end
|
|
118
|
+
barrier.wait do |finished|
|
|
119
|
+
result = finished.wait
|
|
120
|
+
break result if finished.equal?(termination_task)
|
|
121
|
+
end
|
|
122
|
+
ensure
|
|
123
|
+
barrier.cancel if barrier && !barrier.empty?
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def start_auxiliary_tasks(parent)
|
|
127
|
+
start_status_timer(parent: parent)
|
|
116
128
|
end
|
|
117
129
|
|
|
118
130
|
def aggregated_status_changed(component, _options = {})
|
|
@@ -135,16 +147,16 @@ module RSMP
|
|
|
135
147
|
|
|
136
148
|
def send_alarm(alarm)
|
|
137
149
|
@proxies.each do |proxy|
|
|
138
|
-
proxy.
|
|
150
|
+
proxy.send_generated_message alarm if proxy.receive_alarms?
|
|
139
151
|
end
|
|
140
152
|
end
|
|
141
153
|
|
|
142
|
-
def start_status_timer
|
|
154
|
+
def start_status_timer(parent:)
|
|
143
155
|
return if @status_timer
|
|
144
156
|
|
|
145
157
|
interval = @site_settings['intervals']['timer'] || 1
|
|
146
158
|
log "Starting site status timer with interval #{interval} seconds", level: :debug
|
|
147
|
-
@status_timer =
|
|
159
|
+
@status_timer = parent.async do |task|
|
|
148
160
|
task.annotate 'site status timer'
|
|
149
161
|
run_status_timer task, interval
|
|
150
162
|
end
|
|
@@ -155,11 +167,8 @@ module RSMP
|
|
|
155
167
|
loop do
|
|
156
168
|
now = Clock.now
|
|
157
169
|
tick_status_subscriptions now
|
|
158
|
-
rescue StandardError => e
|
|
159
|
-
distribute_error e, level: :internal
|
|
160
|
-
ensure
|
|
161
170
|
next_time += interval
|
|
162
|
-
duration = next_time - Time.now.to_f
|
|
171
|
+
duration = [next_time - Time.now.to_f, 0].max
|
|
163
172
|
task.sleep duration
|
|
164
173
|
end
|
|
165
174
|
end
|
|
@@ -169,14 +178,14 @@ module RSMP
|
|
|
169
178
|
end
|
|
170
179
|
|
|
171
180
|
def stop_status_timer
|
|
172
|
-
@status_timer&.
|
|
181
|
+
@status_timer&.cancel if @status_timer&.running?
|
|
173
182
|
ensure
|
|
174
183
|
@status_timer = nil
|
|
175
184
|
end
|
|
176
185
|
|
|
177
186
|
def stop_subtasks
|
|
178
187
|
stop_status_timer
|
|
179
|
-
@accept_task&.
|
|
188
|
+
@accept_task&.cancel if @accept_task&.running?
|
|
180
189
|
@accept_task = nil
|
|
181
190
|
@endpoint = nil
|
|
182
191
|
super
|
|
@@ -190,11 +199,18 @@ module RSMP
|
|
|
190
199
|
|
|
191
200
|
def wait_for_supervisor(ip, timeout:)
|
|
192
201
|
supervisor = find_supervisor ip
|
|
193
|
-
return supervisor if supervisor
|
|
202
|
+
return Result.success(supervisor) if supervisor
|
|
194
203
|
|
|
195
204
|
wait_for_condition(@proxies_condition, timeout: timeout) { find_supervisor ip }
|
|
196
|
-
|
|
197
|
-
|
|
205
|
+
.map { |proxy| proxy }
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def wait_for_supervisor!(ip, timeout:)
|
|
209
|
+
result = wait_for_supervisor(ip, timeout: timeout)
|
|
210
|
+
return result.value! if result.success?
|
|
211
|
+
|
|
212
|
+
failure = result.failure.with(message: "Supervisor '#{ip}' did not connect within #{timeout}s")
|
|
213
|
+
Result.failure(failure: failure).value!
|
|
198
214
|
end
|
|
199
215
|
|
|
200
216
|
def find_supervisor(ip)
|
|
@@ -35,23 +35,36 @@ module RSMP
|
|
|
35
35
|
sites.each do |site_id, settings|
|
|
36
36
|
raise RSMP::ConfigurationError, "Configuration for site '#{site_id}' is empty" unless settings
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
check_configured_sxls site_id, settings['sxls']
|
|
39
|
+
check_configured_core_version site_id, settings['core_version']
|
|
40
|
+
rescue RSMP::Schema::UnknownSchemaError => e
|
|
41
|
+
raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': #{e}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if name.to_s == 'core'
|
|
44
|
-
raise RSMP::ConfigurationError,
|
|
45
|
-
"Configuration error for site '#{site_id}': SXL name cannot be core"
|
|
46
|
-
end
|
|
45
|
+
def check_configured_sxls(site_id, sxls)
|
|
46
|
+
raise RSMP::ConfigurationError, "Configuration error for site '#{site_id}': No SXLs specified" unless sxls
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
sxls.each do |sxl|
|
|
49
|
+
name = sxl['name']
|
|
50
|
+
if name.to_s == 'core'
|
|
51
|
+
raise RSMP::ConfigurationError,
|
|
52
|
+
"Configuration error for site '#{site_id}': SXL name cannot be core"
|
|
49
53
|
end
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
|
|
55
|
+
RSMP::Schema.find_schema! name, sxl['version'], lenient: true
|
|
52
56
|
end
|
|
53
57
|
end
|
|
54
58
|
|
|
59
|
+
def check_configured_core_version(site_id, core_version)
|
|
60
|
+
return unless core_version
|
|
61
|
+
return if %w[all latest].include? core_version
|
|
62
|
+
return if RSMP::Schema.normalize_core_version core_version
|
|
63
|
+
|
|
64
|
+
raise RSMP::ConfigurationError,
|
|
65
|
+
"Configuration error for site '#{site_id}': Unknown core version: #{core_version}"
|
|
66
|
+
end
|
|
67
|
+
|
|
55
68
|
def site_id_to_site_setting(site_id)
|
|
56
69
|
base = @supervisor_settings['default'] || {}
|
|
57
70
|
|
|
@@ -15,15 +15,29 @@ module RSMP
|
|
|
15
15
|
reject_connection socket, info
|
|
16
16
|
end
|
|
17
17
|
rescue ConnectionError, HandshakeError => e
|
|
18
|
-
|
|
19
|
-
distribute_error e
|
|
20
|
-
rescue StandardError => e
|
|
21
|
-
log "Connection: #{e}", exception: e, level: :error
|
|
22
|
-
distribute_error e, level: :internal
|
|
18
|
+
report_connection_rejection(e, remote_ip, remote_port)
|
|
23
19
|
ensure
|
|
24
20
|
close socket, info
|
|
25
21
|
end
|
|
26
22
|
|
|
23
|
+
def report_connection_rejection(error, remote_ip, remote_port)
|
|
24
|
+
log "Rejected connection from #{remote_ip}:#{remote_port}, #{error}", level: :warning
|
|
25
|
+
failure = Failure.new(
|
|
26
|
+
code: error.is_a?(HandshakeError) ? :handshake_failed : :connection_rejected,
|
|
27
|
+
message: error.message,
|
|
28
|
+
source: :peer,
|
|
29
|
+
context: { ip: remote_ip, port: remote_port }
|
|
30
|
+
)
|
|
31
|
+
publish_event(
|
|
32
|
+
Event.new(
|
|
33
|
+
type: :connection_rejected,
|
|
34
|
+
source: self,
|
|
35
|
+
failure: failure,
|
|
36
|
+
context: { ip: remote_ip, port: remote_port }
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
27
41
|
def accept?(_socket, _info)
|
|
28
42
|
true
|
|
29
43
|
end
|
|
@@ -23,24 +23,35 @@ module RSMP
|
|
|
23
23
|
|
|
24
24
|
def wait_for_site(site_id, timeout:)
|
|
25
25
|
site = find_site site_id
|
|
26
|
-
return site if site
|
|
26
|
+
return Result.success(site) if site
|
|
27
27
|
|
|
28
28
|
wait_for_condition(@site_id_condition, timeout: timeout) do
|
|
29
29
|
find_site site_id
|
|
30
30
|
end
|
|
31
|
-
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def wait_for_site!(site_id, timeout:)
|
|
34
|
+
result = wait_for_site(site_id, timeout: timeout)
|
|
35
|
+
return result.value! if result.success?
|
|
36
|
+
|
|
32
37
|
str = if site_id == :any
|
|
33
38
|
'No site connected'
|
|
34
39
|
else
|
|
35
40
|
"Site '#{site_id}' did not connect"
|
|
36
41
|
end
|
|
37
|
-
|
|
42
|
+
Result.failure(failure: result.failure.with(message: "#{str} within #{timeout}s")).value!
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
def wait_for_site_disconnect(site_id, timeout:)
|
|
41
46
|
wait_for_condition(@site_id_condition, timeout: timeout) { true unless find_site site_id }
|
|
42
|
-
|
|
43
|
-
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def wait_for_site_disconnect!(site_id, timeout:)
|
|
50
|
+
result = wait_for_site_disconnect(site_id, timeout: timeout)
|
|
51
|
+
return result.value! if result.success?
|
|
52
|
+
|
|
53
|
+
failure = result.failure.with(message: "Site '#{site_id}' did not disconnect within #{timeout}s")
|
|
54
|
+
Result.failure(failure: failure).value!
|
|
44
55
|
end
|
|
45
56
|
|
|
46
57
|
def check_site_id(site_id)
|
|
@@ -38,23 +38,19 @@ module RSMP
|
|
|
38
38
|
timestamp: @clock.now
|
|
39
39
|
|
|
40
40
|
@endpoint = IO::Endpoint.tcp('0.0.0.0', @supervisor_settings['port'])
|
|
41
|
-
|
|
41
|
+
barrier = Async::Barrier.new(parent: @task)
|
|
42
|
+
@accept_task = barrier.async do |task|
|
|
42
43
|
task.annotate 'supervisor accept loop'
|
|
43
|
-
@endpoint.accept do |socket|
|
|
44
|
+
@endpoint.accept do |socket|
|
|
44
45
|
handle_connection(socket)
|
|
45
|
-
rescue StandardError => e
|
|
46
|
-
distribute_error e, level: :internal
|
|
47
46
|
end
|
|
48
|
-
|
|
49
|
-
# Expected during shutdown - no action needed
|
|
50
|
-
rescue StandardError => e
|
|
51
|
-
distribute_error e, level: :internal
|
|
47
|
+
task.wait_all
|
|
52
48
|
end
|
|
53
49
|
|
|
54
50
|
@ready_condition.signal
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
barrier.wait
|
|
52
|
+
ensure
|
|
53
|
+
barrier.cancel if barrier && !barrier.empty?
|
|
58
54
|
end
|
|
59
55
|
|
|
60
56
|
def connect_to_sites
|
|
@@ -63,8 +59,11 @@ module RSMP
|
|
|
63
59
|
timestamp: @clock.now
|
|
64
60
|
build_outbound_proxies
|
|
65
61
|
@ready_condition.signal
|
|
66
|
-
|
|
67
|
-
@proxies.each(
|
|
62
|
+
barrier = Async::Barrier.new(parent: @task)
|
|
63
|
+
@proxies.each { |proxy| proxy.start(parent: barrier) }
|
|
64
|
+
barrier.wait
|
|
65
|
+
ensure
|
|
66
|
+
barrier.cancel if barrier && !barrier.empty?
|
|
68
67
|
end
|
|
69
68
|
|
|
70
69
|
def build_outbound_proxies
|
|
@@ -93,7 +92,7 @@ module RSMP
|
|
|
93
92
|
def stop
|
|
94
93
|
log "Stopping supervisor #{@supervisor_settings['site_id']}", level: :info
|
|
95
94
|
|
|
96
|
-
@accept_task&.
|
|
95
|
+
@accept_task&.cancel if @accept_task&.running?
|
|
97
96
|
@accept_task = nil
|
|
98
97
|
|
|
99
98
|
@endpoint = nil
|