ominfra 0.0.0.dev123__py3-none-any.whl → 0.0.0.dev125__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 +7 -4
- ominfra/deploy/poly/_main.py +4 -10
- ominfra/deploy/poly/base.py +7 -8
- ominfra/pyremote/_runcommands.py +7 -4
- ominfra/scripts/journald2aws.py +7 -4
- ominfra/scripts/supervisor.py +1388 -180
- ominfra/supervisor/LICENSE.txt +28 -0
- ominfra/supervisor/context.py +5 -5
- ominfra/supervisor/dispatchers.py +5 -5
- ominfra/supervisor/events.py +3 -3
- ominfra/supervisor/groups.py +12 -18
- ominfra/supervisor/inject.py +64 -0
- ominfra/supervisor/main.py +38 -75
- ominfra/supervisor/process.py +8 -8
- ominfra/supervisor/supervisor.py +11 -16
- ominfra/supervisor/types.py +51 -6
- {ominfra-0.0.0.dev123.dist-info → ominfra-0.0.0.dev125.dist-info}/METADATA +7 -8
- {ominfra-0.0.0.dev123.dist-info → ominfra-0.0.0.dev125.dist-info}/RECORD +22 -20
- {ominfra-0.0.0.dev123.dist-info → ominfra-0.0.0.dev125.dist-info}/WHEEL +1 -1
- {ominfra-0.0.0.dev123.dist-info → ominfra-0.0.0.dev125.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev123.dist-info → ominfra-0.0.0.dev125.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev123.dist-info → ominfra-0.0.0.dev125.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
Supervisor is licensed under the following license:
|
2
|
+
|
3
|
+
A copyright notice accompanies this license document that identifies the copyright holders.
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
6
|
+
following conditions are met:
|
7
|
+
|
8
|
+
1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the
|
9
|
+
following disclaimer.
|
10
|
+
|
11
|
+
2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the
|
12
|
+
following disclaimer in the documentation and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
3. Names of the copyright holders must not be used to endorse or promote products derived from this software without
|
15
|
+
prior written permission from the copyright holders.
|
16
|
+
|
17
|
+
4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the
|
18
|
+
files and the date of any change.
|
19
|
+
|
20
|
+
Disclaimer
|
21
|
+
|
22
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
|
23
|
+
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
24
|
+
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
25
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
26
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
27
|
+
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
28
|
+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
ominfra/supervisor/context.py
CHANGED
@@ -20,8 +20,8 @@ from .exceptions import NotExecutableError
|
|
20
20
|
from .exceptions import NotFoundError
|
21
21
|
from .poller import Poller
|
22
22
|
from .states import SupervisorState
|
23
|
-
from .types import
|
24
|
-
from .types import
|
23
|
+
from .types import Process
|
24
|
+
from .types import ServerContext
|
25
25
|
from .utils import close_fd
|
26
26
|
from .utils import mktempfile
|
27
27
|
from .utils import real_exit
|
@@ -31,7 +31,7 @@ from .utils import try_unlink
|
|
31
31
|
ServerEpoch = ta.NewType('ServerEpoch', int)
|
32
32
|
|
33
33
|
|
34
|
-
class ServerContext
|
34
|
+
class ServerContextImpl(ServerContext):
|
35
35
|
def __init__(
|
36
36
|
self,
|
37
37
|
config: ServerConfig,
|
@@ -45,7 +45,7 @@ class ServerContext(AbstractServerContext):
|
|
45
45
|
self._poller = poller
|
46
46
|
self._epoch = epoch
|
47
47
|
|
48
|
-
self._pid_history: ta.Dict[int,
|
48
|
+
self._pid_history: ta.Dict[int, Process] = {}
|
49
49
|
self._state: SupervisorState = SupervisorState.RUNNING
|
50
50
|
|
51
51
|
if config.user is not None:
|
@@ -78,7 +78,7 @@ class ServerContext(AbstractServerContext):
|
|
78
78
|
self._state = state
|
79
79
|
|
80
80
|
@property
|
81
|
-
def pid_history(self) -> ta.Dict[int,
|
81
|
+
def pid_history(self) -> ta.Dict[int, Process]:
|
82
82
|
return self._pid_history
|
83
83
|
|
84
84
|
@property
|
@@ -12,7 +12,7 @@ from .events import EventCallbacks
|
|
12
12
|
from .events import ProcessCommunicationEvent
|
13
13
|
from .events import ProcessLogStderrEvent
|
14
14
|
from .events import ProcessLogStdoutEvent
|
15
|
-
from .types import
|
15
|
+
from .types import Process
|
16
16
|
from .utils import as_bytes
|
17
17
|
from .utils import compact_traceback
|
18
18
|
from .utils import find_prefix_at_end
|
@@ -23,7 +23,7 @@ from .utils import strip_escapes
|
|
23
23
|
class Dispatcher(abc.ABC):
|
24
24
|
def __init__(
|
25
25
|
self,
|
26
|
-
process:
|
26
|
+
process: Process,
|
27
27
|
channel: str,
|
28
28
|
fd: int,
|
29
29
|
*,
|
@@ -42,7 +42,7 @@ class Dispatcher(abc.ABC):
|
|
42
42
|
return f'<{self.__class__.__name__} at {id(self)} for {self._process} ({self._channel})>'
|
43
43
|
|
44
44
|
@property
|
45
|
-
def process(self) ->
|
45
|
+
def process(self) -> Process:
|
46
46
|
return self._process
|
47
47
|
|
48
48
|
@property
|
@@ -97,7 +97,7 @@ class OutputDispatcher(Dispatcher):
|
|
97
97
|
|
98
98
|
def __init__(
|
99
99
|
self,
|
100
|
-
process:
|
100
|
+
process: Process,
|
101
101
|
event_type: ta.Type[ProcessCommunicationEvent],
|
102
102
|
fd: int,
|
103
103
|
**kwargs: ta.Any,
|
@@ -306,7 +306,7 @@ class OutputDispatcher(Dispatcher):
|
|
306
306
|
class InputDispatcher(Dispatcher):
|
307
307
|
def __init__(
|
308
308
|
self,
|
309
|
-
process:
|
309
|
+
process: Process,
|
310
310
|
channel: str,
|
311
311
|
fd: int,
|
312
312
|
**kwargs: ta.Any,
|
ominfra/supervisor/events.py
CHANGED
@@ -5,6 +5,9 @@ import typing as ta
|
|
5
5
|
from .states import ProcessState
|
6
6
|
|
7
7
|
|
8
|
+
EventCallback = ta.Callable[['Event'], None]
|
9
|
+
|
10
|
+
|
8
11
|
##
|
9
12
|
|
10
13
|
|
@@ -15,9 +18,6 @@ class Event(abc.ABC): # noqa
|
|
15
18
|
##
|
16
19
|
|
17
20
|
|
18
|
-
EventCallback = ta.Callable[['Event'], None]
|
19
|
-
|
20
|
-
|
21
21
|
class EventCallbacks:
|
22
22
|
def __init__(self) -> None:
|
23
23
|
super().__init__()
|
ominfra/supervisor/groups.py
CHANGED
@@ -1,48 +1,42 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
-
import dataclasses as dc
|
3
2
|
import typing as ta
|
4
3
|
|
5
|
-
from .
|
4
|
+
from omlish.lite.typing import Func
|
5
|
+
|
6
6
|
from .configs import ProcessGroupConfig
|
7
|
-
from .context import ServerContext
|
8
7
|
from .dispatchers import Dispatcher
|
9
8
|
from .events import EventCallbacks
|
10
9
|
from .events import ProcessGroupAddedEvent
|
11
10
|
from .events import ProcessGroupRemovedEvent
|
12
11
|
from .states import ProcessState
|
13
|
-
from .types import
|
14
|
-
from .types import
|
15
|
-
from .types import
|
12
|
+
from .types import Process
|
13
|
+
from .types import ProcessGroup
|
14
|
+
from .types import ServerContext
|
16
15
|
|
17
16
|
|
18
17
|
##
|
19
18
|
|
20
19
|
|
21
|
-
|
22
|
-
class SubprocessFactory:
|
23
|
-
fn: ta.Callable[[ProcessConfig, AbstractProcessGroup], AbstractSubprocess]
|
24
|
-
|
25
|
-
def __call__(self, config: ProcessConfig, group: AbstractProcessGroup) -> AbstractSubprocess:
|
26
|
-
return self.fn(config, group)
|
20
|
+
ProcessFactory = ta.NewType('ProcessFactory', Func[Process]) # (config: ProcessConfig, group: ProcessGroup)
|
27
21
|
|
28
22
|
|
29
|
-
class ProcessGroup
|
23
|
+
class ProcessGroupImpl(ProcessGroup):
|
30
24
|
def __init__(
|
31
25
|
self,
|
32
26
|
config: ProcessGroupConfig,
|
33
27
|
context: ServerContext,
|
34
28
|
*,
|
35
|
-
|
29
|
+
process_factory: ProcessFactory,
|
36
30
|
):
|
37
31
|
super().__init__()
|
38
32
|
|
39
33
|
self._config = config
|
40
34
|
self._context = context
|
41
|
-
self.
|
35
|
+
self._process_factory = process_factory
|
42
36
|
|
43
37
|
self._processes = {}
|
44
38
|
for pconfig in self._config.processes or []:
|
45
|
-
process = self.
|
39
|
+
process = self._process_factory(pconfig, self)
|
46
40
|
self._processes[pconfig.name] = process
|
47
41
|
|
48
42
|
@property
|
@@ -54,7 +48,7 @@ class ProcessGroup(AbstractProcessGroup):
|
|
54
48
|
return self._config.name
|
55
49
|
|
56
50
|
@property
|
57
|
-
def context(self) ->
|
51
|
+
def context(self) -> ServerContext:
|
58
52
|
return self._context
|
59
53
|
|
60
54
|
def __repr__(self):
|
@@ -89,7 +83,7 @@ class ProcessGroup(AbstractProcessGroup):
|
|
89
83
|
# BACKOFF -> FATAL
|
90
84
|
proc.give_up()
|
91
85
|
|
92
|
-
def get_unstopped_processes(self) -> ta.List[
|
86
|
+
def get_unstopped_processes(self) -> ta.List[Process]:
|
93
87
|
return [x for x in self._processes.values() if not x.get_state().stopped]
|
94
88
|
|
95
89
|
def get_dispatchers(self) -> ta.Dict[int, Dispatcher]:
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish.lite.inject import InjectorBindingOrBindings
|
5
|
+
from omlish.lite.inject import InjectorBindings
|
6
|
+
from omlish.lite.inject import inj
|
7
|
+
|
8
|
+
from .configs import ServerConfig
|
9
|
+
from .context import ServerContextImpl
|
10
|
+
from .context import ServerEpoch
|
11
|
+
from .events import EventCallbacks
|
12
|
+
from .groups import ProcessFactory
|
13
|
+
from .groups import ProcessGroupImpl
|
14
|
+
from .poller import Poller
|
15
|
+
from .poller import get_poller_impl
|
16
|
+
from .process import InheritedFds
|
17
|
+
from .process import ProcessImpl
|
18
|
+
from .signals import SignalReceiver
|
19
|
+
from .supervisor import ProcessGroupFactory
|
20
|
+
from .supervisor import ProcessGroups
|
21
|
+
from .supervisor import SignalHandler
|
22
|
+
from .supervisor import Supervisor
|
23
|
+
from .types import ServerContext
|
24
|
+
|
25
|
+
|
26
|
+
##
|
27
|
+
|
28
|
+
|
29
|
+
def bind_server(
|
30
|
+
config: ServerConfig,
|
31
|
+
*,
|
32
|
+
server_epoch: ta.Optional[ServerEpoch] = None,
|
33
|
+
inherited_fds: ta.Optional[InheritedFds] = None,
|
34
|
+
) -> InjectorBindings:
|
35
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
36
|
+
inj.bind(config),
|
37
|
+
|
38
|
+
inj.bind(get_poller_impl(), key=Poller, singleton=True),
|
39
|
+
|
40
|
+
inj.bind(ServerContextImpl, singleton=True),
|
41
|
+
inj.bind(ServerContext, to_key=ServerContextImpl),
|
42
|
+
|
43
|
+
inj.bind(EventCallbacks, singleton=True),
|
44
|
+
|
45
|
+
inj.bind(SignalReceiver, singleton=True),
|
46
|
+
|
47
|
+
inj.bind(SignalHandler, singleton=True),
|
48
|
+
inj.bind(ProcessGroups, singleton=True),
|
49
|
+
inj.bind(Supervisor, singleton=True),
|
50
|
+
|
51
|
+
inj.bind_factory(ProcessGroupFactory, ProcessGroupImpl),
|
52
|
+
inj.bind_factory(ProcessFactory, ProcessImpl),
|
53
|
+
]
|
54
|
+
|
55
|
+
#
|
56
|
+
|
57
|
+
if server_epoch is not None:
|
58
|
+
lst.append(inj.bind(server_epoch, key=ServerEpoch))
|
59
|
+
if inherited_fds is not None:
|
60
|
+
lst.append(inj.bind(inherited_fds, key=InheritedFds))
|
61
|
+
|
62
|
+
#
|
63
|
+
|
64
|
+
return inj.as_bindings(*lst)
|
ominfra/supervisor/main.py
CHANGED
@@ -1,41 +1,52 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
# ruff: noqa: UP006 UP007
|
3
3
|
# @omlish-amalg ../scripts/supervisor.py
|
4
|
-
|
4
|
+
# Supervisor is licensed under the following license:
|
5
|
+
#
|
6
|
+
# A copyright notice accompanies this license document that identifies the copyright holders.
|
7
|
+
#
|
8
|
+
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
9
|
+
# following conditions are met:
|
10
|
+
#
|
11
|
+
# 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the
|
12
|
+
# following disclaimer.
|
13
|
+
#
|
14
|
+
# 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the
|
15
|
+
# following disclaimer in the documentation and/or other materials provided with the distribution.
|
16
|
+
#
|
17
|
+
# 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without
|
18
|
+
# prior written permission from the copyright holders.
|
19
|
+
#
|
20
|
+
# 4. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed
|
21
|
+
# the files and the date of any change.
|
22
|
+
#
|
23
|
+
# Disclaimer
|
24
|
+
#
|
25
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
|
26
|
+
# NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
27
|
+
# EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
28
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
29
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
30
|
+
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
31
|
+
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
5
32
|
import itertools
|
6
33
|
import os.path
|
7
34
|
import typing as ta
|
8
35
|
|
9
|
-
from omlish.lite.
|
10
|
-
from omlish.lite.inject import InjectorBindingOrBindings
|
11
|
-
from omlish.lite.inject import InjectorBindings
|
36
|
+
from omlish.lite.http.coroserver import CoroHttpServer
|
12
37
|
from omlish.lite.inject import inj
|
13
38
|
from omlish.lite.journald import journald_log_handler_factory
|
14
39
|
from omlish.lite.logs import configure_standard_logging
|
15
40
|
|
16
41
|
from ..configs import read_config_file
|
17
|
-
from .configs import ProcessConfig
|
18
|
-
from .configs import ProcessGroupConfig
|
19
42
|
from .configs import ServerConfig
|
20
43
|
from .configs import prepare_server_config
|
21
|
-
from .context import
|
44
|
+
from .context import ServerContextImpl
|
22
45
|
from .context import ServerEpoch
|
23
|
-
from .
|
24
|
-
from .groups import ProcessGroup
|
25
|
-
from .groups import SubprocessFactory
|
26
|
-
from .poller import Poller
|
27
|
-
from .poller import get_poller_impl
|
46
|
+
from .inject import bind_server
|
28
47
|
from .process import InheritedFds
|
29
|
-
from .process import Subprocess
|
30
|
-
from .signals import SignalReceiver
|
31
48
|
from .states import SupervisorState
|
32
|
-
from .supervisor import ProcessGroupFactory
|
33
|
-
from .supervisor import ProcessGroups
|
34
|
-
from .supervisor import SignalHandler
|
35
49
|
from .supervisor import Supervisor
|
36
|
-
from .types import AbstractProcessGroup
|
37
|
-
from .types import AbstractServerContext
|
38
|
-
from .types import AbstractSubprocess
|
39
50
|
from .utils import ExitNow
|
40
51
|
from .utils import get_open_fds
|
41
52
|
|
@@ -43,63 +54,15 @@ from .utils import get_open_fds
|
|
43
54
|
##
|
44
55
|
|
45
56
|
|
46
|
-
def build_server_bindings(
|
47
|
-
config: ServerConfig,
|
48
|
-
*,
|
49
|
-
server_epoch: ta.Optional[ServerEpoch] = None,
|
50
|
-
inherited_fds: ta.Optional[InheritedFds] = None,
|
51
|
-
) -> InjectorBindings:
|
52
|
-
lst: ta.List[InjectorBindingOrBindings] = [
|
53
|
-
inj.bind(config),
|
54
|
-
|
55
|
-
inj.bind(get_poller_impl(), key=Poller, singleton=True),
|
56
|
-
|
57
|
-
inj.bind(ServerContext, singleton=True),
|
58
|
-
inj.bind(AbstractServerContext, to_key=ServerContext),
|
59
|
-
|
60
|
-
inj.bind(EventCallbacks, singleton=True),
|
61
|
-
|
62
|
-
inj.bind(SignalReceiver, singleton=True),
|
63
|
-
|
64
|
-
inj.bind(SignalHandler, singleton=True),
|
65
|
-
inj.bind(ProcessGroups, singleton=True),
|
66
|
-
inj.bind(Supervisor, singleton=True),
|
67
|
-
]
|
68
|
-
|
69
|
-
#
|
70
|
-
|
71
|
-
def make_process_group_factory(injector: Injector) -> ProcessGroupFactory:
|
72
|
-
def inner(group_config: ProcessGroupConfig) -> ProcessGroup:
|
73
|
-
return injector.inject(functools.partial(ProcessGroup, group_config))
|
74
|
-
return ProcessGroupFactory(inner)
|
75
|
-
lst.append(inj.bind(make_process_group_factory))
|
76
|
-
|
77
|
-
def make_subprocess_factory(injector: Injector) -> SubprocessFactory:
|
78
|
-
def inner(process_config: ProcessConfig, group: AbstractProcessGroup) -> AbstractSubprocess:
|
79
|
-
return injector.inject(functools.partial(Subprocess, process_config, group))
|
80
|
-
return SubprocessFactory(inner)
|
81
|
-
lst.append(inj.bind(make_subprocess_factory))
|
82
|
-
|
83
|
-
#
|
84
|
-
|
85
|
-
if server_epoch is not None:
|
86
|
-
lst.append(inj.bind(server_epoch, key=ServerEpoch))
|
87
|
-
if inherited_fds is not None:
|
88
|
-
lst.append(inj.bind(inherited_fds, key=InheritedFds))
|
89
|
-
|
90
|
-
#
|
91
|
-
|
92
|
-
return inj.as_bindings(*lst)
|
93
|
-
|
94
|
-
|
95
|
-
##
|
96
|
-
|
97
|
-
|
98
57
|
def main(
|
99
58
|
argv: ta.Optional[ta.Sequence[str]] = None,
|
100
59
|
*,
|
101
60
|
no_logging: bool = False,
|
102
61
|
) -> None:
|
62
|
+
server_cls = CoroHttpServer # noqa
|
63
|
+
|
64
|
+
#
|
65
|
+
|
103
66
|
import argparse
|
104
67
|
|
105
68
|
parser = argparse.ArgumentParser()
|
@@ -133,14 +96,14 @@ def main(
|
|
133
96
|
prepare=prepare_server_config,
|
134
97
|
)
|
135
98
|
|
136
|
-
injector = inj.create_injector(
|
99
|
+
injector = inj.create_injector(bind_server(
|
137
100
|
config,
|
138
101
|
server_epoch=ServerEpoch(epoch),
|
139
102
|
inherited_fds=inherited_fds,
|
140
103
|
))
|
141
104
|
|
142
|
-
context = injector
|
143
|
-
supervisor = injector
|
105
|
+
context = injector[ServerContextImpl]
|
106
|
+
supervisor = injector[Supervisor]
|
144
107
|
|
145
108
|
try:
|
146
109
|
supervisor.main()
|
ominfra/supervisor/process.py
CHANGED
@@ -30,9 +30,9 @@ from .exceptions import ProcessError
|
|
30
30
|
from .signals import sig_name
|
31
31
|
from .states import ProcessState
|
32
32
|
from .states import SupervisorState
|
33
|
-
from .types import
|
34
|
-
from .types import
|
35
|
-
from .types import
|
33
|
+
from .types import Process
|
34
|
+
from .types import ProcessGroup
|
35
|
+
from .types import ServerContext
|
36
36
|
from .utils import as_bytes
|
37
37
|
from .utils import as_string
|
38
38
|
from .utils import close_fd
|
@@ -48,15 +48,15 @@ InheritedFds = ta.NewType('InheritedFds', ta.FrozenSet[int])
|
|
48
48
|
##
|
49
49
|
|
50
50
|
|
51
|
-
class
|
51
|
+
class ProcessImpl(Process):
|
52
52
|
"""A class to manage a subprocess."""
|
53
53
|
|
54
54
|
def __init__(
|
55
55
|
self,
|
56
56
|
config: ProcessConfig,
|
57
|
-
group:
|
57
|
+
group: ProcessGroup,
|
58
58
|
*,
|
59
|
-
context:
|
59
|
+
context: ServerContext,
|
60
60
|
event_callbacks: EventCallbacks,
|
61
61
|
|
62
62
|
inherited_fds: ta.Optional[InheritedFds] = None,
|
@@ -95,7 +95,7 @@ class Subprocess(AbstractSubprocess):
|
|
95
95
|
return self._pid
|
96
96
|
|
97
97
|
@property
|
98
|
-
def group(self) ->
|
98
|
+
def group(self) -> ProcessGroup:
|
99
99
|
return self._group
|
100
100
|
|
101
101
|
@property
|
@@ -103,7 +103,7 @@ class Subprocess(AbstractSubprocess):
|
|
103
103
|
return self._config
|
104
104
|
|
105
105
|
@property
|
106
|
-
def context(self) ->
|
106
|
+
def context(self) -> ServerContext:
|
107
107
|
return self._context
|
108
108
|
|
109
109
|
@property
|
ominfra/supervisor/supervisor.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
-
import dataclasses as dc
|
3
2
|
import signal
|
4
3
|
import time
|
5
4
|
import typing as ta
|
@@ -7,9 +6,10 @@ import typing as ta
|
|
7
6
|
from omlish.lite.cached import cached_nullary
|
8
7
|
from omlish.lite.check import check_not_none
|
9
8
|
from omlish.lite.logs import log
|
9
|
+
from omlish.lite.typing import Func
|
10
10
|
|
11
11
|
from .configs import ProcessGroupConfig
|
12
|
-
from .context import
|
12
|
+
from .context import ServerContextImpl
|
13
13
|
from .dispatchers import Dispatcher
|
14
14
|
from .events import TICK_EVENTS
|
15
15
|
from .events import EventCallbacks
|
@@ -18,10 +18,10 @@ from .events import SupervisorStoppingEvent
|
|
18
18
|
from .groups import ProcessGroup
|
19
19
|
from .groups import ProcessGroups
|
20
20
|
from .poller import Poller
|
21
|
-
from .process import Subprocess
|
22
21
|
from .signals import SignalReceiver
|
23
22
|
from .signals import sig_name
|
24
23
|
from .states import SupervisorState
|
24
|
+
from .types import Process
|
25
25
|
from .utils import ExitNow
|
26
26
|
from .utils import as_string
|
27
27
|
from .utils import decode_wait_status
|
@@ -35,7 +35,7 @@ class SignalHandler:
|
|
35
35
|
def __init__(
|
36
36
|
self,
|
37
37
|
*,
|
38
|
-
context:
|
38
|
+
context: ServerContextImpl,
|
39
39
|
signal_receiver: SignalReceiver,
|
40
40
|
process_groups: ProcessGroups,
|
41
41
|
) -> None:
|
@@ -87,19 +87,14 @@ class SignalHandler:
|
|
87
87
|
##
|
88
88
|
|
89
89
|
|
90
|
-
|
91
|
-
class ProcessGroupFactory:
|
92
|
-
fn: ta.Callable[[ProcessGroupConfig], ProcessGroup]
|
93
|
-
|
94
|
-
def __call__(self, config: ProcessGroupConfig) -> ProcessGroup:
|
95
|
-
return self.fn(config)
|
90
|
+
ProcessGroupFactory = ta.NewType('ProcessGroupFactory', Func[ProcessGroup]) # (config: ProcessGroupConfig)
|
96
91
|
|
97
92
|
|
98
93
|
class Supervisor:
|
99
94
|
def __init__(
|
100
95
|
self,
|
101
96
|
*,
|
102
|
-
context:
|
97
|
+
context: ServerContextImpl,
|
103
98
|
poller: Poller,
|
104
99
|
process_groups: ProcessGroups,
|
105
100
|
signal_handler: SignalHandler,
|
@@ -123,7 +118,7 @@ class Supervisor:
|
|
123
118
|
#
|
124
119
|
|
125
120
|
@property
|
126
|
-
def context(self) ->
|
121
|
+
def context(self) -> ServerContextImpl:
|
127
122
|
return self._context
|
128
123
|
|
129
124
|
def get_state(self) -> SupervisorState:
|
@@ -170,16 +165,16 @@ class Supervisor:
|
|
170
165
|
return True
|
171
166
|
|
172
167
|
def get_process_map(self) -> ta.Dict[int, Dispatcher]:
|
173
|
-
process_map = {}
|
168
|
+
process_map: ta.Dict[int, Dispatcher] = {}
|
174
169
|
for group in self._process_groups:
|
175
170
|
process_map.update(group.get_dispatchers())
|
176
171
|
return process_map
|
177
172
|
|
178
|
-
def shutdown_report(self) -> ta.List[
|
179
|
-
unstopped: ta.List[
|
173
|
+
def shutdown_report(self) -> ta.List[Process]:
|
174
|
+
unstopped: ta.List[Process] = []
|
180
175
|
|
181
176
|
for group in self._process_groups:
|
182
|
-
unstopped.extend(group.get_unstopped_processes())
|
177
|
+
unstopped.extend(group.get_unstopped_processes())
|
183
178
|
|
184
179
|
if unstopped:
|
185
180
|
# throttle 'waiting for x to die' reports
|
ominfra/supervisor/types.py
CHANGED
@@ -10,7 +10,7 @@ from .states import ProcessState
|
|
10
10
|
from .states import SupervisorState
|
11
11
|
|
12
12
|
|
13
|
-
class
|
13
|
+
class ServerContext(abc.ABC):
|
14
14
|
@property
|
15
15
|
@abc.abstractmethod
|
16
16
|
def config(self) -> ServerConfig:
|
@@ -27,12 +27,24 @@ class AbstractServerContext(abc.ABC):
|
|
27
27
|
|
28
28
|
@property
|
29
29
|
@abc.abstractmethod
|
30
|
-
def pid_history(self) -> ta.Dict[int, '
|
30
|
+
def pid_history(self) -> ta.Dict[int, 'Process']:
|
31
31
|
raise NotImplementedError
|
32
32
|
|
33
33
|
|
34
|
+
# class Dispatcher(abc.ABC):
|
35
|
+
# pass
|
36
|
+
#
|
37
|
+
#
|
38
|
+
# class OutputDispatcher(Dispatcher, abc.ABC):
|
39
|
+
# pass
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# class InputDispatcher(Dispatcher, abc.ABC):
|
43
|
+
# pass
|
44
|
+
|
45
|
+
|
34
46
|
@functools.total_ordering
|
35
|
-
class
|
47
|
+
class Process(abc.ABC):
|
36
48
|
@property
|
37
49
|
@abc.abstractmethod
|
38
50
|
def pid(self) -> int:
|
@@ -51,7 +63,7 @@ class AbstractSubprocess(abc.ABC):
|
|
51
63
|
|
52
64
|
@property
|
53
65
|
@abc.abstractmethod
|
54
|
-
def context(self) ->
|
66
|
+
def context(self) -> ServerContext:
|
55
67
|
raise NotImplementedError
|
56
68
|
|
57
69
|
@abc.abstractmethod
|
@@ -87,12 +99,12 @@ class AbstractSubprocess(abc.ABC):
|
|
87
99
|
raise NotImplementedError
|
88
100
|
|
89
101
|
@abc.abstractmethod
|
90
|
-
def get_dispatchers(self) -> ta.Mapping[int, ta.Any]: #
|
102
|
+
def get_dispatchers(self) -> ta.Mapping[int, ta.Any]: # Dispatcher]:
|
91
103
|
raise NotImplementedError
|
92
104
|
|
93
105
|
|
94
106
|
@functools.total_ordering
|
95
|
-
class
|
107
|
+
class ProcessGroup(abc.ABC):
|
96
108
|
@property
|
97
109
|
@abc.abstractmethod
|
98
110
|
def config(self) -> ProcessGroupConfig:
|
@@ -103,3 +115,36 @@ class AbstractProcessGroup(abc.ABC):
|
|
103
115
|
|
104
116
|
def __eq__(self, other):
|
105
117
|
return self.config.priority == other.config.priority
|
118
|
+
|
119
|
+
@abc.abstractmethod
|
120
|
+
def transition(self) -> None:
|
121
|
+
raise NotImplementedError
|
122
|
+
|
123
|
+
@abc.abstractmethod
|
124
|
+
def stop_all(self) -> None:
|
125
|
+
raise NotImplementedError
|
126
|
+
|
127
|
+
@property
|
128
|
+
@abc.abstractmethod
|
129
|
+
def name(self) -> str:
|
130
|
+
raise NotImplementedError
|
131
|
+
|
132
|
+
@abc.abstractmethod
|
133
|
+
def before_remove(self) -> None:
|
134
|
+
raise NotImplementedError
|
135
|
+
|
136
|
+
@abc.abstractmethod
|
137
|
+
def get_dispatchers(self) -> ta.Mapping[int, ta.Any]: # Dispatcher]:
|
138
|
+
raise NotImplementedError
|
139
|
+
|
140
|
+
@abc.abstractmethod
|
141
|
+
def reopen_logs(self) -> None:
|
142
|
+
raise NotImplementedError
|
143
|
+
|
144
|
+
@abc.abstractmethod
|
145
|
+
def get_unstopped_processes(self) -> ta.List[Process]:
|
146
|
+
raise NotImplementedError
|
147
|
+
|
148
|
+
@abc.abstractmethod
|
149
|
+
def after_setuid(self) -> None:
|
150
|
+
raise NotImplementedError
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev125
|
4
4
|
Summary: ominfra
|
5
5
|
Author: wrmsr
|
6
6
|
License: BSD-3-Clause
|
@@ -12,12 +12,11 @@ Classifier: Operating System :: OS Independent
|
|
12
12
|
Classifier: Operating System :: POSIX
|
13
13
|
Requires-Python: >=3.12
|
14
14
|
License-File: LICENSE
|
15
|
-
Requires-Dist: omdev
|
16
|
-
Requires-Dist: omlish
|
15
|
+
Requires-Dist: omdev==0.0.0.dev125
|
16
|
+
Requires-Dist: omlish==0.0.0.dev125
|
17
17
|
Provides-Extra: all
|
18
|
-
Requires-Dist: paramiko
|
19
|
-
Requires-Dist: asyncssh
|
18
|
+
Requires-Dist: paramiko~=3.5; extra == "all"
|
19
|
+
Requires-Dist: asyncssh~=2.18; extra == "all"
|
20
20
|
Provides-Extra: ssh
|
21
|
-
Requires-Dist: paramiko
|
22
|
-
Requires-Dist: asyncssh
|
23
|
-
|
21
|
+
Requires-Dist: paramiko~=3.5; extra == "ssh"
|
22
|
+
Requires-Dist: asyncssh~=2.18; extra == "ssh"
|