ominfra 0.0.0.dev131__tar.gz → 0.0.0.dev132__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {ominfra-0.0.0.dev131/ominfra.egg-info → ominfra-0.0.0.dev132}/PKG-INFO +3 -3
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/scripts/journald2aws.py +8 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/scripts/supervisor.py +172 -66
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/dispatchersimpl.py +4 -3
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/events.py +5 -2
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/http.py +7 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/inject.py +8 -2
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/io.py +2 -2
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/main.py +13 -10
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/processimpl.py +0 -1
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/states.py +11 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/supervisor.py +26 -26
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/types.py +2 -1
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132/ominfra.egg-info}/PKG-INFO +3 -3
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra.egg-info/requires.txt +2 -2
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/pyproject.toml +3 -3
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/LICENSE +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/MANIFEST.in +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/README.rst +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/.manifests.json +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/__about__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/__main__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/auth.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/cli.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/dataclasses.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/journald2aws/main.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/logs.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/aws/metadata.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/gcp/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/clouds/gcp/auth.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/cmds.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/configs.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/_executor.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/configs.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/base.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/dirs.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/nginx.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/repo.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/supervisor.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/systemd.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/user.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/venv.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/main.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/_main.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/base.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/configs.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/deploy.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/main.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/nginx.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/repo.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/runtime.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/site.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/supervisor.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/poly/venv.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/remote.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/journald/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/journald/fields.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/journald/genmessages.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/journald/messages.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/journald/tailer.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/manage/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/manage/manage.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/pyremote/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/pyremote/_runcommands.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/pyremote/bootstrap.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/pyremote/runcommands.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/scripts/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/ssh.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/LICENSE.txt +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/__main__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/configs.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/dispatchers.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/exceptions.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/groups.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/groupsimpl.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/pipes.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/privileges.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/process.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/setup.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/setupimpl.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/signals.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/spawning.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/spawningimpl.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/collections.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/diag.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/fds.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/fs.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/os.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/ostypes.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/signals.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/strings.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/supervisor/utils/users.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/tailscale/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/tailscale/api.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/tailscale/cli.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/threadworkers.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/tools/__init__.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/tools/listresources.py +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra.egg-info/SOURCES.txt +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra.egg-info/dependency_links.txt +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra.egg-info/entry_points.txt +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra.egg-info/top_level.txt +0 -0
- {ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev132
|
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.dev132
|
16
|
+
Requires-Dist: omlish==0.0.0.dev132
|
17
17
|
Provides-Extra: all
|
18
18
|
Requires-Dist: paramiko~=3.5; extra == "all"
|
19
19
|
Requires-Dist: asyncssh~=2.18; extra == "all"
|
@@ -1627,6 +1627,14 @@ class ExitStacked:
|
|
1627
1627
|
##
|
1628
1628
|
|
1629
1629
|
|
1630
|
+
@contextlib.contextmanager
|
1631
|
+
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
1632
|
+
try:
|
1633
|
+
yield fn
|
1634
|
+
finally:
|
1635
|
+
fn()
|
1636
|
+
|
1637
|
+
|
1630
1638
|
@contextlib.contextmanager
|
1631
1639
|
def attr_setting(obj, attr, val, *, default=None): # noqa
|
1632
1640
|
not_set = object()
|
@@ -116,6 +116,10 @@ A2 = ta.TypeVar('A2')
|
|
116
116
|
|
117
117
|
# events.py
|
118
118
|
EventCallback = ta.Callable[['Event'], None]
|
119
|
+
ProcessOutputChannel = ta.Literal['stdout', 'stderr'] # ta.TypeAlias
|
120
|
+
|
121
|
+
# ../../omlish/lite/contextmanagers.py
|
122
|
+
ExitStackedT = ta.TypeVar('ExitStackedT', bound='ExitStacked')
|
119
123
|
|
120
124
|
# ../../omlish/lite/http/parsing.py
|
121
125
|
HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
|
@@ -1077,6 +1081,17 @@ class ProcessState(enum.IntEnum):
|
|
1077
1081
|
return self in SIGNALABLE_STATES
|
1078
1082
|
|
1079
1083
|
|
1084
|
+
# http://supervisord.org/subprocess.html
|
1085
|
+
STATE_TRANSITIONS = {
|
1086
|
+
ProcessState.STOPPED: (ProcessState.STARTING,),
|
1087
|
+
ProcessState.STARTING: (ProcessState.RUNNING, ProcessState.BACKOFF, ProcessState.STOPPING),
|
1088
|
+
ProcessState.RUNNING: (ProcessState.STOPPING, ProcessState.EXITED),
|
1089
|
+
ProcessState.BACKOFF: (ProcessState.STARTING, ProcessState.FATAL),
|
1090
|
+
ProcessState.STOPPING: (ProcessState.STOPPED,),
|
1091
|
+
ProcessState.EXITED: (ProcessState.STARTING,),
|
1092
|
+
ProcessState.FATAL: (ProcessState.STARTING,),
|
1093
|
+
}
|
1094
|
+
|
1080
1095
|
STOPPED_STATES = (
|
1081
1096
|
ProcessState.STOPPED,
|
1082
1097
|
ProcessState.EXITED,
|
@@ -1576,16 +1591,16 @@ class FdIoPoller(abc.ABC):
|
|
1576
1591
|
def register_readable(self, fd: int) -> bool:
|
1577
1592
|
if fd in self._readable:
|
1578
1593
|
return False
|
1579
|
-
self._readable.add(fd)
|
1580
1594
|
self._register_readable(fd)
|
1595
|
+
self._readable.add(fd)
|
1581
1596
|
return True
|
1582
1597
|
|
1583
1598
|
@ta.final
|
1584
1599
|
def register_writable(self, fd: int) -> bool:
|
1585
1600
|
if fd in self._writable:
|
1586
1601
|
return False
|
1587
|
-
self._writable.add(fd)
|
1588
1602
|
self._register_writable(fd)
|
1603
|
+
self._writable.add(fd)
|
1589
1604
|
return True
|
1590
1605
|
|
1591
1606
|
@ta.final
|
@@ -1689,24 +1704,24 @@ if hasattr(select, 'poll'):
|
|
1689
1704
|
|
1690
1705
|
#
|
1691
1706
|
|
1692
|
-
_READ = select.POLLIN | select.POLLPRI | select.POLLHUP
|
1693
|
-
_WRITE = select.POLLOUT
|
1694
|
-
|
1695
1707
|
def _register_readable(self, fd: int) -> None:
|
1696
|
-
self._update_registration(fd)
|
1708
|
+
self._update_registration(fd, r=True, w=fd in self._writable)
|
1697
1709
|
|
1698
1710
|
def _register_writable(self, fd: int) -> None:
|
1699
|
-
self._update_registration(fd)
|
1711
|
+
self._update_registration(fd, r=fd in self._readable, w=True)
|
1700
1712
|
|
1701
1713
|
def _unregister_readable(self, fd: int) -> None:
|
1702
|
-
self._update_registration(fd)
|
1714
|
+
self._update_registration(fd, r=False, w=False)
|
1703
1715
|
|
1704
1716
|
def _unregister_writable(self, fd: int) -> None:
|
1705
|
-
self._update_registration(fd)
|
1717
|
+
self._update_registration(fd, r=fd in self._readable, w=False)
|
1706
1718
|
|
1707
|
-
|
1708
|
-
|
1709
|
-
|
1719
|
+
#
|
1720
|
+
|
1721
|
+
_READ = select.POLLIN | select.POLLPRI | select.POLLHUP
|
1722
|
+
_WRITE = select.POLLOUT
|
1723
|
+
|
1724
|
+
def _update_registration(self, fd: int, *, r: bool, w: bool) -> None:
|
1710
1725
|
if r or w:
|
1711
1726
|
self._poller.register(fd, (self._READ if r else 0) | (self._WRITE if w else 0))
|
1712
1727
|
else:
|
@@ -2119,7 +2134,7 @@ class EventCallbacks:
|
|
2119
2134
|
|
2120
2135
|
|
2121
2136
|
class ProcessLogEvent(Event, abc.ABC):
|
2122
|
-
channel: ta.
|
2137
|
+
channel: ta.ClassVar[ProcessOutputChannel]
|
2123
2138
|
|
2124
2139
|
def __init__(self, process, pid, data):
|
2125
2140
|
super().__init__()
|
@@ -2144,7 +2159,7 @@ class ProcessCommunicationEvent(Event, abc.ABC):
|
|
2144
2159
|
BEGIN_TOKEN = b'<!--XSUPERVISOR:BEGIN-->'
|
2145
2160
|
END_TOKEN = b'<!--XSUPERVISOR:END-->'
|
2146
2161
|
|
2147
|
-
channel: ta.ClassVar[
|
2162
|
+
channel: ta.ClassVar[ProcessOutputChannel]
|
2148
2163
|
|
2149
2164
|
def __init__(self, process, pid, data):
|
2150
2165
|
super().__init__()
|
@@ -2516,6 +2531,64 @@ def get_user(name: str) -> User:
|
|
2516
2531
|
)
|
2517
2532
|
|
2518
2533
|
|
2534
|
+
########################################
|
2535
|
+
# ../../../omlish/lite/contextmanagers.py
|
2536
|
+
|
2537
|
+
|
2538
|
+
##
|
2539
|
+
|
2540
|
+
|
2541
|
+
class ExitStacked:
|
2542
|
+
_exit_stack: ta.Optional[contextlib.ExitStack] = None
|
2543
|
+
|
2544
|
+
def __enter__(self: ExitStackedT) -> ExitStackedT:
|
2545
|
+
check_state(self._exit_stack is None)
|
2546
|
+
es = self._exit_stack = contextlib.ExitStack()
|
2547
|
+
es.__enter__()
|
2548
|
+
return self
|
2549
|
+
|
2550
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
2551
|
+
if (es := self._exit_stack) is None:
|
2552
|
+
return None
|
2553
|
+
self._exit_contexts()
|
2554
|
+
return es.__exit__(exc_type, exc_val, exc_tb)
|
2555
|
+
|
2556
|
+
def _exit_contexts(self) -> None:
|
2557
|
+
pass
|
2558
|
+
|
2559
|
+
def _enter_context(self, cm: ta.ContextManager[T]) -> T:
|
2560
|
+
es = check_not_none(self._exit_stack)
|
2561
|
+
return es.enter_context(cm)
|
2562
|
+
|
2563
|
+
|
2564
|
+
##
|
2565
|
+
|
2566
|
+
|
2567
|
+
@contextlib.contextmanager
|
2568
|
+
def defer(fn: ta.Callable) -> ta.Generator[ta.Callable, None, None]:
|
2569
|
+
try:
|
2570
|
+
yield fn
|
2571
|
+
finally:
|
2572
|
+
fn()
|
2573
|
+
|
2574
|
+
|
2575
|
+
@contextlib.contextmanager
|
2576
|
+
def attr_setting(obj, attr, val, *, default=None): # noqa
|
2577
|
+
not_set = object()
|
2578
|
+
orig = getattr(obj, attr, not_set)
|
2579
|
+
try:
|
2580
|
+
setattr(obj, attr, val)
|
2581
|
+
if orig is not not_set:
|
2582
|
+
yield orig
|
2583
|
+
else:
|
2584
|
+
yield default
|
2585
|
+
finally:
|
2586
|
+
if orig is not_set:
|
2587
|
+
delattr(obj, attr)
|
2588
|
+
else:
|
2589
|
+
setattr(obj, attr, orig)
|
2590
|
+
|
2591
|
+
|
2519
2592
|
########################################
|
2520
2593
|
# ../../../omlish/lite/fdio/handlers.py
|
2521
2594
|
|
@@ -2624,19 +2697,40 @@ if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
|
|
2624
2697
|
#
|
2625
2698
|
|
2626
2699
|
def _register_readable(self, fd: int) -> None:
|
2627
|
-
self.
|
2700
|
+
self._update_registration(fd, 'read', 'add')
|
2628
2701
|
|
2629
2702
|
def _register_writable(self, fd: int) -> None:
|
2630
|
-
self.
|
2703
|
+
self._update_registration(fd, 'write', 'add')
|
2631
2704
|
|
2632
2705
|
def _unregister_readable(self, fd: int) -> None:
|
2633
|
-
self.
|
2706
|
+
self._update_registration(fd, 'read', 'del')
|
2634
2707
|
|
2635
2708
|
def _unregister_writable(self, fd: int) -> None:
|
2636
|
-
self.
|
2709
|
+
self._update_registration(fd, 'write', 'del')
|
2710
|
+
|
2711
|
+
#
|
2637
2712
|
|
2638
|
-
|
2639
|
-
|
2713
|
+
_CONTROL_FILTER_BY_READ_OR_WRITE: ta.ClassVar[ta.Mapping[ta.Literal['read', 'write'], int]] = {
|
2714
|
+
'read': select.KQ_FILTER_READ,
|
2715
|
+
'write': select.KQ_FILTER_WRITE,
|
2716
|
+
}
|
2717
|
+
|
2718
|
+
_CONTROL_FLAGS_BY_ADD_OR_DEL: ta.ClassVar[ta.Mapping[ta.Literal['add', 'del'], int]] = {
|
2719
|
+
'add': select.KQ_EV_ADD,
|
2720
|
+
'del': select.KQ_EV_DELETE,
|
2721
|
+
}
|
2722
|
+
|
2723
|
+
def _update_registration(
|
2724
|
+
self,
|
2725
|
+
fd: int,
|
2726
|
+
read_or_write: ta.Literal['read', 'write'],
|
2727
|
+
add_or_del: ta.Literal['add', 'del'],
|
2728
|
+
) -> None: # noqa
|
2729
|
+
ke = select.kevent(
|
2730
|
+
fd,
|
2731
|
+
filter=self._CONTROL_FILTER_BY_READ_OR_WRITE[read_or_write],
|
2732
|
+
flags=self._CONTROL_FLAGS_BY_ADD_OR_DEL[add_or_del],
|
2733
|
+
)
|
2640
2734
|
kq = self._get_kqueue()
|
2641
2735
|
try:
|
2642
2736
|
kq.control([ke], 0)
|
@@ -2647,7 +2741,8 @@ if sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
|
|
2647
2741
|
pass
|
2648
2742
|
elif exc.errno == errno.ENOENT:
|
2649
2743
|
# Can happen when trying to remove an already closed socket
|
2650
|
-
|
2744
|
+
if add_or_del == 'add':
|
2745
|
+
raise
|
2651
2746
|
else:
|
2652
2747
|
raise
|
2653
2748
|
|
@@ -5951,7 +6046,7 @@ class HasDispatchers(abc.ABC):
|
|
5951
6046
|
class ProcessDispatcher(FdIoHandler, abc.ABC):
|
5952
6047
|
@property
|
5953
6048
|
@abc.abstractmethod
|
5954
|
-
def channel(self) ->
|
6049
|
+
def channel(self) -> ProcessOutputChannel:
|
5955
6050
|
raise NotImplementedError
|
5956
6051
|
|
5957
6052
|
@property
|
@@ -6240,7 +6335,7 @@ class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
|
|
6240
6335
|
def __init__(
|
6241
6336
|
self,
|
6242
6337
|
process: Process,
|
6243
|
-
channel:
|
6338
|
+
channel: ProcessOutputChannel,
|
6244
6339
|
fd: Fd,
|
6245
6340
|
*,
|
6246
6341
|
event_callbacks: EventCallbacks,
|
@@ -6268,7 +6363,7 @@ class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
|
|
6268
6363
|
return self._process
|
6269
6364
|
|
6270
6365
|
@property
|
6271
|
-
def channel(self) ->
|
6366
|
+
def channel(self) -> ProcessOutputChannel:
|
6272
6367
|
return self._channel
|
6273
6368
|
|
6274
6369
|
def fd(self) -> Fd:
|
@@ -6515,7 +6610,7 @@ class ProcessInputDispatcherImpl(BaseProcessDispatcherImpl, ProcessInputDispatch
|
|
6515
6610
|
def __init__(
|
6516
6611
|
self,
|
6517
6612
|
process: Process,
|
6518
|
-
channel:
|
6613
|
+
channel: ProcessOutputChannel,
|
6519
6614
|
fd: Fd,
|
6520
6615
|
*,
|
6521
6616
|
event_callbacks: EventCallbacks,
|
@@ -7024,9 +7119,9 @@ class IoManager(HasDispatchers):
|
|
7024
7119
|
)
|
7025
7120
|
|
7026
7121
|
timeout = 1 # this cannot be fewer than the smallest TickEvent (5)
|
7027
|
-
|
7122
|
+
|
7028
7123
|
polled = self._poller.poll(timeout)
|
7029
|
-
|
7124
|
+
|
7030
7125
|
if polled.msg is not None:
|
7031
7126
|
log.error(polled.msg)
|
7032
7127
|
if polled.exc is not None:
|
@@ -7151,6 +7246,8 @@ class HttpServer(HasDispatchers):
|
|
7151
7246
|
self,
|
7152
7247
|
handler: Handler,
|
7153
7248
|
addr: Address = Address(('localhost', 8000)),
|
7249
|
+
*,
|
7250
|
+
exit_stack: contextlib.ExitStack,
|
7154
7251
|
) -> None:
|
7155
7252
|
super().__init__()
|
7156
7253
|
|
@@ -7161,6 +7258,8 @@ class HttpServer(HasDispatchers):
|
|
7161
7258
|
|
7162
7259
|
self._conns: ta.List[CoroHttpServerConnectionFdIoHandler] = []
|
7163
7260
|
|
7261
|
+
exit_stack.enter_context(defer(self._server.close)) # noqa
|
7262
|
+
|
7164
7263
|
def get_dispatchers(self) -> Dispatchers:
|
7165
7264
|
l = []
|
7166
7265
|
for c in self._conns:
|
@@ -7205,6 +7304,7 @@ class SupervisorHttpHandler:
|
|
7205
7304
|
'processes': {
|
7206
7305
|
p.name: {
|
7207
7306
|
'pid': p.pid,
|
7307
|
+
'state': p.state.name,
|
7208
7308
|
}
|
7209
7309
|
for p in g
|
7210
7310
|
},
|
@@ -7572,7 +7672,6 @@ class ProcessImpl(Process):
|
|
7572
7672
|
self._last_stop = now
|
7573
7673
|
|
7574
7674
|
if now > self._last_start:
|
7575
|
-
log.info(f'{now - self._last_start=}') # noqa
|
7576
7675
|
too_quickly = now - self._last_start < self._config.start_secs
|
7577
7676
|
else:
|
7578
7677
|
too_quickly = False
|
@@ -8227,11 +8326,29 @@ class Supervisor:
|
|
8227
8326
|
#
|
8228
8327
|
|
8229
8328
|
def _run_once(self) -> None:
|
8230
|
-
|
8231
|
-
|
8232
|
-
|
8329
|
+
if self._states.state < SupervisorState.RUNNING:
|
8330
|
+
if not self._stopping:
|
8331
|
+
# first time, set the stopping flag, do a notification and set stop_groups
|
8332
|
+
self._stopping = True
|
8333
|
+
self._stop_groups = sorted(self._process_groups)
|
8334
|
+
self._event_callbacks.notify(SupervisorStoppingEvent())
|
8335
|
+
|
8336
|
+
self._ordered_stop_groups_phase_1()
|
8337
|
+
|
8338
|
+
if not self.shutdown_report():
|
8339
|
+
# if there are no unstopped processes (we're done killing everything), it's OK to shutdown or reload
|
8340
|
+
raise ExitNow
|
8341
|
+
|
8342
|
+
self._io.poll()
|
8343
|
+
|
8344
|
+
for group in sorted(self._process_groups):
|
8345
|
+
for process in group:
|
8346
|
+
process.transition()
|
8347
|
+
|
8233
8348
|
self._reap()
|
8349
|
+
|
8234
8350
|
self._signal_handler.handle_signals()
|
8351
|
+
|
8235
8352
|
self._tick()
|
8236
8353
|
|
8237
8354
|
if self._states.state < SupervisorState.RUNNING:
|
@@ -8253,38 +8370,18 @@ class Supervisor:
|
|
8253
8370
|
# down, so push it back on to the end of the stop group queue
|
8254
8371
|
self._stop_groups.append(group)
|
8255
8372
|
|
8256
|
-
|
8257
|
-
sorted_groups = list(self._process_groups)
|
8258
|
-
sorted_groups.sort()
|
8259
|
-
|
8260
|
-
if self._states.state < SupervisorState.RUNNING:
|
8261
|
-
if not self._stopping:
|
8262
|
-
# first time, set the stopping flag, do a notification and set stop_groups
|
8263
|
-
self._stopping = True
|
8264
|
-
self._stop_groups = sorted_groups[:]
|
8265
|
-
self._event_callbacks.notify(SupervisorStoppingEvent())
|
8266
|
-
|
8267
|
-
self._ordered_stop_groups_phase_1()
|
8268
|
-
|
8269
|
-
if not self.shutdown_report():
|
8270
|
-
# if there are no unstopped processes (we're done killing everything), it's OK to shutdown or reload
|
8271
|
-
raise ExitNow
|
8272
|
-
|
8273
|
-
self._io.poll()
|
8274
|
-
|
8275
|
-
for group in sorted_groups:
|
8276
|
-
for process in group:
|
8277
|
-
process.transition()
|
8373
|
+
#
|
8278
8374
|
|
8279
8375
|
def _reap(self, *, once: bool = False, depth: int = 0) -> None:
|
8280
8376
|
if depth >= 100:
|
8281
8377
|
return
|
8282
8378
|
|
8283
8379
|
wp = waitpid()
|
8284
|
-
|
8380
|
+
|
8285
8381
|
if wp is None or not wp.pid:
|
8286
8382
|
return
|
8287
8383
|
|
8384
|
+
log.info(f'Waited pid: {wp}') # noqa
|
8288
8385
|
process = self._pid_history.get(wp.pid, None)
|
8289
8386
|
if process is None:
|
8290
8387
|
_, msg = decode_wait_status(wp.sts)
|
@@ -8297,6 +8394,8 @@ class Supervisor:
|
|
8297
8394
|
# keep reaping until no more kids to reap, but don't recurse infinitely
|
8298
8395
|
self._reap(once=False, depth=depth + 1)
|
8299
8396
|
|
8397
|
+
#
|
8398
|
+
|
8300
8399
|
def _tick(self, now: ta.Optional[float] = None) -> None:
|
8301
8400
|
"""Send one or more 'tick' events when the timeslice related to the period for the event type rolls over"""
|
8302
8401
|
|
@@ -8362,6 +8461,7 @@ class _FdIoPollerDaemonizeListener(DaemonizeListener):
|
|
8362
8461
|
|
8363
8462
|
|
8364
8463
|
def bind_server(
|
8464
|
+
exit_stack: contextlib.ExitStack,
|
8365
8465
|
config: ServerConfig,
|
8366
8466
|
*,
|
8367
8467
|
server_epoch: ta.Optional[ServerEpoch] = None,
|
@@ -8370,6 +8470,8 @@ def bind_server(
|
|
8370
8470
|
lst: ta.List[InjectorBindingOrBindings] = [
|
8371
8471
|
inj.bind(config),
|
8372
8472
|
|
8473
|
+
inj.bind(exit_stack),
|
8474
|
+
|
8373
8475
|
inj.bind_array(DaemonizeListener),
|
8374
8476
|
inj.bind_array_type(DaemonizeListener, DaemonizeListeners),
|
8375
8477
|
|
@@ -8425,8 +8527,10 @@ def bind_server(
|
|
8425
8527
|
PollFdIoPoller,
|
8426
8528
|
SelectFdIoPoller,
|
8427
8529
|
]))
|
8428
|
-
lst.
|
8429
|
-
|
8530
|
+
lst.extend([
|
8531
|
+
inj.bind(poller_impl, key=FdIoPoller, singleton=True),
|
8532
|
+
inj.bind(_FdIoPollerDaemonizeListener, array=True, singleton=True),
|
8533
|
+
])
|
8430
8534
|
|
8431
8535
|
#
|
8432
8536
|
|
@@ -8495,18 +8599,20 @@ def main(
|
|
8495
8599
|
prepare=prepare_server_config,
|
8496
8600
|
)
|
8497
8601
|
|
8498
|
-
|
8499
|
-
|
8500
|
-
|
8501
|
-
|
8502
|
-
|
8602
|
+
with contextlib.ExitStack() as es:
|
8603
|
+
injector = inj.create_injector(bind_server(
|
8604
|
+
es,
|
8605
|
+
config,
|
8606
|
+
server_epoch=ServerEpoch(epoch),
|
8607
|
+
inherited_fds=inherited_fds,
|
8608
|
+
))
|
8503
8609
|
|
8504
|
-
|
8610
|
+
supervisor = injector[Supervisor]
|
8505
8611
|
|
8506
|
-
|
8507
|
-
|
8508
|
-
|
8509
|
-
|
8612
|
+
try:
|
8613
|
+
supervisor.main()
|
8614
|
+
except ExitNow:
|
8615
|
+
pass
|
8510
8616
|
|
8511
8617
|
if supervisor.state < SupervisorState.RESTARTING:
|
8512
8618
|
break
|
@@ -13,6 +13,7 @@ from .events import EventCallbacks
|
|
13
13
|
from .events import ProcessCommunicationEvent
|
14
14
|
from .events import ProcessLogStderrEvent
|
15
15
|
from .events import ProcessLogStdoutEvent
|
16
|
+
from .events import ProcessOutputChannel
|
16
17
|
from .types import Process
|
17
18
|
from .types import ProcessDispatcher
|
18
19
|
from .types import ProcessInputDispatcher
|
@@ -29,7 +30,7 @@ class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
|
|
29
30
|
def __init__(
|
30
31
|
self,
|
31
32
|
process: Process,
|
32
|
-
channel:
|
33
|
+
channel: ProcessOutputChannel,
|
33
34
|
fd: Fd,
|
34
35
|
*,
|
35
36
|
event_callbacks: EventCallbacks,
|
@@ -57,7 +58,7 @@ class BaseProcessDispatcherImpl(ProcessDispatcher, abc.ABC):
|
|
57
58
|
return self._process
|
58
59
|
|
59
60
|
@property
|
60
|
-
def channel(self) ->
|
61
|
+
def channel(self) -> ProcessOutputChannel:
|
61
62
|
return self._channel
|
62
63
|
|
63
64
|
def fd(self) -> Fd:
|
@@ -304,7 +305,7 @@ class ProcessInputDispatcherImpl(BaseProcessDispatcherImpl, ProcessInputDispatch
|
|
304
305
|
def __init__(
|
305
306
|
self,
|
306
307
|
process: Process,
|
307
|
-
channel:
|
308
|
+
channel: ProcessOutputChannel,
|
308
309
|
fd: Fd,
|
309
310
|
*,
|
310
311
|
event_callbacks: EventCallbacks,
|
@@ -8,6 +8,9 @@ from .states import ProcessState
|
|
8
8
|
EventCallback = ta.Callable[['Event'], None]
|
9
9
|
|
10
10
|
|
11
|
+
ProcessOutputChannel = ta.Literal['stdout', 'stderr'] # ta.TypeAlias
|
12
|
+
|
13
|
+
|
11
14
|
##
|
12
15
|
|
13
16
|
|
@@ -43,7 +46,7 @@ class EventCallbacks:
|
|
43
46
|
|
44
47
|
|
45
48
|
class ProcessLogEvent(Event, abc.ABC):
|
46
|
-
channel: ta.
|
49
|
+
channel: ta.ClassVar[ProcessOutputChannel]
|
47
50
|
|
48
51
|
def __init__(self, process, pid, data):
|
49
52
|
super().__init__()
|
@@ -68,7 +71,7 @@ class ProcessCommunicationEvent(Event, abc.ABC):
|
|
68
71
|
BEGIN_TOKEN = b'<!--XSUPERVISOR:BEGIN-->'
|
69
72
|
END_TOKEN = b'<!--XSUPERVISOR:END-->'
|
70
73
|
|
71
|
-
channel: ta.ClassVar[
|
74
|
+
channel: ta.ClassVar[ProcessOutputChannel]
|
72
75
|
|
73
76
|
def __init__(self, process, pid, data):
|
74
77
|
super().__init__()
|
@@ -1,9 +1,11 @@
|
|
1
1
|
# ruff: noqa: U006 UP007
|
2
|
+
import contextlib
|
2
3
|
import json
|
3
4
|
import socket
|
4
5
|
import typing as ta
|
5
6
|
|
6
7
|
from omlish.lite.check import check_not_none
|
8
|
+
from omlish.lite.contextmanagers import defer
|
7
9
|
from omlish.lite.fdio.corohttp import CoroHttpServerConnectionFdIoHandler
|
8
10
|
from omlish.lite.fdio.handlers import SocketFdIoHandler
|
9
11
|
from omlish.lite.http.handlers import HttpHandler
|
@@ -59,6 +61,8 @@ class HttpServer(HasDispatchers):
|
|
59
61
|
self,
|
60
62
|
handler: Handler,
|
61
63
|
addr: Address = Address(('localhost', 8000)),
|
64
|
+
*,
|
65
|
+
exit_stack: contextlib.ExitStack,
|
62
66
|
) -> None:
|
63
67
|
super().__init__()
|
64
68
|
|
@@ -69,6 +73,8 @@ class HttpServer(HasDispatchers):
|
|
69
73
|
|
70
74
|
self._conns: ta.List[CoroHttpServerConnectionFdIoHandler] = []
|
71
75
|
|
76
|
+
exit_stack.enter_context(defer(self._server.close)) # noqa
|
77
|
+
|
72
78
|
def get_dispatchers(self) -> Dispatchers:
|
73
79
|
l = []
|
74
80
|
for c in self._conns:
|
@@ -113,6 +119,7 @@ class SupervisorHttpHandler:
|
|
113
119
|
'processes': {
|
114
120
|
p.name: {
|
115
121
|
'pid': p.pid,
|
122
|
+
'state': p.state.name,
|
116
123
|
}
|
117
124
|
for p in g
|
118
125
|
},
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
|
+
import contextlib
|
2
3
|
import dataclasses as dc
|
3
4
|
import typing as ta
|
4
5
|
|
@@ -56,6 +57,7 @@ class _FdIoPollerDaemonizeListener(DaemonizeListener):
|
|
56
57
|
|
57
58
|
|
58
59
|
def bind_server(
|
60
|
+
exit_stack: contextlib.ExitStack,
|
59
61
|
config: ServerConfig,
|
60
62
|
*,
|
61
63
|
server_epoch: ta.Optional[ServerEpoch] = None,
|
@@ -64,6 +66,8 @@ def bind_server(
|
|
64
66
|
lst: ta.List[InjectorBindingOrBindings] = [
|
65
67
|
inj.bind(config),
|
66
68
|
|
69
|
+
inj.bind(exit_stack),
|
70
|
+
|
67
71
|
inj.bind_array(DaemonizeListener),
|
68
72
|
inj.bind_array_type(DaemonizeListener, DaemonizeListeners),
|
69
73
|
|
@@ -119,8 +123,10 @@ def bind_server(
|
|
119
123
|
PollFdIoPoller,
|
120
124
|
SelectFdIoPoller,
|
121
125
|
]))
|
122
|
-
lst.
|
123
|
-
|
126
|
+
lst.extend([
|
127
|
+
inj.bind(poller_impl, key=FdIoPoller, singleton=True),
|
128
|
+
inj.bind(_FdIoPollerDaemonizeListener, array=True, singleton=True),
|
129
|
+
])
|
124
130
|
|
125
131
|
#
|
126
132
|
|
@@ -44,9 +44,9 @@ class IoManager(HasDispatchers):
|
|
44
44
|
)
|
45
45
|
|
46
46
|
timeout = 1 # this cannot be fewer than the smallest TickEvent (5)
|
47
|
-
|
47
|
+
|
48
48
|
polled = self._poller.poll(timeout)
|
49
|
-
|
49
|
+
|
50
50
|
if polled.msg is not None:
|
51
51
|
log.error(polled.msg)
|
52
52
|
if polled.exc is not None:
|
@@ -29,6 +29,7 @@
|
|
29
29
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
30
30
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
31
31
|
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
32
|
+
import contextlib
|
32
33
|
import itertools
|
33
34
|
import os.path
|
34
35
|
import typing as ta
|
@@ -96,18 +97,20 @@ def main(
|
|
96
97
|
prepare=prepare_server_config,
|
97
98
|
)
|
98
99
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
100
|
+
with contextlib.ExitStack() as es:
|
101
|
+
injector = inj.create_injector(bind_server(
|
102
|
+
es,
|
103
|
+
config,
|
104
|
+
server_epoch=ServerEpoch(epoch),
|
105
|
+
inherited_fds=inherited_fds,
|
106
|
+
))
|
104
107
|
|
105
|
-
|
108
|
+
supervisor = injector[Supervisor]
|
106
109
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
110
|
+
try:
|
111
|
+
supervisor.main()
|
112
|
+
except ExitNow:
|
113
|
+
pass
|
111
114
|
|
112
115
|
if supervisor.state < SupervisorState.RESTARTING:
|
113
116
|
break
|
@@ -27,6 +27,17 @@ class ProcessState(enum.IntEnum):
|
|
27
27
|
return self in SIGNALABLE_STATES
|
28
28
|
|
29
29
|
|
30
|
+
# http://supervisord.org/subprocess.html
|
31
|
+
STATE_TRANSITIONS = {
|
32
|
+
ProcessState.STOPPED: (ProcessState.STARTING,),
|
33
|
+
ProcessState.STARTING: (ProcessState.RUNNING, ProcessState.BACKOFF, ProcessState.STOPPING),
|
34
|
+
ProcessState.RUNNING: (ProcessState.STOPPING, ProcessState.EXITED),
|
35
|
+
ProcessState.BACKOFF: (ProcessState.STARTING, ProcessState.FATAL),
|
36
|
+
ProcessState.STOPPING: (ProcessState.STOPPED,),
|
37
|
+
ProcessState.EXITED: (ProcessState.STARTING,),
|
38
|
+
ProcessState.FATAL: (ProcessState.STARTING,),
|
39
|
+
}
|
40
|
+
|
30
41
|
STOPPED_STATES = (
|
31
42
|
ProcessState.STOPPED,
|
32
43
|
ProcessState.EXITED,
|
@@ -182,11 +182,29 @@ class Supervisor:
|
|
182
182
|
#
|
183
183
|
|
184
184
|
def _run_once(self) -> None:
|
185
|
-
|
186
|
-
|
187
|
-
|
185
|
+
if self._states.state < SupervisorState.RUNNING:
|
186
|
+
if not self._stopping:
|
187
|
+
# first time, set the stopping flag, do a notification and set stop_groups
|
188
|
+
self._stopping = True
|
189
|
+
self._stop_groups = sorted(self._process_groups)
|
190
|
+
self._event_callbacks.notify(SupervisorStoppingEvent())
|
191
|
+
|
192
|
+
self._ordered_stop_groups_phase_1()
|
193
|
+
|
194
|
+
if not self.shutdown_report():
|
195
|
+
# if there are no unstopped processes (we're done killing everything), it's OK to shutdown or reload
|
196
|
+
raise ExitNow
|
197
|
+
|
198
|
+
self._io.poll()
|
199
|
+
|
200
|
+
for group in sorted(self._process_groups):
|
201
|
+
for process in group:
|
202
|
+
process.transition()
|
203
|
+
|
188
204
|
self._reap()
|
205
|
+
|
189
206
|
self._signal_handler.handle_signals()
|
207
|
+
|
190
208
|
self._tick()
|
191
209
|
|
192
210
|
if self._states.state < SupervisorState.RUNNING:
|
@@ -208,38 +226,18 @@ class Supervisor:
|
|
208
226
|
# down, so push it back on to the end of the stop group queue
|
209
227
|
self._stop_groups.append(group)
|
210
228
|
|
211
|
-
|
212
|
-
sorted_groups = list(self._process_groups)
|
213
|
-
sorted_groups.sort()
|
214
|
-
|
215
|
-
if self._states.state < SupervisorState.RUNNING:
|
216
|
-
if not self._stopping:
|
217
|
-
# first time, set the stopping flag, do a notification and set stop_groups
|
218
|
-
self._stopping = True
|
219
|
-
self._stop_groups = sorted_groups[:]
|
220
|
-
self._event_callbacks.notify(SupervisorStoppingEvent())
|
221
|
-
|
222
|
-
self._ordered_stop_groups_phase_1()
|
223
|
-
|
224
|
-
if not self.shutdown_report():
|
225
|
-
# if there are no unstopped processes (we're done killing everything), it's OK to shutdown or reload
|
226
|
-
raise ExitNow
|
227
|
-
|
228
|
-
self._io.poll()
|
229
|
-
|
230
|
-
for group in sorted_groups:
|
231
|
-
for process in group:
|
232
|
-
process.transition()
|
229
|
+
#
|
233
230
|
|
234
231
|
def _reap(self, *, once: bool = False, depth: int = 0) -> None:
|
235
232
|
if depth >= 100:
|
236
233
|
return
|
237
234
|
|
238
235
|
wp = waitpid()
|
239
|
-
|
236
|
+
|
240
237
|
if wp is None or not wp.pid:
|
241
238
|
return
|
242
239
|
|
240
|
+
log.info(f'Waited pid: {wp}') # noqa
|
243
241
|
process = self._pid_history.get(wp.pid, None)
|
244
242
|
if process is None:
|
245
243
|
_, msg = decode_wait_status(wp.sts)
|
@@ -252,6 +250,8 @@ class Supervisor:
|
|
252
250
|
# keep reaping until no more kids to reap, but don't recurse infinitely
|
253
251
|
self._reap(once=False, depth=depth + 1)
|
254
252
|
|
253
|
+
#
|
254
|
+
|
255
255
|
def _tick(self, now: ta.Optional[float] = None) -> None:
|
256
256
|
"""Send one or more 'tick' events when the timeslice related to the period for the event type rolls over"""
|
257
257
|
|
@@ -7,6 +7,7 @@ from omlish.lite.fdio.handlers import FdIoHandler
|
|
7
7
|
|
8
8
|
from .configs import ProcessConfig
|
9
9
|
from .configs import ProcessGroupConfig
|
10
|
+
from .events import ProcessOutputChannel
|
10
11
|
from .states import ProcessState
|
11
12
|
from .states import SupervisorState
|
12
13
|
from .utils.collections import KeyedCollectionAccessors
|
@@ -71,7 +72,7 @@ class HasDispatchers(abc.ABC):
|
|
71
72
|
class ProcessDispatcher(FdIoHandler, abc.ABC):
|
72
73
|
@property
|
73
74
|
@abc.abstractmethod
|
74
|
-
def channel(self) ->
|
75
|
+
def channel(self) -> ProcessOutputChannel:
|
75
76
|
raise NotImplementedError
|
76
77
|
|
77
78
|
@property
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev132
|
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.dev132
|
16
|
+
Requires-Dist: omlish==0.0.0.dev132
|
17
17
|
Provides-Extra: all
|
18
18
|
Requires-Dist: paramiko~=3.5; extra == "all"
|
19
19
|
Requires-Dist: asyncssh~=2.18; extra == "all"
|
@@ -12,7 +12,7 @@ authors = [
|
|
12
12
|
urls = {source = 'https://github.com/wrmsr/omlish'}
|
13
13
|
license = {text = 'BSD-3-Clause'}
|
14
14
|
requires-python = '>=3.12'
|
15
|
-
version = '0.0.0.
|
15
|
+
version = '0.0.0.dev132'
|
16
16
|
classifiers = [
|
17
17
|
'License :: OSI Approved :: BSD License',
|
18
18
|
'Development Status :: 2 - Pre-Alpha',
|
@@ -22,8 +22,8 @@ classifiers = [
|
|
22
22
|
]
|
23
23
|
description = 'ominfra'
|
24
24
|
dependencies = [
|
25
|
-
'omdev == 0.0.0.
|
26
|
-
'omlish == 0.0.0.
|
25
|
+
'omdev == 0.0.0.dev132',
|
26
|
+
'omlish == 0.0.0.dev132',
|
27
27
|
]
|
28
28
|
|
29
29
|
[project.optional-dependencies]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ominfra-0.0.0.dev131 → ominfra-0.0.0.dev132}/ominfra/deploy/executor/concerns/supervisor.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|