ominfra 0.0.0.dev120__py3-none-any.whl → 0.0.0.dev122__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/clouds/aws/journald2aws/__main__.py +4 -0
- ominfra/clouds/aws/journald2aws/driver.py +34 -13
- ominfra/clouds/aws/journald2aws/main.py +2 -5
- ominfra/configs.py +70 -0
- ominfra/deploy/_executor.py +10 -1
- ominfra/deploy/poly/_main.py +1 -1
- ominfra/pyremote/_runcommands.py +10 -1
- ominfra/scripts/journald2aws.py +1002 -26
- ominfra/scripts/supervisor.py +1848 -138
- ominfra/supervisor/compat.py +13 -0
- ominfra/supervisor/configs.py +21 -0
- ominfra/supervisor/context.py +13 -2
- ominfra/supervisor/main.py +82 -11
- ominfra/supervisor/process.py +39 -4
- ominfra/supervisor/supervisor.py +23 -2
- ominfra/supervisor/types.py +5 -0
- ominfra/threadworkers.py +66 -9
- {ominfra-0.0.0.dev120.dist-info → ominfra-0.0.0.dev122.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev120.dist-info → ominfra-0.0.0.dev122.dist-info}/RECORD +23 -21
- {ominfra-0.0.0.dev120.dist-info → ominfra-0.0.0.dev122.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev120.dist-info → ominfra-0.0.0.dev122.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev120.dist-info → ominfra-0.0.0.dev122.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev120.dist-info → ominfra-0.0.0.dev122.dist-info}/top_level.txt +0 -0
ominfra/supervisor/compat.py
CHANGED
@@ -161,6 +161,19 @@ def close_fd(fd: int) -> bool:
|
|
161
161
|
return True
|
162
162
|
|
163
163
|
|
164
|
+
def is_fd_open(fd: int) -> bool:
|
165
|
+
try:
|
166
|
+
n = os.dup(fd)
|
167
|
+
except OSError:
|
168
|
+
return False
|
169
|
+
os.close(n)
|
170
|
+
return True
|
171
|
+
|
172
|
+
|
173
|
+
def get_open_fds(limit: int) -> ta.FrozenSet[int]:
|
174
|
+
return frozenset(filter(is_fd_open, range(limit)))
|
175
|
+
|
176
|
+
|
164
177
|
def mktempfile(suffix: str, prefix: str, dir: str) -> str: # noqa
|
165
178
|
fd, filename = tempfile.mkstemp(suffix, prefix, dir)
|
166
179
|
os.close(fd)
|
ominfra/supervisor/configs.py
CHANGED
@@ -5,6 +5,8 @@ import signal
|
|
5
5
|
import tempfile
|
6
6
|
import typing as ta
|
7
7
|
|
8
|
+
from ..configs import ConfigMapping
|
9
|
+
from ..configs import build_config_named_children
|
8
10
|
from .datatypes import byte_size
|
9
11
|
from .datatypes import existing_directory
|
10
12
|
from .datatypes import existing_dirpath
|
@@ -12,6 +14,9 @@ from .datatypes import logging_level
|
|
12
14
|
from .datatypes import octal_type
|
13
15
|
|
14
16
|
|
17
|
+
##
|
18
|
+
|
19
|
+
|
15
20
|
@dc.dataclass(frozen=True)
|
16
21
|
class ProcessConfig:
|
17
22
|
name: str
|
@@ -108,3 +113,19 @@ class ServerConfig:
|
|
108
113
|
child_logdir=child_logdir if child_logdir else tempfile.gettempdir(),
|
109
114
|
**kwargs,
|
110
115
|
)
|
116
|
+
|
117
|
+
|
118
|
+
##
|
119
|
+
|
120
|
+
|
121
|
+
def prepare_process_group_config(dct: ConfigMapping) -> ConfigMapping:
|
122
|
+
out = dict(dct)
|
123
|
+
out['processes'] = build_config_named_children(out.get('processes'))
|
124
|
+
return out
|
125
|
+
|
126
|
+
|
127
|
+
def prepare_server_config(dct: ta.Mapping[str, ta.Any]) -> ta.Mapping[str, ta.Any]:
|
128
|
+
out = dict(dct)
|
129
|
+
group_dcts = build_config_named_children(out.get('groups'))
|
130
|
+
out['groups'] = [prepare_process_group_config(group_dct) for group_dct in group_dcts or []]
|
131
|
+
return out
|
ominfra/supervisor/context.py
CHANGED
@@ -32,17 +32,24 @@ from .types import AbstractServerContext
|
|
32
32
|
from .types import AbstractSubprocess
|
33
33
|
|
34
34
|
|
35
|
+
ServerEpoch = ta.NewType('ServerEpoch', int)
|
36
|
+
|
37
|
+
InheritedFds = ta.NewType('InheritedFds', ta.FrozenSet[int])
|
38
|
+
|
39
|
+
|
35
40
|
class ServerContext(AbstractServerContext):
|
36
41
|
def __init__(
|
37
42
|
self,
|
38
43
|
config: ServerConfig,
|
39
44
|
*,
|
40
|
-
epoch:
|
45
|
+
epoch: ServerEpoch = ServerEpoch(0),
|
46
|
+
inherited_fds: ta.Optional[InheritedFds] = None,
|
41
47
|
) -> None:
|
42
48
|
super().__init__()
|
43
49
|
|
44
50
|
self._config = config
|
45
51
|
self._epoch = epoch
|
52
|
+
self._inherited_fds = InheritedFds(frozenset(inherited_fds or []))
|
46
53
|
|
47
54
|
self._pid_history: ta.Dict[int, AbstractSubprocess] = {}
|
48
55
|
self._state: SupervisorState = SupervisorStates.RUNNING
|
@@ -66,7 +73,7 @@ class ServerContext(AbstractServerContext):
|
|
66
73
|
return self._config
|
67
74
|
|
68
75
|
@property
|
69
|
-
def epoch(self) ->
|
76
|
+
def epoch(self) -> ServerEpoch:
|
70
77
|
return self._epoch
|
71
78
|
|
72
79
|
@property
|
@@ -96,6 +103,10 @@ class ServerContext(AbstractServerContext):
|
|
96
103
|
def gid(self) -> ta.Optional[int]:
|
97
104
|
return self._gid
|
98
105
|
|
106
|
+
@property
|
107
|
+
def inherited_fds(self) -> InheritedFds:
|
108
|
+
return self._inherited_fds
|
109
|
+
|
99
110
|
##
|
100
111
|
|
101
112
|
def set_signals(self) -> None:
|
ominfra/supervisor/main.py
CHANGED
@@ -1,19 +1,82 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
# ruff: noqa: UP006 UP007
|
3
3
|
# @omlish-amalg ../scripts/supervisor.py
|
4
|
+
import functools
|
4
5
|
import itertools
|
5
|
-
import
|
6
|
+
import os.path
|
6
7
|
import typing as ta
|
7
8
|
|
9
|
+
from omlish.lite.inject import Injector
|
10
|
+
from omlish.lite.inject import InjectorBindingOrBindings
|
11
|
+
from omlish.lite.inject import InjectorBindings
|
12
|
+
from omlish.lite.inject import inj
|
8
13
|
from omlish.lite.journald import journald_log_handler_factory
|
9
14
|
from omlish.lite.logs import configure_standard_logging
|
10
|
-
from omlish.lite.marshal import unmarshal_obj
|
11
15
|
|
16
|
+
from ..configs import read_config_file
|
12
17
|
from .compat import ExitNow
|
18
|
+
from .compat import get_open_fds
|
19
|
+
from .configs import ProcessConfig
|
20
|
+
from .configs import ProcessGroupConfig
|
13
21
|
from .configs import ServerConfig
|
22
|
+
from .configs import prepare_server_config
|
23
|
+
from .context import InheritedFds
|
14
24
|
from .context import ServerContext
|
25
|
+
from .context import ServerEpoch
|
26
|
+
from .process import ProcessGroup
|
27
|
+
from .process import Subprocess
|
28
|
+
from .process import SubprocessFactory
|
15
29
|
from .states import SupervisorStates
|
30
|
+
from .supervisor import ProcessGroupFactory
|
16
31
|
from .supervisor import Supervisor
|
32
|
+
from .types import AbstractServerContext
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
|
37
|
+
|
38
|
+
def build_server_bindings(
|
39
|
+
config: ServerConfig,
|
40
|
+
*,
|
41
|
+
server_epoch: ta.Optional[ServerEpoch] = None,
|
42
|
+
inherited_fds: ta.Optional[InheritedFds] = None,
|
43
|
+
) -> InjectorBindings:
|
44
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
45
|
+
inj.bind(config),
|
46
|
+
|
47
|
+
inj.bind(ServerContext, singleton=True),
|
48
|
+
inj.bind(AbstractServerContext, to_key=ServerContext),
|
49
|
+
|
50
|
+
inj.bind(Supervisor, singleton=True),
|
51
|
+
]
|
52
|
+
|
53
|
+
#
|
54
|
+
|
55
|
+
def make_process_group_factory(injector: Injector) -> ProcessGroupFactory:
|
56
|
+
def inner(group_config: ProcessGroupConfig) -> ProcessGroup:
|
57
|
+
return injector.inject(functools.partial(ProcessGroup, group_config))
|
58
|
+
return ProcessGroupFactory(inner)
|
59
|
+
lst.append(inj.bind(make_process_group_factory))
|
60
|
+
|
61
|
+
def make_subprocess_factory(injector: Injector) -> SubprocessFactory:
|
62
|
+
def inner(process_config: ProcessConfig, group: ProcessGroup) -> Subprocess:
|
63
|
+
return injector.inject(functools.partial(Subprocess, process_config, group))
|
64
|
+
return SubprocessFactory(inner)
|
65
|
+
lst.append(inj.bind(make_subprocess_factory))
|
66
|
+
|
67
|
+
#
|
68
|
+
|
69
|
+
if server_epoch is not None:
|
70
|
+
lst.append(inj.bind(server_epoch, key=ServerEpoch))
|
71
|
+
if inherited_fds is not None:
|
72
|
+
lst.append(inj.bind(inherited_fds, key=InheritedFds))
|
73
|
+
|
74
|
+
#
|
75
|
+
|
76
|
+
return inj.as_bindings(*lst)
|
77
|
+
|
78
|
+
|
79
|
+
##
|
17
80
|
|
18
81
|
|
19
82
|
def main(
|
@@ -26,6 +89,7 @@ def main(
|
|
26
89
|
parser = argparse.ArgumentParser()
|
27
90
|
parser.add_argument('config_file', metavar='config-file')
|
28
91
|
parser.add_argument('--no-journald', action='store_true')
|
92
|
+
parser.add_argument('--inherit-initial-fds', action='store_true')
|
29
93
|
args = parser.parse_args(argv)
|
30
94
|
|
31
95
|
#
|
@@ -41,20 +105,27 @@ def main(
|
|
41
105
|
|
42
106
|
#
|
43
107
|
|
108
|
+
initial_fds: ta.Optional[InheritedFds] = None
|
109
|
+
if args.inherit_initial_fds:
|
110
|
+
initial_fds = InheritedFds(get_open_fds(0x10000))
|
111
|
+
|
44
112
|
# if we hup, restart by making a new Supervisor()
|
45
113
|
for epoch in itertools.count():
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
114
|
+
config = read_config_file(
|
115
|
+
os.path.expanduser(cf),
|
116
|
+
ServerConfig,
|
117
|
+
prepare=prepare_server_config,
|
118
|
+
)
|
51
119
|
|
52
|
-
|
120
|
+
injector = inj.create_injector(build_server_bindings(
|
53
121
|
config,
|
54
|
-
|
55
|
-
|
122
|
+
server_epoch=ServerEpoch(epoch),
|
123
|
+
inherited_fds=initial_fds,
|
124
|
+
))
|
125
|
+
|
126
|
+
context = injector.provide(ServerContext)
|
127
|
+
supervisor = injector.provide(Supervisor)
|
56
128
|
|
57
|
-
supervisor = Supervisor(context)
|
58
129
|
try:
|
59
130
|
supervisor.main()
|
60
131
|
except ExitNow:
|
ominfra/supervisor/process.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
+
import dataclasses as dc
|
2
3
|
import errno
|
3
4
|
import functools
|
4
5
|
import os.path
|
@@ -55,6 +56,9 @@ from .types import AbstractServerContext
|
|
55
56
|
from .types import AbstractSubprocess
|
56
57
|
|
57
58
|
|
59
|
+
##
|
60
|
+
|
61
|
+
|
58
62
|
@functools.total_ordering
|
59
63
|
class Subprocess(AbstractSubprocess):
|
60
64
|
"""A class to manage a subprocess."""
|
@@ -80,7 +84,12 @@ class Subprocess(AbstractSubprocess):
|
|
80
84
|
spawn_err = None # error message attached by spawn() if any
|
81
85
|
group = None # ProcessGroup instance if process is in the group
|
82
86
|
|
83
|
-
def __init__(
|
87
|
+
def __init__(
|
88
|
+
self,
|
89
|
+
config: ProcessConfig,
|
90
|
+
group: 'ProcessGroup',
|
91
|
+
context: AbstractServerContext,
|
92
|
+
) -> None:
|
84
93
|
super().__init__()
|
85
94
|
self._config = config
|
86
95
|
self.group = group
|
@@ -324,8 +333,10 @@ class Subprocess(AbstractSubprocess):
|
|
324
333
|
os.dup2(self._pipes['child_stdout'], 2)
|
325
334
|
else:
|
326
335
|
os.dup2(self._pipes['child_stderr'], 2)
|
327
|
-
|
336
|
+
|
328
337
|
for i in range(3, self.context.config.minfds):
|
338
|
+
if i in self.context.inherited_fds:
|
339
|
+
continue
|
329
340
|
close_fd(i)
|
330
341
|
|
331
342
|
def _spawn_as_child(self, filename: str, argv: ta.Sequence[str]) -> None:
|
@@ -719,15 +730,39 @@ class Subprocess(AbstractSubprocess):
|
|
719
730
|
pass
|
720
731
|
|
721
732
|
|
733
|
+
##
|
734
|
+
|
735
|
+
|
736
|
+
@dc.dataclass(frozen=True)
|
737
|
+
class SubprocessFactory:
|
738
|
+
fn: ta.Callable[[ProcessConfig, 'ProcessGroup'], Subprocess]
|
739
|
+
|
740
|
+
def __call__(self, config: ProcessConfig, group: 'ProcessGroup') -> Subprocess:
|
741
|
+
return self.fn(config, group)
|
742
|
+
|
743
|
+
|
722
744
|
@functools.total_ordering
|
723
745
|
class ProcessGroup:
|
724
|
-
def __init__(
|
746
|
+
def __init__(
|
747
|
+
self,
|
748
|
+
config: ProcessGroupConfig,
|
749
|
+
context: ServerContext,
|
750
|
+
*,
|
751
|
+
subprocess_factory: ta.Optional[SubprocessFactory] = None,
|
752
|
+
):
|
725
753
|
super().__init__()
|
726
754
|
self.config = config
|
727
755
|
self.context = context
|
756
|
+
|
757
|
+
if subprocess_factory is None:
|
758
|
+
def make_subprocess(config: ProcessConfig, group: ProcessGroup) -> Subprocess:
|
759
|
+
return Subprocess(config, group, self.context)
|
760
|
+
subprocess_factory = SubprocessFactory(make_subprocess)
|
761
|
+
self._subprocess_factory = subprocess_factory
|
762
|
+
|
728
763
|
self.processes = {}
|
729
764
|
for pconfig in self.config.processes or []:
|
730
|
-
process =
|
765
|
+
process = self._subprocess_factory(pconfig, self)
|
731
766
|
self.processes[pconfig.name] = process
|
732
767
|
|
733
768
|
def __lt__(self, other):
|
ominfra/supervisor/supervisor.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
+
import dataclasses as dc
|
2
3
|
import signal
|
3
4
|
import time
|
4
5
|
import typing as ta
|
@@ -31,12 +32,32 @@ def timeslice(period: int, when: float) -> int:
|
|
31
32
|
return int(when - (when % period))
|
32
33
|
|
33
34
|
|
35
|
+
@dc.dataclass(frozen=True)
|
36
|
+
class ProcessGroupFactory:
|
37
|
+
fn: ta.Callable[[ProcessGroupConfig], ProcessGroup]
|
38
|
+
|
39
|
+
def __call__(self, config: ProcessGroupConfig) -> ProcessGroup:
|
40
|
+
return self.fn(config)
|
41
|
+
|
42
|
+
|
34
43
|
class Supervisor:
|
35
44
|
|
36
|
-
def __init__(
|
45
|
+
def __init__(
|
46
|
+
self,
|
47
|
+
context: ServerContext,
|
48
|
+
*,
|
49
|
+
process_group_factory: ta.Optional[ProcessGroupFactory] = None,
|
50
|
+
) -> None:
|
37
51
|
super().__init__()
|
38
52
|
|
39
53
|
self._context = context
|
54
|
+
|
55
|
+
if process_group_factory is None:
|
56
|
+
def make_process_group(config: ProcessGroupConfig) -> ProcessGroup:
|
57
|
+
return ProcessGroup(config, self._context)
|
58
|
+
process_group_factory = ProcessGroupFactory(make_process_group)
|
59
|
+
self._process_group_factory = process_group_factory
|
60
|
+
|
40
61
|
self._ticks: ta.Dict[int, float] = {}
|
41
62
|
self._process_groups: ta.Dict[str, ProcessGroup] = {} # map of process group name to process group object
|
42
63
|
self._stop_groups: ta.Optional[ta.List[ProcessGroup]] = None # list used for priority ordered shutdown
|
@@ -78,7 +99,7 @@ class Supervisor:
|
|
78
99
|
if name in self._process_groups:
|
79
100
|
return False
|
80
101
|
|
81
|
-
group = self._process_groups[name] =
|
102
|
+
group = self._process_groups[name] = self._process_group_factory(config)
|
82
103
|
group.after_setuid()
|
83
104
|
|
84
105
|
EVENT_CALLBACKS.notify(ProcessGroupAddedEvent(name))
|
ominfra/supervisor/types.py
CHANGED
@@ -27,6 +27,11 @@ class AbstractServerContext(abc.ABC):
|
|
27
27
|
def pid_history(self) -> ta.Dict[int, 'AbstractSubprocess']:
|
28
28
|
raise NotImplementedError
|
29
29
|
|
30
|
+
@property
|
31
|
+
@abc.abstractmethod
|
32
|
+
def inherited_fds(self) -> ta.FrozenSet[int]:
|
33
|
+
raise NotImplementedError
|
34
|
+
|
30
35
|
|
31
36
|
class AbstractSubprocess(abc.ABC):
|
32
37
|
@property
|
ominfra/threadworkers.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
# @omlish-lite
|
3
3
|
"""
|
4
|
+
FIXME:
|
5
|
+
- group is racy af - meditate on has_started, etc
|
6
|
+
|
4
7
|
TODO:
|
5
|
-
-
|
6
|
-
- collective heartbeat monitoring - ThreadWorkerGroups
|
8
|
+
- overhaul stop lol
|
7
9
|
- group -> 'context'? :|
|
8
10
|
- shared stop_event?
|
9
11
|
"""
|
@@ -29,6 +31,7 @@ class ThreadWorker(ExitStacked, abc.ABC):
|
|
29
31
|
self,
|
30
32
|
*,
|
31
33
|
stop_event: ta.Optional[threading.Event] = None,
|
34
|
+
worker_groups: ta.Optional[ta.Iterable['ThreadWorkerGroup']] = None,
|
32
35
|
) -> None:
|
33
36
|
super().__init__()
|
34
37
|
|
@@ -40,6 +43,9 @@ class ThreadWorker(ExitStacked, abc.ABC):
|
|
40
43
|
self._thread: ta.Optional[threading.Thread] = None
|
41
44
|
self._last_heartbeat: ta.Optional[float] = None
|
42
45
|
|
46
|
+
for g in worker_groups or []:
|
47
|
+
g.add(self)
|
48
|
+
|
43
49
|
#
|
44
50
|
|
45
51
|
def __enter__(self: ThreadWorkerT) -> ThreadWorkerT:
|
@@ -84,13 +90,13 @@ class ThreadWorker(ExitStacked, abc.ABC):
|
|
84
90
|
if self._thread is not None:
|
85
91
|
raise RuntimeError('Thread already started: %r', self)
|
86
92
|
|
87
|
-
thr = threading.Thread(target=self.
|
93
|
+
thr = threading.Thread(target=self.__thread_main)
|
88
94
|
self._thread = thr
|
89
95
|
thr.start()
|
90
96
|
|
91
97
|
#
|
92
98
|
|
93
|
-
def
|
99
|
+
def __thread_main(self) -> None:
|
94
100
|
try:
|
95
101
|
self._run()
|
96
102
|
except ThreadWorker.Stopping:
|
@@ -108,10 +114,17 @@ class ThreadWorker(ExitStacked, abc.ABC):
|
|
108
114
|
def stop(self) -> None:
|
109
115
|
self._stop_event.set()
|
110
116
|
|
111
|
-
def join(
|
117
|
+
def join(
|
118
|
+
self,
|
119
|
+
timeout: ta.Optional[float] = None,
|
120
|
+
*,
|
121
|
+
unless_not_started: bool = False,
|
122
|
+
) -> None:
|
112
123
|
with self._lock:
|
113
124
|
if self._thread is None:
|
114
|
-
|
125
|
+
if not unless_not_started:
|
126
|
+
raise RuntimeError('Thread not started: %r', self)
|
127
|
+
return
|
115
128
|
self._thread.join(timeout)
|
116
129
|
|
117
130
|
|
@@ -120,20 +133,64 @@ class ThreadWorker(ExitStacked, abc.ABC):
|
|
120
133
|
|
121
134
|
class ThreadWorkerGroup:
|
122
135
|
@dc.dataclass()
|
123
|
-
class
|
136
|
+
class _State:
|
124
137
|
worker: ThreadWorker
|
125
138
|
|
139
|
+
last_heartbeat: ta.Optional[float] = None
|
140
|
+
|
126
141
|
def __init__(self) -> None:
|
127
142
|
super().__init__()
|
128
143
|
|
129
144
|
self._lock = threading.RLock()
|
130
|
-
self._states: ta.Dict[ThreadWorker, ThreadWorkerGroup.
|
145
|
+
self._states: ta.Dict[ThreadWorker, ThreadWorkerGroup._State] = {}
|
146
|
+
self._last_heartbeat_check: ta.Optional[float] = None
|
147
|
+
|
148
|
+
#
|
131
149
|
|
132
150
|
def add(self, *workers: ThreadWorker) -> 'ThreadWorkerGroup':
|
133
151
|
with self._lock:
|
134
152
|
for w in workers:
|
135
153
|
if w in self._states:
|
136
154
|
raise KeyError(w)
|
137
|
-
self._states[w] = ThreadWorkerGroup.
|
155
|
+
self._states[w] = ThreadWorkerGroup._State(w)
|
138
156
|
|
139
157
|
return self
|
158
|
+
|
159
|
+
#
|
160
|
+
|
161
|
+
def start_all(self) -> None:
|
162
|
+
thrs = list(self._states)
|
163
|
+
with self._lock:
|
164
|
+
for thr in thrs:
|
165
|
+
if not thr.has_started():
|
166
|
+
thr.start()
|
167
|
+
|
168
|
+
def stop_all(self) -> None:
|
169
|
+
for w in reversed(list(self._states)):
|
170
|
+
if w.has_started():
|
171
|
+
w.stop()
|
172
|
+
|
173
|
+
def join_all(self, timeout: ta.Optional[float] = None) -> None:
|
174
|
+
for w in reversed(list(self._states)):
|
175
|
+
if w.has_started():
|
176
|
+
w.join(timeout, unless_not_started=True)
|
177
|
+
|
178
|
+
#
|
179
|
+
|
180
|
+
def get_dead(self) -> ta.List[ThreadWorker]:
|
181
|
+
with self._lock:
|
182
|
+
return [thr for thr in self._states if not thr.is_alive()]
|
183
|
+
|
184
|
+
def check_heartbeats(self) -> ta.Dict[ThreadWorker, float]:
|
185
|
+
with self._lock:
|
186
|
+
dct: ta.Dict[ThreadWorker, float] = {}
|
187
|
+
for thr, st in self._states.items():
|
188
|
+
if not thr.has_started():
|
189
|
+
continue
|
190
|
+
hb = thr.last_heartbeat
|
191
|
+
if hb is None:
|
192
|
+
hb = time.time()
|
193
|
+
st.last_heartbeat = hb
|
194
|
+
dct[st.worker] = time.time() - hb
|
195
|
+
self._last_heartbeat_check = time.time()
|
196
|
+
return dct
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev122
|
4
4
|
Summary: ominfra
|
5
5
|
Author: wrmsr
|
6
6
|
License: BSD-3-Clause
|
@@ -12,8 +12,8 @@ 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 ==0.0.0.
|
16
|
-
Requires-Dist: omlish ==0.0.0.
|
15
|
+
Requires-Dist: omdev ==0.0.0.dev122
|
16
|
+
Requires-Dist: omlish ==0.0.0.dev122
|
17
17
|
Provides-Extra: all
|
18
18
|
Requires-Dist: paramiko ~=3.5 ; extra == 'all'
|
19
19
|
Requires-Dist: asyncssh ~=2.18 ; extra == 'all'
|
@@ -2,8 +2,9 @@ ominfra/.manifests.json,sha256=8KREXxMAlsilZOktXPYru1ND3V5hFI22vnrp6hT3bio,589
|
|
2
2
|
ominfra/__about__.py,sha256=6i1AoruFYQCd-PyhhbDQDWY2d1tiQu9nkwWr-fXAqfY,705
|
3
3
|
ominfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
ominfra/cmds.py,sha256=E0AfnvEmnKntXWvmLW5L05_NeDpBET1VBXn7vV6EwBQ,2083
|
5
|
+
ominfra/configs.py,sha256=8aU1Qmbr-qjaE2iP3gAbA2SWJYMPZ-uGK007L01PoOI,1727
|
5
6
|
ominfra/ssh.py,sha256=jQpc4WvkMckIfk4vILda8zFaeharRqc_6wxW50b0OjQ,5431
|
6
|
-
ominfra/threadworkers.py,sha256=
|
7
|
+
ominfra/threadworkers.py,sha256=oX4ubZn7h932saXpRIJu2MNhBExgGGMuGhdXarZxLJw,4948
|
7
8
|
ominfra/clouds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
9
|
ominfra/clouds/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
10
|
ominfra/clouds/aws/__main__.py,sha256=HXMoxEl9KHhv6zOOPQxiJAftfR2SjBqeVTYw-og9aFw,163
|
@@ -13,14 +14,15 @@ ominfra/clouds/aws/dataclasses.py,sha256=rKhtJKJ0JhMssU9n9CABX_JaUiokIboEATJ9TZg
|
|
13
14
|
ominfra/clouds/aws/logs.py,sha256=z9ouU2IYXNHsl7_Whbjs1FGtlUwsEq0RV8LNrM_QNTE,5471
|
14
15
|
ominfra/clouds/aws/metadata.py,sha256=XR1BuMdQheyeFjjA3MN8GCNWVAp5ahoPdbWXEmViutQ,2767
|
15
16
|
ominfra/clouds/aws/journald2aws/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
17
|
+
ominfra/clouds/aws/journald2aws/__main__.py,sha256=d23loR_cKfTYZwYiqpt_CmKI7dd5WcYFgIYzqMep75E,68
|
16
18
|
ominfra/clouds/aws/journald2aws/cursor.py,sha256=tQ7O6BHlEdaalbiI_Rqagj0aHfdtTQ_ZJwdOSRUjNvQ,1173
|
17
|
-
ominfra/clouds/aws/journald2aws/driver.py,sha256=
|
18
|
-
ominfra/clouds/aws/journald2aws/main.py,sha256=
|
19
|
+
ominfra/clouds/aws/journald2aws/driver.py,sha256=E9RhdMzDXxOnDfLyHqU7N_7iygXqxy4apq2xz_EDxkA,6127
|
20
|
+
ominfra/clouds/aws/journald2aws/main.py,sha256=RQJhk4aPtnp4EHzC-ST1Rs9BN6D7bqQQVjCRxGU7JuQ,2147
|
19
21
|
ominfra/clouds/aws/journald2aws/poster.py,sha256=hz1XuctW8GtLmfjhRvCFY6py52D4BzXHYny5XKFpHSA,2833
|
20
22
|
ominfra/clouds/gcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
23
|
ominfra/clouds/gcp/auth.py,sha256=3PyfRJNgajjMqJFem3SKui0CqGeHEsZlvbRhuxFcZG8,1348
|
22
24
|
ominfra/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
|
-
ominfra/deploy/_executor.py,sha256=
|
25
|
+
ominfra/deploy/_executor.py,sha256=46QRoTcnPzhu7RvIL9m7zfx4_AwysA4XdZ1LvABCcHA,34589
|
24
26
|
ominfra/deploy/configs.py,sha256=qi0kwT7G2NH7dXLOQic-u6R3yeadup_QtvrjwWIggbM,435
|
25
27
|
ominfra/deploy/remote.py,sha256=6ACmpXU1uBdyGs3Xsp97ktKFq30cJlzN9LRWNUWlGY4,2144
|
26
28
|
ominfra/deploy/executor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
@@ -35,7 +37,7 @@ ominfra/deploy/executor/concerns/systemd.py,sha256=MtsSEToEa1HNouern_JukcYTnypw_
|
|
35
37
|
ominfra/deploy/executor/concerns/user.py,sha256=j5LDfQXquIp-eEM7t6aShsrYoQrM_ILXZycTmTcRVxA,686
|
36
38
|
ominfra/deploy/executor/concerns/venv.py,sha256=jbRriqJHO4r9Zyo5Hfl_qVmcU6Qm6UgrouBroKcPn2g,775
|
37
39
|
ominfra/deploy/poly/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
38
|
-
ominfra/deploy/poly/_main.py,sha256=
|
40
|
+
ominfra/deploy/poly/_main.py,sha256=N-ajqT7UPdacBbFViTyzDkbM8y2kyH4lzGKsV4lN7uo,24198
|
39
41
|
ominfra/deploy/poly/base.py,sha256=Bd-CzUTaDvTRbdXKiTxMxs77WCEXItwNoBYCRnTk1u4,4167
|
40
42
|
ominfra/deploy/poly/configs.py,sha256=9bzWdbxhOk_Q4KokDjmRz254KHnUU71Vl1frLlhQyU4,584
|
41
43
|
ominfra/deploy/poly/deploy.py,sha256=tMYKslXLjstcv86siRt5j37USsS0Wd6lsfeGRE26zio,544
|
@@ -54,35 +56,35 @@ ominfra/journald/tailer.py,sha256=5abcFMfgi7fnY9ZEQe2ZVobaJxjQkeu6d9Kagw33a1w,33
|
|
54
56
|
ominfra/manage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
57
|
ominfra/manage/manage.py,sha256=BttL8LFEknHZE_h2Pt5dAqbfUkv6qy43WI0raXBZ1a8,151
|
56
58
|
ominfra/pyremote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
-
ominfra/pyremote/_runcommands.py,sha256=
|
59
|
+
ominfra/pyremote/_runcommands.py,sha256=SGMQpN5-LDM99yNYR7lvjdkkJ_jVinSfmxm9mU34hjc,28425
|
58
60
|
ominfra/pyremote/bootstrap.py,sha256=RvMO3YGaN1E4sgUi1JEtiPak8cjvqtc_vRCq1yqbeZg,3370
|
59
61
|
ominfra/pyremote/runcommands.py,sha256=bviS0_TDIoZVAe4h-_iavbvJtVSFu8lnk7fQ5iasCWE,1571
|
60
62
|
ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
61
|
-
ominfra/scripts/journald2aws.py,sha256=
|
62
|
-
ominfra/scripts/supervisor.py,sha256=
|
63
|
+
ominfra/scripts/journald2aws.py,sha256=G56Fx-fHv3p2brOObTwdX7ORA2blhslFfooG2LQL6h4,128491
|
64
|
+
ominfra/scripts/supervisor.py,sha256=Vv4x7BER3xAZ-ORNM-5SB1HsSJvu-eSYfcgx3kx5Vfo,173118
|
63
65
|
ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
64
66
|
ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
|
65
|
-
ominfra/supervisor/compat.py,sha256=
|
66
|
-
ominfra/supervisor/configs.py,sha256=
|
67
|
-
ominfra/supervisor/context.py,sha256=
|
67
|
+
ominfra/supervisor/compat.py,sha256=mutfnQbSCDaE7TSuQArOcFdfGVw4uEE_E04rIhs1IqU,5312
|
68
|
+
ominfra/supervisor/configs.py,sha256=TtVyWdxinrd3tueM6q8j2YVcEqauFTJqlbykkSjByEo,3524
|
69
|
+
ominfra/supervisor/context.py,sha256=Ss4xqnLe8LafIcg10oSr8KyqAJZxjofgNDB_pVoyBA8,16164
|
68
70
|
ominfra/supervisor/datatypes.py,sha256=UnXO_UlCyJD9u0uvea1wvnk_UZCzxNMeFvPK83gv530,4432
|
69
71
|
ominfra/supervisor/dispatchers.py,sha256=sJ61yTo9EEbxHwe2NbzOTAFFFCuuyIhYli_xJioQBoo,10423
|
70
72
|
ominfra/supervisor/events.py,sha256=IhdL7Fj-hEvTvZ5WF6aIa2YjSPQhuUoasoJSMmRLQkU,7650
|
71
73
|
ominfra/supervisor/exceptions.py,sha256=Qbu211H3CLlSmi9LsSikOwrcL5HgJP9ugvcKWlGTAoI,750
|
72
|
-
ominfra/supervisor/main.py,sha256=
|
74
|
+
ominfra/supervisor/main.py,sha256=v3Ezr7ECzqYX33HmhWbZrh78-h-1OnnxqPqmjS4zkFw,4000
|
73
75
|
ominfra/supervisor/poller.py,sha256=VCBxLItfA4Vj69jet2XFbFScPbmdD9JA1evaofk_AnY,7709
|
74
|
-
ominfra/supervisor/process.py,sha256=
|
76
|
+
ominfra/supervisor/process.py,sha256=oEd58g6KcLNWhwaZBu8wZJjb6Vd0t1sKPMVE4BjbBSI,32270
|
75
77
|
ominfra/supervisor/states.py,sha256=JMxXYTZhJkMNQZ2tTV6wId7wrvnWgiZteskACprKskM,1374
|
76
|
-
ominfra/supervisor/supervisor.py,sha256=
|
77
|
-
ominfra/supervisor/types.py,sha256=
|
78
|
+
ominfra/supervisor/supervisor.py,sha256=7T7RAtGc9qsb0s9fBiimReVyaWknl1e3rGU9nNxjll8,13002
|
79
|
+
ominfra/supervisor/types.py,sha256=GhVixieeJ00fWclxPLM2oMugCe9wEjW44wJzQ2AO0V0,1264
|
78
80
|
ominfra/tailscale/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
81
|
ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
|
80
82
|
ominfra/tailscale/cli.py,sha256=DSGp4hn5xwOW-l_u_InKlSF6kIobxtUtVssf_73STs0,3567
|
81
83
|
ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
82
84
|
ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
|
83
|
-
ominfra-0.0.0.
|
84
|
-
ominfra-0.0.0.
|
85
|
-
ominfra-0.0.0.
|
86
|
-
ominfra-0.0.0.
|
87
|
-
ominfra-0.0.0.
|
88
|
-
ominfra-0.0.0.
|
85
|
+
ominfra-0.0.0.dev122.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
86
|
+
ominfra-0.0.0.dev122.dist-info/METADATA,sha256=deSke0MO3j-CHNhTN8uGgI-3374_P5fgfqJgo4GRVMU,742
|
87
|
+
ominfra-0.0.0.dev122.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
88
|
+
ominfra-0.0.0.dev122.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
|
89
|
+
ominfra-0.0.0.dev122.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
|
90
|
+
ominfra-0.0.0.dev122.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|