ominfra 0.0.0.dev120__py3-none-any.whl → 0.0.0.dev121__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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