83 lines
2.9 KiB
Python
83 lines
2.9 KiB
Python
"""Static checks for the production Compose packaging slice."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
from backup_tool.cli import build_parser
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def test_health_command_selects_an_explicit_runtime_role() -> None:
|
|
parsed = build_parser().parse_args(["health", "worker"])
|
|
|
|
assert parsed.role == "health"
|
|
assert parsed.health_role == "worker"
|
|
|
|
|
|
def test_runtime_images_are_pinned_non_root_and_exclude_database_clients() -> None:
|
|
runtime = (ROOT / "Dockerfile").read_text()
|
|
proxy = (ROOT / "frontend" / "Dockerfile").read_text()
|
|
|
|
for dockerfile in (runtime, proxy):
|
|
from_lines = [line for line in dockerfile.splitlines() if line.startswith("FROM ")]
|
|
assert from_lines
|
|
assert all("@sha256:" in line for line in from_lines)
|
|
|
|
assert "USER backup-tool:backup-tool" in runtime
|
|
assert 'ENTRYPOINT ["backup-tool"]' in runtime
|
|
assert 'CMD ["web"]' in runtime
|
|
assert "USER 10001:0" in proxy
|
|
assert not re.search(r"\b(pg_dump|mysqldump|postgresql-client|mysql-client)\b", runtime)
|
|
|
|
|
|
def test_compose_runs_one_isolated_role_per_service_without_reload() -> None:
|
|
compose = (ROOT / "docker-compose.yml").read_text()
|
|
|
|
for role in ("web", "scheduler", "worker", "migrate", "admin"):
|
|
assert f" {role}:" in compose
|
|
for command in (
|
|
'["web"]',
|
|
'["scheduler"]',
|
|
'["worker"]',
|
|
'["migrate", "upgrade"]',
|
|
'["admin", "--help"]',
|
|
):
|
|
assert command in compose
|
|
assert "--reload" not in compose
|
|
assert "backup-tool-runtime:/run/backup-tool" in compose
|
|
assert '["CMD", "backup-tool", "health", "web"]' in compose
|
|
assert '["CMD", "backup-tool", "health", "scheduler"]' in compose
|
|
assert '["CMD", "backup-tool", "health", "worker"]' in compose
|
|
|
|
|
|
def test_proxy_is_the_only_published_endpoint_and_uses_same_origin_socket() -> None:
|
|
compose = (ROOT / "docker-compose.yml").read_text()
|
|
nginx = (ROOT / "frontend" / "nginx.conf").read_text()
|
|
|
|
assert compose.count(" ports:") == 1
|
|
assert '"127.0.0.1:${BACKUP_TOOL_PORT:-8080}:8080"' in compose
|
|
assert '"${BACKUP_TOOL_PORT:-8080}:8080"' not in compose
|
|
assert "server unix:/run/backup-tool/web.sock;" in nginx
|
|
assert "location /api/" in nginx
|
|
assert "location = /readyz" in nginx
|
|
assert "location = /livez" in nginx
|
|
assert "location = /metrics" in nginx
|
|
assert "proxy_pass http://backup_tool_web;" in nginx
|
|
|
|
|
|
def test_operational_artifacts_cover_sbom_provenance_and_recovery() -> None:
|
|
assert (ROOT / "docs/release/m14-sbom.json").is_file()
|
|
assert (ROOT / "docs/release/m14-provenance.md").is_file()
|
|
for runbook in (
|
|
"metadata.md",
|
|
"repositories.md",
|
|
"keys.md",
|
|
"upgrade.md",
|
|
"disaster-recovery.md",
|
|
"observability.md",
|
|
):
|
|
assert (ROOT / "docs/runbooks" / runbook).is_file()
|