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.
@@ -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)
@@ -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
@@ -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: int = 0,
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) -> int:
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:
@@ -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 json
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
- with open(cf) as f:
47
- config_src = f.read()
48
-
49
- config_dct = json.loads(config_src)
50
- config: ServerConfig = unmarshal_obj(config_dct, ServerConfig)
114
+ config = read_config_file(
115
+ os.path.expanduser(cf),
116
+ ServerConfig,
117
+ prepare=prepare_server_config,
118
+ )
51
119
 
52
- context = ServerContext(
120
+ injector = inj.create_injector(build_server_bindings(
53
121
  config,
54
- epoch=epoch,
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:
@@ -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__(self, config: ProcessConfig, group: 'ProcessGroup', context: AbstractServerContext) -> None:
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
- # FIXME: leave debugger fds
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__(self, config: ProcessGroupConfig, context: ServerContext):
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 = Subprocess(pconfig, self, self.context)
765
+ process = self._subprocess_factory(pconfig, self)
731
766
  self.processes[pconfig.name] = process
732
767
 
733
768
  def __lt__(self, other):
@@ -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__(self, context: ServerContext) -> None:
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] = ProcessGroup(config, self._context)
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))
@@ -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
- - implement stop lol
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.__run)
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 __run(self) -> None:
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(self, timeout: ta.Optional[float] = None) -> None:
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
- raise RuntimeError('Thread not started: %r', self)
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 State:
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.State] = {}
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.State(w)
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.dev120
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.dev120
16
- Requires-Dist: omlish ==0.0.0.dev120
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=QuRpz9Yjyb4F8_IjzqmL7eNCAmXZfy3XLl7QoVF7Ohw,3273
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=8jiuEpOgKFSucpEJBBTBiSVg6L_tA4alUNK-I788HWU,5452
18
- ominfra/clouds/aws/journald2aws/main.py,sha256=xFkEhkYKtFfW0XRfY0UaX_gd_FU66WTZOMCyiIaPY3E,2237
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=YnEw91L55EXvR51UTl95FtXFUe3rUmidlkD4qccE7Tg,34211
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=iKhlTayS2j3Ra_eCSK6Qro8j4H573DLoPw8490RMIA8,24194
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=XWBVKBE6QOlzue9lu5_wFdySt_pJ1sOj7N9KTjawW-A,28047
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=0HHYi_uBV1t2KefVrExs3IZ6Zy-mQa7xN_ka9W9Obb8,94910
62
- ominfra/scripts/supervisor.py,sha256=C1eT7pIqPRJgN4U87tOjR_SuOSUwd5aUswvPeTy5Xlw,121831
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=Y1d_pk4eN18AbVYjDHAXMMnPwOKTFpc7JDb1uClYMsQ,5064
66
- ominfra/supervisor/configs.py,sha256=KpibZJ-V-4UpoJM2fnjXOXJLvDbwRJzNLXLGESUljV4,2966
67
- ominfra/supervisor/context.py,sha256=Fg_wD6oUR8vxe3JMM14mR-5ssIrTNwxRr-AXfoGbzJQ,15795
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=0bj_9AzIfDlB1BB8zcX9npQIknamN7FGoVEYgLMLuP0,1701
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=94cglin7qBwxTNXjOBxqec4qsACu-VfeboW-JfzvvbE,31454
76
+ ominfra/supervisor/process.py,sha256=oEd58g6KcLNWhwaZBu8wZJjb6Vd0t1sKPMVE4BjbBSI,32270
75
77
  ominfra/supervisor/states.py,sha256=JMxXYTZhJkMNQZ2tTV6wId7wrvnWgiZteskACprKskM,1374
76
- ominfra/supervisor/supervisor.py,sha256=p612yph5aKzkVLH6CfOMcRHEbNfs_TMlmjfLwtG8Jo0,12324
77
- ominfra/supervisor/types.py,sha256=ec62QG0CDJc0XNxCnf3lXxhsxrr4CCScLPI-1SpQjlc,1141
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.dev120.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
84
- ominfra-0.0.0.dev120.dist-info/METADATA,sha256=xuFwZN7LnvBCDHvhqgdKONRKkDYUYJ6SZqTjaE4k2rI,742
85
- ominfra-0.0.0.dev120.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
86
- ominfra-0.0.0.dev120.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
87
- ominfra-0.0.0.dev120.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
88
- ominfra-0.0.0.dev120.dist-info/RECORD,,
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,,