ominfra 0.0.0.dev131__py3-none-any.whl → 0.0.0.dev133__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 → manage/deploy}/_executor.py +10 -10
- ominfra/{deploy → manage/deploy}/poly/_main.py +6 -6
- ominfra/{deploy → manage/deploy}/remote.py +1 -1
- ominfra/pyremote.py +357 -0
- ominfra/scripts/journald2aws.py +8 -0
- ominfra/scripts/supervisor.py +173 -67
- ominfra/supervisor/dispatchersimpl.py +4 -3
- ominfra/supervisor/events.py +5 -2
- ominfra/supervisor/http.py +8 -1
- ominfra/supervisor/inject.py +8 -2
- ominfra/supervisor/io.py +2 -2
- ominfra/supervisor/main.py +13 -10
- ominfra/supervisor/processimpl.py +0 -1
- ominfra/supervisor/states.py +11 -0
- ominfra/supervisor/supervisor.py +26 -26
- ominfra/supervisor/types.py +2 -1
- {ominfra-0.0.0.dev131.dist-info → ominfra-0.0.0.dev133.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev131.dist-info → ominfra-0.0.0.dev133.dist-info}/RECORD +46 -49
- ominfra/pyremote/__init__.py +0 -0
- ominfra/pyremote/_runcommands.py +0 -1201
- ominfra/pyremote/bootstrap.py +0 -149
- ominfra/pyremote/runcommands.py +0 -56
- /ominfra/{deploy → manage/deploy}/__init__.py +0 -0
- /ominfra/{deploy → manage/deploy}/configs.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/__init__.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/base.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/__init__.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/dirs.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/nginx.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/repo.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/supervisor.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/systemd.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/user.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/concerns/venv.py +0 -0
- /ominfra/{deploy → manage/deploy}/executor/main.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/__init__.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/base.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/configs.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/deploy.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/main.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/nginx.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/repo.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/runtime.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/site.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/supervisor.py +0 -0
- /ominfra/{deploy → manage/deploy}/poly/venv.py +0 -0
- {ominfra-0.0.0.dev131.dist-info → ominfra-0.0.0.dev133.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev131.dist-info → ominfra-0.0.0.dev133.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev131.dist-info → ominfra-0.0.0.dev133.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev131.dist-info → ominfra-0.0.0.dev133.dist-info}/top_level.txt +0 -0
ominfra/scripts/supervisor.py
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# @omlish-lite
|
4
4
|
# @omlish-script
|
5
5
|
# @omlish-amalg-output ../supervisor/main.py
|
6
|
-
# ruff: noqa: N802
|
6
|
+
# ruff: noqa: N802 UP006 UP007 UP012 UP036
|
7
7
|
# Supervisor is licensed under the following license:
|
8
8
|
#
|
9
9
|
# A copyright notice accompanies this license document that identifies the copyright holders.
|
@@ -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,
|
ominfra/supervisor/events.py
CHANGED
@@ -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__()
|
ominfra/supervisor/http.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
# ruff: noqa:
|
1
|
+
# ruff: noqa: UP006 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
|
},
|
ominfra/supervisor/inject.py
CHANGED
@@ -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
|
|
ominfra/supervisor/io.py
CHANGED
@@ -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:
|
ominfra/supervisor/main.py
CHANGED
@@ -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
|
ominfra/supervisor/states.py
CHANGED
@@ -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,
|