mirror of
https://github.com/frappe/gunicorn.git
synced 2026-07-02 10:41:30 +08:00
Run the companion manager as a single arbiter child with its own supervision loop, and host the config model with its loader. config.py holds CompanionConfig (moved from process.py) and build_companion_configs(cfg), which expands each companion_workers entry into a CompanionConfig, filling omitted fields from the global companion_* settings. It is also the reread config_loader. process.py keeps State and CompanionProcess. CompanionManager.run() is the forked-child body: installs SIGCHLD/SIGTERM/SIGINT via a self-pipe, brings up the control socket, starts every companion, then select-waits on the socket and the pipe. Each tick reaps exits, retries backoff, promotes past startsecs, and SIGKILLs companions past their stop deadline. SIGTERM/SIGINT stop all companions and return. Arbiter gains companion_manager_pid, manage_companion_manager (respawns the manager when it is gone and companions are configured), spawn_companion_manager (fork; child runs the loop), and reap detection that clears the pid on exit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
1.4 KiB
Python
48 lines
1.4 KiB
Python
#
|
|
# This file is part of gunicorn released under the MIT license.
|
|
# See the NOTICE for more information.
|
|
|
|
import pytest
|
|
|
|
from gunicorn.config import Config
|
|
from gunicorn.companion.config import build_companion_configs
|
|
|
|
|
|
def make_config(workers, **overrides):
|
|
cfg = Config()
|
|
cfg.set("companion_workers", workers)
|
|
for key, value in overrides.items():
|
|
cfg.set(key, value)
|
|
return cfg
|
|
|
|
|
|
def test_build_applies_global_defaults():
|
|
cfg = make_config(
|
|
[{"name": "rq", "target": "pkg:run"}],
|
|
companion_stop_signal="SIGINT",
|
|
companion_startsecs=5)
|
|
config, = build_companion_configs(cfg)
|
|
assert config.name == "rq"
|
|
assert config.target == "pkg:run"
|
|
assert config.stop_signal == "SIGINT"
|
|
assert config.startsecs == 5
|
|
|
|
|
|
def test_build_per_spec_overrides_global():
|
|
cfg = make_config(
|
|
[{"name": "rq", "target": "pkg:run", "stop_signal": "SIGTERM"}],
|
|
companion_stop_signal="SIGINT")
|
|
config, = build_companion_configs(cfg)
|
|
assert config.stop_signal == "SIGTERM"
|
|
|
|
|
|
def test_build_empty_when_none_configured():
|
|
assert build_companion_configs(make_config([])) == []
|
|
|
|
|
|
def test_build_requires_name_and_target():
|
|
with pytest.raises(ValueError):
|
|
build_companion_configs(make_config([{"name": "rq"}]))
|
|
with pytest.raises(ValueError):
|
|
build_companion_configs(make_config([{"target": "pkg:run"}]))
|