ominfra 0.0.0.dev89__tar.gz → 0.0.0.dev91__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. {ominfra-0.0.0.dev89/ominfra.egg-info → ominfra-0.0.0.dev91}/PKG-INFO +4 -4
  2. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/journald2aws/main.py +41 -18
  3. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/logs.py +2 -2
  4. {ominfra-0.0.0.dev89/ominfra/clouds/aws/journald2aws → ominfra-0.0.0.dev91/ominfra}/journald/messages.py +27 -15
  5. ominfra-0.0.0.dev91/ominfra/journald/tailer.py +453 -0
  6. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/scripts/journald2aws.py +553 -146
  7. {ominfra-0.0.0.dev89/ominfra/clouds/aws/journald2aws → ominfra-0.0.0.dev91/ominfra}/threadworker.py +6 -3
  8. ominfra-0.0.0.dev91/ominfra/tools/__init__.py +0 -0
  9. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91/ominfra.egg-info}/PKG-INFO +4 -4
  10. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra.egg-info/SOURCES.txt +5 -5
  11. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra.egg-info/requires.txt +2 -2
  12. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/pyproject.toml +4 -4
  13. ominfra-0.0.0.dev89/ominfra/clouds/aws/journald2aws/journald/tailer.py +0 -108
  14. ominfra-0.0.0.dev89/ominfra/supervisor/__init__.py +0 -1
  15. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/LICENSE +0 -0
  16. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/MANIFEST.in +0 -0
  17. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/README.rst +0 -0
  18. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/.manifests.json +0 -0
  19. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/__about__.py +0 -0
  20. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/__init__.py +0 -0
  21. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/__init__.py +0 -0
  22. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/__init__.py +0 -0
  23. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/__main__.py +0 -0
  24. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/auth.py +0 -0
  25. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/cli.py +0 -0
  26. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/dataclasses.py +0 -0
  27. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
  28. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/clouds/aws/metadata.py +0 -0
  29. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/cmds.py +0 -0
  30. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/__init__.py +0 -0
  31. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/_executor.py +0 -0
  32. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/configs.py +0 -0
  33. {ominfra-0.0.0.dev89/ominfra/clouds/aws/journald2aws/journald → ominfra-0.0.0.dev91/ominfra/deploy/executor}/__init__.py +0 -0
  34. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/base.py +0 -0
  35. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/__init__.py +0 -0
  36. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/dirs.py +0 -0
  37. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/nginx.py +0 -0
  38. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/repo.py +0 -0
  39. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/supervisor.py +0 -0
  40. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/systemd.py +0 -0
  41. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/user.py +0 -0
  42. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/concerns/venv.py +0 -0
  43. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/executor/main.py +0 -0
  44. {ominfra-0.0.0.dev89/ominfra/deploy/executor → ominfra-0.0.0.dev91/ominfra/deploy/poly}/__init__.py +0 -0
  45. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/_main.py +0 -0
  46. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/base.py +0 -0
  47. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/configs.py +0 -0
  48. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/deploy.py +0 -0
  49. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/main.py +0 -0
  50. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/nginx.py +0 -0
  51. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/repo.py +0 -0
  52. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/runtime.py +0 -0
  53. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/site.py +0 -0
  54. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/supervisor.py +0 -0
  55. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/poly/venv.py +0 -0
  56. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/deploy/remote.py +0 -0
  57. {ominfra-0.0.0.dev89/ominfra/manage → ominfra-0.0.0.dev91/ominfra/journald}/__init__.py +0 -0
  58. {ominfra-0.0.0.dev89/ominfra/clouds/aws/journald2aws → ominfra-0.0.0.dev91/ominfra}/journald/genmessages.py +0 -0
  59. {ominfra-0.0.0.dev89/ominfra/pyremote → ominfra-0.0.0.dev91/ominfra/manage}/__init__.py +0 -0
  60. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/manage/manage.py +0 -0
  61. {ominfra-0.0.0.dev89/ominfra/scripts → ominfra-0.0.0.dev91/ominfra/pyremote}/__init__.py +0 -0
  62. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/pyremote/_runcommands.py +0 -0
  63. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/pyremote/bootstrap.py +0 -0
  64. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/pyremote/runcommands.py +0 -0
  65. {ominfra-0.0.0.dev89/ominfra/tailscale → ominfra-0.0.0.dev91/ominfra/scripts}/__init__.py +0 -0
  66. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/scripts/supervisor.py +0 -0
  67. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/ssh.py +0 -0
  68. {ominfra-0.0.0.dev89/ominfra/deploy/poly → ominfra-0.0.0.dev91/ominfra/supervisor}/__init__.py +0 -0
  69. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/__main__.py +0 -0
  70. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/compat.py +0 -0
  71. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/configs.py +0 -0
  72. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/context.py +0 -0
  73. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/datatypes.py +0 -0
  74. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/dispatchers.py +0 -0
  75. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/events.py +0 -0
  76. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/exceptions.py +0 -0
  77. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/poller.py +0 -0
  78. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/process.py +0 -0
  79. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/states.py +0 -0
  80. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/supervisor.py +0 -0
  81. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/supervisor/types.py +0 -0
  82. {ominfra-0.0.0.dev89/ominfra/tools → ominfra-0.0.0.dev91/ominfra/tailscale}/__init__.py +0 -0
  83. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/tailscale/cli.py +0 -0
  84. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra/tools/listresources.py +0 -0
  85. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra.egg-info/dependency_links.txt +0 -0
  86. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra.egg-info/entry_points.txt +0 -0
  87. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/ominfra.egg-info/top_level.txt +0 -0
  88. {ominfra-0.0.0.dev89 → ominfra-0.0.0.dev91}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev89
