diff --git a/psi/unitgen.py b/psi/unitgen.py index 61bce02..896ca45 100644 --- a/psi/unitgen.py +++ b/psi/unitgen.py @@ -354,6 +354,13 @@ def generate_container_serve_quadlet(image: str, settings: PsiSettings) -> str: f"Image={image}", "Exec=serve", "Network=host", + "SecurityLabelType=container_runtime_t", + "Notify=healthy", + f"HealthCmd=curl -sf --unix-socket {sock} http://localhost/healthz", + "HealthInterval=30s", + "HealthRetries=10", + "HealthStartPeriod=60s", + "HealthTimeout=5s", f"Volume={config_dir}:{config_dir}:ro", f"Volume={state}:{state}:Z", f"Volume={runtime_dir}:{runtime_dir}:Z", diff --git a/tests/test_unitgen.py b/tests/test_unitgen.py index 6f3587f..e1df13f 100644 --- a/tests/test_unitgen.py +++ b/tests/test_unitgen.py @@ -419,3 +419,24 @@ def test_tls_renew_quadlet_has_container_name(self, tmp_path: Path) -> None: settings = _mock_settings(tmp_path) content = generate_container_tls_renew_quadlet("psi:latest", settings) assert "ContainerName=psi-tls-renew" in content + + def test_serve_quadlet_has_security_label_type(self, tmp_path: Path) -> None: + """Without SecurityLabelType=container_runtime_t the container cannot + read /etc/psi/config.yaml from the host without a :Z relabel, which we + do not want on shared config directories. + """ + settings = _mock_settings(tmp_path) + content = generate_container_serve_quadlet("psi:latest", settings) + assert "SecurityLabelType=container_runtime_t" in content + + def test_serve_quadlet_has_notify_healthy(self, tmp_path: Path) -> None: + """Quadlet emits Type=notify by default and expects an sd_notify ready + signal. Notify=healthy makes podman send it once the healthcheck first + passes. Without this the unit hangs in 'activating' until TimeoutStartSec. + """ + settings = _mock_settings(tmp_path) + content = generate_container_serve_quadlet("psi:latest", settings) + assert "Notify=healthy" in content + assert "HealthCmd=curl -sf --unix-socket " in content + assert "http://localhost/healthz" in content + assert "HealthStartPeriod=60s" in content