ominfra 0.0.0.dev121__py3-none-any.whl → 0.0.0.dev123__py3-none-any.whl
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.
- ominfra/deploy/_executor.py +10 -1
- ominfra/journald/messages.py +1 -1
- ominfra/pyremote/_runcommands.py +10 -1
- ominfra/scripts/journald2aws.py +10 -2
- ominfra/scripts/supervisor.py +2227 -2043
- ominfra/supervisor/context.py +16 -41
- ominfra/supervisor/dispatchers.py +88 -58
- ominfra/supervisor/events.py +59 -70
- ominfra/supervisor/groups.py +162 -0
- ominfra/supervisor/main.py +27 -11
- ominfra/supervisor/poller.py +51 -53
- ominfra/supervisor/process.py +267 -352
- ominfra/supervisor/signals.py +52 -0
- ominfra/supervisor/states.py +26 -47
- ominfra/supervisor/supervisor.py +105 -81
- ominfra/supervisor/types.py +57 -6
- ominfra/supervisor/{compat.py → utils.py} +34 -53
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/RECORD +23 -21
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev121.dist-info → ominfra-0.0.0.dev123.dist-info}/top_level.txt +0 -0
ominfra/supervisor/context.py
CHANGED
@@ -6,57 +6,47 @@ import os
|
|
6
6
|
import pwd
|
7
7
|
import re
|
8
8
|
import resource
|
9
|
-
import signal
|
10
9
|
import stat
|
11
10
|
import typing as ta
|
12
11
|
import warnings
|
13
12
|
|
14
13
|
from omlish.lite.logs import log
|
15
14
|
|
16
|
-
from .compat import SignalReceiver
|
17
|
-
from .compat import close_fd
|
18
|
-
from .compat import mktempfile
|
19
|
-
from .compat import real_exit
|
20
|
-
from .compat import try_unlink
|
21
15
|
from .configs import ServerConfig
|
22
16
|
from .datatypes import gid_for_uid
|
23
17
|
from .datatypes import name_to_uid
|
24
18
|
from .exceptions import NoPermissionError
|
25
19
|
from .exceptions import NotExecutableError
|
26
20
|
from .exceptions import NotFoundError
|
27
|
-
from .poller import BasePoller
|
28
21
|
from .poller import Poller
|
29
22
|
from .states import SupervisorState
|
30
|
-
from .states import SupervisorStates
|
31
23
|
from .types import AbstractServerContext
|
32
24
|
from .types import AbstractSubprocess
|
25
|
+
from .utils import close_fd
|
26
|
+
from .utils import mktempfile
|
27
|
+
from .utils import real_exit
|
28
|
+
from .utils import try_unlink
|
33
29
|
|
34
30
|
|
35
31
|
ServerEpoch = ta.NewType('ServerEpoch', int)
|
36
32
|
|
37
|
-
InheritedFds = ta.NewType('InheritedFds', ta.FrozenSet[int])
|
38
|
-
|
39
33
|
|
40
34
|
class ServerContext(AbstractServerContext):
|
41
35
|
def __init__(
|
42
36
|
self,
|
43
37
|
config: ServerConfig,
|
38
|
+
poller: Poller,
|
44
39
|
*,
|
45
40
|
epoch: ServerEpoch = ServerEpoch(0),
|
46
|
-
inherited_fds: ta.Optional[InheritedFds] = None,
|
47
41
|
) -> None:
|
48
42
|
super().__init__()
|
49
43
|
|
50
44
|
self._config = config
|
45
|
+
self._poller = poller
|
51
46
|
self._epoch = epoch
|
52
|
-
self._inherited_fds = InheritedFds(frozenset(inherited_fds or []))
|
53
47
|
|
54
48
|
self._pid_history: ta.Dict[int, AbstractSubprocess] = {}
|
55
|
-
self._state: SupervisorState =
|
56
|
-
|
57
|
-
self._signal_receiver = SignalReceiver()
|
58
|
-
|
59
|
-
self._poller: BasePoller = Poller()
|
49
|
+
self._state: SupervisorState = SupervisorState.RUNNING
|
60
50
|
|
61
51
|
if config.user is not None:
|
62
52
|
uid = name_to_uid(config.user)
|
@@ -87,10 +77,6 @@ class ServerContext(AbstractServerContext):
|
|
87
77
|
def set_state(self, state: SupervisorState) -> None:
|
88
78
|
self._state = state
|
89
79
|
|
90
|
-
@property
|
91
|
-
def poller(self) -> BasePoller:
|
92
|
-
return self._poller
|
93
|
-
|
94
80
|
@property
|
95
81
|
def pid_history(self) -> ta.Dict[int, AbstractSubprocess]:
|
96
82
|
return self._pid_history
|
@@ -103,22 +89,8 @@ class ServerContext(AbstractServerContext):
|
|
103
89
|
def gid(self) -> ta.Optional[int]:
|
104
90
|
return self._gid
|
105
91
|
|
106
|
-
@property
|
107
|
-
def inherited_fds(self) -> InheritedFds:
|
108
|
-
return self._inherited_fds
|
109
|
-
|
110
92
|
##
|
111
93
|
|
112
|
-
def set_signals(self) -> None:
|
113
|
-
self._signal_receiver.install(
|
114
|
-
signal.SIGTERM,
|
115
|
-
signal.SIGINT,
|
116
|
-
signal.SIGQUIT,
|
117
|
-
signal.SIGHUP,
|
118
|
-
signal.SIGCHLD,
|
119
|
-
signal.SIGUSR2,
|
120
|
-
)
|
121
|
-
|
122
94
|
def waitpid(self) -> ta.Tuple[ta.Optional[int], ta.Optional[int]]:
|
123
95
|
# Need pthread_sigmask here to avoid concurrent sigchld, but Python doesn't offer in Python < 3.4. There is
|
124
96
|
# still a race condition here; we can get a sigchld while we're sitting in the waitpid call. However, AFAICT, if
|
@@ -223,7 +195,7 @@ class ServerContext(AbstractServerContext):
|
|
223
195
|
def cleanup(self) -> None:
|
224
196
|
if self._unlink_pidfile:
|
225
197
|
try_unlink(self.config.pidfile)
|
226
|
-
self.
|
198
|
+
self._poller.close()
|
227
199
|
|
228
200
|
def cleanup_fds(self) -> None:
|
229
201
|
# try to close any leaked file descriptors (for reload)
|
@@ -249,9 +221,9 @@ class ServerContext(AbstractServerContext):
|
|
249
221
|
log.warning('Failed to clean up %r', pathname)
|
250
222
|
|
251
223
|
def daemonize(self) -> None:
|
252
|
-
self.
|
224
|
+
self._poller.before_daemonize()
|
253
225
|
self._daemonize()
|
254
|
-
self.
|
226
|
+
self._poller.after_daemonize()
|
255
227
|
|
256
228
|
def _daemonize(self) -> None:
|
257
229
|
# To daemonize, we need to become the leader of our own session (process) group. If we do not, signals sent to
|
@@ -306,9 +278,6 @@ class ServerContext(AbstractServerContext):
|
|
306
278
|
)
|
307
279
|
return logfile
|
308
280
|
|
309
|
-
def get_signal(self) -> ta.Optional[int]:
|
310
|
-
return self._signal_receiver.get_signal()
|
311
|
-
|
312
281
|
def write_pidfile(self) -> None:
|
313
282
|
pid = os.getpid()
|
314
283
|
try:
|
@@ -395,19 +364,25 @@ def make_pipes(stderr=True) -> ta.Mapping[str, int]:
|
|
395
364
|
'stderr': None,
|
396
365
|
'child_stderr': None,
|
397
366
|
}
|
367
|
+
|
398
368
|
try:
|
399
369
|
stdin, child_stdin = os.pipe()
|
400
370
|
pipes['child_stdin'], pipes['stdin'] = stdin, child_stdin
|
371
|
+
|
401
372
|
stdout, child_stdout = os.pipe()
|
402
373
|
pipes['stdout'], pipes['child_stdout'] = stdout, child_stdout
|
374
|
+
|
403
375
|
if stderr:
|
404
376
|
stderr, child_stderr = os.pipe()
|
405
377
|
pipes['stderr'], pipes['child_stderr'] = stderr, child_stderr
|
378
|
+
|
406
379
|
for fd in (pipes['stdout'], pipes['stderr'], pipes['stdin']):
|
407
380
|
if fd is not None:
|
408
381
|
flags = fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NDELAY
|
409
382
|
fcntl.fcntl(fd, fcntl.F_SETFL, flags)
|
383
|
+
|
410
384
|
return pipes # type: ignore
|
385
|
+
|
411
386
|
except OSError:
|
412
387
|
for fd in pipes.values():
|
413
388
|
if fd is not None:
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# ruff: noqa: UP007
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
2
|
import abc
|
3
3
|
import errno
|
4
4
|
import logging
|
@@ -7,26 +7,35 @@ import typing as ta
|
|
7
7
|
|
8
8
|
from omlish.lite.logs import log
|
9
9
|
|
10
|
-
from .compat import as_bytes
|
11
|
-
from .compat import compact_traceback
|
12
|
-
from .compat import find_prefix_at_end
|
13
|
-
from .compat import readfd
|
14
|
-
from .compat import strip_escapes
|
15
10
|
from .configs import ProcessConfig
|
16
|
-
from .events import
|
11
|
+
from .events import EventCallbacks
|
12
|
+
from .events import ProcessCommunicationEvent
|
17
13
|
from .events import ProcessLogStderrEvent
|
18
14
|
from .events import ProcessLogStdoutEvent
|
19
15
|
from .types import AbstractSubprocess
|
16
|
+
from .utils import as_bytes
|
17
|
+
from .utils import compact_traceback
|
18
|
+
from .utils import find_prefix_at_end
|
19
|
+
from .utils import read_fd
|
20
|
+
from .utils import strip_escapes
|
20
21
|
|
21
22
|
|
22
23
|
class Dispatcher(abc.ABC):
|
23
|
-
|
24
|
-
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
process: AbstractSubprocess,
|
27
|
+
channel: str,
|
28
|
+
fd: int,
|
29
|
+
*,
|
30
|
+
event_callbacks: EventCallbacks,
|
31
|
+
) -> None:
|
25
32
|
super().__init__()
|
26
33
|
|
27
34
|
self._process = process # process which "owns" this dispatcher
|
28
35
|
self._channel = channel # 'stderr' or 'stdout'
|
29
36
|
self._fd = fd
|
37
|
+
self._event_callbacks = event_callbacks
|
38
|
+
|
30
39
|
self._closed = False # True if close() has been called
|
31
40
|
|
32
41
|
def __repr__(self) -> str:
|
@@ -86,18 +95,23 @@ class OutputDispatcher(Dispatcher):
|
|
86
95
|
- route the output to the appropriate log handlers as specified in the config.
|
87
96
|
"""
|
88
97
|
|
89
|
-
def __init__(
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
super().__init__(
|
98
|
+
def __init__(
|
99
|
+
self,
|
100
|
+
process: AbstractSubprocess,
|
101
|
+
event_type: ta.Type[ProcessCommunicationEvent],
|
102
|
+
fd: int,
|
103
|
+
**kwargs: ta.Any,
|
104
|
+
) -> None:
|
105
|
+
super().__init__(
|
106
|
+
process,
|
107
|
+
event_type.channel,
|
108
|
+
fd,
|
109
|
+
**kwargs,
|
110
|
+
)
|
97
111
|
|
98
|
-
self.
|
112
|
+
self._event_type = event_type
|
99
113
|
|
100
|
-
self.
|
114
|
+
self._lc: ProcessConfig.Log = getattr(process.config, self._channel)
|
101
115
|
|
102
116
|
self._init_normal_log()
|
103
117
|
self._init_capture_log()
|
@@ -109,8 +123,8 @@ class OutputDispatcher(Dispatcher):
|
|
109
123
|
|
110
124
|
# all code below is purely for minor speedups
|
111
125
|
|
112
|
-
begin_token = self.
|
113
|
-
end_token = self.
|
126
|
+
begin_token = self._event_type.BEGIN_TOKEN
|
127
|
+
end_token = self._event_type.END_TOKEN
|
114
128
|
self._begin_token_data = (begin_token, len(begin_token))
|
115
129
|
self._end_token_data = (end_token, len(end_token))
|
116
130
|
|
@@ -135,10 +149,10 @@ class OutputDispatcher(Dispatcher):
|
|
135
149
|
config = self._process.config # noqa
|
136
150
|
channel = self._channel # noqa
|
137
151
|
|
138
|
-
logfile = self.
|
139
|
-
maxbytes = self.
|
140
|
-
backups = self.
|
141
|
-
to_syslog = self.
|
152
|
+
logfile = self._lc.file
|
153
|
+
maxbytes = self._lc.maxbytes # noqa
|
154
|
+
backups = self._lc.backups # noqa
|
155
|
+
to_syslog = self._lc.syslog
|
142
156
|
|
143
157
|
if logfile or to_syslog:
|
144
158
|
self._normal_log = logging.getLogger(__name__)
|
@@ -165,7 +179,7 @@ class OutputDispatcher(Dispatcher):
|
|
165
179
|
is detected. Sets self.capture_log if capturing is enabled.
|
166
180
|
"""
|
167
181
|
|
168
|
-
capture_maxbytes = self.
|
182
|
+
capture_maxbytes = self._lc.capture_maxbytes
|
169
183
|
if capture_maxbytes:
|
170
184
|
self._capture_log = logging.getLogger(__name__)
|
171
185
|
# loggers.handle_boundIO(
|
@@ -174,45 +188,47 @@ class OutputDispatcher(Dispatcher):
|
|
174
188
|
# maxbytes=capture_maxbytes,
|
175
189
|
# )
|
176
190
|
|
177
|
-
def remove_logs(self):
|
191
|
+
def remove_logs(self) -> None:
|
178
192
|
for l in (self._normal_log, self._capture_log):
|
179
193
|
if l is not None:
|
180
194
|
for handler in l.handlers:
|
181
195
|
handler.remove() # type: ignore
|
182
196
|
handler.reopen() # type: ignore
|
183
197
|
|
184
|
-
def reopen_logs(self):
|
198
|
+
def reopen_logs(self) -> None:
|
185
199
|
for l in (self._normal_log, self._capture_log):
|
186
200
|
if l is not None:
|
187
201
|
for handler in l.handlers:
|
188
202
|
handler.reopen() # type: ignore
|
189
203
|
|
190
|
-
def _log(self, data):
|
191
|
-
if data:
|
192
|
-
|
193
|
-
data = strip_escapes(data)
|
204
|
+
def _log(self, data: ta.Union[str, bytes, None]) -> None:
|
205
|
+
if not data:
|
206
|
+
return
|
194
207
|
|
195
|
-
|
196
|
-
|
208
|
+
if self._process.context.config.strip_ansi:
|
209
|
+
data = strip_escapes(as_bytes(data))
|
197
210
|
|
198
|
-
|
199
|
-
|
200
|
-
text = data
|
201
|
-
else:
|
202
|
-
try:
|
203
|
-
text = data.decode('utf-8')
|
204
|
-
except UnicodeDecodeError:
|
205
|
-
text = f'Undecodable: {data!r}'
|
206
|
-
log.log(self._main_log_level, '%r %s output:\n%s', self._process.config.name, self._channel, text) # noqa
|
211
|
+
if self._child_log:
|
212
|
+
self._child_log.info(data)
|
207
213
|
|
208
|
-
|
209
|
-
|
210
|
-
|
214
|
+
if self._log_to_main_log:
|
215
|
+
if not isinstance(data, bytes):
|
216
|
+
text = data
|
217
|
+
else:
|
218
|
+
try:
|
219
|
+
text = data.decode('utf-8')
|
220
|
+
except UnicodeDecodeError:
|
221
|
+
text = f'Undecodable: {data!r}'
|
222
|
+
log.log(self._main_log_level, '%r %s output:\n%s', self._process.config.name, self._channel, text) # noqa
|
223
|
+
|
224
|
+
if self._channel == 'stdout':
|
225
|
+
if self._stdout_events_enabled:
|
226
|
+
self._event_callbacks.notify(ProcessLogStdoutEvent(self._process, self._process.pid, data))
|
211
227
|
|
212
|
-
|
213
|
-
|
228
|
+
elif self._stderr_events_enabled:
|
229
|
+
self._event_callbacks.notify(ProcessLogStderrEvent(self._process, self._process.pid, data))
|
214
230
|
|
215
|
-
def record_output(self):
|
231
|
+
def record_output(self) -> None:
|
216
232
|
if self._capture_log is None:
|
217
233
|
# shortcut trying to find capture data
|
218
234
|
data = self._output_buffer
|
@@ -221,11 +237,11 @@ class OutputDispatcher(Dispatcher):
|
|
221
237
|
return
|
222
238
|
|
223
239
|
if self._capture_mode:
|
224
|
-
token,
|
240
|
+
token, token_len = self._end_token_data
|
225
241
|
else:
|
226
|
-
token,
|
242
|
+
token, token_len = self._begin_token_data
|
227
243
|
|
228
|
-
if len(self._output_buffer) <=
|
244
|
+
if len(self._output_buffer) <= token_len:
|
229
245
|
return # not enough data
|
230
246
|
|
231
247
|
data = self._output_buffer
|
@@ -248,7 +264,7 @@ class OutputDispatcher(Dispatcher):
|
|
248
264
|
if after:
|
249
265
|
self.record_output()
|
250
266
|
|
251
|
-
def toggle_capture_mode(self):
|
267
|
+
def toggle_capture_mode(self) -> None:
|
252
268
|
self._capture_mode = not self._capture_mode
|
253
269
|
|
254
270
|
if self._capture_log is not None:
|
@@ -260,8 +276,8 @@ class OutputDispatcher(Dispatcher):
|
|
260
276
|
data = self._capture_log.getvalue() # type: ignore
|
261
277
|
channel = self._channel
|
262
278
|
procname = self._process.config.name
|
263
|
-
event = self.
|
264
|
-
|
279
|
+
event = self._event_type(self._process, self._process.pid, data)
|
280
|
+
self._event_callbacks.notify(event)
|
265
281
|
|
266
282
|
log.debug('%r %s emitted a comm event', procname, channel)
|
267
283
|
for handler in self._capture_log.handlers:
|
@@ -278,7 +294,7 @@ class OutputDispatcher(Dispatcher):
|
|
278
294
|
return True
|
279
295
|
|
280
296
|
def handle_read_event(self) -> None:
|
281
|
-
data =
|
297
|
+
data = read_fd(self._fd)
|
282
298
|
self._output_buffer += data
|
283
299
|
self.record_output()
|
284
300
|
if not data:
|
@@ -288,11 +304,25 @@ class OutputDispatcher(Dispatcher):
|
|
288
304
|
|
289
305
|
|
290
306
|
class InputDispatcher(Dispatcher):
|
307
|
+
def __init__(
|
308
|
+
self,
|
309
|
+
process: AbstractSubprocess,
|
310
|
+
channel: str,
|
311
|
+
fd: int,
|
312
|
+
**kwargs: ta.Any,
|
313
|
+
) -> None:
|
314
|
+
super().__init__(
|
315
|
+
process,
|
316
|
+
channel,
|
317
|
+
fd,
|
318
|
+
**kwargs,
|
319
|
+
)
|
291
320
|
|
292
|
-
def __init__(self, process: AbstractSubprocess, channel: str, fd: int) -> None:
|
293
|
-
super().__init__(process, channel, fd)
|
294
321
|
self._input_buffer = b''
|
295
322
|
|
323
|
+
def write(self, chars: ta.Union[bytes, str]) -> None:
|
324
|
+
self._input_buffer += as_bytes(chars)
|
325
|
+
|
296
326
|
def writable(self) -> bool:
|
297
327
|
if self._input_buffer and not self._closed:
|
298
328
|
return True
|
ominfra/supervisor/events.py
CHANGED
@@ -2,36 +2,44 @@
|
|
2
2
|
import abc
|
3
3
|
import typing as ta
|
4
4
|
|
5
|
-
from .
|
6
|
-
|
5
|
+
from .states import ProcessState
|
6
|
+
|
7
|
+
|
8
|
+
##
|
9
|
+
|
10
|
+
|
11
|
+
class Event(abc.ABC): # noqa
|
12
|
+
"""Abstract event type."""
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
|
17
|
+
|
18
|
+
EventCallback = ta.Callable[['Event'], None]
|
7
19
|
|
8
20
|
|
9
21
|
class EventCallbacks:
|
10
22
|
def __init__(self) -> None:
|
11
23
|
super().__init__()
|
12
24
|
|
13
|
-
self._callbacks: ta.List[ta.Tuple[
|
25
|
+
self._callbacks: ta.List[ta.Tuple[ta.Type[Event], EventCallback]] = []
|
14
26
|
|
15
|
-
def subscribe(self, type, callback): # noqa
|
27
|
+
def subscribe(self, type: ta.Type[Event], callback: EventCallback) -> None: # noqa
|
16
28
|
self._callbacks.append((type, callback))
|
17
29
|
|
18
|
-
def unsubscribe(self, type, callback): # noqa
|
30
|
+
def unsubscribe(self, type: ta.Type[Event], callback: EventCallback) -> None: # noqa
|
19
31
|
self._callbacks.remove((type, callback))
|
20
32
|
|
21
|
-
def notify(self, event):
|
33
|
+
def notify(self, event: Event) -> None:
|
22
34
|
for type, callback in self._callbacks: # noqa
|
23
35
|
if isinstance(event, type):
|
24
36
|
callback(event)
|
25
37
|
|
26
|
-
def clear(self):
|
38
|
+
def clear(self) -> None:
|
27
39
|
self._callbacks[:] = []
|
28
40
|
|
29
41
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
class Event(abc.ABC): # noqa
|
34
|
-
"""Abstract event type."""
|
42
|
+
##
|
35
43
|
|
36
44
|
|
37
45
|
class ProcessLogEvent(Event, abc.ABC):
|
@@ -43,24 +51,6 @@ class ProcessLogEvent(Event, abc.ABC):
|
|
43
51
|
self.pid = pid
|
44
52
|
self.data = data
|
45
53
|
|
46
|
-
def payload(self):
|
47
|
-
groupname = ''
|
48
|
-
if self.process.group is not None:
|
49
|
-
groupname = self.process.group.config.name
|
50
|
-
try:
|
51
|
-
data = as_string(self.data)
|
52
|
-
except UnicodeDecodeError:
|
53
|
-
data = f'Undecodable: {self.data!r}'
|
54
|
-
|
55
|
-
result = 'processname:%s groupname:%s pid:%s channel:%s\n%s' % ( # noqa
|
56
|
-
as_string(self.process.config.name),
|
57
|
-
as_string(groupname),
|
58
|
-
self.pid,
|
59
|
-
as_string(self.channel), # type: ignore
|
60
|
-
data,
|
61
|
-
)
|
62
|
-
return result
|
63
|
-
|
64
54
|
|
65
55
|
class ProcessLogStdoutEvent(ProcessLogEvent):
|
66
56
|
channel = 'stdout'
|
@@ -70,27 +60,22 @@ class ProcessLogStderrEvent(ProcessLogEvent):
|
|
70
60
|
channel = 'stderr'
|
71
61
|
|
72
62
|
|
63
|
+
#
|
64
|
+
|
65
|
+
|
73
66
|
class ProcessCommunicationEvent(Event, abc.ABC):
|
74
67
|
# event mode tokens
|
75
68
|
BEGIN_TOKEN = b'<!--XSUPERVISOR:BEGIN-->'
|
76
69
|
END_TOKEN = b'<!--XSUPERVISOR:END-->'
|
77
70
|
|
71
|
+
channel: ta.ClassVar[str]
|
72
|
+
|
78
73
|
def __init__(self, process, pid, data):
|
79
74
|
super().__init__()
|
80
75
|
self.process = process
|
81
76
|
self.pid = pid
|
82
77
|
self.data = data
|
83
78
|
|
84
|
-
def payload(self):
|
85
|
-
groupname = ''
|
86
|
-
if self.process.group is not None:
|
87
|
-
groupname = self.process.group.config.name
|
88
|
-
try:
|
89
|
-
data = as_string(self.data)
|
90
|
-
except UnicodeDecodeError:
|
91
|
-
data = f'Undecodable: {self.data!r}'
|
92
|
-
return f'processname:{self.process.config.name} groupname:{groupname} pid:{self.pid}\n{data}'
|
93
|
-
|
94
79
|
|
95
80
|
class ProcessCommunicationStdoutEvent(ProcessCommunicationEvent):
|
96
81
|
channel = 'stdout'
|
@@ -100,22 +85,22 @@ class ProcessCommunicationStderrEvent(ProcessCommunicationEvent):
|
|
100
85
|
channel = 'stderr'
|
101
86
|
|
102
87
|
|
88
|
+
#
|
89
|
+
|
90
|
+
|
103
91
|
class RemoteCommunicationEvent(Event):
|
104
92
|
def __init__(self, type, data): # noqa
|
105
93
|
super().__init__()
|
106
94
|
self.type = type
|
107
95
|
self.data = data
|
108
96
|
|
109
|
-
|
110
|
-
|
97
|
+
|
98
|
+
#
|
111
99
|
|
112
100
|
|
113
101
|
class SupervisorStateChangeEvent(Event):
|
114
102
|
"""Abstract class."""
|
115
103
|
|
116
|
-
def payload(self):
|
117
|
-
return ''
|
118
|
-
|
119
104
|
|
120
105
|
class SupervisorRunningEvent(SupervisorStateChangeEvent):
|
121
106
|
pass
|
@@ -125,6 +110,9 @@ class SupervisorStoppingEvent(SupervisorStateChangeEvent):
|
|
125
110
|
pass
|
126
111
|
|
127
112
|
|
113
|
+
#
|
114
|
+
|
115
|
+
|
128
116
|
class EventRejectedEvent: # purposely does not subclass Event
|
129
117
|
def __init__(self, process, event):
|
130
118
|
super().__init__()
|
@@ -132,6 +120,9 @@ class EventRejectedEvent: # purposely does not subclass Event
|
|
132
120
|
self.event = event
|
133
121
|
|
134
122
|
|
123
|
+
#
|
124
|
+
|
125
|
+
|
135
126
|
class ProcessStateEvent(Event):
|
136
127
|
"""Abstract class, never raised directly."""
|
137
128
|
frm = None
|
@@ -146,19 +137,6 @@ class ProcessStateEvent(Event):
|
|
146
137
|
# us, we stash the values at the time the event was sent
|
147
138
|
self.extra_values = self.get_extra_values()
|
148
139
|
|
149
|
-
def payload(self):
|
150
|
-
groupname = ''
|
151
|
-
if self.process.group is not None:
|
152
|
-
groupname = self.process.group.config.name
|
153
|
-
l = [
|
154
|
-
('processname', self.process.config.name),
|
155
|
-
('groupname', groupname),
|
156
|
-
('from_state', get_process_state_description(self.from_state)),
|
157
|
-
]
|
158
|
-
l.extend(self.extra_values)
|
159
|
-
s = ' '.join([f'{name}:{val}' for name, val in l])
|
160
|
-
return s
|
161
|
-
|
162
140
|
def get_extra_values(self):
|
163
141
|
return []
|
164
142
|
|
@@ -204,14 +182,26 @@ class ProcessStateStoppedEvent(ProcessStateEvent):
|
|
204
182
|
return [('pid', self.process.pid)]
|
205
183
|
|
206
184
|
|
185
|
+
PROCESS_STATE_EVENT_MAP: ta.Mapping[ProcessState, ta.Type[ProcessStateEvent]] = {
|
186
|
+
ProcessState.BACKOFF: ProcessStateBackoffEvent,
|
187
|
+
ProcessState.FATAL: ProcessStateFatalEvent,
|
188
|
+
ProcessState.UNKNOWN: ProcessStateUnknownEvent,
|
189
|
+
ProcessState.STOPPED: ProcessStateStoppedEvent,
|
190
|
+
ProcessState.EXITED: ProcessStateExitedEvent,
|
191
|
+
ProcessState.RUNNING: ProcessStateRunningEvent,
|
192
|
+
ProcessState.STARTING: ProcessStateStartingEvent,
|
193
|
+
ProcessState.STOPPING: ProcessStateStoppingEvent,
|
194
|
+
}
|
195
|
+
|
196
|
+
|
197
|
+
#
|
198
|
+
|
199
|
+
|
207
200
|
class ProcessGroupEvent(Event):
|
208
201
|
def __init__(self, group):
|
209
202
|
super().__init__()
|
210
203
|
self.group = group
|
211
204
|
|
212
|
-
def payload(self):
|
213
|
-
return f'groupname:{self.group}\n'
|
214
|
-
|
215
205
|
|
216
206
|
class ProcessGroupAddedEvent(ProcessGroupEvent):
|
217
207
|
pass
|
@@ -221,6 +211,9 @@ class ProcessGroupRemovedEvent(ProcessGroupEvent):
|
|
221
211
|
pass
|
222
212
|
|
223
213
|
|
214
|
+
#
|
215
|
+
|
216
|
+
|
224
217
|
class TickEvent(Event):
|
225
218
|
"""Abstract."""
|
226
219
|
|
@@ -229,9 +222,6 @@ class TickEvent(Event):
|
|
229
222
|
self.when = when
|
230
223
|
self.supervisord = supervisord
|
231
224
|
|
232
|
-
def payload(self):
|
233
|
-
return f'when:{self.when}'
|
234
|
-
|
235
225
|
|
236
226
|
class Tick5Event(TickEvent):
|
237
227
|
period = 5
|
@@ -245,11 +235,14 @@ class Tick3600Event(TickEvent):
|
|
245
235
|
period = 3600
|
246
236
|
|
247
237
|
|
248
|
-
TICK_EVENTS =
|
238
|
+
TICK_EVENTS = ( # imported elsewhere
|
249
239
|
Tick5Event,
|
250
240
|
Tick60Event,
|
251
241
|
Tick3600Event,
|
252
|
-
|
242
|
+
)
|
243
|
+
|
244
|
+
|
245
|
+
##
|
253
246
|
|
254
247
|
|
255
248
|
class EventTypes:
|
@@ -294,7 +287,3 @@ def get_event_name_by_type(requested):
|
|
294
287
|
if typ is requested:
|
295
288
|
return name
|
296
289
|
return None
|
297
|
-
|
298
|
-
|
299
|
-
def register(name, event):
|
300
|
-
setattr(EventTypes, name, event)
|