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.
@@ -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.dev121
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.dev121
16
+ Requires-Dist: omlish ==0.0.0.dev121
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=7-A5aScQKhkvh1RgMHBYhccIXECaSHEf0Cv0tqIE_DY,34215
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=g-e_vL1if4ls-GokLpUdAlwLQFTEwtJwsc31Vgb1uw8,28051
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=PAhsys4Ya_FERZ6fcYWJDqTNB2PNfE-dpZwaq4tw2nI,128130
64
+ ominfra/scripts/supervisor.py,sha256=zk8oHSBar7wCW890NBELECAZxQ5oYhRjHvqhYnSxBmM,172749
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.dev121.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
86
+ ominfra-0.0.0.dev121.dist-info/METADATA,sha256=SBtjNf2M-A5Y1F8QRg2785aRubDRD_ff15vrWTLA90E,742
87
+ ominfra-0.0.0.dev121.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
88
+ ominfra-0.0.0.dev121.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
89
+ ominfra-0.0.0.dev121.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
90
+ ominfra-0.0.0.dev121.dist-info/RECORD,,