ominfra 0.0.0.dev128__py3-none-any.whl → 0.0.0.dev130__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/deploy/_executor.py +39 -0
- ominfra/pyremote/_runcommands.py +39 -0
- ominfra/scripts/journald2aws.py +136 -0
- ominfra/scripts/supervisor.py +1732 -880
- ominfra/supervisor/dispatchers.py +10 -9
- ominfra/supervisor/dispatchersimpl.py +20 -18
- ominfra/supervisor/groups.py +16 -1
- ominfra/supervisor/groupsimpl.py +2 -2
- ominfra/supervisor/http.py +130 -0
- ominfra/supervisor/inject.py +61 -17
- ominfra/supervisor/io.py +97 -0
- ominfra/supervisor/main.py +5 -6
- ominfra/supervisor/processimpl.py +10 -18
- ominfra/supervisor/signals.py +66 -0
- ominfra/supervisor/spawningimpl.py +11 -11
- ominfra/supervisor/supervisor.py +70 -137
- ominfra/supervisor/types.py +21 -58
- {ominfra-0.0.0.dev128.dist-info → ominfra-0.0.0.dev130.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev128.dist-info → ominfra-0.0.0.dev130.dist-info}/RECORD +23 -22
- ominfra/supervisor/context.py +0 -80
- ominfra/supervisor/poller.py +0 -240
- {ominfra-0.0.0.dev128.dist-info → ominfra-0.0.0.dev130.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev128.dist-info → ominfra-0.0.0.dev130.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev128.dist-info → ominfra-0.0.0.dev130.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev128.dist-info → ominfra-0.0.0.dev130.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,14 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
-
from .
|
3
|
-
|
2
|
+
from omlish.lite.fdio.handlers import FdIoHandler
|
3
|
+
|
4
|
+
from .types import ProcessOutputDispatcher
|
4
5
|
from .utils.collections import KeyedCollection
|
5
6
|
from .utils.ostypes import Fd
|
6
7
|
|
7
8
|
|
8
|
-
class Dispatchers(KeyedCollection[Fd,
|
9
|
-
def _key(self, v:
|
10
|
-
return v.fd
|
9
|
+
class Dispatchers(KeyedCollection[Fd, FdIoHandler]):
|
10
|
+
def _key(self, v: FdIoHandler) -> Fd:
|
11
|
+
return Fd(v.fd())
|
11
12
|
|
12
13
|
#
|
13
14
|
|
@@ -16,18 +17,18 @@ class Dispatchers(KeyedCollection[Fd, Dispatcher]):
|
|
16
17
|
# note that we *must* call readable() for every dispatcher, as it may have side effects for a given
|
17
18
|
# dispatcher (eg. call handle_listener_state_change for event listener processes)
|
18
19
|
if d.readable():
|
19
|
-
d.
|
20
|
+
d.on_readable()
|
20
21
|
if d.writable():
|
21
|
-
d.
|
22
|
+
d.on_writable()
|
22
23
|
|
23
24
|
#
|
24
25
|
|
25
26
|
def remove_logs(self) -> None:
|
26
27
|
for d in self:
|
27
|
-
if isinstance(d,
|
28
|
+
if isinstance(d, ProcessOutputDispatcher):
|
28
29
|
d.remove_logs()
|
29
30
|
|
30
31
|
def reopen_logs(self) -> None:
|
31
32
|
for d in self:
|
32
|
-
if isinstance(d,
|
33
|
+
if isinstance(d, ProcessOutputDispatcher):
|
33
34
|
d.reopen_logs()
|
@@ -8,14 +8,15 @@ import typing as ta
|
|
8
8
|
from omlish.lite.logs import log
|
9
9
|
|
10
10
|
from .configs import ProcessConfig
|
11
|
+
from .configs import ServerConfig
|
11
12
|
from .events import EventCallbacks
|
12
13
|
from .events import ProcessCommunicationEvent
|
13
14
|
from .events import ProcessLogStderrEvent
|
14
15
|
from .events import ProcessLogStdoutEvent
|
15
|
-
from .types import Dispatcher
|
16
|
-
from .types import InputDispatcher
|
17
|
-
from .types import OutputDispatcher
|
18
16
|
from .types import Process
|
17
|
+
from .types import ProcessDispatcher
|
18
|
+
from .types import ProcessInputDispatcher
|
19
|
+
from .types import ProcessOutputDispatcher
|
19
20
|
from .utils.diag import compact_traceback
|
20
21
|
from .utils.fds import read_fd
|
21
22
|
from .utils.ostypes import Fd
|
@@ -24,7 +25,7 @@ from .utils.strings import find_prefix_at_end
|
|
24
25
|
from .utils.strings import strip_escapes
|
25
26
|
|
26
27
|
|
27
|
-
class
|
28
|
+
class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
|
28
29
|
def __init__(
|
29
30
|
self,
|
30
31
|
process: Process,
|
@@ -32,6 +33,7 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
|
|
32
33
|
fd: Fd,
|
33
34
|
*,
|
34
35
|
event_callbacks: EventCallbacks,
|
36
|
+
server_config: ServerConfig,
|
35
37
|
) -> None:
|
36
38
|
super().__init__()
|
37
39
|
|
@@ -39,6 +41,7 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
|
|
39
41
|
self._channel = channel # 'stderr' or 'stdout'
|
40
42
|
self._fd = fd
|
41
43
|
self._event_callbacks = event_callbacks
|
44
|
+
self._server_config = server_config
|
42
45
|
|
43
46
|
self._closed = False # True if close() has been called
|
44
47
|
|
@@ -57,7 +60,6 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
|
|
57
60
|
def channel(self) -> str:
|
58
61
|
return self._channel
|
59
62
|
|
60
|
-
@property
|
61
63
|
def fd(self) -> Fd:
|
62
64
|
return self._fd
|
63
65
|
|
@@ -72,14 +74,14 @@ class BaseDispatcherImpl(Dispatcher, abc.ABC):
|
|
72
74
|
log.debug('fd %s closed, stopped monitoring %s', self._fd, self)
|
73
75
|
self._closed = True
|
74
76
|
|
75
|
-
def
|
77
|
+
def on_error(self, exc: ta.Optional[BaseException] = None) -> None:
|
76
78
|
nil, t, v, tbinfo = compact_traceback()
|
77
79
|
|
78
80
|
log.critical('uncaptured python exception, closing channel %s (%s:%s %s)', repr(self), t, v, tbinfo)
|
79
81
|
self.close()
|
80
82
|
|
81
83
|
|
82
|
-
class
|
84
|
+
class ProcessOutputDispatcherImpl(BaseProcessDispatcherImpl, ProcessOutputDispatcher):
|
83
85
|
"""
|
84
86
|
Dispatcher for one channel (stdout or stderr) of one process. Serves several purposes:
|
85
87
|
|
@@ -95,12 +97,14 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
|
|
95
97
|
fd: Fd,
|
96
98
|
*,
|
97
99
|
event_callbacks: EventCallbacks,
|
100
|
+
server_config: ServerConfig,
|
98
101
|
) -> None:
|
99
102
|
super().__init__(
|
100
103
|
process,
|
101
104
|
event_type.channel,
|
102
105
|
fd,
|
103
106
|
event_callbacks=event_callbacks,
|
107
|
+
server_config=server_config,
|
104
108
|
)
|
105
109
|
|
106
110
|
self._event_type = event_type
|
@@ -124,11 +128,10 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
|
|
124
128
|
|
125
129
|
self._main_log_level = logging.DEBUG
|
126
130
|
|
127
|
-
self._log_to_main_log =
|
131
|
+
self._log_to_main_log = self._server_config.loglevel <= self._main_log_level
|
128
132
|
|
129
|
-
|
130
|
-
self.
|
131
|
-
self._stderr_events_enabled = config.stderr.events_enabled
|
133
|
+
self._stdout_events_enabled = self._process.config.stdout.events_enabled
|
134
|
+
self._stderr_events_enabled = self._process.config.stderr.events_enabled
|
132
135
|
|
133
136
|
_child_log: ta.Optional[logging.Logger] = None # the current logger (normal_log or capture_log)
|
134
137
|
_normal_log: ta.Optional[logging.Logger] = None # the "normal" (non-capture) logger
|
@@ -199,7 +202,7 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
|
|
199
202
|
if not data:
|
200
203
|
return
|
201
204
|
|
202
|
-
if self.
|
205
|
+
if self._server_config.strip_ansi:
|
203
206
|
data = strip_escapes(as_bytes(data))
|
204
207
|
|
205
208
|
if self._child_log:
|
@@ -287,7 +290,7 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
|
|
287
290
|
return False
|
288
291
|
return True
|
289
292
|
|
290
|
-
def
|
293
|
+
def on_readable(self) -> None:
|
291
294
|
data = read_fd(self._fd)
|
292
295
|
self._output_buffer += data
|
293
296
|
self.record_output()
|
@@ -297,7 +300,7 @@ class OutputDispatcherImpl(BaseDispatcherImpl, OutputDispatcher):
|
|
297
300
|
self.close()
|
298
301
|
|
299
302
|
|
300
|
-
class
|
303
|
+
class ProcessInputDispatcherImpl(BaseProcessDispatcherImpl, ProcessInputDispatcher):
|
301
304
|
def __init__(
|
302
305
|
self,
|
303
306
|
process: Process,
|
@@ -305,12 +308,14 @@ class InputDispatcherImpl(BaseDispatcherImpl, InputDispatcher):
|
|
305
308
|
fd: Fd,
|
306
309
|
*,
|
307
310
|
event_callbacks: EventCallbacks,
|
311
|
+
server_config: ServerConfig,
|
308
312
|
) -> None:
|
309
313
|
super().__init__(
|
310
314
|
process,
|
311
315
|
channel,
|
312
316
|
fd,
|
313
317
|
event_callbacks=event_callbacks,
|
318
|
+
server_config=server_config,
|
314
319
|
)
|
315
320
|
|
316
321
|
self._input_buffer = b''
|
@@ -323,15 +328,12 @@ class InputDispatcherImpl(BaseDispatcherImpl, InputDispatcher):
|
|
323
328
|
return True
|
324
329
|
return False
|
325
330
|
|
326
|
-
def readable(self) -> bool:
|
327
|
-
return False
|
328
|
-
|
329
331
|
def flush(self) -> None:
|
330
332
|
# other code depends on this raising EPIPE if the pipe is closed
|
331
333
|
sent = os.write(self._fd, as_bytes(self._input_buffer))
|
332
334
|
self._input_buffer = self._input_buffer[sent:]
|
333
335
|
|
334
|
-
def
|
336
|
+
def on_writable(self) -> None:
|
335
337
|
if self._input_buffer:
|
336
338
|
try:
|
337
339
|
self.flush()
|
ominfra/supervisor/groups.py
CHANGED
@@ -2,15 +2,20 @@
|
|
2
2
|
import typing as ta
|
3
3
|
|
4
4
|
from .configs import ProcessGroupConfig
|
5
|
+
from .dispatchers import Dispatchers
|
5
6
|
from .events import EventCallbacks
|
6
7
|
from .events import ProcessGroupAddedEvent
|
7
8
|
from .events import ProcessGroupRemovedEvent
|
9
|
+
from .types import HasDispatchers
|
8
10
|
from .types import Process
|
9
11
|
from .types import ProcessGroup
|
10
12
|
from .utils.collections import KeyedCollectionAccessors
|
11
13
|
|
12
14
|
|
13
|
-
class ProcessGroupManager(
|
15
|
+
class ProcessGroupManager(
|
16
|
+
KeyedCollectionAccessors[str, ProcessGroup],
|
17
|
+
HasDispatchers,
|
18
|
+
):
|
14
19
|
def __init__(
|
15
20
|
self,
|
16
21
|
*,
|
@@ -34,6 +39,16 @@ class ProcessGroupManager(KeyedCollectionAccessors[str, ProcessGroup]):
|
|
34
39
|
|
35
40
|
#
|
36
41
|
|
42
|
+
def get_dispatchers(self) -> Dispatchers:
|
43
|
+
return Dispatchers(
|
44
|
+
d
|
45
|
+
for g in self
|
46
|
+
for p in g
|
47
|
+
for d in p.get_dispatchers()
|
48
|
+
)
|
49
|
+
|
50
|
+
#
|
51
|
+
|
37
52
|
def add(self, group: ProcessGroup) -> None:
|
38
53
|
if (name := group.name) in self._by_name:
|
39
54
|
raise KeyError(f'Process group already exists: {name}')
|
ominfra/supervisor/groupsimpl.py
CHANGED
@@ -61,7 +61,7 @@ class ProcessGroupImpl(ProcessGroup):
|
|
61
61
|
#
|
62
62
|
|
63
63
|
def get_unstopped_processes(self) -> ta.List[Process]:
|
64
|
-
return [x for x in self if not x.
|
64
|
+
return [x for x in self if not x.state.stopped]
|
65
65
|
|
66
66
|
def stop_all(self) -> None:
|
67
67
|
processes = list(self._by_name.values())
|
@@ -69,7 +69,7 @@ class ProcessGroupImpl(ProcessGroup):
|
|
69
69
|
processes.reverse() # stop in desc priority order
|
70
70
|
|
71
71
|
for proc in processes:
|
72
|
-
state = proc.
|
72
|
+
state = proc.state
|
73
73
|
if state == ProcessState.RUNNING:
|
74
74
|
# RUNNING -> STOPPING
|
75
75
|
proc.stop()
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# ruff: noqa: U006 UP007
|
2
|
+
import json
|
3
|
+
import socket
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from omlish.lite.check import check_not_none
|
7
|
+
from omlish.lite.fdio.corohttp import CoroHttpServerConnectionFdIoHandler
|
8
|
+
from omlish.lite.fdio.handlers import SocketFdIoHandler
|
9
|
+
from omlish.lite.http.handlers import HttpHandler
|
10
|
+
from omlish.lite.http.handlers import HttpHandlerRequest
|
11
|
+
from omlish.lite.http.handlers import HttpHandlerResponse
|
12
|
+
from omlish.lite.json import JSON_PRETTY_KWARGS
|
13
|
+
from omlish.lite.socket import SocketAddress
|
14
|
+
|
15
|
+
from .dispatchers import Dispatchers
|
16
|
+
from .groups import ProcessGroupManager
|
17
|
+
from .types import HasDispatchers
|
18
|
+
|
19
|
+
|
20
|
+
##
|
21
|
+
|
22
|
+
|
23
|
+
class SocketServerFdIoHandler(SocketFdIoHandler):
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
addr: SocketAddress,
|
27
|
+
on_connect: ta.Callable[[socket.socket, SocketAddress], None],
|
28
|
+
) -> None:
|
29
|
+
sock = socket.create_server(addr)
|
30
|
+
sock.setblocking(False)
|
31
|
+
|
32
|
+
super().__init__(addr, sock)
|
33
|
+
|
34
|
+
self._on_connect = on_connect
|
35
|
+
|
36
|
+
sock.listen(1)
|
37
|
+
|
38
|
+
def readable(self) -> bool:
|
39
|
+
return True
|
40
|
+
|
41
|
+
def on_readable(self) -> None:
|
42
|
+
cli_sock, cli_addr = check_not_none(self._sock).accept()
|
43
|
+
cli_sock.setblocking(False)
|
44
|
+
|
45
|
+
self._on_connect(cli_sock, cli_addr)
|
46
|
+
|
47
|
+
|
48
|
+
##
|
49
|
+
|
50
|
+
|
51
|
+
class HttpServer(HasDispatchers):
|
52
|
+
class Address(ta.NamedTuple):
|
53
|
+
a: SocketAddress
|
54
|
+
|
55
|
+
class Handler(ta.NamedTuple):
|
56
|
+
h: HttpHandler
|
57
|
+
|
58
|
+
def __init__(
|
59
|
+
self,
|
60
|
+
handler: Handler,
|
61
|
+
addr: Address = Address(('localhost', 8000)),
|
62
|
+
) -> None:
|
63
|
+
super().__init__()
|
64
|
+
|
65
|
+
self._handler = handler.h
|
66
|
+
self._addr = addr.a
|
67
|
+
|
68
|
+
self._server = SocketServerFdIoHandler(self._addr, self._on_connect)
|
69
|
+
|
70
|
+
self._conns: ta.List[CoroHttpServerConnectionFdIoHandler] = []
|
71
|
+
|
72
|
+
def get_dispatchers(self) -> Dispatchers:
|
73
|
+
l = []
|
74
|
+
for c in self._conns:
|
75
|
+
if not c.closed:
|
76
|
+
l.append(c)
|
77
|
+
self._conns = l
|
78
|
+
return Dispatchers([
|
79
|
+
self._server,
|
80
|
+
*l,
|
81
|
+
])
|
82
|
+
|
83
|
+
def _on_connect(self, sock: socket.socket, addr: SocketAddress) -> None:
|
84
|
+
conn = CoroHttpServerConnectionFdIoHandler(
|
85
|
+
addr,
|
86
|
+
sock,
|
87
|
+
self._handler,
|
88
|
+
)
|
89
|
+
|
90
|
+
self._conns.append(conn)
|
91
|
+
|
92
|
+
|
93
|
+
##
|
94
|
+
|
95
|
+
|
96
|
+
class SupervisorHttpHandler:
|
97
|
+
def __init__(
|
98
|
+
self,
|
99
|
+
*,
|
100
|
+
groups: ProcessGroupManager,
|
101
|
+
) -> None:
|
102
|
+
super().__init__()
|
103
|
+
|
104
|
+
self._groups = groups
|
105
|
+
|
106
|
+
def handle(self, req: HttpHandlerRequest) -> HttpHandlerResponse:
|
107
|
+
dct = {
|
108
|
+
'method': req.method,
|
109
|
+
'path': req.path,
|
110
|
+
'data': len(req.data or b''),
|
111
|
+
'groups': {
|
112
|
+
g.name: {
|
113
|
+
'processes': {
|
114
|
+
p.name: {
|
115
|
+
'pid': p.pid,
|
116
|
+
}
|
117
|
+
for p in g
|
118
|
+
},
|
119
|
+
}
|
120
|
+
for g in self._groups
|
121
|
+
},
|
122
|
+
}
|
123
|
+
|
124
|
+
return HttpHandlerResponse(
|
125
|
+
200,
|
126
|
+
data=json.dumps(dct, **JSON_PRETTY_KWARGS).encode('utf-8') + b'\n',
|
127
|
+
headers={
|
128
|
+
'Content-Type': 'application/json',
|
129
|
+
},
|
130
|
+
)
|
ominfra/supervisor/inject.py
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
+
import dataclasses as dc
|
2
3
|
import typing as ta
|
3
4
|
|
5
|
+
from omlish.lite.fdio.kqueue import KqueueFdIoPoller # noqa
|
6
|
+
from omlish.lite.fdio.pollers import FdIoPoller
|
7
|
+
from omlish.lite.fdio.pollers import PollFdIoPoller # noqa
|
8
|
+
from omlish.lite.fdio.pollers import SelectFdIoPoller
|
4
9
|
from omlish.lite.inject import InjectorBindingOrBindings
|
5
10
|
from omlish.lite.inject import InjectorBindings
|
6
11
|
from omlish.lite.inject import inj
|
7
12
|
|
8
13
|
from .configs import ServerConfig
|
9
|
-
from .
|
10
|
-
from .dispatchersimpl import
|
11
|
-
from .dispatchersimpl import OutputDispatcherImpl
|
14
|
+
from .dispatchersimpl import ProcessInputDispatcherImpl
|
15
|
+
from .dispatchersimpl import ProcessOutputDispatcherImpl
|
12
16
|
from .events import EventCallbacks
|
13
17
|
from .groups import ProcessGroupManager
|
14
18
|
from .groupsimpl import ProcessFactory
|
15
19
|
from .groupsimpl import ProcessGroupImpl
|
16
|
-
from .
|
17
|
-
from .
|
20
|
+
from .http import HttpServer
|
21
|
+
from .http import SupervisorHttpHandler
|
22
|
+
from .io import HasDispatchersList
|
23
|
+
from .io import IoManager
|
18
24
|
from .process import PidHistory
|
19
25
|
from .processimpl import ProcessImpl
|
20
26
|
from .processimpl import ProcessSpawningFactory
|
@@ -23,19 +29,32 @@ from .setup import DaemonizeListeners
|
|
23
29
|
from .setup import SupervisorUser
|
24
30
|
from .setupimpl import SupervisorSetup
|
25
31
|
from .setupimpl import SupervisorSetupImpl
|
32
|
+
from .signals import SignalHandler
|
26
33
|
from .spawningimpl import InheritedFds
|
27
|
-
from .spawningimpl import
|
28
|
-
from .spawningimpl import
|
34
|
+
from .spawningimpl import ProcessInputDispatcherFactory
|
35
|
+
from .spawningimpl import ProcessOutputDispatcherFactory
|
29
36
|
from .spawningimpl import ProcessSpawningImpl
|
30
37
|
from .supervisor import ProcessGroupFactory
|
31
|
-
from .supervisor import SignalHandler
|
32
38
|
from .supervisor import Supervisor
|
33
|
-
from .
|
39
|
+
from .supervisor import SupervisorStateManagerImpl
|
40
|
+
from .types import HasDispatchers
|
34
41
|
from .types import ServerEpoch
|
42
|
+
from .types import SupervisorStateManager
|
35
43
|
from .utils.signals import SignalReceiver
|
36
44
|
from .utils.users import get_user
|
37
45
|
|
38
46
|
|
47
|
+
@dc.dataclass(frozen=True)
|
48
|
+
class _FdIoPollerDaemonizeListener(DaemonizeListener):
|
49
|
+
_poller: FdIoPoller
|
50
|
+
|
51
|
+
def before_daemonize(self) -> None:
|
52
|
+
self._poller.close()
|
53
|
+
|
54
|
+
def after_daemonize(self) -> None:
|
55
|
+
self._poller.reopen()
|
56
|
+
|
57
|
+
|
39
58
|
def bind_server(
|
40
59
|
config: ServerConfig,
|
41
60
|
*,
|
@@ -45,24 +64,30 @@ def bind_server(
|
|
45
64
|
lst: ta.List[InjectorBindingOrBindings] = [
|
46
65
|
inj.bind(config),
|
47
66
|
|
67
|
+
inj.bind_array(DaemonizeListener),
|
48
68
|
inj.bind_array_type(DaemonizeListener, DaemonizeListeners),
|
49
69
|
|
50
70
|
inj.bind(SupervisorSetupImpl, singleton=True),
|
51
71
|
inj.bind(SupervisorSetup, to_key=SupervisorSetupImpl),
|
52
72
|
|
53
|
-
inj.bind(DaemonizeListener, array=True, to_key=Poller),
|
54
|
-
|
55
|
-
inj.bind(ServerContextImpl, singleton=True),
|
56
|
-
inj.bind(ServerContext, to_key=ServerContextImpl),
|
57
|
-
|
58
73
|
inj.bind(EventCallbacks, singleton=True),
|
59
74
|
|
60
75
|
inj.bind(SignalReceiver, singleton=True),
|
61
76
|
|
77
|
+
inj.bind(IoManager, singleton=True),
|
78
|
+
inj.bind_array(HasDispatchers),
|
79
|
+
inj.bind_array_type(HasDispatchers, HasDispatchersList),
|
80
|
+
|
62
81
|
inj.bind(SignalHandler, singleton=True),
|
82
|
+
|
63
83
|
inj.bind(ProcessGroupManager, singleton=True),
|
84
|
+
inj.bind(HasDispatchers, array=True, to_key=ProcessGroupManager),
|
85
|
+
|
64
86
|
inj.bind(Supervisor, singleton=True),
|
65
87
|
|
88
|
+
inj.bind(SupervisorStateManagerImpl, singleton=True),
|
89
|
+
inj.bind(SupervisorStateManager, to_key=SupervisorStateManagerImpl),
|
90
|
+
|
66
91
|
inj.bind(PidHistory()),
|
67
92
|
|
68
93
|
inj.bind_factory(ProcessGroupImpl, ProcessGroupFactory),
|
@@ -70,8 +95,8 @@ def bind_server(
|
|
70
95
|
|
71
96
|
inj.bind_factory(ProcessSpawningImpl, ProcessSpawningFactory),
|
72
97
|
|
73
|
-
inj.bind_factory(
|
74
|
-
inj.bind_factory(
|
98
|
+
inj.bind_factory(ProcessOutputDispatcherImpl, ProcessOutputDispatcherFactory),
|
99
|
+
inj.bind_factory(ProcessInputDispatcherImpl, ProcessInputDispatcherFactory),
|
75
100
|
]
|
76
101
|
|
77
102
|
#
|
@@ -89,7 +114,26 @@ def bind_server(
|
|
89
114
|
|
90
115
|
#
|
91
116
|
|
92
|
-
|
117
|
+
poller_impl = next(filter(None, [
|
118
|
+
KqueueFdIoPoller,
|
119
|
+
PollFdIoPoller,
|
120
|
+
SelectFdIoPoller,
|
121
|
+
]))
|
122
|
+
lst.append(inj.bind(poller_impl, key=FdIoPoller, singleton=True))
|
123
|
+
inj.bind(_FdIoPollerDaemonizeListener, array=True, singleton=True)
|
124
|
+
|
125
|
+
#
|
126
|
+
|
127
|
+
def _provide_http_handler(s: SupervisorHttpHandler) -> HttpServer.Handler:
|
128
|
+
return HttpServer.Handler(s.handle)
|
129
|
+
|
130
|
+
lst.extend([
|
131
|
+
inj.bind(HttpServer, singleton=True, eager=True),
|
132
|
+
inj.bind(HasDispatchers, array=True, to_key=HttpServer),
|
133
|
+
|
134
|
+
inj.bind(SupervisorHttpHandler, singleton=True),
|
135
|
+
inj.bind(_provide_http_handler),
|
136
|
+
])
|
93
137
|
|
94
138
|
#
|
95
139
|
|
ominfra/supervisor/io.py
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish.lite.fdio.pollers import FdIoPoller
|
5
|
+
from omlish.lite.logs import log
|
6
|
+
|
7
|
+
from .dispatchers import Dispatchers
|
8
|
+
from .types import ExitNow
|
9
|
+
from .types import HasDispatchers
|
10
|
+
from .utils.ostypes import Fd
|
11
|
+
|
12
|
+
|
13
|
+
##
|
14
|
+
|
15
|
+
|
16
|
+
HasDispatchersList = ta.NewType('HasDispatchersList', ta.Sequence[HasDispatchers])
|
17
|
+
|
18
|
+
|
19
|
+
class IoManager(HasDispatchers):
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
*,
|
23
|
+
poller: FdIoPoller,
|
24
|
+
has_dispatchers_list: HasDispatchersList,
|
25
|
+
) -> None:
|
26
|
+
super().__init__()
|
27
|
+
|
28
|
+
self._poller = poller
|
29
|
+
self._has_dispatchers_list = has_dispatchers_list
|
30
|
+
|
31
|
+
def get_dispatchers(self) -> Dispatchers:
|
32
|
+
return Dispatchers(
|
33
|
+
d
|
34
|
+
for hd in self._has_dispatchers_list
|
35
|
+
for d in hd.get_dispatchers()
|
36
|
+
)
|
37
|
+
|
38
|
+
def poll(self) -> None:
|
39
|
+
dispatchers = self.get_dispatchers()
|
40
|
+
|
41
|
+
self._poller.update(
|
42
|
+
{fd for fd, d in dispatchers.items() if d.readable()},
|
43
|
+
{fd for fd, d in dispatchers.items() if d.writable()},
|
44
|
+
)
|
45
|
+
|
46
|
+
timeout = 1 # this cannot be fewer than the smallest TickEvent (5)
|
47
|
+
log.info(f'Polling: {timeout=}') # noqa
|
48
|
+
polled = self._poller.poll(timeout)
|
49
|
+
log.info(f'Polled: {polled=}') # noqa
|
50
|
+
if polled.msg is not None:
|
51
|
+
log.error(polled.msg)
|
52
|
+
if polled.exc is not None:
|
53
|
+
log.error('Poll exception: %r', polled.exc)
|
54
|
+
|
55
|
+
for r in polled.r:
|
56
|
+
fd = Fd(r)
|
57
|
+
if fd in dispatchers:
|
58
|
+
dispatcher = dispatchers[fd]
|
59
|
+
try:
|
60
|
+
log.debug('read event caused by %r', dispatcher)
|
61
|
+
dispatcher.on_readable()
|
62
|
+
if not dispatcher.readable():
|
63
|
+
self._poller.unregister_readable(fd)
|
64
|
+
except ExitNow:
|
65
|
+
raise
|
66
|
+
except Exception as exc: # noqa
|
67
|
+
log.exception('Error in dispatcher: %r', dispatcher)
|
68
|
+
dispatcher.on_error(exc)
|
69
|
+
else:
|
70
|
+
# if the fd is not in combined map, we should unregister it. otherwise, it will be polled every
|
71
|
+
# time, which may cause 100% cpu usage
|
72
|
+
log.debug('unexpected read event from fd %r', fd)
|
73
|
+
try:
|
74
|
+
self._poller.unregister_readable(fd)
|
75
|
+
except Exception: # noqa
|
76
|
+
pass
|
77
|
+
|
78
|
+
for w in polled.w:
|
79
|
+
fd = Fd(w)
|
80
|
+
if fd in dispatchers:
|
81
|
+
dispatcher = dispatchers[fd]
|
82
|
+
try:
|
83
|
+
log.debug('write event caused by %r', dispatcher)
|
84
|
+
dispatcher.on_writable()
|
85
|
+
if not dispatcher.writable():
|
86
|
+
self._poller.unregister_writable(fd)
|
87
|
+
except ExitNow:
|
88
|
+
raise
|
89
|
+
except Exception as exc: # noqa
|
90
|
+
log.exception('Error in dispatcher: %r', dispatcher)
|
91
|
+
dispatcher.on_error(exc)
|
92
|
+
else:
|
93
|
+
log.debug('unexpected write event from fd %r', fd)
|
94
|
+
try:
|
95
|
+
self._poller.unregister_writable(fd)
|
96
|
+
except Exception: # noqa
|
97
|
+
pass
|
ominfra/supervisor/main.py
CHANGED
@@ -37,17 +37,17 @@ from omlish.lite.http.coroserver import CoroHttpServer
|
|
37
37
|
from omlish.lite.inject import inj
|
38
38
|
from omlish.lite.journald import journald_log_handler_factory
|
39
39
|
from omlish.lite.logs import configure_standard_logging
|
40
|
+
from omlish.lite.runtime import is_debugger_attached
|
40
41
|
|
41
42
|
from ..configs import read_config_file
|
42
43
|
from .configs import ServerConfig
|
43
44
|
from .configs import prepare_server_config
|
44
|
-
from .context import ServerContextImpl
|
45
|
-
from .context import ServerEpoch
|
46
45
|
from .inject import bind_server
|
47
46
|
from .spawningimpl import InheritedFds
|
48
47
|
from .states import SupervisorState
|
49
|
-
from .supervisor import ExitNow
|
50
48
|
from .supervisor import Supervisor
|
49
|
+
from .types import ExitNow
|
50
|
+
from .types import ServerEpoch
|
51
51
|
from .utils.fds import get_open_fds
|
52
52
|
|
53
53
|
|
@@ -79,7 +79,7 @@ def main(
|
|
79
79
|
if not no_logging:
|
80
80
|
configure_standard_logging(
|
81
81
|
'INFO',
|
82
|
-
handler_factory=journald_log_handler_factory if not args.no_journald else None,
|
82
|
+
handler_factory=journald_log_handler_factory if not (args.no_journald or is_debugger_attached()) else None,
|
83
83
|
)
|
84
84
|
|
85
85
|
#
|
@@ -102,7 +102,6 @@ def main(
|
|
102
102
|
inherited_fds=inherited_fds,
|
103
103
|
))
|
104
104
|
|
105
|
-
context = injector[ServerContextImpl]
|
106
105
|
supervisor = injector[Supervisor]
|
107
106
|
|
108
107
|
try:
|
@@ -110,7 +109,7 @@ def main(
|
|
110
109
|
except ExitNow:
|
111
110
|
pass
|
112
111
|
|
113
|
-
if
|
112
|
+
if supervisor.state < SupervisorState.RESTARTING:
|
114
113
|
break
|
115
114
|
|
116
115
|
|