3
+ Version: 0.0.0.dev91
4
4
  Summary: ominfra
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -10,10 +10,10 @@ Classifier: Development Status :: 2 - Pre-Alpha
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Operating System :: OS Independent
12
12
  Classifier: Operating System :: POSIX
13
- Requires-Python: ~=3.12
13
+ Requires-Python: >=3.12
14
14
  License-File: LICENSE
15
- Requires-Dist: omdev==0.0.0.dev89
16
- Requires-Dist: omlish==0.0.0.dev89
15
+ Requires-Dist: omdev==0.0.0.dev91
16
+ Requires-Dist: omlish==0.0.0.dev91
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -2,6 +2,12 @@
2
2
  # ruff: noqa: UP007
3
3
  # @omlish-amalg ../../../scripts/journald2aws.py
4
4
  """
5
+ TODO:
6
+ - create log group
7
+ - log stats - chunk sizes, byte count, num calls, etc
8
+
9
+ ==
10
+
5
11
  https://www.freedesktop.org/software/systemd/man/latest/journalctl.html
6
12
 
7
13
  journalctl:
@@ -47,11 +53,11 @@ from omlish.lite.marshal import unmarshal_obj
47
53
  from omlish.lite.pidfile import Pidfile
48
54
  from omlish.lite.runtime import is_debugger_attached
49
55
 
56
+ from ....journald.messages import JournalctlMessage # noqa
57
+ from ....journald.tailer import JournalctlTailerWorker
50
58
  from ..auth import AwsSigner
51
59
  from ..logs import AwsLogMessagePoster
52
60
  from ..logs import AwsPutLogEventsResponse
53
- from .journald.messages import JournalctlMessage # noqa
54
- from .journald.tailer import JournalctlTailerWorker
55
61
 
56
62
 
57
63
  @dc.dataclass(frozen=True)
@@ -178,15 +184,18 @@ class JournalctlToAws:
178
184
 
179
185
  @cached_nullary
180
186
  def _journalctl_tailer_worker(self) -> JournalctlTailerWorker:
181
- ac: ta.Optional[str] = self._config.journalctl_after_cursor
182
- if ac is None:
183
- ac = self._read_cursor_file()
184
- if ac is not None:
185
- log.info('Starting from cursor %s', ac)
187
+ ac: ta.Optional[str] = None
186
188
 
187
189
  if (since := self._config.journalctl_since):
188
190
  log.info('Starting since %s', since)
189
191
 
192
+ else:
193
+ ac = self._config.journalctl_after_cursor
194
+ if ac is None:
195
+ ac = self._read_cursor_file()
196
+ if ac is not None:
197
+ log.info('Starting from cursor %s', ac)
198
+
190
199
  return JournalctlTailerWorker(
191
200
  self._journalctl_message_queue(),
192
201
 
@@ -202,9 +211,9 @@ class JournalctlToAws:
202
211
  def run(self) -> None:
203
212
  self._ensure_locked()
204
213
 
205
- q = self._journalctl_message_queue()
206
- jtw = self._journalctl_tailer_worker()
207
- mp = self._aws_log_message_poster()
214
+ q = self._journalctl_message_queue() # type: queue.Queue[ta.Sequence[JournalctlMessage]]
215
+ jtw = self._journalctl_tailer_worker() # type: JournalctlTailerWorker
216
+ mp = self._aws_log_message_poster() # type: AwsLogMessagePoster
208
217
 
209
218
  jtw.start()
210
219
 
@@ -214,7 +223,13 @@ class JournalctlToAws:
214
223
  log.critical('Journalctl tailer worker died')
215
224
  break
216
225
 
217
- msgs: ta.Sequence[JournalctlMessage] = q.get()
226
+ try:
227
+ msgs: ta.Sequence[JournalctlMessage] = q.get(timeout=1.)
228
+ except queue.Empty:
229
+ msgs = []
230
+ if not msgs:
231
+ continue
232
+
218
233
  log.debug('%r', msgs)
219
234
 
220
235
  cur_cursor: ta.Optional[str] = None
@@ -227,10 +242,14 @@ class JournalctlToAws:
227
242
  log.warning('Empty queue chunk')
228
243
  continue
229
244
 
230
- [post] = mp.feed([mp.Message(
231
- message=json.dumps(m.dct),
232
- ts_ms=int(time.time() * 1000.),
233
- ) for m in msgs])
245
+ feed_msgs = []
246
+ for m in msgs:
247
+ feed_msgs.append(mp.Message(
248
+ message=json.dumps(m.dct, sort_keys=True),
249
+ ts_ms=int((m.ts_us / 1000.) if m.ts_us is not None else (time.time() * 1000.)),
250
+ ))
251
+
252
+ [post] = mp.feed(feed_msgs)
234
253
  log.debug('%r', post)
235
254
 
236
255
  if not self._config.dry_run:
@@ -297,9 +316,13 @@ def _main() -> None:
297
316
 
298
317
  #
299
318
 
300
- for a in ['after_cursor', 'since', 'dry_run']:
301
- if (pa := getattr(args, a)):
302
- config = dc.replace(config, **{a: pa})
319
+ for ca, pa in [
320
+ ('journalctl_after_cursor', 'after_cursor'),
321
+ ('journalctl_since', 'since'),
322
+ ('dry_run', 'dry_run'),
323
+ ]:
324
+ if (av := getattr(args, pa)):
325
+ config = dc.replace(config, **{ca: av})
303
326
 
304
327
  #
305
328
 
@@ -68,7 +68,7 @@ class AwsLogMessagePoster:
68
68
  - max_items
69
69
  - max_bytes - manually build body
70
70
  - flush_interval
71
- - !! sort by timestamp
71
+ - split sorted chunks if span over 24h
72
72
  """
