ominfra 0.0.0.dev127__py3-none-any.whl → 0.0.0.dev129__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.
- ominfra/deploy/_executor.py +24 -0
- ominfra/pyremote/_runcommands.py +24 -0
- ominfra/scripts/journald2aws.py +24 -0
- ominfra/scripts/supervisor.py +1320 -1225
- ominfra/supervisor/configs.py +34 -11
- ominfra/supervisor/dispatchers.py +7 -6
- ominfra/supervisor/dispatchersimpl.py +29 -22
- ominfra/supervisor/groups.py +1 -1
- ominfra/supervisor/groupsimpl.py +2 -2
- ominfra/supervisor/inject.py +22 -17
- ominfra/supervisor/io.py +82 -0
- ominfra/supervisor/main.py +6 -7
- ominfra/supervisor/pipes.py +15 -13
- ominfra/supervisor/poller.py +36 -35
- ominfra/supervisor/{processes.py → process.py} +2 -1
- ominfra/supervisor/{processesimpl.py → processimpl.py} +42 -54
- ominfra/supervisor/setup.py +1 -1
- ominfra/supervisor/setupimpl.py +4 -3
- ominfra/supervisor/signals.py +56 -50
- ominfra/supervisor/spawning.py +2 -1
- ominfra/supervisor/spawningimpl.py +24 -21
- ominfra/supervisor/supervisor.py +72 -134
- ominfra/supervisor/types.py +45 -34
- ominfra/supervisor/utils/__init__.py +0 -0
- ominfra/supervisor/utils/diag.py +31 -0
- ominfra/supervisor/utils/fds.py +46 -0
- ominfra/supervisor/utils/fs.py +47 -0
- ominfra/supervisor/utils/os.py +45 -0
- ominfra/supervisor/utils/ostypes.py +9 -0
- ominfra/supervisor/utils/signals.py +60 -0
- ominfra/supervisor/utils/strings.py +105 -0
- ominfra/supervisor/{users.py → utils/users.py} +11 -8
- {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev129.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev129.dist-info}/RECORD +39 -33
- ominfra/supervisor/context.py +0 -84
- ominfra/supervisor/datatypes.py +0 -113
- ominfra/supervisor/utils.py +0 -206
- /ominfra/supervisor/{collections.py → utils/collections.py} +0 -0
- {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev129.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev129.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev129.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev127.dist-info → ominfra-0.0.0.dev129.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import os
|
3
|
+
import tempfile
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
|
7
|
+
def try_unlink(path: str) -> bool:
|
8
|
+
try:
|
9
|
+
os.unlink(path)
|
10
|
+
except OSError:
|
11
|
+
return False
|
12
|
+
return True
|
13
|
+
|
14
|
+
|
15
|
+
def mktempfile(suffix: str, prefix: str, dir: str) -> str: # noqa
|
16
|
+
fd, filename = tempfile.mkstemp(suffix, prefix, dir)
|
17
|
+
os.close(fd)
|
18
|
+
return filename
|
19
|
+
|
20
|
+
|
21
|
+
def get_path() -> ta.Sequence[str]:
|
22
|
+
"""Return a list corresponding to $PATH, or a default."""
|
23
|
+
|
24
|
+
path = ['/bin', '/usr/bin', '/usr/local/bin']
|
25
|
+
if 'PATH' in os.environ:
|
26
|
+
p = os.environ['PATH']
|
27
|
+
if p:
|
28
|
+
path = p.split(os.pathsep)
|
29
|
+
return path
|
30
|
+
|
31
|
+
|
32
|
+
def check_existing_dir(v: str) -> str:
|
33
|
+
nv = os.path.expanduser(v)
|
34
|
+
if os.path.isdir(nv):
|
35
|
+
return nv
|
36
|
+
raise ValueError(f'{v} is not an existing directory')
|
37
|
+
|
38
|
+
|
39
|
+
def check_path_with_existing_dir(v: str) -> str:
|
40
|
+
nv = os.path.expanduser(v)
|
41
|
+
dir = os.path.dirname(nv) # noqa
|
42
|
+
if not dir:
|
43
|
+
# relative pathname with no directory component
|
44
|
+
return nv
|
45
|
+
if os.path.isdir(dir):
|
46
|
+
return nv
|
47
|
+
raise ValueError(f'The directory named as part of the path {v} does not exist')
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import os
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
from .ostypes import Rc
|
6
|
+
from .signals import sig_name
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
def real_exit(code: Rc) -> None:
|
13
|
+
os._exit(code) # noqa
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
def decode_wait_status(sts: int) -> ta.Tuple[Rc, str]:
|
20
|
+
"""
|
21
|
+
Decode the status returned by wait() or waitpid().
|
22
|
+
|
23
|
+
Return a tuple (exitstatus, message) where exitstatus is the exit status, or -1 if the process was killed by a
|
24
|
+
signal; and message is a message telling what happened. It is the caller's responsibility to display the message.
|
25
|
+
"""
|
26
|
+
|
27
|
+
if os.WIFEXITED(sts):
|
28
|
+
es = os.WEXITSTATUS(sts) & 0xffff
|
29
|
+
msg = f'exit status {es}'
|
30
|
+
return Rc(es), msg
|
31
|
+
|
32
|
+
elif os.WIFSIGNALED(sts):
|
33
|
+
sig = os.WTERMSIG(sts)
|
34
|
+
msg = f'terminated by {sig_name(sig)}'
|
35
|
+
if hasattr(os, 'WCOREDUMP'):
|
36
|
+
iscore = os.WCOREDUMP(sts)
|
37
|
+
else:
|
38
|
+
iscore = bool(sts & 0x80)
|
39
|
+
if iscore:
|
40
|
+
msg += ' (core dumped)'
|
41
|
+
return Rc(-1), msg
|
42
|
+
|
43
|
+
else:
|
44
|
+
msg = 'unknown termination cause 0x%04x' % sts # noqa
|
45
|
+
return Rc(-1), msg
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import signal
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
##
|
7
|
+
|
8
|
+
|
9
|
+
_SIGS_BY_NUM: ta.Mapping[int, signal.Signals] = {s.value: s for s in signal.Signals}
|
10
|
+
_SIGS_BY_NAME: ta.Mapping[str, signal.Signals] = {s.name: s for s in signal.Signals}
|
11
|
+
|
12
|
+
|
13
|
+
def sig_num(value: ta.Union[int, str]) -> int:
|
14
|
+
try:
|
15
|
+
num = int(value)
|
16
|
+
|
17
|
+
except (ValueError, TypeError):
|
18
|
+
name = value.strip().upper() # type: ignore
|
19
|
+
if not name.startswith('SIG'):
|
20
|
+
name = f'SIG{name}'
|
21
|
+
|
22
|
+
if (sn := _SIGS_BY_NAME.get(name)) is None:
|
23
|
+
raise ValueError(f'value {value!r} is not a valid signal name') # noqa
|
24
|
+
num = sn
|
25
|
+
|
26
|
+
if num not in _SIGS_BY_NUM:
|
27
|
+
raise ValueError(f'value {value!r} is not a valid signal number')
|
28
|
+
|
29
|
+
return num
|
30
|
+
|
31
|
+
|
32
|
+
def sig_name(num: int) -> str:
|
33
|
+
if (sig := _SIGS_BY_NUM.get(num)) is not None:
|
34
|
+
return sig.name
|
35
|
+
return f'signal {sig}'
|
36
|
+
|
37
|
+
|
38
|
+
##
|
39
|
+
|
40
|
+
|
41
|
+
class SignalReceiver:
|
42
|
+
def __init__(self) -> None:
|
43
|
+
super().__init__()
|
44
|
+
|
45
|
+
self._signals_recvd: ta.List[int] = []
|
46
|
+
|
47
|
+
def receive(self, sig: int, frame: ta.Any = None) -> None:
|
48
|
+
if sig not in self._signals_recvd:
|
49
|
+
self._signals_recvd.append(sig)
|
50
|
+
|
51
|
+
def install(self, *sigs: int) -> None:
|
52
|
+
for sig in sigs:
|
53
|
+
signal.signal(sig, self.receive)
|
54
|
+
|
55
|
+
def get_signal(self) -> ta.Optional[int]:
|
56
|
+
if self._signals_recvd:
|
57
|
+
sig = self._signals_recvd.pop(0)
|
58
|
+
else:
|
59
|
+
sig = None
|
60
|
+
return sig
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
|
5
|
+
##
|
6
|
+
|
7
|
+
|
8
|
+
def as_bytes(s: ta.Union[str, bytes], encoding: str = 'utf8') -> bytes:
|
9
|
+
if isinstance(s, bytes):
|
10
|
+
return s
|
11
|
+
else:
|
12
|
+
return s.encode(encoding)
|
13
|
+
|
14
|
+
|
15
|
+
@ta.overload
|
16
|
+
def find_prefix_at_end(haystack: str, needle: str) -> int:
|
17
|
+
...
|
18
|
+
|
19
|
+
|
20
|
+
@ta.overload
|
21
|
+
def find_prefix_at_end(haystack: bytes, needle: bytes) -> int:
|
22
|
+
...
|
23
|
+
|
24
|
+
|
25
|
+
def find_prefix_at_end(haystack, needle):
|
26
|
+
l = len(needle) - 1
|
27
|
+
while l and not haystack.endswith(needle[:l]):
|
28
|
+
l -= 1
|
29
|
+
return l
|
30
|
+
|
31
|
+
|
32
|
+
##
|
33
|
+
|
34
|
+
|
35
|
+
ANSI_ESCAPE_BEGIN = b'\x1b['
|
36
|
+
ANSI_TERMINATORS = (b'H', b'f', b'A', b'B', b'C', b'D', b'R', b's', b'u', b'J', b'K', b'h', b'l', b'p', b'm')
|
37
|
+
|
38
|
+
|
39
|
+
def strip_escapes(s: bytes) -> bytes:
|
40
|
+
"""Remove all ANSI color escapes from the given string."""
|
41
|
+
|
42
|
+
result = b''
|
43
|
+
show = 1
|
44
|
+
i = 0
|
45
|
+
l = len(s)
|
46
|
+
while i < l:
|
47
|
+
if show == 0 and s[i:i + 1] in ANSI_TERMINATORS:
|
48
|
+
show = 1
|
49
|
+
elif show:
|
50
|
+
n = s.find(ANSI_ESCAPE_BEGIN, i)
|
51
|
+
if n == -1:
|
52
|
+
return result + s[i:]
|
53
|
+
else:
|
54
|
+
result = result + s[i:n]
|
55
|
+
i = n
|
56
|
+
show = 0
|
57
|
+
i += 1
|
58
|
+
return result
|
59
|
+
|
60
|
+
|
61
|
+
##
|
62
|
+
|
63
|
+
|
64
|
+
class SuffixMultiplier:
|
65
|
+
# d is a dictionary of suffixes to integer multipliers. If no suffixes match, default is the multiplier. Matches
|
66
|
+
# are case insensitive. Return values are in the fundamental unit.
|
67
|
+
def __init__(self, d, default=1):
|
68
|
+
super().__init__()
|
69
|
+
self._d = d
|
70
|
+
self._default = default
|
71
|
+
# all keys must be the same size
|
72
|
+
self._keysz = None
|
73
|
+
for k in d:
|
74
|
+
if self._keysz is None:
|
75
|
+
self._keysz = len(k)
|
76
|
+
elif self._keysz != len(k): # type: ignore
|
77
|
+
raise ValueError(k)
|
78
|
+
|
79
|
+
def __call__(self, v: ta.Union[str, int]) -> int:
|
80
|
+
if isinstance(v, int):
|
81
|
+
return v
|
82
|
+
v = v.lower()
|
83
|
+
for s, m in self._d.items():
|
84
|
+
if v[-self._keysz:] == s: # type: ignore
|
85
|
+
return int(v[:-self._keysz]) * m # type: ignore
|
86
|
+
return int(v) * self._default
|
87
|
+
|
88
|
+
|
89
|
+
parse_bytes_size = SuffixMultiplier({
|
90
|
+
'kb': 1024,
|
91
|
+
'mb': 1024 * 1024,
|
92
|
+
'gb': 1024 * 1024 * 1024,
|
93
|
+
})
|
94
|
+
|
95
|
+
|
96
|
+
#
|
97
|
+
|
98
|
+
|
99
|
+
def parse_octal(arg: ta.Union[str, int]) -> int:
|
100
|
+
if isinstance(arg, int):
|
101
|
+
return arg
|
102
|
+
try:
|
103
|
+
return int(arg, 8)
|
104
|
+
except (TypeError, ValueError):
|
105
|
+
raise ValueError(f'{arg} can not be converted to an octal type') # noqa
|
@@ -3,11 +3,14 @@ import dataclasses as dc
|
|
3
3
|
import grp
|
4
4
|
import pwd
|
5
5
|
|
6
|
+
from .ostypes import Gid
|
7
|
+
from .ostypes import Uid
|
8
|
+
|
6
9
|
|
7
10
|
##
|
8
11
|
|
9
12
|
|
10
|
-
def name_to_uid(name: str) ->
|
13
|
+
def name_to_uid(name: str) -> Uid:
|
11
14
|
try:
|
12
15
|
uid = int(name)
|
13
16
|
except ValueError:
|
@@ -21,10 +24,10 @@ def name_to_uid(name: str) -> int:
|
|
21
24
|
pwd.getpwuid(uid) # check if uid is valid
|
22
25
|
except KeyError:
|
23
26
|
raise ValueError(f'Invalid user id {name}') # noqa
|
24
|
-
return uid
|
27
|
+
return Uid(uid)
|
25
28
|
|
26
29
|
|
27
|
-
def name_to_gid(name: str) ->
|
30
|
+
def name_to_gid(name: str) -> Gid:
|
28
31
|
try:
|
29
32
|
gid = int(name)
|
30
33
|
except ValueError:
|
@@ -38,12 +41,12 @@ def name_to_gid(name: str) -> int:
|
|
38
41
|
grp.getgrgid(gid) # check if gid is valid
|
39
42
|
except KeyError:
|
40
43
|
raise ValueError(f'Invalid group id {name}') # noqa
|
41
|
-
return gid
|
44
|
+
return Gid(gid)
|
42
45
|
|
43
46
|
|
44
|
-
def gid_for_uid(uid:
|
47
|
+
def gid_for_uid(uid: Uid) -> Gid:
|
45
48
|
pwrec = pwd.getpwuid(uid)
|
46
|
-
return pwrec[3]
|
49
|
+
return Gid(pwrec[3])
|
47
50
|
|
48
51
|
|
49
52
|
##
|
@@ -52,8 +55,8 @@ def gid_for_uid(uid: int) -> int:
|
|
52
55
|
@dc.dataclass(frozen=True)
|
53
56
|
class User:
|
54
57
|
name: str
|
55
|
-
uid:
|
56
|
-
gid:
|
58
|
+
uid: Uid
|
59
|
+
gid: Gid
|
57
60
|
|
58
61
|
|
59
62
|
def get_user(name: str) -> User:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev129
|
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.
|
16
|
-
Requires-Dist: omlish==0.0.0.
|
15
|
+
Requires-Dist: omdev==0.0.0.dev129
|
16
|
+
Requires-Dist: omlish==0.0.0.dev129
|
17
17
|
Provides-Extra: all
|
18
18
|
Requires-Dist: paramiko~=3.5; extra == "all"
|
19
19
|
Requires-Dist: asyncssh~=2.18; extra == "all"
|
@@ -22,7 +22,7 @@ ominfra/clouds/aws/journald2aws/poster.py,sha256=hz1XuctW8GtLmfjhRvCFY6py52D4BzX
|
|
22
22
|
ominfra/clouds/gcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
23
|
ominfra/clouds/gcp/auth.py,sha256=3PyfRJNgajjMqJFem3SKui0CqGeHEsZlvbRhuxFcZG8,1348
|
24
24
|
ominfra/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
-
ominfra/deploy/_executor.py,sha256=
|
25
|
+
ominfra/deploy/_executor.py,sha256=1DQxeHJjxtRGxdFO7PiPhzxwiJlm8K2BelJfLXb_OxU,35079
|
26
26
|
ominfra/deploy/configs.py,sha256=qi0kwT7G2NH7dXLOQic-u6R3yeadup_QtvrjwWIggbM,435
|
27
27
|
ominfra/deploy/remote.py,sha256=6ACmpXU1uBdyGs3Xsp97ktKFq30cJlzN9LRWNUWlGY4,2144
|
28
28
|
ominfra/deploy/executor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
@@ -56,50 +56,56 @@ ominfra/journald/tailer.py,sha256=5abcFMfgi7fnY9ZEQe2ZVobaJxjQkeu6d9Kagw33a1w,33
|
|
56
56
|
ominfra/manage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
57
|
ominfra/manage/manage.py,sha256=BttL8LFEknHZE_h2Pt5dAqbfUkv6qy43WI0raXBZ1a8,151
|
58
58
|
ominfra/pyremote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
|
-
ominfra/pyremote/_runcommands.py,sha256=
|
59
|
+
ominfra/pyremote/_runcommands.py,sha256=bzN-253qyoKwS-5OIqPmfhxVJdt4HtrnspeUbpKKPWM,28915
|
60
60
|
ominfra/pyremote/bootstrap.py,sha256=RvMO3YGaN1E4sgUi1JEtiPak8cjvqtc_vRCq1yqbeZg,3370
|
61
61
|
ominfra/pyremote/runcommands.py,sha256=bviS0_TDIoZVAe4h-_iavbvJtVSFu8lnk7fQ5iasCWE,1571
|
62
62
|
ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
|
-
ominfra/scripts/journald2aws.py,sha256=
|
64
|
-
ominfra/scripts/supervisor.py,sha256=
|
63
|
+
ominfra/scripts/journald2aws.py,sha256=1fqTSLiZE3BCCxdyj5QMCWGHN4leS5INORaDuKyRm_A,128964
|
64
|
+
ominfra/scripts/supervisor.py,sha256=y84cLJj98qvyHiwWf0bDly2Rwexvc9QaT0qrQx06lGc,224746
|
65
65
|
ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
|
66
66
|
ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
67
67
|
ominfra/supervisor/__main__.py,sha256=I0yFw-C08OOiZ3BF6lF1Oiv789EQXu-_j6whDhQUTEA,66
|
68
|
-
ominfra/supervisor/
|
69
|
-
ominfra/supervisor/
|
70
|
-
ominfra/supervisor/
|
71
|
-
ominfra/supervisor/datatypes.py,sha256=3iMPodw0n9xnQzFN16gwYFU6wVb6NMMxKMZSSeAaXM4,2788
|
72
|
-
ominfra/supervisor/dispatchers.py,sha256=nbgTaWrRxBvVfJSBsf2wnL4hTAG63PETXXpFaol6VCw,936
|
73
|
-
ominfra/supervisor/dispatchersimpl.py,sha256=Shey0RVWxX1cgShEIlOsM3iJY5LAmayigi9sNnu4fQc,10897
|
68
|
+
ominfra/supervisor/configs.py,sha256=AhBlbifwDXc0acEhcbdv9jphJL-SBFODFDAWDVckzAE,3945
|
69
|
+
ominfra/supervisor/dispatchers.py,sha256=8yReui-dVPfvqYrHLsCU7M0siluOs1p5Up7Ag4nMAeI,991
|
70
|
+
ominfra/supervisor/dispatchersimpl.py,sha256=EZYxK_RbOkjMv4N3pjH6-_fBhXDmcxFhEyZEgEAbtUk,11294
|
74
71
|
ominfra/supervisor/events.py,sha256=w3HQFrq-SuroYWoQfNFYeU1phnTvHTgsAqA6TGtAafI,6593
|
75
72
|
ominfra/supervisor/exceptions.py,sha256=Qbu211H3CLlSmi9LsSikOwrcL5HgJP9ugvcKWlGTAoI,750
|
76
|
-
ominfra/supervisor/groups.py,sha256=
|
77
|
-
ominfra/supervisor/groupsimpl.py,sha256=
|
78
|
-
ominfra/supervisor/inject.py,sha256=
|
79
|
-
ominfra/supervisor/
|
80
|
-
ominfra/supervisor/
|
81
|
-
ominfra/supervisor/
|
73
|
+
ominfra/supervisor/groups.py,sha256=g5Zp_lkVhn1FSe6GSEPbaELincG5a46ctv1xpB-WmnQ,2163
|
74
|
+
ominfra/supervisor/groupsimpl.py,sha256=nIrW4SmB0W6c2jOR_HhkfVcH4eHyLZnG1FJ0MCzc6mQ,2292
|
75
|
+
ominfra/supervisor/inject.py,sha256=J1XGUkgCuaD-MPASso-2sbTSoVuDDIhcGbazQLVjajY,3217
|
76
|
+
ominfra/supervisor/io.py,sha256=iYYqQO0Hu1FIYzYXdSGHEIHYW-tGm4SCWky_ZTHaKWY,2727
|
77
|
+
ominfra/supervisor/main.py,sha256=ebe7skFPfwXV2meMVRndhuLZmz-LiuHH1x1CgiarR0o,4132
|
78
|
+
ominfra/supervisor/pipes.py,sha256=XrJ9lD04tPdzZD3xhhYKxpBKHWhZ0Ii315E78bgj7ws,2233
|
79
|
+
ominfra/supervisor/poller.py,sha256=LnQVttPCm8a1UtnDvsho6zLw8NP-2_2VUiNM-d0w_FU,7776
|
82
80
|
ominfra/supervisor/privileges.py,sha256=bO7rJGT7cMOBALK_4D4NiQnOS5dOYb14Sz66R-ymG24,2071
|
83
|
-
ominfra/supervisor/
|
84
|
-
ominfra/supervisor/
|
85
|
-
ominfra/supervisor/setup.py,sha256=
|
86
|
-
ominfra/supervisor/setupimpl.py,sha256=
|
87
|
-
ominfra/supervisor/signals.py,sha256=
|
88
|
-
ominfra/supervisor/spawning.py,sha256=
|
89
|
-
ominfra/supervisor/spawningimpl.py,sha256=
|
81
|
+
ominfra/supervisor/process.py,sha256=UaubVxsxVqDnbuWVpTH0DTGbJGLO0vGJ9mNcvy2kCXM,217
|
82
|
+
ominfra/supervisor/processimpl.py,sha256=bFhf9RqAB_yNlMM5dM1_4m964T9m3vKThEv3n7OYQN4,18684
|
83
|
+
ominfra/supervisor/setup.py,sha256=7HwwwI-WT_Z0WjZ9_l5Orr4K298nKKhQ1f_ZgGsi9TU,622
|
84
|
+
ominfra/supervisor/setupimpl.py,sha256=S_YgCH3XzLsFIAriJROvDMUDh7OzVVJoxzEzCkbb4g4,9648
|
85
|
+
ominfra/supervisor/signals.py,sha256=jY52naUifcAjd6nICTP1ZW3IQSPsHB4cvbsJo8_QV_U,2196
|
86
|
+
ominfra/supervisor/spawning.py,sha256=i1k3tmqWyU-KIN7kel-JVxTVGnLiTIVmZzlstJSZpjM,622
|
87
|
+
ominfra/supervisor/spawningimpl.py,sha256=E702F10TexP5-V6i97U6ysiBcTKyLSPzQoJZtedUjQs,11140
|
90
88
|
ominfra/supervisor/states.py,sha256=9yoNOSwalRcKEnCP9zG6tVS0oivo5tCeuH6AaaW7Jpc,890
|
91
|
-
ominfra/supervisor/supervisor.py,sha256=
|
92
|
-
ominfra/supervisor/types.py,sha256=
|
93
|
-
ominfra/supervisor/
|
94
|
-
ominfra/supervisor/utils.py,sha256=
|
89
|
+
ominfra/supervisor/supervisor.py,sha256=bLl4cHu6G4Kr_nDAr6Q6jG6g5oRWIx0rQaPHGVG0WMQ,9359
|
90
|
+
ominfra/supervisor/types.py,sha256=TZYvdX_3XQ82PgpOOJF2PT5VMlOD9zqMVZhB-ZofTRI,4891
|
91
|
+
ominfra/supervisor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
92
|
+
ominfra/supervisor/utils/collections.py,sha256=vcfmVYS4QngMdtEI1DvdRIcubmy55Wj40NCzW27_rIY,1361
|
93
|
+
ominfra/supervisor/utils/diag.py,sha256=ujz4gkW7p3wmbaKFM8Hz5eHEwpoUkbB8JeDvcHilCz0,705
|
94
|
+
ominfra/supervisor/utils/fds.py,sha256=lz8DWXzGYvu93dqhWK0WrhXrrJVQ_psoom4Nj_o8g2g,849
|
95
|
+
ominfra/supervisor/utils/fs.py,sha256=ABbNcsCpzSXAvq_ZZSCj61mj5kGnVuC4spUmoWenlqw,1155
|
96
|
+
ominfra/supervisor/utils/os.py,sha256=9fw--tpHOrSjGTCkUo1KRBgbGGxKW2va5xKw2cHwtRU,1096
|
97
|
+
ominfra/supervisor/utils/ostypes.py,sha256=B7VjwbzVesz9we9MztoSk8bH8sTxMIWtILy_Qde0G7w,164
|
98
|
+
ominfra/supervisor/utils/signals.py,sha256=uZkTvissbtq7TlJD4MkTiL3F-zyWmAFUuWQtFjsf0MI,1474
|
99
|
+
ominfra/supervisor/utils/strings.py,sha256=B0UOuVM_NIWmcznycmiEbwJ0lcoTGEd3Ir1AeLkBXeU,2478
|
100
|
+
ominfra/supervisor/utils/users.py,sha256=PRUhWy74WQCxix4BLNYcWW1i2mF1IyAxj1RzElnP4iM,1345
|
95
101
|
ominfra/tailscale/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
96
102
|
ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
|
97
103
|
ominfra/tailscale/cli.py,sha256=DSGp4hn5xwOW-l_u_InKlSF6kIobxtUtVssf_73STs0,3567
|
98
104
|
ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
99
105
|
ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
|
100
|
-
ominfra-0.0.0.
|
101
|
-
ominfra-0.0.0.
|
102
|
-
ominfra-0.0.0.
|
103
|
-
ominfra-0.0.0.
|
104
|
-
ominfra-0.0.0.
|
105
|
-
ominfra-0.0.0.
|
106
|
+
ominfra-0.0.0.dev129.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
107
|
+
ominfra-0.0.0.dev129.dist-info/METADATA,sha256=VsF9d4SW55_llwPjsmpCXtrLrw5L0r86L5DndQ7YOwo,731
|
108
|
+
ominfra-0.0.0.dev129.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
109
|
+
ominfra-0.0.0.dev129.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
|
110
|
+
ominfra-0.0.0.dev129.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
|
111
|
+
ominfra-0.0.0.dev129.dist-info/RECORD,,
|
ominfra/supervisor/context.py
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
# ruff: noqa: UP006 UP007
|
2
|
-
import errno
|
3
|
-
import os
|
4
|
-
import typing as ta
|
5
|
-
|
6
|
-
from omlish.lite.logs import log
|
7
|
-
|
8
|
-
from .configs import ServerConfig
|
9
|
-
from .poller import Poller
|
10
|
-
from .states import SupervisorState
|
11
|
-
from .types import Process
|
12
|
-
from .types import ServerContext
|
13
|
-
from .types import ServerEpoch
|
14
|
-
from .utils import mktempfile
|
15
|
-
|
16
|
-
|
17
|
-
class ServerContextImpl(ServerContext):
|
18
|
-
def __init__(
|
19
|
-
self,
|
20
|
-
config: ServerConfig,
|
21
|
-
poller: Poller,
|
22
|
-
*,
|
23
|
-
epoch: ServerEpoch = ServerEpoch(0),
|
24
|
-
) -> None:
|
25
|
-
super().__init__()
|
26
|
-
|
27
|
-
self._config = config
|
28
|
-
self._poller = poller
|
29
|
-
self._epoch = epoch
|
30
|
-
|
31
|
-
self._pid_history: ta.Dict[int, Process] = {}
|
32
|
-
self._state: SupervisorState = SupervisorState.RUNNING
|
33
|
-
|
34
|
-
@property
|
35
|
-
def config(self) -> ServerConfig:
|
36
|
-
return self._config
|
37
|
-
|
38
|
-
@property
|
39
|
-
def epoch(self) -> ServerEpoch:
|
40
|
-
return self._epoch
|
41
|
-
|
42
|
-
@property
|
43
|
-
def first(self) -> bool:
|
44
|
-
return not self._epoch
|
45
|
-
|
46
|
-
@property
|
47
|
-
def state(self) -> SupervisorState:
|
48
|
-
return self._state
|
49
|
-
|
50
|
-
def set_state(self, state: SupervisorState) -> None:
|
51
|
-
self._state = state
|
52
|
-
|
53
|
-
@property
|
54
|
-
def pid_history(self) -> ta.Dict[int, Process]:
|
55
|
-
return self._pid_history
|
56
|
-
|
57
|
-
#
|
58
|
-
|
59
|
-
def waitpid(self) -> ta.Tuple[ta.Optional[int], ta.Optional[int]]:
|
60
|
-
# Need pthread_sigmask here to avoid concurrent sigchld, but Python doesn't offer in Python < 3.4. There is
|
61
|
-
# still a race condition here; we can get a sigchld while we're sitting in the waitpid call. However, AFAICT, if
|
62
|
-
# waitpid is interrupted by SIGCHLD, as long as we call waitpid again (which happens every so often during the
|
63
|
-
# normal course in the mainloop), we'll eventually reap the child that we tried to reap during the interrupted
|
64
|
-
# call. At least on Linux, this appears to be true, or at least stopping 50 processes at once never left zombies
|
65
|
-
# lying around.
|
66
|
-
try:
|
67
|
-
pid, sts = os.waitpid(-1, os.WNOHANG)
|
68
|
-
except OSError as exc:
|
69
|
-
code = exc.args[0]
|
70
|
-
if code not in (errno.ECHILD, errno.EINTR):
|
71
|
-
log.critical('waitpid error %r; a process may not be cleaned up properly', code)
|
72
|
-
if code == errno.EINTR:
|
73
|
-
log.debug('EINTR during reap')
|
74
|
-
pid, sts = None, None
|
75
|
-
return pid, sts
|
76
|
-
|
77
|
-
def get_auto_child_log_name(self, name: str, identifier: str, channel: str) -> str:
|
78
|
-
prefix = f'{name}-{channel}---{identifier}-'
|
79
|
-
logfile = mktempfile(
|
80
|
-
suffix='.log',
|
81
|
-
prefix=prefix,
|
82
|
-
dir=self.config.child_logdir,
|
83
|
-
)
|
84
|
-
return logfile
|
ominfra/supervisor/datatypes.py
DELETED
@@ -1,113 +0,0 @@
|
|
1
|
-
# ruff: noqa: UP007
|
2
|
-
import logging
|
3
|
-
import os
|
4
|
-
import typing as ta
|
5
|
-
|
6
|
-
|
7
|
-
class Automatic:
|
8
|
-
pass
|
9
|
-
|
10
|
-
|
11
|
-
class Syslog:
|
12
|
-
"""TODO deprecated; remove this special 'syslog' filename in the future"""
|
13
|
-
|
14
|
-
|
15
|
-
LOGFILE_NONES = ('none', 'off', None)
|
16
|
-
LOGFILE_AUTOS = (Automatic, 'auto')
|
17
|
-
LOGFILE_SYSLOGS = (Syslog, 'syslog')
|
18
|
-
|
19
|
-
|
20
|
-
def logfile_name(val):
|
21
|
-
if hasattr(val, 'lower'):
|
22
|
-
coerced = val.lower()
|
23
|
-
else:
|
24
|
-
coerced = val
|
25
|
-
|
26
|
-
if coerced in LOGFILE_NONES:
|
27
|
-
return None
|
28
|
-
elif coerced in LOGFILE_AUTOS:
|
29
|
-
return Automatic
|
30
|
-
elif coerced in LOGFILE_SYSLOGS:
|
31
|
-
return Syslog
|
32
|
-
else:
|
33
|
-
return existing_dirpath(val)
|
34
|
-
|
35
|
-
|
36
|
-
##
|
37
|
-
|
38
|
-
|
39
|
-
def octal_type(arg: ta.Union[str, int]) -> int:
|
40
|
-
if isinstance(arg, int):
|
41
|
-
return arg
|
42
|
-
try:
|
43
|
-
return int(arg, 8)
|
44
|
-
except (TypeError, ValueError):
|
45
|
-
raise ValueError(f'{arg} can not be converted to an octal type') # noqa
|
46
|
-
|
47
|
-
|
48
|
-
def existing_directory(v: str) -> str:
|
49
|
-
nv = os.path.expanduser(v)
|
50
|
-
if os.path.isdir(nv):
|
51
|
-
return nv
|
52
|
-
raise ValueError(f'{v} is not an existing directory')
|
53
|
-
|
54
|
-
|
55
|
-
def existing_dirpath(v: str) -> str:
|
56
|
-
nv = os.path.expanduser(v)
|
57
|
-
dir = os.path.dirname(nv) # noqa
|
58
|
-
if not dir:
|
59
|
-
# relative pathname with no directory component
|
60
|
-
return nv
|
61
|
-
if os.path.isdir(dir):
|
62
|
-
return nv
|
63
|
-
raise ValueError(f'The directory named as part of the path {v} does not exist')
|
64
|
-
|
65
|
-
|
66
|
-
def logging_level(value: ta.Union[str, int]) -> int:
|
67
|
-
if isinstance(value, int):
|
68
|
-
return value
|
69
|
-
s = str(value).lower()
|
70
|
-
level = logging.getLevelNamesMapping().get(s.upper())
|
71
|
-
if level is None:
|
72
|
-
raise ValueError(f'bad logging level name {value!r}')
|
73
|
-
return level
|
74
|
-
|
75
|
-
|
76
|
-
class SuffixMultiplier:
|
77
|
-
# d is a dictionary of suffixes to integer multipliers. If no suffixes match, default is the multiplier. Matches
|
78
|
-
# are case insensitive. Return values are in the fundamental unit.
|
79
|
-
def __init__(self, d, default=1):
|
80
|
-
super().__init__()
|
81
|
-
self._d = d
|
82
|
-
self._default = default
|
83
|
-
# all keys must be the same size
|
84
|
-
self._keysz = None
|
85
|
-
for k in d:
|
86
|
-
if self._keysz is None:
|
87
|
-
self._keysz = len(k)
|
88
|
-
elif self._keysz != len(k): # type: ignore
|
89
|
-
raise ValueError(k)
|
90
|
-
|
91
|
-
def __call__(self, v: ta.Union[str, int]) -> int:
|
92
|
-
if isinstance(v, int):
|
93
|
-
return v
|
94
|
-
v = v.lower()
|
95
|
-
for s, m in self._d.items():
|
96
|
-
if v[-self._keysz:] == s: # type: ignore
|
97
|
-
return int(v[:-self._keysz]) * m # type: ignore
|
98
|
-
return int(v) * self._default
|
99
|
-
|
100
|
-
|
101
|
-
byte_size = SuffixMultiplier({
|
102
|
-
'kb': 1024,
|
103
|
-
'mb': 1024 * 1024,
|
104
|
-
'gb': 1024 * 1024 * 1024,
|
105
|
-
})
|
106
|
-
|
107
|
-
|
108
|
-
class RestartWhenExitUnexpected:
|
109
|
-
pass
|
110
|
-
|
111
|
-
|
112
|
-
class RestartUnconditionally:
|
113
|
-
pass
|