220 lines
8.5 KiB
Python
220 lines
8.5 KiB
Python
from __future__ import annotations
|
|
|
|
import re
|
|
import shlex
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def test_promoter_archive_covers_every_required_installer_source():
|
|
from backend.app.services.controller_artifact_promoter import helper_container_spec
|
|
|
|
spec = helper_container_spec(revision="a" * 40, image_id="sha256:" + "b" * 64)
|
|
command = spec["Cmd"][2].replace("\\\n", " ")
|
|
archive = re.search(
|
|
r'archive --format=tar "\$revision"(.*?)> "\$work/controller.tar"',
|
|
command,
|
|
re.S,
|
|
)
|
|
assert archive is not None
|
|
packaged = shlex.split(archive.group(1))
|
|
installer = (ROOT / "scripts/upgrade_deployment_controller.sh").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
required_block = re.search(r"(?m)^source_paths=\(\n(.*?)\n\)", installer, re.S)
|
|
assert required_block is not None
|
|
required = shlex.split(required_block.group(1))
|
|
assert required
|
|
assert all(
|
|
any(path == prefix or path.startswith(prefix + "/") for prefix in packaged)
|
|
for path in required
|
|
)
|
|
|
|
|
|
def test_controller_promoter_action_is_fixed_and_registered():
|
|
registry = (ROOT / "backend/app/services/action_registry.py").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
execution = (ROOT / "backend/app/services/action_execution.py").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "'controller.artifact.promote'" in registry
|
|
assert "return 'tower/deployment-controller'" in registry
|
|
assert 'action == "controller.artifact.promote"' in execution
|
|
assert 'target.strip().lower() != "tower/deployment-controller"' in execution
|
|
|
|
|
|
def test_controller_promoter_helper_spec_is_fixed_and_bounded():
|
|
from backend.app.services.controller_artifact_promoter import (
|
|
CONTROLLER_VERSION,
|
|
HELPER_NAME,
|
|
helper_container_spec,
|
|
)
|
|
|
|
revision = "a" * 40
|
|
image_id = "sha256:" + "b" * 64
|
|
spec = helper_container_spec(revision=revision, image_id=image_id)
|
|
|
|
assert HELPER_NAME == "itworx-controller-artifact-promoter-modern"
|
|
assert CONTROLLER_VERSION == "2.1.21"
|
|
assert re.fullmatch(r"2\.1\.\d+", CONTROLLER_VERSION)
|
|
assert spec["Image"] == image_id
|
|
assert spec["HostConfig"]["Privileged"] is False
|
|
assert spec["HostConfig"]["NetworkMode"] == "host"
|
|
assert spec["HostConfig"]["SecurityOpt"] == ["no-new-privileges:true"]
|
|
assert spec["HostConfig"]["ReadonlyRootfs"] is True
|
|
assert spec["HostConfig"]["PidsLimit"] <= 128
|
|
assert spec["HostConfig"]["Memory"] <= 1024 * 1024 * 1024
|
|
assert spec["HostConfig"]["CpuQuota"] <= spec["HostConfig"]["CpuPeriod"]
|
|
assert set(spec["HostConfig"]["Tmpfs"]) == {"/tmp"}
|
|
assert set(spec["HostConfig"]["Binds"]) == {
|
|
"/mnt/cache:/mnt/cache",
|
|
"/mnt/user:/mnt/user",
|
|
"/var/lib/docker:/var/lib/docker:ro",
|
|
"/srv/example/appdata/gitea/git/repositories:/gitea-repositories:ro",
|
|
"/var/run/docker.sock:/var/run/docker.sock",
|
|
}
|
|
assert spec["Cmd"][:2] == ["/bin/bash", "-lc"]
|
|
command = spec["Cmd"][2]
|
|
assert "mktemp -d -p /tmp itworx-controller-artifact-promoter-" in command
|
|
assert f"itworx-controller-artifact-promoter-{revision[:12]}.XXXXXX" in command
|
|
assert (
|
|
"/mnt/cache/appdata/gitea-deploy-runners/.controller-promoter-" not in command
|
|
)
|
|
assert "work='/mnt/cache" not in command
|
|
assert 'rm -rf -- "$work"' in command
|
|
assert (
|
|
'scripts/upgrade_deployment_controller.sh > "$work/controller.tar"' in command
|
|
)
|
|
assert 'chmod 0600 "$work/controller.tar"' in command
|
|
assert "scripts/prepare_controller_hub_release_volume.py" in command
|
|
assert "scripts/run_repopulse_adoption.py" in command
|
|
assert "UPGRADE_DEPLOYMENT_CONTROLLER_V2" in command
|
|
assert revision in command
|
|
assert "--image-id" in command
|
|
assert CONTROLLER_VERSION in command
|
|
assert "version='2.1.21'" in command
|
|
assert "/gitea-repositories/jens/unraid-appops-gateway.git" in command
|
|
archive_start = command.index('git --git-dir="$repo" archive')
|
|
archive_end = command.index('> "$work/controller.tar"', archive_start)
|
|
assert "scripts/vendor" in command[archive_start:archive_end]
|
|
assert (
|
|
'scripts/upgrade_deployment_controller.sh > "$work/controller.tar"' in command
|
|
)
|
|
assert '--image-id "$image_id"' in command
|
|
|
|
|
|
def test_controller_promoter_bootstrap_child_spec_binds_legacy_target_identity():
|
|
from backend.app.services.controller_artifact_promoter import helper_container_spec
|
|
|
|
revision = "a" * 40
|
|
image_id = "sha256:" + "b" * 64
|
|
target_id = "c" * 64
|
|
target_revision = "d" * 40
|
|
target_tree = "e" * 40
|
|
spec = helper_container_spec(
|
|
revision=revision,
|
|
image_id=image_id,
|
|
current_controller_id=target_id,
|
|
current_controller_revision=target_revision,
|
|
current_controller_tree=target_tree,
|
|
current_controller_version="2.1.12",
|
|
)
|
|
|
|
assert spec["Image"] == image_id
|
|
assert spec["Labels"] == {
|
|
"itworx.controller.promoter": "true",
|
|
"itworx.source.repository": "jens/unraid-appops-gateway",
|
|
"itworx.source.revision": revision,
|
|
"itworx.controller.version": "2.1.21",
|
|
"itworx.controller.bootstrap.target.id": target_id,
|
|
"itworx.controller.bootstrap.target.image": image_id,
|
|
"itworx.controller.bootstrap.target.revision": target_revision,
|
|
"itworx.controller.bootstrap.target.tree": target_tree,
|
|
"itworx.controller.bootstrap.target.version": "2.1.12",
|
|
}
|
|
|
|
|
|
def test_controller_promoter_rejects_partial_or_mutable_bootstrap_binding():
|
|
from backend.app.services.controller_artifact_promoter import helper_container_spec
|
|
|
|
base = {"revision": "a" * 40, "image_id": "sha256:" + "b" * 64}
|
|
with pytest.raises(ValueError):
|
|
helper_container_spec(**base, current_controller_id="c" * 64)
|
|
with pytest.raises(ValueError):
|
|
helper_container_spec(
|
|
**base,
|
|
current_controller_id="c" * 64,
|
|
current_controller_revision="../controller",
|
|
current_controller_tree="e" * 40,
|
|
current_controller_version="2.1.12",
|
|
)
|
|
with pytest.raises(ValueError):
|
|
helper_container_spec(
|
|
**base,
|
|
current_controller_id="../controller",
|
|
current_controller_revision="d" * 40,
|
|
current_controller_tree="e" * 40,
|
|
current_controller_version="2.1.12",
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"revision,image_id",
|
|
[
|
|
("main", "sha256:" + "b" * 64),
|
|
("a" * 39, "sha256:" + "b" * 64),
|
|
("a" * 40, "ubuntu:latest"),
|
|
("a" * 40, "sha256:" + "z" * 64),
|
|
],
|
|
)
|
|
def test_controller_promoter_rejects_mutable_or_invalid_identity(
|
|
revision: str, image_id: str
|
|
):
|
|
from backend.app.services.controller_artifact_promoter import helper_container_spec
|
|
|
|
with pytest.raises(ValueError):
|
|
helper_container_spec(revision=revision, image_id=image_id)
|
|
|
|
|
|
def test_controller_promoter_uses_only_protected_main_and_push_gate():
|
|
source = (ROOT / "backend/app/services/controller_artifact_promoter.py").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert 'branch = "main"' in source
|
|
assert 'REQUIRED_PUSH_CONTEXT = "Managed validation / full (push)"' in source
|
|
assert "controller_promoter_main_not_validated" in source
|
|
assert "caller_revision" not in source
|
|
|
|
|
|
def test_deployment_broker_exposes_bounded_controller_health():
|
|
source = (ROOT / "backend/app/services/deployment_broker.py").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "def controller_health(self)" in source
|
|
assert 'f"{self.base_url}/health"' in source
|
|
assert "_MAX_CONTROLLER_HEALTH_BYTES" in source
|
|
|
|
|
|
def test_modern_promoter_21_binds_actual_installed_19_without_widening_bootstrap_baselines():
|
|
from backend.app.services.controller_artifact_promoter import helper_container_spec
|
|
|
|
actual_revision = "0f89e1506065c5365c48968b1d7845d666a18be8"
|
|
spec = helper_container_spec(
|
|
revision="a" * 40,
|
|
image_id="sha256:" + "b" * 64,
|
|
current_controller_id="c" * 64,
|
|
current_controller_revision=actual_revision,
|
|
current_controller_tree="d" * 40,
|
|
current_controller_version="2.1.19",
|
|
)
|
|
labels = spec["Labels"]
|
|
assert labels["itworx.controller.version"] == "2.1.21"
|
|
assert labels["itworx.controller.bootstrap.target.revision"] == actual_revision
|
|
assert labels["itworx.controller.bootstrap.target.version"] == "2.1.19"
|
|
assert labels["itworx.controller.bootstrap.target.id"] == "c" * 64
|
|
assert labels["itworx.controller.bootstrap.target.tree"] == "d" * 40
|