73
73
 
74
74
  DEFAULT_URL = 'https://logs.{region_name}.amazonaws.com/' # noqa
@@ -141,7 +141,7 @@ class AwsLogMessagePoster:
141
141
  message=m.message,
142
142
  timestamp=m.ts_ms,
143
143
  )
144
- for m in messages
144
+ for m in sorted(messages, key=lambda m: m.ts_ms)
145
145
  ],
146
146
  )
147
147
 
@@ -1,4 +1,5 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ # @omlish-lite
2
3
  import dataclasses as dc
3
4
  import json
4
5
  import typing as ta
@@ -23,7 +24,31 @@ class JournalctlMessageBuilder:
23
24
  self._buf = DelimitingBuffer(b'\n')
24
25
 
25
26
  _cursor_field = '__CURSOR'
26
- _timestamp_field = '_SOURCE_REALTIME_TIMESTAMP'
27
+
28
+ _timestamp_fields: ta.Sequence[str] = [
29
+ '_SOURCE_REALTIME_TIMESTAMP',
30
+ '__REALTIME_TIMESTAMP',
31
+ ]
32
+
33
+ def _get_message_timestamp(self, dct: ta.Mapping[str, ta.Any]) -> ta.Optional[int]:
34
+ for fld in self._timestamp_fields:
35
+ if (tsv := dct.get(fld)) is None:
36
+ continue
37
+
38
+ if isinstance(tsv, str):
39
+ try:
40
+ return int(tsv)
41
+ except ValueError:
42
+ try:
43
+ return int(float(tsv))
44
+ except ValueError:
45
+ log.exception('Failed to parse timestamp: %r', tsv)
46
+
47
+ elif isinstance(tsv, (int, float)):
48
+ return int(tsv)
49
+
50
+ log.error('Invalid timestamp: %r', dct)
51
+ return None
27
52
 
28
53
  def _make_message(self, raw: bytes) -> JournalctlMessage:
29
54
  dct = None
@@ -37,20 +62,7 @@ class JournalctlMessageBuilder:
37
62
 
38
63
  else:
39
64
  cursor = dct.get(self._cursor_field)
40
-
41
- if tsv := dct.get(self._timestamp_field):
42
- if isinstance(tsv, str):
43
- try:
44
- ts = int(tsv)
45
- except ValueError:
46
- try:
47
- ts = int(float(tsv))
48
- except ValueError:
49
- log.exception('Failed to parse timestamp: %r', tsv)
50
- elif isinstance(tsv, (int, float)):
51
- ts = int(tsv)
52
- else:
53
- log.exception('Invalid timestamp: %r', tsv)
65
+ ts = self._get_message_timestamp(dct)
54
66
 
55
67
  return JournalctlMessage(
56
68
  raw=raw,