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
@@ -46,6 +46,7 @@ from omlish.lite.runtime import is_debugger_attached
|
|
46
46
|
|
47
47
|
from ....journald.messages import JournalctlMessage # noqa
|
48
48
|
from ....journald.tailer import JournalctlTailerWorker
|
49
|
+
from ....threadworkers import ThreadWorkerGroup
|
49
50
|
from ..auth import AwsSigner
|
50
51
|
from ..logs import AwsLogMessageBuilder
|
51
52
|
from .cursor import JournalctlToAwsCursor
|
@@ -63,6 +64,7 @@ class JournalctlToAwsDriver(ExitStacked):
|
|
63
64
|
cursor_file: ta.Optional[str] = None
|
64
65
|
|
65
66
|
runtime_limit: ta.Optional[float] = None
|
67
|
+
heartbeat_age_limit: ta.Optional[float] = 60.
|
66
68
|
|
67
69
|
#
|
68
70
|
|
@@ -139,6 +141,12 @@ class JournalctlToAwsDriver(ExitStacked):
|
|
139
141
|
|
140
142
|
#
|
141
143
|
|
144
|
+
@cached_nullary
|
145
|
+
def _worker_group(self) -> ThreadWorkerGroup:
|
146
|
+
return ThreadWorkerGroup()
|
147
|
+
|
148
|
+
#
|
149
|
+
|
142
150
|
@cached_nullary
|
143
151
|
def _journalctl_message_queue(self): # type: () -> queue.Queue[ta.Sequence[JournalctlMessage]]
|
144
152
|
return queue.Queue()
|
@@ -165,6 +173,8 @@ class JournalctlToAwsDriver(ExitStacked):
|
|
165
173
|
|
166
174
|
cmd=self._config.journalctl_cmd,
|
167
175
|
shell_wrap=is_debugger_attached(),
|
176
|
+
|
177
|
+
worker_groups=[self._worker_group()],
|
168
178
|
)
|
169
179
|
|
170
180
|
#
|
@@ -178,26 +188,38 @@ class JournalctlToAwsDriver(ExitStacked):
|
|
178
188
|
|
179
189
|
ensure_locked=self._ensure_locked,
|
180
190
|
dry_run=self._config.aws_dry_run,
|
191
|
+
|
192
|
+
worker_groups=[self._worker_group()],
|
181
193
|
)
|
182
194
|
|
183
195
|
#
|
184
196
|
|
185
|
-
def
|
186
|
-
|
187
|
-
|
197
|
+
def _exit_contexts(self) -> None:
|
198
|
+
wg = self._worker_group()
|
199
|
+
wg.stop_all()
|
200
|
+
wg.join_all()
|
188
201
|
|
189
|
-
|
202
|
+
def run(self) -> None:
|
203
|
+
self._aws_poster_worker()
|
204
|
+
self._journalctl_tailer_worker()
|
190
205
|
|
191
|
-
|
192
|
-
|
206
|
+
wg = self._worker_group()
|
207
|
+
wg.start_all()
|
193
208
|
|
194
209
|
start = time.time()
|
195
210
|
|
196
211
|
while True:
|
197
|
-
for w in
|
198
|
-
|
199
|
-
|
200
|
-
|
212
|
+
for w in wg.get_dead():
|
213
|
+
log.critical('Worker died: %r', w)
|
214
|
+
break
|
215
|
+
|
216
|
+
if (al := self._config.heartbeat_age_limit) is not None:
|
217
|
+
hbs = wg.check_heartbeats()
|
218
|
+
log.debug('Worker heartbeats: %r', hbs)
|
219
|
+
for w, age in hbs.items():
|
220
|
+
if age > al:
|
221
|
+
log.critical('Worker heartbeat age limit exceeded: %r %f > %f', w, age, al)
|
222
|
+
break
|
201
223
|
|
202
224
|
if (rl := self._config.runtime_limit) is not None and time.time() - start >= rl:
|
203
225
|
log.warning('Runtime limit reached')
|
@@ -205,6 +227,5 @@ class JournalctlToAwsDriver(ExitStacked):
|
|
205
227
|
|
206
228
|
time.sleep(1.)
|
207
229
|
|
208
|
-
|
209
|
-
|
210
|
-
w.join()
|
230
|
+
wg.stop_all()
|
231
|
+
wg.join_all()
|
@@ -2,13 +2,12 @@
|
|
2
2
|
# @omlish-amalg ../../../scripts/journald2aws.py
|
3
3
|
import argparse
|
4
4
|
import dataclasses as dc
|
5
|
-
import json
|
6
5
|
import os.path
|
7
6
|
import sys
|
8
7
|
|
9
8
|
from omlish.lite.logs import configure_standard_logging
|
10
|
-
from omlish.lite.marshal import unmarshal_obj
|
11
9
|
|
10
|
+
from ....configs import read_config_file
|
12
11
|
from .driver import JournalctlToAwsDriver
|
13
12
|
|
14
13
|
|
@@ -37,9 +36,7 @@ def _main() -> None:
|
|
37
36
|
|
38
37
|
config: JournalctlToAwsDriver.Config
|
39
38
|
if args.config_file:
|
40
|
-
|
41
|
-
config_dct = json.load(cf)
|
42
|
-
config = unmarshal_obj(config_dct, JournalctlToAwsDriver.Config)
|
39
|
+
config = read_config_file(os.path.expanduser(args.config_file), JournalctlToAwsDriver.Config)
|
43
40
|
else:
|
44
41
|
config = JournalctlToAwsDriver.Config()
|
45
42
|
|
ominfra/configs.py
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
# @omlish-lite
|
3
|
+
import json
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from omdev.toml.parser import toml_loads
|
7
|
+
from omlish.lite.check import check_isinstance
|
8
|
+
from omlish.lite.check import check_not_isinstance
|
9
|
+
from omlish.lite.marshal import unmarshal_obj
|
10
|
+
|
11
|
+
|
12
|
+
T = ta.TypeVar('T')
|
13
|
+
|
14
|
+
ConfigMapping = ta.Mapping[str, ta.Any]
|
15
|
+
|
16
|
+
|
17
|
+
def read_config_file(
|
18
|
+
path: str,
|
19
|
+
cls: ta.Type[T],
|
20
|
+
*,
|
21
|
+
prepare: ta.Optional[ta.Callable[[ConfigMapping], ConfigMapping]] = None,
|
22
|
+
) -> T:
|
23
|
+
with open(path) as cf:
|
24
|
+
if path.endswith('.toml'):
|
25
|
+
config_dct = toml_loads(cf.read())
|
26
|
+
else:
|
27
|
+
config_dct = json.loads(cf.read())
|
28
|
+
|
29
|
+
if prepare is not None:
|
30
|
+
config_dct = prepare(config_dct) # type: ignore
|
31
|
+
|
32
|
+
return unmarshal_obj(config_dct, cls)
|
33
|
+
|
34
|
+
|
35
|
+
def build_config_named_children(
|
36
|
+
o: ta.Union[
|
37
|
+
ta.Sequence[ConfigMapping],
|
38
|
+
ta.Mapping[str, ConfigMapping],
|
39
|
+
None,
|
40
|
+
],
|
41
|
+
*,
|
42
|
+
name_key: str = 'name',
|
43
|
+
) -> ta.Optional[ta.Sequence[ConfigMapping]]:
|
44
|
+
if o is None:
|
45
|
+
return None
|
46
|
+
|
47
|
+
lst: ta.List[ConfigMapping] = []
|
48
|
+
if isinstance(o, ta.Mapping):
|
49
|
+
for k, v in o.items():
|
50
|
+
check_isinstance(v, ta.Mapping)
|
51
|
+
if name_key in v:
|
52
|
+
n = v[name_key]
|
53
|
+
if k != n:
|
54
|
+
raise KeyError(f'Given names do not match: {n} != {k}')
|
55
|
+
lst.append(v)
|
56
|
+
else:
|
57
|
+
lst.append({name_key: k, **v})
|
58
|
+
|
59
|
+
else:
|
60
|
+
check_not_isinstance(o, str)
|
61
|
+
lst.extend(o)
|
62
|
+
|
63
|
+
seen = set()
|
64
|
+
for d in lst:
|
65
|
+
n = d['name']
|
66
|
+
if n in d:
|
67
|
+
raise KeyError(f'Duplicate name: {n}')
|
68
|
+
seen.add(n)
|
69
|
+
|
70
|
+
return lst
|
ominfra/deploy/_executor.py
CHANGED
@@ -66,6 +66,7 @@ import subprocess
|
|
66
66
|
import sys
|
67
67
|
import textwrap
|
68
68
|
import threading
|
69
|
+
import types
|
69
70
|
import typing as ta
|
70
71
|
import uuid
|
71
72
|
import weakref # noqa
|
@@ -129,7 +130,7 @@ class _cached_nullary: # noqa
|
|
129
130
|
return bound
|
130
131
|
|
131
132
|
|
132
|
-
def cached_nullary(fn: ta.Callable[..., T]) -> ta.Callable[..., T]:
|
133
|
+
def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
|
133
134
|
return _cached_nullary(fn)
|
134
135
|
|
135
136
|
|
@@ -255,6 +256,14 @@ def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
|
|
255
256
|
return it
|
256
257
|
|
257
258
|
|
259
|
+
def is_new_type(spec: ta.Any) -> bool:
|
260
|
+
if isinstance(ta.NewType, type):
|
261
|
+
return isinstance(spec, ta.NewType)
|
262
|
+
else:
|
263
|
+
# Before https://github.com/python/cpython/commit/c2f33dfc83ab270412bf243fb21f724037effa1a
|
264
|
+
return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
|
265
|
+
|
266
|
+
|
258
267
|
def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
|
259
268
|
seen = set()
|
260
269
|
todo = list(reversed(cls.__subclasses__()))
|
ominfra/deploy/poly/_main.py
CHANGED
ominfra/pyremote/_runcommands.py
CHANGED
@@ -24,6 +24,7 @@ import subprocess
|
|
24
24
|
import sys
|
25
25
|
import textwrap
|
26
26
|
import threading
|
27
|
+
import types
|
27
28
|
import typing as ta
|
28
29
|
import uuid
|
29
30
|
import weakref # noqa
|
@@ -212,7 +213,7 @@ class _cached_nullary: # noqa
|
|
212
213
|
return bound
|
213
214
|
|
214
215
|
|
215
|
-
def cached_nullary(fn: ta.Callable[..., T]) -> ta.Callable[..., T]:
|
216
|
+
def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
|
216
217
|
return _cached_nullary(fn)
|
217
218
|
|
218
219
|
|
@@ -338,6 +339,14 @@ def get_optional_alias_arg(spec: ta.Any) -> ta.Any:
|
|
338
339
|
return it
|
339
340
|
|
340
341
|
|
342
|
+
def is_new_type(spec: ta.Any) -> bool:
|
343
|
+
if isinstance(ta.NewType, type):
|
344
|
+
return isinstance(spec, ta.NewType)
|
345
|
+
else:
|
346
|
+
# Before https://github.com/python/cpython/commit/c2f33dfc83ab270412bf243fb21f724037effa1a
|
347
|
+
return isinstance(spec, types.FunctionType) and spec.__code__ is ta.NewType.__code__.co_consts[1] # type: ignore # noqa
|
348
|
+
|
349
|
+
|
341
350
|
def deep_subclasses(cls: ta.Type[T]) -> ta.Iterator[ta.Type[T]]:
|
342
351
|
seen = set()
|
343
352
|
todo = list(reversed(cls.__subclasses__()))
|