ominfra 0.0.0.dev120__py3-none-any.whl → 0.0.0.dev121__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.
@@ -0,0 +1,4 @@
1
+ if __name__ == '__main__':
2
+ from .main import _main
3
+
4
+ _main()
@@ -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 run(self) -> None:
186
- pw: JournalctlToAwsPosterWorker = self._aws_poster_worker()
187
- tw: JournalctlTailerWorker = self._journalctl_tailer_worker()
197
+ def _exit_contexts(self) -> None:
198
+ wg = self._worker_group()
199
+ wg.stop_all()
200
+ wg.join_all()
188
201
 
189
- ws = [pw, tw]
202
+ def run(self) -> None:
203
+ self._aws_poster_worker()
204
+ self._journalctl_tailer_worker()
190
205
 
191
- for w in ws:
192
- w.start()
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 ws:
198
- if not w.is_alive():
199
- log.critical('Worker died: %r', w)
200
- break
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
- for w in reversed(ws):
209
- w.stop()
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
- with open(os.path.expanduser(args.config_file)) as cf:
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
@@ -129,7 +129,7 @@ class _cached_nullary: # noqa
129
129
  return bound
130
130
 
131
131
 
132
- def cached_nullary(fn: ta.Callable[..., T]) -> ta.Callable[..., T]:
132
+ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
133
133
  return _cached_nullary(fn)
134
134
 
135
135
 
@@ -103,7 +103,7 @@ class _cached_nullary: # noqa
103
103
  return bound
104
104
 
105
105
 
106
- def cached_nullary(fn: ta.Callable[..., T]) -> ta.Callable[..., T]:
106
+ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
107
107
  return _cached_nullary(fn)
108
108
 
109
109
 
@@ -212,7 +212,7 @@ class _cached_nullary: # noqa
212
212
  return bound
213
213
 
214
214
 
215
- def cached_nullary(fn: ta.Callable[..., T]) -> ta.Callable[..., T]:
215
+ def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
216
216
  return _cached_nullary(fn)
217
217
 
218
218