ominfra 0.0.0.dev126__py3-none-any.whl → 0.0.0.dev128__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.
Files changed (44) hide show
  1. ominfra/clouds/aws/auth.py +1 -1
  2. ominfra/deploy/_executor.py +1 -1
  3. ominfra/deploy/poly/_main.py +1 -1
  4. ominfra/pyremote/_runcommands.py +1 -1
  5. ominfra/scripts/journald2aws.py +2 -2
  6. ominfra/scripts/supervisor.py +4736 -4166
  7. ominfra/supervisor/configs.py +34 -11
  8. ominfra/supervisor/context.py +7 -345
  9. ominfra/supervisor/dispatchers.py +21 -324
  10. ominfra/supervisor/dispatchersimpl.py +343 -0
  11. ominfra/supervisor/groups.py +33 -111
  12. ominfra/supervisor/groupsimpl.py +86 -0
  13. ominfra/supervisor/inject.py +45 -20
  14. ominfra/supervisor/main.py +3 -3
  15. ominfra/supervisor/pipes.py +85 -0
  16. ominfra/supervisor/poller.py +42 -38
  17. ominfra/supervisor/privileges.py +65 -0
  18. ominfra/supervisor/process.py +6 -742
  19. ominfra/supervisor/processimpl.py +516 -0
  20. ominfra/supervisor/setup.py +38 -0
  21. ominfra/supervisor/setupimpl.py +262 -0
  22. ominfra/supervisor/spawning.py +32 -0
  23. ominfra/supervisor/spawningimpl.py +350 -0
  24. ominfra/supervisor/supervisor.py +67 -84
  25. ominfra/supervisor/types.py +101 -47
  26. ominfra/supervisor/utils/__init__.py +0 -0
  27. ominfra/supervisor/utils/collections.py +52 -0
  28. ominfra/supervisor/utils/diag.py +31 -0
  29. ominfra/supervisor/utils/fds.py +46 -0
  30. ominfra/supervisor/utils/fs.py +47 -0
  31. ominfra/supervisor/utils/os.py +45 -0
  32. ominfra/supervisor/utils/ostypes.py +9 -0
  33. ominfra/supervisor/utils/signals.py +60 -0
  34. ominfra/supervisor/utils/strings.py +105 -0
  35. ominfra/supervisor/utils/users.py +67 -0
  36. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/METADATA +3 -3
  37. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/RECORD +41 -25
  38. ominfra/supervisor/datatypes.py +0 -175
  39. ominfra/supervisor/signals.py +0 -52
  40. ominfra/supervisor/utils.py +0 -206
  41. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/LICENSE +0 -0
  42. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/WHEEL +0 -0
  43. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.dist-info}/entry_points.txt +0 -0
  44. {ominfra-0.0.0.dev126.dist-info → ominfra-0.0.0.dev128.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,9 @@
1
+ import typing as ta
2
+
3
+
4
+ Fd = ta.NewType('Fd', int)
5
+ Pid = ta.NewType('Pid', int)
6
+ Rc = ta.NewType('Rc', int)
7
+
8
+ Uid = ta.NewType('Uid', int)
9
+ Gid = ta.NewType('Gid', int)
@@ -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
@@ -0,0 +1,67 @@
1
+ # ruff: noqa: UP007
2
+ import dataclasses as dc
3
+ import grp
4
+ import pwd
5
+
6
+ from .ostypes import Gid
7
+ from .ostypes import Uid
8
+
9
+
10
+ ##
11
+
12
+
13
+ def name_to_uid(name: str) -> Uid:
14
+ try:
15
+ uid = int(name)
16
+ except ValueError:
17
+ try:
18
+ pwdrec = pwd.getpwnam(name)
19
+ except KeyError:
20
+ raise ValueError(f'Invalid user name {name}') # noqa
21
+ uid = pwdrec[2]
22
+ else:
23
+ try:
24
+ pwd.getpwuid(uid) # check if uid is valid
25
+ except KeyError:
26
+ raise ValueError(f'Invalid user id {name}') # noqa
27
+ return Uid(uid)
28
+
29
+
30
+ def name_to_gid(name: str) -> Gid:
31
+ try:
32
+ gid = int(name)
33
+ except ValueError:
34
+ try:
35
+ grprec = grp.getgrnam(name)
36
+ except KeyError:
37
+ raise ValueError(f'Invalid group name {name}') # noqa
38
+ gid = grprec[2]
39
+ else:
40
+ try:
41
+ grp.getgrgid(gid) # check if gid is valid
42
+ except KeyError:
43
+ raise ValueError(f'Invalid group id {name}') # noqa
44
+ return Gid(gid)
45
+
46
+
47
+ def gid_for_uid(uid: Uid) -> Gid:
48
+ pwrec = pwd.getpwuid(uid)
49
+ return Gid(pwrec[3])
50
+
51
+
52
+ ##
53
+
54
+
55
+ @dc.dataclass(frozen=True)
56
+ class User:
57
+ name: str
58
+ uid: Uid
59
+ gid: Gid
60
+
61
+
62
+ def get_user(name: str) -> User:
63
+ return User(
64
+ name=name,
65
+ uid=(uid := name_to_uid(name)),
66
+ gid=gid_for_uid(uid),
67
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ominfra
3
- Version: 0.0.0.dev126
3
+ Version: 0.0.0.dev128
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.dev126
16
- Requires-Dist: omlish==0.0.0.dev126
15
+ Requires-Dist: omdev==0.0.0.dev128
16
+ Requires-Dist: omlish==0.0.0.dev128
17
17
  Provides-Extra: all
18
18
  Requires-Dist: paramiko~=3.5; extra == "all"
19
19
  Requires-Dist: asyncssh~=2.18; extra == "all"
@@ -8,7 +8,7 @@ ominfra/threadworkers.py,sha256=oX4ubZn7h932saXpRIJu2MNhBExgGGMuGhdXarZxLJw,4948
8
8
  ominfra/clouds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  ominfra/clouds/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  ominfra/clouds/aws/__main__.py,sha256=HXMoxEl9KHhv6zOOPQxiJAftfR2SjBqeVTYw-og9aFw,163
11
- ominfra/clouds/aws/auth.py,sha256=p50hnm8SU4CWAkLX0C4XCTAm7yAAg-HqcP1YvL5cW94,6205
11
+ ominfra/clouds/aws/auth.py,sha256=qXS3X6Zl0FHqvfv1MNVYK1iy1TLbN0Md6S8N4Goq6kU,6204
12
12
  ominfra/clouds/aws/cli.py,sha256=OJVVLIwSy1378drkgP1ke_JltbyzBmnrB_Lom6A83os,510
13
13
  ominfra/clouds/aws/dataclasses.py,sha256=rKhtJKJ0JhMssU9n9CABX_JaUiokIboEATJ9TZgZQ6A,3868
14
14
  ominfra/clouds/aws/logs.py,sha256=z9ouU2IYXNHsl7_Whbjs1FGtlUwsEq0RV8LNrM_QNTE,5471
@@ -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=AZ6F7zcVMQRKZwOxfFTfsrj1IA42bIrZF22jiDLapoE,34654
25
+ ominfra/deploy/_executor.py,sha256=osVBYirgpX1iJKyLHf7JwKD3AhPqJrnCrjI4qVrNFJo,34662
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
@@ -37,7 +37,7 @@ ominfra/deploy/executor/concerns/systemd.py,sha256=MtsSEToEa1HNouern_JukcYTnypw_
37
37
  ominfra/deploy/executor/concerns/user.py,sha256=j5LDfQXquIp-eEM7t6aShsrYoQrM_ILXZycTmTcRVxA,686
38
38
  ominfra/deploy/executor/concerns/venv.py,sha256=jbRriqJHO4r9Zyo5Hfl_qVmcU6Qm6UgrouBroKcPn2g,775
39
39
  ominfra/deploy/poly/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
40
- ominfra/deploy/poly/_main.py,sha256=MT2KKuPmJ1Z1sTVQH_Rlt-qbMvhTaJTjAaQjWP7KbsE,24172
40
+ ominfra/deploy/poly/_main.py,sha256=RyuYQCytPyRqBx8qtzrMiP8xa9C9E3nF2fpLTubt5v8,24180
41
41
  ominfra/deploy/poly/base.py,sha256=1dGuzWxi2Z6Hm6-YlkVxPk9r3In2aCJ0p8lGR-QQI_s,4166
42
42
  ominfra/deploy/poly/configs.py,sha256=9bzWdbxhOk_Q4KokDjmRz254KHnUU71Vl1frLlhQyU4,584
43
43
  ominfra/deploy/poly/deploy.py,sha256=tMYKslXLjstcv86siRt5j37USsS0Wd6lsfeGRE26zio,544
@@ -56,39 +56,55 @@ 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=EDTp6NOgux8ZJw-tcIHtfafc-fHLh5KuhghA3Xemhy8,28490
59
+ ominfra/pyremote/_runcommands.py,sha256=86bIzkhi5YLmtxQYyk8SBwPv8fCCfdu2NHHlxug4ACk,28498
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=veQDwe0RQ3EYLW5jSsNJCUOx6RTLb_7FXUN7fBSgWUc,128540
64
- ominfra/scripts/supervisor.py,sha256=NP8lcOQVuvSVWlZV0v8d-2OSc78x75iIje_hELW-UaU,211854
63
+ ominfra/scripts/journald2aws.py,sha256=UpwV8P1ZE8ywXW-8xpCaTCBRWe94BuG_MS1CXfD3wcw,128547
64
+ ominfra/scripts/supervisor.py,sha256=ZqUDlxRxSML5Ty0sOKuils35km5-9xjYBQF4yQDnVaY,222164
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/configs.py,sha256=TtVyWdxinrd3tueM6q8j2YVcEqauFTJqlbykkSjByEo,3524
69
- ominfra/supervisor/context.py,sha256=YryPUIsRG48xVtrGeYiUPGSJDRHCQgwk0egIV6VDf80,15249
70
- ominfra/supervisor/datatypes.py,sha256=UnXO_UlCyJD9u0uvea1wvnk_UZCzxNMeFvPK83gv530,4432
71
- ominfra/supervisor/dispatchers.py,sha256=9-skIUYgrJotcDpMPzc2qHlhZzXc2xS3HngvfS2ipo8,10876
68
+ ominfra/supervisor/configs.py,sha256=AhBlbifwDXc0acEhcbdv9jphJL-SBFODFDAWDVckzAE,3945
69
+ ominfra/supervisor/context.py,sha256=UDJc0mVHHFg3T7MyO7gehWX1wwmFvw-7xeKLl-cC8jc,2595
70
+ ominfra/supervisor/dispatchers.py,sha256=qWtVLx-4H3I-Io7OPcVgRpIYehGsA4q3Duo5ZWdUM4Y,970
71
+ ominfra/supervisor/dispatchersimpl.py,sha256=t1VFcofj1kTH1q13Z-S1OUTXixPwgSqJkh5A5IkHKPA,10956
72
72
  ominfra/supervisor/events.py,sha256=w3HQFrq-SuroYWoQfNFYeU1phnTvHTgsAqA6TGtAafI,6593
73
73
  ominfra/supervisor/exceptions.py,sha256=Qbu211H3CLlSmi9LsSikOwrcL5HgJP9ugvcKWlGTAoI,750
74
- ominfra/supervisor/groups.py,sha256=5niQPfvAmpN4TC764a-Uzl5OQf8tCYg9pAijDOhDf7U,4396
75
- ominfra/supervisor/inject.py,sha256=rYt37SNbx_5N9cdWag7GaMm2EGRjaQCVJ5LCLHFjsLk,2145
76
- ominfra/supervisor/main.py,sha256=iaJWFNmAmmy5dY2KjA7HRjp2uT67kJGeG8DXQ4dD1e4,4126
77
- ominfra/supervisor/poller.py,sha256=-gY_GIjuYr0J6ql_tFwKhlzRN4_mQaiVgMDOEHx_54Y,7693
78
- ominfra/supervisor/process.py,sha256=nmP3JUSQVE69DEZiDsjk7BUFKAySzjXK7-OnBzKWv0w,29003
79
- ominfra/supervisor/signals.py,sha256=tv1CJm3Xrb69KVSswqm_u2vr2Lb2AbTxBVtQqIOSeyk,1176
74
+ ominfra/supervisor/groups.py,sha256=g5Zp_lkVhn1FSe6GSEPbaELincG5a46ctv1xpB-WmnQ,2163
75
+ ominfra/supervisor/groupsimpl.py,sha256=_5Mxf6VdVV375KgwcuUTpErUH4FBpmzbNm3vbGB_JaQ,2304
76
+ ominfra/supervisor/inject.py,sha256=ZCfloXxURjiVTutzudGFWybrVCvjDD_C3HayMATCMrY,3042
77
+ ominfra/supervisor/main.py,sha256=RCCpMX4D2RKT8vZMF82pENKpXfW3GUdNgMEUzUtpqL0,4140
78
+ ominfra/supervisor/pipes.py,sha256=XrJ9lD04tPdzZD3xhhYKxpBKHWhZ0Ii315E78bgj7ws,2233
79
+ ominfra/supervisor/poller.py,sha256=LnQVttPCm8a1UtnDvsho6zLw8NP-2_2VUiNM-d0w_FU,7776
80
+ ominfra/supervisor/privileges.py,sha256=bO7rJGT7cMOBALK_4D4NiQnOS5dOYb14Sz66R-ymG24,2071
81
+ ominfra/supervisor/process.py,sha256=UaubVxsxVqDnbuWVpTH0DTGbJGLO0vGJ9mNcvy2kCXM,217
82
+ ominfra/supervisor/processimpl.py,sha256=0i0P3gdxuJsgcvy2VTKN6-NYgcBjozUaPsA_tUcEmb4,18769
83
+ ominfra/supervisor/setup.py,sha256=7HwwwI-WT_Z0WjZ9_l5Orr4K298nKKhQ1f_ZgGsi9TU,622
84
+ ominfra/supervisor/setupimpl.py,sha256=S_YgCH3XzLsFIAriJROvDMUDh7OzVVJoxzEzCkbb4g4,9648
85
+ ominfra/supervisor/spawning.py,sha256=i1k3tmqWyU-KIN7kel-JVxTVGnLiTIVmZzlstJSZpjM,622
86
+ ominfra/supervisor/spawningimpl.py,sha256=0InDyRPh7gAEX07lg6eUYMymX0RikHOz_ieAghxKx8Y,11063
80
87
  ominfra/supervisor/states.py,sha256=9yoNOSwalRcKEnCP9zG6tVS0oivo5tCeuH6AaaW7Jpc,890
81
- ominfra/supervisor/supervisor.py,sha256=El8FsOXQJ03kWZ834dgRbJjI7pmZU5AZ_greG48PkfU,13102
82
- ominfra/supervisor/types.py,sha256=dL2D_1Plv2ZoDAtJLPjP4okH0rAlq3emtnoD_zWBrwU,4211
83
- ominfra/supervisor/utils.py,sha256=jlUy5WaCIG7YqOLRoV_9aU5i26K0Cn7ID4_Mj-Y0Z2w,4368
88
+ ominfra/supervisor/supervisor.py,sha256=3yBaGJHJGqDXlARjpHWg7371N6jb29HaFEI-_fSp-xU,12028
89
+ ominfra/supervisor/types.py,sha256=i2C7ZBvLqjVliIYAYQnPx2WwIQ14HIVV1hTUgB2mbBM,4740
90
+ ominfra/supervisor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
+ ominfra/supervisor/utils/collections.py,sha256=vcfmVYS4QngMdtEI1DvdRIcubmy55Wj40NCzW27_rIY,1361
92
+ ominfra/supervisor/utils/diag.py,sha256=ujz4gkW7p3wmbaKFM8Hz5eHEwpoUkbB8JeDvcHilCz0,705
93
+ ominfra/supervisor/utils/fds.py,sha256=lz8DWXzGYvu93dqhWK0WrhXrrJVQ_psoom4Nj_o8g2g,849
94
+ ominfra/supervisor/utils/fs.py,sha256=ABbNcsCpzSXAvq_ZZSCj61mj5kGnVuC4spUmoWenlqw,1155
95
+ ominfra/supervisor/utils/os.py,sha256=9fw--tpHOrSjGTCkUo1KRBgbGGxKW2va5xKw2cHwtRU,1096
96
+ ominfra/supervisor/utils/ostypes.py,sha256=B7VjwbzVesz9we9MztoSk8bH8sTxMIWtILy_Qde0G7w,164
97
+ ominfra/supervisor/utils/signals.py,sha256=uZkTvissbtq7TlJD4MkTiL3F-zyWmAFUuWQtFjsf0MI,1474
98
+ ominfra/supervisor/utils/strings.py,sha256=B0UOuVM_NIWmcznycmiEbwJ0lcoTGEd3Ir1AeLkBXeU,2478
99
+ ominfra/supervisor/utils/users.py,sha256=PRUhWy74WQCxix4BLNYcWW1i2mF1IyAxj1RzElnP4iM,1345
84
100
  ominfra/tailscale/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
101
  ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
86
102
  ominfra/tailscale/cli.py,sha256=DSGp4hn5xwOW-l_u_InKlSF6kIobxtUtVssf_73STs0,3567
87
103
  ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
104
  ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
89
- ominfra-0.0.0.dev126.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
90
- ominfra-0.0.0.dev126.dist-info/METADATA,sha256=Rw0aUxGHU_HOKaFaHDpRVHgsIcK6T6eHSZuJUU9SQtY,731
91
- ominfra-0.0.0.dev126.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
92
- ominfra-0.0.0.dev126.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
93
- ominfra-0.0.0.dev126.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
94
- ominfra-0.0.0.dev126.dist-info/RECORD,,
105
+ ominfra-0.0.0.dev128.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
106
+ ominfra-0.0.0.dev128.dist-info/METADATA,sha256=JV6Hw2ZOpDub73wDD4p69Qq9F6XbbKlthV7CmH6G_-k,731
107
+ ominfra-0.0.0.dev128.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
108
+ ominfra-0.0.0.dev128.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
109
+ ominfra-0.0.0.dev128.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
110
+ ominfra-0.0.0.dev128.dist-info/RECORD,,
@@ -1,175 +0,0 @@
1
- # ruff: noqa: UP007
2
- import grp
3
- import logging
4
- import os
5
- import pwd
6
- import signal
7
- import typing as ta
8
-
9
-
10
- class Automatic:
11
- pass
12
-
13
-
14
- class Syslog:
15
- """TODO deprecated; remove this special 'syslog' filename in the future"""
16
-
17
-
18
- LOGFILE_NONES = ('none', 'off', None)
19
- LOGFILE_AUTOS = (Automatic, 'auto')
20
- LOGFILE_SYSLOGS = (Syslog, 'syslog')
21
-
22
-
23
- def logfile_name(val):
24
- if hasattr(val, 'lower'):
25
- coerced = val.lower()
26
- else:
27
- coerced = val
28
-
29
- if coerced in LOGFILE_NONES:
30
- return None
31
- elif coerced in LOGFILE_AUTOS:
32
- return Automatic
33
- elif coerced in LOGFILE_SYSLOGS:
34
- return Syslog
35
- else:
36
- return existing_dirpath(val)
37
-
38
-
39
- def name_to_uid(name: str) -> int:
40
- try:
41
- uid = int(name)
42
- except ValueError:
43
- try:
44
- pwdrec = pwd.getpwnam(name)
45
- except KeyError:
46
- raise ValueError(f'Invalid user name {name}') # noqa
47
- uid = pwdrec[2]
48
- else:
49
- try:
50
- pwd.getpwuid(uid) # check if uid is valid
51
- except KeyError:
52
- raise ValueError(f'Invalid user id {name}') # noqa
53
- return uid
54
-
55
-
56
- def name_to_gid(name: str) -> int:
57
- try:
58
- gid = int(name)
59
- except ValueError:
60
- try:
61
- grprec = grp.getgrnam(name)
62
- except KeyError:
63
- raise ValueError(f'Invalid group name {name}') # noqa
64
- gid = grprec[2]
65
- else:
66
- try:
67
- grp.getgrgid(gid) # check if gid is valid
68
- except KeyError:
69
- raise ValueError(f'Invalid group id {name}') # noqa
70
- return gid
71
-
72
-
73
- def gid_for_uid(uid: int) -> int:
74
- pwrec = pwd.getpwuid(uid)
75
- return pwrec[3]
76
-
77
-
78
- def octal_type(arg: ta.Union[str, int]) -> int:
79
- if isinstance(arg, int):
80
- return arg
81
- try:
82
- return int(arg, 8)
83
- except (TypeError, ValueError):
84
- raise ValueError(f'{arg} can not be converted to an octal type') # noqa
85
-
86
-
87
- def existing_directory(v: str) -> str:
88
- nv = os.path.expanduser(v)
89
- if os.path.isdir(nv):
90
- return nv
91
- raise ValueError(f'{v} is not an existing directory')
92
-
93
-
94
- def existing_dirpath(v: str) -> str:
95
- nv = os.path.expanduser(v)
96
- dir = os.path.dirname(nv) # noqa
97
- if not dir:
98
- # relative pathname with no directory component
99
- return nv
100
- if os.path.isdir(dir):
101
- return nv
102
- raise ValueError(f'The directory named as part of the path {v} does not exist')
103
-
104
-
105
- def logging_level(value: ta.Union[str, int]) -> int:
106
- if isinstance(value, int):
107
- return value
108
- s = str(value).lower()
109
- level = logging.getLevelNamesMapping().get(s.upper())
110
- if level is None:
111
- raise ValueError(f'bad logging level name {value!r}')
112
- return level
113
-
114
-
115
- class SuffixMultiplier:
116
- # d is a dictionary of suffixes to integer multipliers. If no suffixes match, default is the multiplier. Matches
117
- # are case insensitive. Return values are in the fundamental unit.
118
- def __init__(self, d, default=1):
119
- super().__init__()
120
- self._d = d
121
- self._default = default
122
- # all keys must be the same size
123
- self._keysz = None
124
- for k in d:
125
- if self._keysz is None:
126
- self._keysz = len(k)
127
- elif self._keysz != len(k): # type: ignore
128
- raise ValueError(k)
129
-
130
- def __call__(self, v: ta.Union[str, int]) -> int:
131
- if isinstance(v, int):
132
- return v
133
- v = v.lower()
134
- for s, m in self._d.items():
135
- if v[-self._keysz:] == s: # type: ignore
136
- return int(v[:-self._keysz]) * m # type: ignore
137
- return int(v) * self._default
138
-
139
-
140
- byte_size = SuffixMultiplier({
141
- 'kb': 1024,
142
- 'mb': 1024 * 1024,
143
- 'gb': 1024 * 1024 * 1024,
144
- })
145
-
146
-
147
- # all valid signal numbers
148
- SIGNUMS = [getattr(signal, k) for k in dir(signal) if k.startswith('SIG')]
149
-
150
-
151
- def signal_number(value: ta.Union[int, str]) -> int:
152
- try:
153
- num = int(value)
154
-
155
- except (ValueError, TypeError):
156
- name = value.strip().upper() # type: ignore
157
- if not name.startswith('SIG'):
158
- name = f'SIG{name}'
159
-
160
- num = getattr(signal, name, None) # type: ignore
161
- if num is None:
162
- raise ValueError(f'value {value!r} is not a valid signal name') # noqa
163
-
164
- if num not in SIGNUMS:
165
- raise ValueError(f'value {value!r} is not a valid signal number')
166
-
167
- return num
168
-
169
-
170
- class RestartWhenExitUnexpected:
171
- pass
172
-
173
-
174
- class RestartUnconditionally:
175
- pass
@@ -1,52 +0,0 @@
1
- # ruff: noqa: UP006 UP007
2
- import signal
3
- import typing as ta
4
-
5
-
6
- ##
7
-
8
-
9
- _SIG_NAMES: ta.Optional[ta.Mapping[int, str]] = None
10
-
11
-
12
- def sig_name(sig: int) -> str:
13
- global _SIG_NAMES
14
- if _SIG_NAMES is None:
15
- _SIG_NAMES = _init_sig_names()
16
- return _SIG_NAMES.get(sig) or 'signal %d' % sig
17
-
18
-
19
- def _init_sig_names() -> ta.Dict[int, str]:
20
- d = {}
21
- for k, v in signal.__dict__.items(): # noqa
22
- k_startswith = getattr(k, 'startswith', None)
23
- if k_startswith is None:
24
- continue
25
- if k_startswith('SIG') and not k_startswith('SIG_'):
26
- d[v] = k
27
- return d
28
-
29
-
30
- ##
31
-
32
-
33
- class SignalReceiver:
34
- def __init__(self) -> None:
35
- super().__init__()
36
-
37
- self._signals_recvd: ta.List[int] = []
38
-
39
- def receive(self, sig: int, frame: ta.Any) -> None:
40
- if sig not in self._signals_recvd:
41
- self._signals_recvd.append(sig)
42
-
43
- def install(self, *sigs: int) -> None:
44
- for sig in sigs:
45
- signal.signal(sig, self.receive)
46
-
47
- def get_signal(self) -> ta.Optional[int]:
48
- if self._signals_recvd:
49
- sig = self._signals_recvd.pop(0)
50
- else:
51
- sig = None
52
- return sig