rsmp 0.48.4 → 0.51.0
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 +6 -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/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 -3
- 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 +9 -7
- data/lib/rsmp/node/node.rb +3 -21
- data/lib/rsmp/node/site/connections.rb +4 -9
- data/lib/rsmp/node/site/site.rb +31 -16
- 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 +8 -8
- data/lib/rsmp/proxy/modules/watchdogs.rb +15 -2
- data/lib/rsmp/proxy/proxy.rb +4 -66
- 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/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 +34 -30
- data/lib/rsmp/result.rb +95 -0
- data/lib/rsmp/schema/validation.rb +5 -3
- 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/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 +25 -4
- data/lib/rsmp/collect/collector/status.rb +0 -34
data/documentation/tasks.md
CHANGED
|
@@ -1,148 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Async task ownership
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
RSMP uses Ruby's fiber scheduler and the Async gem for connections, readers,
|
|
4
|
+
timers, and waits. A site, supervisor, or proxy is not itself an Async task. It
|
|
5
|
+
owns a main task exposed through `task`.
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
## Starting and waiting
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- Wait for messages, connections or states
|
|
11
|
-
|
|
12
|
-
Classes don't inherit from Async::Task. Instead they include task as instance variables. This means that the hierachy of objects and tasks can be different.
|
|
13
|
-
|
|
14
|
-
## Task module
|
|
15
|
-
The `Task` module is used by the `Node` and `Proxy` classes and defines a life cycle for handling async tasks.
|
|
16
|
-
|
|
17
|
-
A single main tasks is kept in `@task`. If subclasses need subtask, they can start them as needed, inside the main task.
|
|
18
|
-
|
|
19
|
-
You first call `start`. If `@task` already exists, it will return immedatiately.
|
|
20
|
-
Otherwise an async task is created and stored in `@task`, and `run` is called inside this task, to handle any long-running jobs, like listening for incoming messages. By default `run` calls `start_subtasks`, but should be overriden to do actual work as well.
|
|
21
|
-
The call to `start` returns immediately, with the async task running concurently.
|
|
22
|
-
|
|
23
|
-
If you want to stop the task, call `stop`. It will fist call `stop_subtasks` and then `stop_task` which calls Async#stop on `@task` and sets `@task` to `nil`.
|
|
24
|
-
|
|
25
|
-
## Stopping tasks
|
|
26
|
-
If a task stops itself, code after the call to `stop` will not be run, unless you use an ensure block:
|
|
9
|
+
`start` must run below an active Async parent. It returns the created
|
|
10
|
+
`Async::Task`; it never creates a hidden root reactor.
|
|
27
11
|
|
|
28
12
|
```ruby
|
|
29
|
-
require 'async'
|
|
30
|
-
|
|
31
13
|
Async do |task|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
end
|
|
14
|
+
site = RSMP::Site.new(site_settings: settings)
|
|
15
|
+
site.start(parent: task)
|
|
35
16
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
ensure
|
|
39
|
-
puts "I just stopped" # this will be reached
|
|
17
|
+
# Other work can run here.
|
|
18
|
+
site.stop
|
|
40
19
|
end
|
|
41
20
|
```
|
|
42
|
-
This can happen if either the task stops itself directly, or if it stops task higher up in the task hierarchy.
|
|
43
|
-
|
|
44
|
-
For example, when a timer task finds that an acknowledgement was not received in time, and then closes the connection by calling stop() on the Proxy, the proxy which will then in turn stop the timer task. So any code that should be run after that must be inside an ensure block.
|
|
45
|
-
|
|
46
|
-
## Proxies
|
|
47
|
-
A site connects to one of more supervisors. A proxy is created and run to handle each connection.
|
|
48
|
-
|
|
49
|
-
A site can connect to one or more supervisor. It creates a proxy for each and runs them.
|
|
50
|
-
|
|
51
|
-
When the proxy is run, it creates an async task to read incoming messages. This reader task is stored in `@reader`and will be a sub task of the site/supervisor main task.
|
|
52
|
-
|
|
53
|
-
A proxy also creates a timer task, stored in `@timer`. This task is used to check watchdog and acknowledgement timeouts.
|
|
54
|
-
|
|
55
|
-
Proxies build on the Task module functionality by handling RSMP communication via a TCP socket.
|
|
56
|
-
|
|
57
|
-
The TCP socket can be open or closed. The RSMP communication first goes through a handshake sequence before being ready. The `status` attribute keeps track of this, and can be either `:disconnected`, `:connected` or `:ready`
|
|
58
|
-
|
|
59
|
-
Proxies provide `connect` and `close` for starting and stopping commununication.
|
|
60
21
|
|
|
61
|
-
|
|
22
|
+
Inside an Async task, the current task is the default parent, so `site.start` is
|
|
23
|
+
equivalent. Calling `start` again while the task is running returns the existing
|
|
24
|
+
task.
|
|
62
25
|
|
|
63
|
-
|
|
26
|
+
`wait` uses `Async::Task#wait`. If the task ends because of an unexpected
|
|
27
|
+
exception, `wait` raises that same exception with its original backtrace.
|
|
28
|
+
Cancellation is handled by Async and is not translated into an operational
|
|
29
|
+
failure.
|
|
64
30
|
|
|
65
|
-
|
|
31
|
+
## Structured task trees
|
|
66
32
|
|
|
67
|
-
|
|
68
|
-
A Site has one or more SupervisorProxies, which represent connections to the remote supervisor.
|
|
33
|
+
Every long-lived child has an explicit owner:
|
|
69
34
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
When you start an Async task, the parent will be the currently running task, unless you specify a different parent task.
|
|
76
|
-
|
|
77
|
-
The Async task hierarchy is similar to the object hierachy, with the difference that proxies have reader and timer tasks. And IO::Endpoint which is used to handle TCP connections concurently, will create some intermediate tasks:
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
Supervisor @task
|
|
81
|
-
accepting connections # this task is created by IO::Endpoint
|
|
82
|
-
incoming connection 1 # this task is created by IO::Endpoint
|
|
83
|
-
SiteProxy @task
|
|
84
|
-
SiteProxy @reader
|
|
85
|
-
SiteProxy @timer
|
|
86
|
-
incoming connection 2 # this task is created by IO::Endpoint
|
|
87
|
-
SiteProxy @task
|
|
88
|
-
SiteProxy @reader
|
|
89
|
-
SiteProxy @timer
|
|
35
|
+
```text
|
|
36
|
+
Site or Supervisor main task
|
|
37
|
+
├── listener or connection proxy tasks
|
|
38
|
+
├── site status timer
|
|
39
|
+
└── explicit termination waiter
|
|
90
40
|
|
|
41
|
+
Proxy main task
|
|
42
|
+
├── connection reader
|
|
43
|
+
└── connection timer
|
|
91
44
|
```
|
|
92
45
|
|
|
46
|
+
Sibling tasks are registered in an `Async::Barrier`. Their owner observes task
|
|
47
|
+
completion in completion order. A reader's expected EOF or transport failure is
|
|
48
|
+
returned as an `RSMP::Result`, closes that connection session, and can lead to a
|
|
49
|
+
reconnect. An exception from message processing or a timer is not caught as a
|
|
50
|
+
connection error: it propagates through the barrier to the proxy and then the
|
|
51
|
+
node owner.
|
|
93
52
|
|
|
94
|
-
|
|
53
|
+
Session IDs prevent a late task from an old connection from closing or
|
|
54
|
+
publishing termination for a newer connection. Closing is idempotent and
|
|
55
|
+
cancels the remaining reader/timer sibling.
|
|
95
56
|
|
|
96
|
-
|
|
97
|
-
#<Async::Reactor:0xb90 1 children (running)>
|
|
98
|
-
#<Async::Task:0xba4 cli (running)>
|
|
99
|
-
#<Async::Task:0xbcc RSMP::TLC::TrafficControllerSite main task (running)>
|
|
100
|
-
#<Async::Task:0xbe0 RSMP::SupervisorProxy main task (running)>
|
|
101
|
-
#<Async::Task:0xbf4 tlc timer (running)>
|
|
102
|
-
```
|
|
57
|
+
## Finite waits
|
|
103
58
|
|
|
104
|
-
|
|
59
|
+
Finite waits return `RSMP::Result`:
|
|
105
60
|
|
|
61
|
+
```ruby
|
|
62
|
+
result = supervisor.wait_for_site('RN+SI0001', timeout: 5)
|
|
63
|
+
result = proxy.wait_for_state(:ready, timeout: 5)
|
|
106
64
|
```
|
|
107
|
-
#<Async::Reactor:0xadc 1 children (running)>
|
|
108
|
-
#<Async::Task:0xaf0 cli (running)>
|
|
109
|
-
#<Async::Task:0xb18 RSMP::TLC::TrafficControllerSite main task (running)>
|
|
110
|
-
#<Async::Task:0xb2c RSMP::SupervisorProxy main task (running)>
|
|
111
|
-
#<Async::Task:0xb40 reader (running)>
|
|
112
|
-
#<Async::Task:0xb54 timer (running)>
|
|
113
|
-
#<Async::Task:0xb68 tlc timer (running)>
|
|
114
|
-
```
|
|
115
|
-
|
|
116
65
|
|
|
66
|
+
A normal timeout is `Result::Failure` with code `:timeout`. Use
|
|
67
|
+
`wait_for_site!`, `wait_for_state!`, or another bang variant to raise
|
|
68
|
+
`RSMP::OperationError` explicitly.
|
|
117
69
|
|
|
118
|
-
|
|
70
|
+
## Events from long-running services
|
|
119
71
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
#<Async::Task:0x99c accepting connections #<Addrinfo: 0.0.0.0:14111 TCP> [fd=12] (running)>
|
|
125
|
-
```
|
|
72
|
+
Events report occurrences that are not the result of a single finite call, such
|
|
73
|
+
as a failed connection attempt, invalid peer message, missing acknowledgement,
|
|
74
|
+
missing watchdog, or connection end. Register an explicit subscriber on the
|
|
75
|
+
node:
|
|
126
76
|
|
|
127
|
-
|
|
77
|
+
```ruby
|
|
78
|
+
receiver = Object.new
|
|
79
|
+
receiver.define_singleton_method(:receive_event) do |event|
|
|
80
|
+
warn "#{event.type}: #{event.failure}"
|
|
81
|
+
end
|
|
128
82
|
|
|
83
|
+
site.add_event_receiver(receiver)
|
|
129
84
|
```
|
|
130
|
-
#<Async::Reactor:0x94c 1 children (running)>
|
|
131
|
-
#<Async::Task:0x960 cli (running)>
|
|
132
|
-
#<Async::Task:0x988 RSMP::Supervisor main task (running)>
|
|
133
|
-
#<Async::Task:0x99c accepting connections #<Addrinfo: 0.0.0.0:13111 TCP> [fd=12] (running)>
|
|
134
|
-
#<Async::Task:0xac8 incoming connection #<Addrinfo: 127.0.0.1:51778 TCP> [fd=13] (running)>
|
|
135
|
-
#<Async::Task:0xadc RSMP::SiteProxy main task (running)>
|
|
136
|
-
#<Async::Task:0xaf0 reader (running)>
|
|
137
|
-
#<Async::Task:0xb04 timer (running)>
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
The task hierachy matters when you stop a task or iterate on subtasks. Note that calling `Task#wait`
|
|
141
|
-
does not wait for subtasks, whereas Task#stop stops all subtasks.
|
|
142
85
|
|
|
143
|
-
|
|
144
|
-
|
|
86
|
+
Events are ordered and delivered in the reactor. There is no implicit global
|
|
87
|
+
error queue. Subscribers must have an owner that drains or handles them. A
|
|
88
|
+
subscriber callback defect propagates unless the subscriber implements `crash`,
|
|
89
|
+
in which case it receives that exception through its own completion mechanism.
|
|
145
90
|
|
|
146
|
-
|
|
91
|
+
## Restart and shutdown
|
|
147
92
|
|
|
148
|
-
|
|
93
|
+
`stop` cancels the owned main task and its descendants. A TLC restart request is
|
|
94
|
+
not an exception: it resolves an explicit `RSMP::Termination` with reason
|
|
95
|
+
`:restart`. The CLI observes this value, lets the old structured task tree clean
|
|
96
|
+
up, and then constructs a new site.
|
data/lib/rsmp/cli.rb
CHANGED
|
@@ -21,6 +21,91 @@ module RSMP
|
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
# CLI subcommands for SXL dependency resolution and manifests.
|
|
25
|
+
class SXLCLI < Thor
|
|
26
|
+
namespace :sxl
|
|
27
|
+
|
|
28
|
+
desc 'resolve ROOT...', 'Resolve SXL dependencies and write a manifest'
|
|
29
|
+
method_option :source, type: :array, aliases: '-s', default: [],
|
|
30
|
+
banner: 'Local SXL file or directory paths'
|
|
31
|
+
method_option :out, type: :string, aliases: '-o', default: 'manifest.yaml',
|
|
32
|
+
banner: 'Manifest output path'
|
|
33
|
+
method_option :format, type: :string, default: RSMP::Schema.latest_core_version,
|
|
34
|
+
enum: RSMP::Schema.core_versions,
|
|
35
|
+
banner: 'RSMP Core manifest format version'
|
|
36
|
+
method_option :force, type: :boolean, aliases: '-f', default: false,
|
|
37
|
+
banner: 'Overwrite an existing manifest'
|
|
38
|
+
def resolve(*roots)
|
|
39
|
+
raise RSMP::SXL::Processing::Error, 'sxl resolve requires at least one root' if roots.empty?
|
|
40
|
+
|
|
41
|
+
ensure_manifest_output_available!
|
|
42
|
+
catalogue = RSMP::SXL::Processing::Catalogue.new(options[:source])
|
|
43
|
+
requirements = roots.map { |root| root_requirement(root, catalogue) }
|
|
44
|
+
documents = RSMP::SXL::Processing::Resolver.new(catalogue).resolve(requirements)
|
|
45
|
+
manifest = RSMP::SXL::Processing::Manifest.create(documents, format: options[:format])
|
|
46
|
+
write_manifest(manifest)
|
|
47
|
+
puts "Wrote #{options[:out]}"
|
|
48
|
+
rescue RSMP::SXL::Processing::Error, SystemCallError => e
|
|
49
|
+
fail_sxl_command(e)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc 'verify [MANIFEST]', 'Verify an SXL manifest against local sources'
|
|
53
|
+
method_option :source, type: :array, aliases: '-s', default: [],
|
|
54
|
+
banner: 'Local SXL file or directory paths'
|
|
55
|
+
def verify(path = 'manifest.yaml')
|
|
56
|
+
catalogue = RSMP::SXL::Processing::Catalogue.new(options[:source])
|
|
57
|
+
manifest = RSMP::SXL::Processing::Manifest.load(path)
|
|
58
|
+
RSMP::SXL::Processing::Manifest.verify!(manifest, catalogue)
|
|
59
|
+
puts 'OK'
|
|
60
|
+
rescue RSMP::SXL::Processing::Error, SystemCallError => e
|
|
61
|
+
fail_sxl_command(e)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def root_requirement(root, catalogue)
|
|
67
|
+
return path_root_requirement(root, catalogue) if File.exist?(root)
|
|
68
|
+
|
|
69
|
+
name, requirement = root.split(':', 2)
|
|
70
|
+
unless requirement
|
|
71
|
+
raise RSMP::SXL::Processing::Error,
|
|
72
|
+
"Invalid root #{root.inspect}; expected an SXL YAML path or name:requirement"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
RSMP::SXL::Processing::Document.validate_name!(name)
|
|
76
|
+
[name, RSMP::SXL::Processing::VersionRequirement.new(requirement)]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def path_root_requirement(path, catalogue)
|
|
80
|
+
raise RSMP::SXL::Processing::Error, "Root SXL #{path} is not a file" unless File.file?(path)
|
|
81
|
+
|
|
82
|
+
document = RSMP::SXL::Processing::Document.load(path)
|
|
83
|
+
catalogue.add_document(document)
|
|
84
|
+
[document.name, RSMP::SXL::Processing::VersionRequirement.new(document.version_string)]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def ensure_manifest_output_available!
|
|
88
|
+
output = options[:out]
|
|
89
|
+
return unless File.exist?(output)
|
|
90
|
+
raise RSMP::SXL::Processing::Error, "Manifest output #{output} is a directory" if File.directory?(output)
|
|
91
|
+
return if options[:force]
|
|
92
|
+
|
|
93
|
+
raise RSMP::SXL::Processing::Error,
|
|
94
|
+
"Refusing to overwrite existing manifest #{output}; use --force to replace it"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def write_manifest(manifest)
|
|
98
|
+
output = options[:out]
|
|
99
|
+
FileUtils.mkdir_p(File.dirname(File.expand_path(output)))
|
|
100
|
+
File.write(output, RSMP::SXL::Processing::Manifest.dump(manifest))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def fail_sxl_command(error)
|
|
104
|
+
puts "Error: #{error.message}"
|
|
105
|
+
exit 1
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
24
109
|
# CLI subcommands for RSMP configuration validation.
|
|
25
110
|
class ConfigCLI < Thor
|
|
26
111
|
namespace :config
|
|
@@ -78,8 +163,9 @@ module RSMP
|
|
|
78
163
|
puts "Error: #{e}"
|
|
79
164
|
exit 1
|
|
80
165
|
rescue StandardError => e
|
|
81
|
-
|
|
82
|
-
|
|
166
|
+
warn "Uncaught error: #{e.class}: #{e.message}"
|
|
167
|
+
warn e.full_message(highlight: false, order: :top)
|
|
168
|
+
exit 1
|
|
83
169
|
end
|
|
84
170
|
|
|
85
171
|
desc 'supervisor', 'Run RSMP supervisor'
|
|
@@ -100,6 +186,7 @@ module RSMP
|
|
|
100
186
|
end
|
|
101
187
|
|
|
102
188
|
register SchemaCLI, 'schema', 'schema COMMAND', 'SXL schema commands'
|
|
189
|
+
register SXLCLI, 'sxl', 'sxl COMMAND', 'SXL dependency and manifest commands'
|
|
103
190
|
register ConfigCLI, 'config', 'config COMMAND', 'Configuration commands'
|
|
104
191
|
|
|
105
192
|
private
|
|
@@ -194,7 +281,10 @@ module RSMP
|
|
|
194
281
|
loop do
|
|
195
282
|
site = site_class.new(site_settings: settings, log_settings: log_settings)
|
|
196
283
|
site.start
|
|
197
|
-
site.wait
|
|
284
|
+
termination = site.wait
|
|
285
|
+
break unless termination&.success? && termination.value.reason == :restart
|
|
286
|
+
|
|
287
|
+
site.stop
|
|
198
288
|
rescue Psych::SyntaxError => e
|
|
199
289
|
puts "Cannot read config file #{e}"
|
|
200
290
|
break
|
|
@@ -202,8 +292,6 @@ module RSMP
|
|
|
202
292
|
RSMP::ConfigurationError => e
|
|
203
293
|
puts "Cannot start site: #{e}"
|
|
204
294
|
break
|
|
205
|
-
rescue RSMP::Restart
|
|
206
|
-
site.stop
|
|
207
295
|
end
|
|
208
296
|
end
|
|
209
297
|
end
|