from __future__ import annotations import ast import json import os import re import shutil import subprocess import sys from pathlib import Path from typing import Any import pytest from scripts import bootstrap_controller_artifact_promoter as bootstrap ROOT = Path(__file__).resolve().parents[2] LEGACY_REVISION = "5553c04ce497c08a35ed301052dc5e0b02e5d9b4" LEGACY_TREE = "1624e1748053f998d8427c8c8a4203277d22f13c" APPOPS_CONTAINER_ID = "72daa9f638defc4f8897d8aad23c4181054b2a1bc04c993078a5ac9597eeb0ec" APPOPS_IMAGE_ID = "sha256:9b5eddf32c8eb5361106fc35cc0e056c49d287a20d1fb26490662cc8447f09d4" CONTROLLER_CONTAINER_ID = "e4e7550405aa2a9a91901faf1177d46a35b0fc43c304976ec8971865d43e30eb" CONTROLLER_IMAGE_ID = "sha256:c474d435a4f55ac5205570da0caa47e103f89a6c2ef96ea45861a23eb7b005dc" CONTROLLER_IMAGE_REF = "docker.gitea.com/runner-images:ubuntu-latest" CONTROLLER_REVISION = "034c9f844d2455850ba37b31379b7edaa786583e" CONTROLLER_TREE = "6b8e92942e6e5eca8a9aa9da0a1509b2019d7b7b" REVISION = "a" * 40 TREE = "b" * 40 CALLER_ID = "c" * 64 MODERN_HELPER_ID = "f" * 64 RETIRED_HELPER_ID = bootstrap.RETIRED_FAILED_MODERN_HELPER_ID HOST_HOSTNAME = "Tower" LEGACY_HELPER_SOURCE_FIXTURE = ''' def helper_container_spec(*, revision: str, image_id: str) -> dict[str, Any]: if not _REVISION.fullmatch(revision): raise ValueError("controller_promoter_revision_invalid") if not _IMAGE_ID.fullmatch(image_id): raise ValueError("controller_promoter_image_invalid") command = f"""set -Eeuo pipefail revision='{revision}' version='{CONTROLLER_VERSION}' repo='/gitea-repositories/jens/unraid-appops-gateway.git' work='/mnt/cache/appdata/gitea-deploy-runners/.controller-promoter-{revision[:12]}' rm -rf -- "$work" install -d -m 0700 "$work" cleanup() {{ rm -rf -- "$work"; }} trap cleanup EXIT command -v git >/dev/null command -v docker >/dev/null docker compose version >/dev/null command -v python3 >/dev/null command -v tar >/dev/null command -v curl >/dev/null git --git-dir="$repo" cat-file -e "$revision^{{commit}}" tree="$(git --git-dir="$repo" rev-parse "$revision^{{tree}}")" git --git-dir="$repo" archive --format=tar "$revision" \\ ops/gitea-deploy-broker \\ scripts/prepare_controller_artifact.py \\ scripts/reconcile_controller_state.py \\ scripts/run_repopulse_adoption.py > "$work/controller.tar" archive_sha="$(sha256sum "$work/controller.tar" | cut -d ' ' -f 1)" git --git-dir="$repo" show "$revision:scripts/upgrade_deployment_controller.sh" > "$work/upgrade.sh" chmod 0700 "$work/upgrade.sh" bash "$work/upgrade.sh" \\ --archive "$work/controller.tar" \\ --archive-sha256 "$archive_sha" \\ --source-repository '{SOURCE_REPOSITORY}' \\ --source-revision "$revision" \\ --source-tree "$tree" \\ --controller-version "$version" \\ --confirm UPGRADE_DEPLOYMENT_CONTROLLER_V2 """ return { "Image": image_id, "User": "0:0", "Cmd": ["/bin/bash", "-lc", command], "Labels": { HELPER_LABEL: "true", "itworx.source.repository": SOURCE_REPOSITORY, "itworx.source.revision": revision, "itworx.controller.version": CONTROLLER_VERSION, }, "HostConfig": { "Binds": [ "/mnt/cache:/mnt/cache", "/mnt/user:/mnt/user", "/srv/example/appdata/gitea/git/repositories:/gitea-repositories:ro", "/var/run/docker.sock:/var/run/docker.sock", ], "NetworkMode": "host", "Privileged": False, "SecurityOpt": ["no-new-privileges:true"], "RestartPolicy": {"Name": "no"}, }, } ''' def _legacy_helper_spec(revision: str = REVISION) -> dict[str, Any]: tree = ast.parse(LEGACY_HELPER_SOURCE_FIXTURE) function = next( node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == "helper_container_spec" ) namespace: dict[str, Any] = { "re": re, "Any": Any, "_REVISION": re.compile(r"^[0-9a-f]{40}$"), "_IMAGE_ID": re.compile(r"^sha256:[0-9a-f]{64}$"), "SOURCE_REPOSITORY": "jens/unraid-appops-gateway", "HELPER_NAME": "itworx-controller-artifact-promoter", "CONTROLLER_VERSION": "2.1.5", "HELPER_LABEL": "itworx.controller.promoter", } exec( compile(ast.Module(body=[function], type_ignores=[]), "legacy-promoter", "exec"), namespace, ) return namespace["helper_container_spec"](revision=revision, image_id=CONTROLLER_IMAGE_ID) def _image(labels: dict[str, str]) -> dict[str, Any]: return {"Id": APPOPS_IMAGE_ID, "Config": {"Labels": dict(labels)}} def _appops_labels() -> dict[str, str]: return { "org.opencontainers.image.source": "https://gitea.example.com/Jens/Unraid-AppOps-Gateway", "org.opencontainers.image.revision": LEGACY_REVISION, "org.opencontainers.image.source-tree": LEGACY_TREE, "org.opencontainers.image.version": "1.8.0-platform-5553c04ce497", "org.opencontainers.image.created": "2026-10-01T16:32:01Z", "com.docker.compose.project": "unraid-appops-gateway", "com.docker.compose.service": "appops-gateway", "com.docker.compose.project.working_dir": "/srv/example/appdata/unraid-appops-gateway/source", "com.docker.compose.project.config_files": "/srv/example/appdata/unraid-appops-gateway/source/docker-compose.yml", } def _controller_labels() -> dict[str, str]: return { "io.buildah.version": "1.35.0", "org.opencontainers.image.authors": "Gitea", "org.opencontainers.image.created": "2026-10-01T00:01:57Z", "org.opencontainers.image.description": "Official docker images used by Gitea runner to run workflows.", "org.opencontainers.image.documentation": "https://gitea.com/gitea/runner-images/src/branch/main/README.md", "org.opencontainers.image.licenses": "MIT", "org.opencontainers.image.revision": "317f8e3c510e5ec773a103f5d9a9e5769105c59b", "org.opencontainers.image.source": "https://gitea.com/gitea/runner-images", "org.opencontainers.image.title": "Official Gitea Actions runner images", "org.opencontainers.image.url": "https://gitea.com/gitea/runner-images", "org.opencontainers.image.vendor": "Gitea", "org.opencontainers.image.version": "v26.10.01", } def _controller_container_labels() -> dict[str, str]: return { **_controller_labels(), "com.docker.compose.config-hash": "577d77247471213db8a5c465ee2450765ba52cb4048f983c42083ac45a16ddb7", "com.docker.compose.container-number": "1", "com.docker.compose.depends_on": "", "com.docker.compose.image": "sha256:adbb51b2873bf05546019c341caf6fd4368159546c36a5aea81b4d8bf088804e", "com.docker.compose.oneoff": "False", "com.docker.compose.project": "gitea-deploy-runners", "com.docker.compose.project.config_files": "/mnt/cache/appdata/gitea-deploy-runners/docker-compose.yml", "com.docker.compose.project.working_dir": "/mnt/cache/appdata/gitea-deploy-runners", "com.docker.compose.replace": "gitea-deploy-control", "com.docker.compose.service": "deploy-control", "com.docker.compose.version": "2.40.3", "tech.itworx.deployment.target": "unraid", "tech.itworx.role": "gitea-deploy-controller", "tech.itworx.gitea.scope": "Jens", } def _modern_controller_labels() -> dict[str, str]: return { **_controller_labels(), "com.docker.compose.config-hash": "a" * 64, "com.docker.compose.container-number": "1", "com.docker.compose.depends_on": "", "com.docker.compose.image": CONTROLLER_IMAGE_ID, "com.docker.compose.oneoff": "False", "com.docker.compose.project": "gitea-deploy-runners", "com.docker.compose.project.config_files": "-", "com.docker.compose.project.environment_file": "/dev/null", "com.docker.compose.project.working_dir": "/var/lib/docker/volumes/itworx-controller-hub-releases/_data", "com.docker.compose.replace": "gitea-deploy-control", "com.docker.compose.service": "deploy-control", "com.docker.compose.version": "2.40.3", "tech.itworx.deployment.target": "unraid", "tech.itworx.gitea.scope": "Jens", "tech.itworx.role": "gitea-deploy-controller", } def _modern_controller_mounts(source_revision: str) -> list[dict[str, Any]]: artifact_source = ( "/var/lib/docker/volumes/itworx-controller-hub-releases/_data/" f"controller-artifacts/{source_revision}" ) return [ {"Type": "bind", "Source": "/boot", "Destination": "/boot", "RW": True, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/var/lib/docker", "Destination": "/var/lib/docker", "RW": False, "Propagation": "rslave"}, { "Type": "bind", "Source": "/srv/example/appdata/gitea/git/repositories", "Destination": "/gitea-repositories", "RW": False, "Propagation": "rprivate", }, {"Type": "bind", "Source": artifact_source, "Destination": "/opt/gitea-deploy", "RW": False, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/var/run/docker.sock", "Destination": "/var/run/docker.sock", "RW": True, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/mnt/user", "Destination": "/mnt/user", "RW": True, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/mnt/cache", "Destination": "/mnt/cache", "RW": True, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/mnt/disks", "Destination": "/mnt/disks", "RW": False, "Propagation": "rprivate"}, { "Type": "volume", "Name": "itworx-controller-hub-releases", "Source": "/var/lib/docker/volumes/itworx-controller-hub-releases/_data", "Destination": "/controller-hub-releases", "RW": True, "Propagation": "", }, { "Type": "bind", "Source": "/var/lib/docker/volumes/itworx-controller-hub-releases/_data/controller-config", "Destination": "/controller-config", "RW": False, "Propagation": "rprivate", }, ] def _modern_controller_inspect(source_revision: str) -> dict[str, Any]: controller = _controller_inspect() controller.update({"Id": "e" * 64, "Image": CONTROLLER_IMAGE_ID}) controller["Config"]["Image"] = CONTROLLER_IMAGE_ID controller["Config"]["Labels"] = _modern_controller_labels() controller["Mounts"] = _modern_controller_mounts(source_revision) return controller def _appops_inspect() -> dict[str, Any]: labels = _appops_labels() return { "Id": APPOPS_CONTAINER_ID, "Image": APPOPS_IMAGE_ID, "Name": "/Unraid-AppOps-Gateway", "State": {"Running": True, "Health": {"Status": "healthy"}}, "Config": { "Hostname": "unraid-appops-gateway", "Image": "unraid-appops-gateway:platform-5553c04ce497", "Labels": labels, }, "HostConfig": {"RestartPolicy": {"Name": "unless-stopped"}}, "Mounts": [ { "Type": "bind", "Source": "/srv/example/appdata/unraid-appops-gateway-1216", "Destination": "/data", "RW": True, }, { "Type": "bind", "Source": "/srv/example/appdata/comfyui-nvidia/basedir/models", "Destination": "/mnt/privatemuse-comfyui-models", "RW": True, }, ], } def _controller_inspect() -> dict[str, Any]: return { "Id": CONTROLLER_CONTAINER_ID, "Image": CONTROLLER_IMAGE_ID, "Name": "/gitea-deploy-control", "State": {"Running": True}, "Config": {"Image": CONTROLLER_IMAGE_REF, "Labels": _controller_container_labels()}, "HostConfig": { "Binds": [ "/mnt/user:/mnt/user:rw", "/mnt/cache:/mnt/cache:rw", "/mnt/disks:/mnt/disks:ro", "/boot:/boot:rw", "/var/lib/docker:/var/lib/docker:ro", "/srv/example/appdata/gitea/git/repositories:/gitea-repositories:ro", "/mnt/cache/appdata/gitea-deploy-runners/controller:/opt/gitea-deploy:ro", "/var/run/docker.sock:/var/run/docker.sock:rw", ], "Mounts": None, }, "Mounts": [ { "Type": "bind", "Source": "/boot", "Destination": "/boot", "RW": True, "Propagation": "rprivate", }, { "Type": "bind", "Source": "/var/lib/docker", "Destination": "/var/lib/docker", "RW": False, "Propagation": "rslave", }, { "Type": "bind", "Source": "/srv/example/appdata/gitea/git/repositories", "Destination": "/gitea-repositories", "RW": False, "Propagation": "rprivate", }, { "Type": "bind", "Source": "/mnt/cache/appdata/gitea-deploy-runners/controller", "Destination": "/opt/gitea-deploy", "RW": False, "Propagation": "rprivate", }, { "Type": "bind", "Source": "/var/run/docker.sock", "Destination": "/var/run/docker.sock", "RW": True, "Propagation": "rprivate", }, { "Type": "bind", "Source": "/mnt/user", "Destination": "/mnt/user", "RW": True, "Propagation": "rprivate", }, { "Type": "bind", "Source": "/mnt/cache", "Destination": "/mnt/cache", "RW": True, "Propagation": "rprivate", }, { "Type": "bind", "Source": "/mnt/disks", "Destination": "/mnt/disks", "RW": False, "Propagation": "rprivate", }, ], } def _inspect_from_spec( name: str, identifier: str, image_id: str, spec: dict[str, Any], *, state: dict[str, Any], hostname: str | None = None, ) -> dict[str, Any]: return { "Id": identifier, "Image": image_id, "Name": f"/{name}", "State": dict(state), "Config": { "Image": spec["Image"], "User": spec["User"], "Cmd": spec["Cmd"], "Labels": {**_controller_labels(), **spec["Labels"]}, "Hostname": hostname if hostname is not None else identifier[:12], }, "HostConfig": {"ReadonlyRootfs": False, **spec["HostConfig"]}, } class FakeDocker: def __init__(self, caller_spec: dict[str, Any], *, existing_child: dict[str, Any] | None = None): legacy_caller = _inspect_from_spec( "itworx-controller-artifact-promoter", CALLER_ID, CONTROLLER_IMAGE_ID, caller_spec, state={"Running": True}, hostname=HOST_HOSTNAME, ) self.containers = { APPOPS_CONTAINER_ID: _appops_inspect(), "gitea-deploy-control": _controller_inspect(), bootstrap.LEGACY_HELPER_NAME: legacy_caller, CALLER_ID[:12]: legacy_caller, } self.images = { APPOPS_IMAGE_ID: _image(_appops_labels()), CONTROLLER_IMAGE_ID: { "Id": CONTROLLER_IMAGE_ID, "Config": {"Labels": _controller_labels()}, }, } self.existing_child = existing_child self.created: list[dict[str, Any]] = [] self.started: list[str] = [] self.removed: list[str] = [] self.inspected: list[str] = [] self.host_name = HOST_HOSTNAME self.log_bytes = b"controller_v2_ready revision=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" self.named_inspect_override: dict[str, Any] | None = None self.named_inspect_override_at = 0 self.retired_id_inspect_override: dict[str, Any] | None = None self.appops_inspect_count = 0 self.appops_inspect_override: dict[str, Any] | None = None self.appops_inspect_override_at = 0 self._inspect_counts: dict[str, int] = {} def inspect_container(self, reference: str) -> dict[str, Any] | None: self.inspected.append(reference) self._inspect_counts[reference] = self._inspect_counts.get(reference, 0) + 1 if reference == "itworx-controller-artifact-promoter-modern": if ( self.named_inspect_override is not None and self._inspect_counts[reference] == self.named_inspect_override_at ): return self.named_inspect_override return self.existing_child if ( reference == RETIRED_HELPER_ID and self.retired_id_inspect_override is not None ): return self.retired_id_inspect_override if ( reference == RETIRED_HELPER_ID and self.existing_child is not None and self.existing_child.get("Id") == RETIRED_HELPER_ID ): return self.existing_child if reference == APPOPS_CONTAINER_ID: self.appops_inspect_count += 1 if self.appops_inspect_count == self.appops_inspect_override_at: return self.appops_inspect_override return self.containers.get(reference) def daemon_name(self) -> str: return self.host_name def inspect_image(self, image_id: str) -> dict[str, Any] | None: return self.images.get(image_id) def create_container(self, name: str, spec: dict[str, Any]) -> str: assert name == "itworx-controller-artifact-promoter-modern" self.created.append(spec) container = _inspect_from_spec( name, MODERN_HELPER_ID, spec["Image"], spec, state={"Running": False, "ExitCode": 0, "Status": "exited"}, ) self.containers[name] = container self.containers[MODERN_HELPER_ID] = container return MODERN_HELPER_ID def start_container(self, identifier: str) -> None: self.started.append(identifier) source_revision = self.created[-1]["Labels"]["itworx.source.revision"] self.containers["gitea-deploy-control"] = _modern_controller_inspect(source_revision) def remove_retired_failed_container(self, identifier: str) -> None: assert identifier == RETIRED_HELPER_ID assert self.existing_child is not None assert self.existing_child["Id"] == RETIRED_HELPER_ID self.removed.append(identifier) self.existing_child = None def container_logs(self, identifier: str, *, tail: int, max_bytes: int) -> bytes: assert identifier in {MODERN_HELPER_ID, RETIRED_HELPER_ID} assert tail <= 40 assert max_bytes <= 8192 return self.log_bytes[:max_bytes] class FakeProtectedGit: def __init__(self, helper_source: str): self.revision = REVISION self.tree = TREE self.helper_source = helper_source self.blobs_read: list[tuple[str, str]] = [] self.main_checks = 0 self.fail_current_main_at: int | None = None self.retired_tree_valid = True def verify_current_main(self, revision: str, tree: str) -> None: self.main_checks += 1 if self.fail_current_main_at == self.main_checks: raise bootstrap.BootstrapError("controller_promoter_source_not_current_main") if (revision, tree) != (self.revision, self.tree): raise bootstrap.BootstrapError("controller_promoter_source_not_current_main") def verify_legacy_revision(self, revision: str, tree: str) -> None: if (revision, tree) != (LEGACY_REVISION, LEGACY_TREE): raise bootstrap.BootstrapError("controller_promoter_legacy_provenance_invalid") def verify_retired_modern_revision(self, revision: str, tree: str) -> None: if (revision, tree) != ( bootstrap.RETIRED_FAILED_MODERN_REVISION, bootstrap.RETIRED_FAILED_MODERN_TREE, ) or not self.retired_tree_valid: raise bootstrap.BootstrapError( "controller_promoter_retired_helper_provenance_invalid" ) def read_blob(self, revision: str, path: str) -> bytes: self.blobs_read.append((revision, path)) if revision not in {self.revision, bootstrap.RETIRED_FAILED_MODERN_REVISION} or path != "backend/app/services/controller_artifact_helper_spec.py": raise bootstrap.BootstrapError("controller_promoter_source_blob_invalid") return self.helper_source.encode("utf-8") def read_legacy_blob(self, revision: str, path: str) -> bytes: self.blobs_read.append((revision, path)) if (revision, path) != ( LEGACY_REVISION, "backend/app/services/controller_artifact_promoter.py", ): raise bootstrap.BootstrapError("controller_promoter_legacy_blob_invalid") return LEGACY_HELPER_SOURCE_FIXTURE.encode("utf-8") def _health( *, revision: str = REVISION, tree: str = TREE, version: str = "2.1.14", artifact_sha256: str = "1" * 64, ) -> dict[str, Any]: return { "status": "ok", "protocolVersion": "itworx-controller-request/v2", "artifact": { "apiVersion": "itworx.controller-artifact/v1", "sourceRepository": "jens/unraid-appops-gateway", "sourceRevision": revision, "sourceTree": tree, "controllerVersion": version, "artifactSha256": artifact_sha256, }, "delivery": { "health": { "configuration": "healthy", "inbox": "healthy", "artifact": "healthy", } }, } def _run_bootstrap( docker: FakeDocker, git: FakeProtectedGit, *, health: dict[str, Any] | None = None, initial_health: dict[str, Any] | None = None, health_sequence: list[dict[str, Any]] | None = None, hostname: str = HOST_HOSTNAME, source_revision: str = REVISION, source_tree: str = TREE, ): source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text( encoding="utf-8" ) git.helper_source = source initial = initial_health or _health( revision=CONTROLLER_REVISION, tree=CONTROLLER_TREE, version="2.1.12", artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256, ) health_reads = health_sequence or [initial, health or _health()] def read_health() -> dict[str, Any]: return health_reads.pop(0) if health_reads else health or _health() return bootstrap.run_bootstrap( "jens/unraid-appops-gateway", source_revision, source_tree, docker=docker, protected_git=git, hostname=hostname, health_reader=read_health, monotonic=lambda: 0.0, sleeper=lambda _seconds: None, child_timeout_seconds=0.0, ) def _retired_failed_child(helper_source: str) -> dict[str, Any]: spec = bootstrap.render_modern_helper_spec( helper_source, revision=bootstrap.RETIRED_FAILED_MODERN_REVISION, image_id=CONTROLLER_IMAGE_ID, current_controller_id=CONTROLLER_CONTAINER_ID, current_controller_revision=CONTROLLER_REVISION, current_controller_tree=CONTROLLER_TREE, current_controller_version="2.1.12", ) child = _inspect_from_spec( "itworx-controller-artifact-promoter-modern", RETIRED_HELPER_ID, CONTROLLER_IMAGE_ID, spec, state={"Running": False, "ExitCode": 1, "Status": "exited"}, ) child["Config"]["Volumes"] = None child["Mounts"] = [ {"Type": "bind", "Source": "/mnt/cache", "Destination": "/mnt/cache", "RW": True, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/mnt/user", "Destination": "/mnt/user", "RW": True, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/var/lib/docker", "Destination": "/var/lib/docker", "RW": False, "Propagation": "rslave"}, {"Type": "bind", "Source": "/srv/example/appdata/gitea/git/repositories", "Destination": "/gitea-repositories", "RW": False, "Propagation": "rprivate"}, {"Type": "bind", "Source": "/var/run/docker.sock", "Destination": "/var/run/docker.sock", "RW": True, "Propagation": "rprivate"}, ] child["HostConfig"].update( { "Mounts": None, "CapAdd": None, "CapDrop": None, "Devices": None, "DeviceRequests": None, "PortBindings": None, "VolumesFrom": None, "PidMode": "", "UTSMode": "", "UsernsMode": "", "CgroupParent": "", "IpcMode": "private", "CgroupnsMode": "private", "Runtime": "runc", "AutoRemove": False, "PublishAllPorts": False, } ) return child def test_initial_controller_runtime_accepts_exact_current_compose_container(): docker = FakeDocker(_legacy_helper_spec()) base_image_labels = bootstrap._inspect_controller_base_image(docker) health = _health( revision=CONTROLLER_REVISION, tree=CONTROLLER_TREE, version="2.1.12", artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256, ) assert ( bootstrap._verify_controller_runtime( docker, health_payload=health, base_image_labels=base_image_labels, ) == base_image_labels ) def test_immutable_legacy_helper_source_fixture_passes_bootstrap_identity_guard(): legacy_spec = _legacy_helper_spec() docker = FakeDocker(legacy_spec) git = FakeProtectedGit("") result = _run_bootstrap(docker, git) assert result["status"] == "promoted" assert docker.created assert git.blobs_read == [ (REVISION, "backend/app/services/controller_artifact_helper_spec.py"), (LEGACY_REVISION, "backend/app/services/controller_artifact_promoter.py"), ] def test_host_network_legacy_caller_is_resolved_by_fixed_name_not_host_hostname(): docker = FakeDocker(_legacy_helper_spec()) git = FakeProtectedGit("") result = _run_bootstrap(docker, git, hostname=HOST_HOSTNAME) assert result["status"] == "promoted" assert bootstrap.LEGACY_HELPER_NAME in docker.inspected assert HOST_HOSTNAME not in docker.inspected assert docker.created def test_docker_info_hostname_reader_returns_only_bounded_name(monkeypatch: pytest.MonkeyPatch): client = object.__new__(bootstrap.DockerSocketClient) requests: list[tuple[str, str, int]] = [] def fake_request(method: str, path: str, *, max_bytes: int) -> tuple[int, bytes]: requests.append((method, path, max_bytes)) return 200, json.dumps({"Name": HOST_HOSTNAME, "ID": "must-not-be-returned"}).encode() monkeypatch.setattr(client, "_request", fake_request) assert client.daemon_name() == HOST_HOSTNAME assert requests == [("GET", "/info", bootstrap.MAX_DOCKER_JSON_BYTES)] @pytest.mark.parametrize( "name", ["", "Tower/other", "Tower\n", "Töwer", "bad..name", "-tower", "tower-", "a" * 254], ) def test_docker_info_hostname_reader_rejects_malformed_or_non_ascii_names( monkeypatch: pytest.MonkeyPatch, name: str ): client = object.__new__(bootstrap.DockerSocketClient) monkeypatch.setattr( client, "_request", lambda *_args, **_kwargs: (200, json.dumps({"Name": name}).encode()), ) with pytest.raises(bootstrap.BootstrapError): client.daemon_name() def test_docker_info_hostname_reader_rejects_oversized_response(monkeypatch: pytest.MonkeyPatch): read_limits: list[int] = [] class Response: status = 200 def read(self, limit: int) -> bytes: read_limits.append(limit) return b"x" * limit class Connection: def __init__(self, _socket_path: str): pass def request(self, *_args: Any, **_kwargs: Any) -> None: pass def getresponse(self) -> Response: return Response() def close(self) -> None: pass monkeypatch.setattr(bootstrap, "_UnixHTTPConnection", Connection) with pytest.raises(bootstrap.BootstrapError, match="response_too_large"): bootstrap.DockerSocketClient().daemon_name() assert read_limits == [bootstrap.MAX_DOCKER_JSON_BYTES + 1] @pytest.mark.parametrize( ("daemon_name", "process_hostname", "caller_hostname"), [ ("Tower", "tower", "Tower"), ("OtherHost", "Tower", "Tower"), ("Tower", "Tower", "OtherHost"), ("bad/name", "bad/name", "bad/name"), ("Töwer", "Töwer", "Töwer"), ], ) def test_legacy_caller_rejects_hostname_source_disagreement_without_creating_child( daemon_name: str, process_hostname: str, caller_hostname: str ): docker = FakeDocker(_legacy_helper_spec()) docker.host_name = daemon_name docker.containers[bootstrap.LEGACY_HELPER_NAME]["Config"]["Hostname"] = caller_hostname git = FakeProtectedGit("") with pytest.raises(bootstrap.BootstrapError): _run_bootstrap(docker, git, hostname=process_hostname) assert bootstrap.LEGACY_HELPER_NAME in docker.inspected assert process_hostname not in docker.inspected assert docker.created == [] def test_legacy_caller_rejects_malformed_container_id_without_creating_child(): docker = FakeDocker(_legacy_helper_spec()) docker.containers[bootstrap.LEGACY_HELPER_NAME]["Id"] = "not-a-container-id" git = FakeProtectedGit("") with pytest.raises(bootstrap.BootstrapError): _run_bootstrap(docker, git) assert docker.created == [] def test_missing_fixed_legacy_name_does_not_fall_back_to_process_hostname(): docker = FakeDocker(_legacy_helper_spec()) docker.containers[HOST_HOSTNAME] = docker.containers.pop(bootstrap.LEGACY_HELPER_NAME) git = FakeProtectedGit("") with pytest.raises(bootstrap.BootstrapError): _run_bootstrap(docker, git, hostname=HOST_HOSTNAME) assert bootstrap.LEGACY_HELPER_NAME in docker.inspected assert HOST_HOSTNAME not in docker.inspected assert docker.created == [] @pytest.mark.parametrize( "mutation", [ "caller-image", "caller-label", "caller-revision", "caller-command", "caller-bind", "appops-image", "appops-provenance", "target-id", "target-source", "target-tree", "target-version", "old-target-id", "old-target-image", "old-target-version", "target-config-image", "target-compose-hash", "target-compose-missing", "target-compose-extra", "target-compose-image", "target-tech-label", "target-tech-extra", "target-path", "target-extra-mount", "target-bind-mode", "base-image-installed-provenance", ], ) def test_bootstrap_rejects_unknown_runtime_identity_without_creating_child(mutation: str): docker = FakeDocker(_legacy_helper_spec()) if mutation == "caller-image": docker.containers[CALLER_ID[:12]]["Image"] = "sha256:" + "9" * 64 elif mutation == "caller-label": docker.containers[CALLER_ID[:12]]["Config"]["Labels"]["itworx.controller.version"] = "2.1.4" elif mutation == "caller-revision": docker.containers[CALLER_ID[:12]]["Config"]["Labels"]["itworx.source.revision"] = "d" * 40 elif mutation == "caller-command": docker.containers[CALLER_ID[:12]]["Config"]["Cmd"].append("--image-id") elif mutation == "caller-bind": docker.containers[CALLER_ID[:12]]["HostConfig"]["Binds"].append("/tmp:/payload") elif mutation == "appops-image": docker.containers[APPOPS_CONTAINER_ID]["Image"] = "sha256:" + "8" * 64 elif mutation == "appops-provenance": docker.images[APPOPS_IMAGE_ID]["Config"]["Labels"]["org.opencontainers.image.revision"] = "f" * 40 elif mutation == "target-id": docker.containers["gitea-deploy-control"]["Id"] = "0" * 64 elif mutation == "old-target-id": docker.containers["gitea-deploy-control"]["Id"] = "5539b672e66890e657d0f3b0e6e94f9c9ca5b47c245e120d330f6f1d5be73fae" elif mutation == "old-target-image": docker.containers["gitea-deploy-control"]["Image"] = "sha256:adbb51b2873bf05546019c341caf6fd4368159546c36a5aea81b4d8bf088804e" elif mutation == "target-source": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["org.opencontainers.image.revision"] = "f" * 40 elif mutation == "target-tree": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["org.opencontainers.image.source-tree"] = "f" * 40 elif mutation == "target-version": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["org.opencontainers.image.version"] = "2.1.11" elif mutation == "old-target-version": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["org.opencontainers.image.version"] = "v26.09.29" elif mutation == "target-config-image": docker.containers["gitea-deploy-control"]["Config"]["Image"] = "docker.gitea.com/runner-images:ubuntu-22.04" elif mutation == "target-compose-hash": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["com.docker.compose.config-hash"] = "0" * 64 elif mutation == "target-compose-missing": docker.containers["gitea-deploy-control"]["Config"]["Labels"].pop("com.docker.compose.service") elif mutation == "target-compose-extra": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["com.docker.compose.container_name"] = "gitea-deploy-control" elif mutation == "target-compose-image": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["com.docker.compose.image"] = CONTROLLER_IMAGE_ID elif mutation == "target-tech-label": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["tech.itworx.role"] = "other" elif mutation == "target-tech-extra": docker.containers["gitea-deploy-control"]["Config"]["Labels"]["tech.itworx.extra"] = "unexpected" elif mutation == "target-path": docker.containers["gitea-deploy-control"]["Mounts"][3]["Destination"] = "/controller-config" elif mutation == "target-extra-mount": docker.containers["gitea-deploy-control"]["Mounts"].append( { "Type": "bind", "Source": "/tmp", "Destination": "/tmp", "RW": True, "Propagation": "rprivate", } ) docker.containers["gitea-deploy-control"]["HostConfig"]["Binds"].append( "/tmp:/tmp:rw" ) elif mutation == "target-bind-mode": docker.containers["gitea-deploy-control"]["HostConfig"]["Binds"][6] = ( "/mnt/cache/appdata/gitea-deploy-runners/controller:/opt/gitea-deploy:rw" ) docker.containers["gitea-deploy-control"]["Mounts"][3]["RW"] = True elif mutation == "base-image-installed-provenance": labels = docker.images[CONTROLLER_IMAGE_ID]["Config"]["Labels"] labels["org.opencontainers.image.revision"] = CONTROLLER_REVISION labels["org.opencontainers.image.source-tree"] = CONTROLLER_TREE git = FakeProtectedGit("") with pytest.raises(bootstrap.BootstrapError): _run_bootstrap(docker, git) assert docker.created == [] @pytest.mark.parametrize( "mutation", ["revision", "tree", "version", "artifact-sha", "delivery-health"], ) def test_bootstrap_requires_exact_mounted_controller_artifact_health(mutation: str): initial = _health( revision=CONTROLLER_REVISION, tree=CONTROLLER_TREE, version="2.1.12", artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256, ) artifact = initial["artifact"] if mutation == "revision": artifact["sourceRevision"] = "9" * 40 elif mutation == "tree": artifact["sourceTree"] = "9" * 40 elif mutation == "version": artifact["controllerVersion"] = "2.1.11" elif mutation == "artifact-sha": artifact["artifactSha256"] = "9" * 64 elif mutation == "delivery-health": initial["delivery"]["health"]["artifact"] = "degraded" docker = FakeDocker(_legacy_helper_spec()) git = FakeProtectedGit("") with pytest.raises(bootstrap.BootstrapError): _run_bootstrap(docker, git, initial_health=initial) assert docker.created == [] def test_running_modern_child_returns_unknown_without_duplicate_or_stop(): helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text( encoding="utf-8" ) helper_spec = bootstrap.render_modern_helper_spec( helper_source, revision=REVISION, image_id=CONTROLLER_IMAGE_ID, current_controller_id=CONTROLLER_CONTAINER_ID, current_controller_revision=CONTROLLER_REVISION, current_controller_tree=CONTROLLER_TREE, current_controller_version="2.1.12", ) existing = _inspect_from_spec( "itworx-controller-artifact-promoter-modern", MODERN_HELPER_ID, CONTROLLER_IMAGE_ID, helper_spec, state={"Running": True, "Status": "running"}, ) docker = FakeDocker(_legacy_helper_spec(), existing_child=existing) git = FakeProtectedGit(helper_source) result = _run_bootstrap(docker, git, health=_health()) assert result["status"] == "outcome_unknown" assert result["reason"] == "controller_promoter_modern_helper_running" assert docker.created == [] assert docker.started == [] def test_exact_failed_historical_child_is_removed_before_current_helper_launch(): helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text( encoding="utf-8" ) legacy_health = _health( revision=CONTROLLER_REVISION, tree=CONTROLLER_TREE, version="2.1.12", artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256, ) failed_child = _retired_failed_child(helper_source) docker = FakeDocker(_legacy_helper_spec(), existing_child=failed_child) git = FakeProtectedGit(helper_source) result = _run_bootstrap( docker, git, health_sequence=[legacy_health, legacy_health, legacy_health, _health()], ) assert result["status"] == "promoted" assert docker.removed == [RETIRED_HELPER_ID] assert docker.created and docker.started == [MODERN_HELPER_ID] assert RETIRED_HELPER_ID in docker.inspected assert docker.appops_inspect_count == 2 assert git.blobs_read.count( (bootstrap.RETIRED_FAILED_MODERN_REVISION, bootstrap.MODERN_HELPER_SOURCE_PATH) ) == 1 @pytest.mark.parametrize( "mutation", [ "running", "successful-exit", "boolean-exit", "other-exit", "wrong-id", "config-volumes", "extra-host-mount", "cap-add", "device-request", "docker-root-writable", "socket-bind-changed", "legacy-controller-stopped", "current-main-changed", "second-name-inspect-changed", "second-id-inspect-changed", "appops-stopped-before-delete", "historical-tree-proof-failed", ], ) def test_retired_helper_cleanup_fails_closed_on_any_identity_or_state_drift(mutation: str): helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text( encoding="utf-8" ) failed_child = _retired_failed_child(helper_source) docker = FakeDocker(_legacy_helper_spec(), existing_child=failed_child) git = FakeProtectedGit(helper_source) if mutation == "running": failed_child["State"].update({"Running": True, "Status": "running"}) elif mutation == "successful-exit": failed_child["State"]["ExitCode"] = 0 elif mutation == "boolean-exit": failed_child["State"]["ExitCode"] = True elif mutation == "other-exit": failed_child["State"]["ExitCode"] = 2 elif mutation == "wrong-id": failed_child["Id"] = "9" * 64 elif mutation == "config-volumes": failed_child["Config"]["Volumes"] = {"/extra": {}} elif mutation == "extra-host-mount": failed_child["HostConfig"]["Mounts"] = [{"Type": "volume"}] elif mutation == "cap-add": failed_child["HostConfig"]["CapAdd"] = ["SYS_ADMIN"] elif mutation == "device-request": failed_child["HostConfig"]["DeviceRequests"] = [{"Capabilities": [["gpu"]]}] elif mutation == "docker-root-writable": failed_child["HostConfig"]["Binds"][2] = "/var/lib/docker:/var/lib/docker:rw" failed_child["Mounts"][2]["RW"] = True elif mutation == "socket-bind-changed": failed_child["HostConfig"]["Binds"][4] = "/tmp/docker.sock:/var/run/docker.sock" failed_child["Mounts"][4]["Source"] = "/tmp/docker.sock" elif mutation == "legacy-controller-stopped": docker.containers["gitea-deploy-control"]["State"]["Running"] = False elif mutation == "current-main-changed": git.fail_current_main_at = 2 elif mutation == "second-name-inspect-changed": docker.named_inspect_override = json.loads(json.dumps(failed_child)) docker.named_inspect_override["Config"]["Cmd"].append("unexpected") docker.named_inspect_override_at = 2 elif mutation == "second-id-inspect-changed": docker.retired_id_inspect_override = json.loads(json.dumps(failed_child)) docker.retired_id_inspect_override["State"]["ExitCode"] = 0 elif mutation == "appops-stopped-before-delete": docker.appops_inspect_override = json.loads( json.dumps(docker.containers[APPOPS_CONTAINER_ID]) ) docker.appops_inspect_override["State"]["Running"] = False docker.appops_inspect_override_at = 2 elif mutation == "historical-tree-proof-failed": git.retired_tree_valid = False legacy_health = _health( revision=CONTROLLER_REVISION, tree=CONTROLLER_TREE, version="2.1.12", artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256, ) with pytest.raises(bootstrap.BootstrapError): _run_bootstrap( docker, git, health_sequence=[legacy_health, legacy_health, legacy_health], ) assert docker.removed == [] assert docker.created == [] assert docker.started == [] def test_retired_helper_remove_uses_non_force_non_volume_docker_delete(): client = bootstrap.DockerSocketClient() calls: list[tuple[str, str]] = [] def request(method: str, path: str, **_kwargs: Any) -> tuple[int, bytes]: calls.append((method, path)) return 204, b"" client._request = request # type: ignore[method-assign] client.remove_retired_failed_container(RETIRED_HELPER_ID) assert calls == [ ("DELETE", f"/containers/{RETIRED_HELPER_ID}?force=false&v=false") ] @pytest.mark.parametrize("status", [404, 409]) def test_retired_helper_delete_refuses_unexpected_docker_status(status: int): client = bootstrap.DockerSocketClient() def request(_method: str, _path: str, **_kwargs: Any) -> tuple[int, bytes]: return status, b"" client._request = request # type: ignore[method-assign] with pytest.raises(bootstrap.BootstrapError): client.remove_retired_failed_container(RETIRED_HELPER_ID) def test_same_failed_source_revision_is_never_retried_or_removed(): helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text( encoding="utf-8" ) docker = FakeDocker( _legacy_helper_spec(bootstrap.RETIRED_FAILED_MODERN_REVISION), existing_child=_retired_failed_child(helper_source), ) git = FakeProtectedGit(helper_source) git.revision = bootstrap.RETIRED_FAILED_MODERN_REVISION git.tree = bootstrap.RETIRED_FAILED_MODERN_TREE legacy_health = _health( revision=CONTROLLER_REVISION, tree=CONTROLLER_TREE, version="2.1.12", artifact_sha256=bootstrap.LEGACY_CONTROLLER_ARTIFACT_SHA256, ) result = _run_bootstrap( docker, git, health_sequence=[legacy_health], source_revision=git.revision, source_tree=git.tree, ) assert result["status"] == "failed" assert result["exitCode"] == 1 assert docker.removed == [] assert docker.created == [] def test_completed_modern_child_with_wrong_artifact_is_not_relaunched(): helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text( encoding="utf-8" ) helper_spec = bootstrap.render_modern_helper_spec( helper_source, revision=REVISION, image_id=CONTROLLER_IMAGE_ID, current_controller_id=CONTROLLER_CONTAINER_ID, current_controller_revision=CONTROLLER_REVISION, current_controller_tree=CONTROLLER_TREE, current_controller_version="2.1.12", ) existing = _inspect_from_spec( "itworx-controller-artifact-promoter-modern", MODERN_HELPER_ID, CONTROLLER_IMAGE_ID, helper_spec, state={"Running": False, "ExitCode": 0, "Status": "exited"}, ) docker = FakeDocker(_legacy_helper_spec(), existing_child=existing) git = FakeProtectedGit(helper_source) result = _run_bootstrap( docker, git, initial_health=_health(tree="9" * 40), ) assert result["status"] == "outcome_unknown" assert result["reason"] == "controller_promoter_postcheck_failed" assert docker.created == [] def test_completed_child_uses_new_health_after_controller_recreation(): helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text( encoding="utf-8" ) helper_spec = bootstrap.render_modern_helper_spec( helper_source, revision=REVISION, image_id=CONTROLLER_IMAGE_ID, current_controller_id=CONTROLLER_CONTAINER_ID, current_controller_revision=CONTROLLER_REVISION, current_controller_tree=CONTROLLER_TREE, current_controller_version="2.1.12", ) existing = _inspect_from_spec( "itworx-controller-artifact-promoter-modern", MODERN_HELPER_ID, CONTROLLER_IMAGE_ID, helper_spec, state={"Running": False, "ExitCode": 0, "Status": "exited"}, ) docker = FakeDocker(_legacy_helper_spec(), existing_child=existing) docker.containers["gitea-deploy-control"] = _modern_controller_inspect(REVISION) git = FakeProtectedGit(helper_source) result = _run_bootstrap( docker, git, initial_health=_health(), ) assert result["status"] == "already_applied" assert result["revision"] == REVISION assert docker.created == [] assert docker.started == [] @pytest.mark.parametrize( "mutation", [ "post-image-config", "post-compose-hash", "post-compose-image", "post-compose-extra", "post-compose-environment-file", "post-tech-label", "post-tech-extra", "post-artifact-revision", "post-volume-name", "post-volume-source", "post-volume-mode", "post-config-bind-source", "post-config-bind-mode", "post-extra-mount", ], ) def test_postactivation_controller_requires_exact_compose_runtime(mutation: str): docker = FakeDocker(_legacy_helper_spec()) controller = _modern_controller_inspect(REVISION) docker.containers["gitea-deploy-control"] = controller if mutation == "post-image-config": controller["Config"]["Image"] = CONTROLLER_IMAGE_REF elif mutation == "post-compose-hash": controller["Config"]["Labels"]["com.docker.compose.config-hash"] = "bad" elif mutation == "post-compose-image": controller["Config"]["Labels"]["com.docker.compose.image"] = "sha256:" + "d" * 64 elif mutation == "post-compose-extra": controller["Config"]["Labels"]["com.docker.compose.extra"] = "unexpected" elif mutation == "post-compose-environment-file": controller["Config"]["Labels"]["com.docker.compose.project.environment_file"] = "/tmp/.env" elif mutation == "post-tech-label": controller["Config"]["Labels"]["tech.itworx.gitea.scope"] = "Other" elif mutation == "post-tech-extra": controller["Config"]["Labels"]["tech.itworx.extra"] = "unexpected" elif mutation == "post-artifact-revision": controller["Mounts"][3]["Source"] = ( "/var/lib/docker/volumes/itworx-controller-hub-releases/_data/" f"controller-artifacts/{'d' * 40}" ) elif mutation == "post-volume-name": controller["Mounts"][8]["Name"] = "foreign-volume" elif mutation == "post-volume-source": controller["Mounts"][8]["Source"] = "/tmp/foreign-volume" elif mutation == "post-volume-mode": controller["Mounts"][8]["RW"] = False elif mutation == "post-config-bind-source": controller["Mounts"][9]["Source"] = "/tmp/config" elif mutation == "post-config-bind-mode": controller["Mounts"][9]["RW"] = True elif mutation == "post-extra-mount": controller["Mounts"].append(dict(controller["Mounts"][0])) assert not bootstrap._verified_live_controller(docker, source_revision=REVISION) def test_postactivation_accepts_well_formed_compose_hash(): docker = FakeDocker(_legacy_helper_spec()) controller = _modern_controller_inspect(REVISION) controller["Config"]["Labels"]["com.docker.compose.config-hash"] = "b" * 64 docker.containers["gitea-deploy-control"] = controller assert bootstrap._verified_live_controller(docker, source_revision=REVISION) def test_successful_child_requires_exact_source_tree_version_and_health(): docker = FakeDocker(_legacy_helper_spec()) git = FakeProtectedGit("") result = _run_bootstrap(docker, git) assert result["status"] == "promoted" assert result["revision"] == REVISION assert result["controllerVersion"] == "2.1.14" assert result["artifactSha256"] == "1" * 64 assert docker.started == [MODERN_HELPER_ID] assert docker.created[0]["Image"] == CONTROLLER_IMAGE_ID assert docker.created[0]["HostConfig"]["ReadonlyRootfs"] is True def test_child_logs_are_bounded_and_redacted_before_result_output(): docker = FakeDocker(_legacy_helper_spec()) docker.log_bytes = ( b"controller_v2_ready revision=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" b"GITEA_ADMIN_TOKEN=very-secret-value\n" ) git = FakeProtectedGit("") result = _run_bootstrap(docker, git) assert result["status"] == "promoted" assert "very-secret-value" not in result["logTail"] assert "[redacted]" in result["logTail"] assert len(result["logTail"].encode("utf-8")) <= 4096 @pytest.mark.parametrize( "temporary_root_info,accepted", [ ("1777:0:0", True), ("700:0:0", True), ("777:0:0", False), ("755:0:0", False), ("770:0:0", False), ("1700:0:0", False), ("1777:1000:0", False), ("700:0:1000", False), ], ) def test_legacy_upgrade_signature_fetches_bootstrap_only_from_protected_git( tmp_path: Path, temporary_root_info: str, accepted: bool ): bash = shutil.which("bash") if bash is None: pytest.skip("bash is required to exercise the upgrade script") bin_dir = tmp_path / "bin" bin_dir.mkdir() git_trace = tmp_path / "git-args.jsonl" python_trace = tmp_path / "python-args.json" protected_source = "# trusted protected Git bootstrap source\n" git_shim = bin_dir / "git" git_shim.write_text( f"#!{sys.executable}\n" "import json, os, sys\n" f"trace = {str(git_trace)!r}\n" f"with open(trace, 'a', encoding='utf-8') as stream: stream.write(json.dumps(sys.argv[1:]) + '\\n')\n" "args = [arg for arg in sys.argv[1:] if not arg.startswith('--git-dir=')]\n" f"revision = {REVISION!r}\n" f"tree = {TREE!r}\n" "if args[-3:] == ['rev-parse', 'refs/heads/main', '--'] or args[-2:] == ['rev-parse', 'refs/heads/main']: print(revision)\n" "elif any(arg.endswith('^{tree}') for arg in args): print(tree)\n" f"elif len(args) >= 3 and args[:2] == ['cat-file', '-s']: print({len(protected_source)})\n" "elif len(args) >= 3 and args[0] == 'cat-file' and args[1] == 'blob' and args[-1].endswith(':scripts/bootstrap_controller_artifact_promoter.py'):\n" f" sys.stdout.write({protected_source!r})\n" "else: raise SystemExit(91)\n", encoding="utf-8", ) stat_shim = bin_dir / "stat" stat_shim.write_text( f"#!{sys.executable}\n" "import pathlib, sys\n" "args = sys.argv[1:]\n" "target = args[-1]\n" f"if target == '/tmp': print({temporary_root_info!r})\n" "elif args[1] == '%u:%a': print('0:700')\n" "elif args[1] == '%u:%a:%s': print('0:600:' + str(pathlib.Path(target).stat().st_size))\n" "else: raise SystemExit(1)\n", encoding="utf-8", ) python_shim = bin_dir / "python3" python_shim.write_text( f"#!{sys.executable}\n" "import json, pathlib, sys\n" f"path = pathlib.Path({str(python_trace)!r})\n" "path.write_text(json.dumps({'script': pathlib.Path(sys.argv[1]).read_text(), 'args': sys.argv[2:]}), encoding='utf-8')\n", encoding="utf-8", ) git_shim.chmod(0o755) stat_shim.chmod(0o755) python_shim.chmod(0o755) archive = f"/mnt/cache/appdata/gitea-deploy-runners/.controller-promoter-{REVISION[:12]}/controller.tar" result = subprocess.run( [ bash, str(ROOT / "scripts/upgrade_deployment_controller.sh"), "--archive", archive, "--archive-sha256", "a" * 64, "--source-repository", "jens/unraid-appops-gateway", "--source-revision", REVISION, "--source-tree", TREE, "--controller-version", "2.1.5", "--confirm", "UPGRADE_DEPLOYMENT_CONTROLLER_V2", ], cwd=ROOT, env={**os.environ, "PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}"}, capture_output=True, text=True, check=False, ) if not accepted: assert result.returncode == 66, result.stderr assert "temporary root is unsafe" in result.stderr assert not python_trace.exists() calls = [json.loads(line) for line in git_trace.read_text(encoding="utf-8").splitlines()] assert not any("cat-file" in args for args in calls) return assert result.returncode == 0, result.stderr bootstrap_call = json.loads(python_trace.read_text(encoding="utf-8")) assert bootstrap_call["script"] == protected_source assert bootstrap_call["args"] == [ "--source-repository", "jens/unraid-appops-gateway", "--source-revision", REVISION, "--source-tree", TREE, ] calls = [json.loads(line) for line in git_trace.read_text(encoding="utf-8").splitlines()] assert any(args[-1:] == [f"{REVISION}:scripts/bootstrap_controller_artifact_promoter.py"] for args in calls) assert all(archive not in args for args in calls) @pytest.mark.parametrize( "mutation", ["source", "revision", "tree", "version", "extra", "image-id"], ) def test_upgrade_script_rejects_mutated_legacy_call_without_bootstrap( tmp_path: Path, mutation: str ): bash = shutil.which("bash") if bash is None: pytest.skip("bash is required to exercise the upgrade script") bin_dir = tmp_path / "bin" bin_dir.mkdir() marker = tmp_path / "bootstrap-ran" python_shim = bin_dir / "python3" python_shim.write_text( f"#!/usr/bin/env python3\nfrom pathlib import Path\nPath({str(marker)!r}).write_text('ran')\n", encoding="utf-8", ) python_shim.chmod(0o755) args = [ "--archive", f"/mnt/cache/appdata/gitea-deploy-runners/.controller-promoter-{REVISION[:12]}/controller.tar", "--archive-sha256", "a" * 64, "--source-repository", "jens/unraid-appops-gateway", "--source-revision", REVISION, "--source-tree", TREE, "--controller-version", "2.1.5", "--confirm", "UPGRADE_DEPLOYMENT_CONTROLLER_V2", ] if mutation == "source": args[args.index("jens/unraid-appops-gateway")] = "attacker/repo" elif mutation == "revision": args[args.index(REVISION)] = "A" * 40 elif mutation == "tree": args[args.index(TREE)] = "f" * 40 elif mutation == "version": args[args.index("2.1.5")] = "2.1.4" elif mutation == "extra": args.extend(["--unexpected", "value"]) elif mutation == "image-id": args.extend(["--image-id", CONTROLLER_IMAGE_ID]) result = subprocess.run( [bash, str(ROOT / "scripts/upgrade_deployment_controller.sh"), *args], cwd=ROOT, env={**os.environ, "PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}"}, capture_output=True, text=True, check=False, ) assert result.returncode != 0 assert not marker.exists() @pytest.mark.parametrize("modern_call", ["missing-image", "shared-archive"]) @pytest.mark.parametrize("temporary_root_info", ["1777:0:0", "700:0:0", "777:0:0"]) def test_modern_upgrade_path_still_requires_private_archive_and_image_id( tmp_path: Path, modern_call: str, temporary_root_info: str ): bash = shutil.which("bash") if bash is None: pytest.skip("bash is required to exercise the upgrade script") bin_dir = tmp_path / "bin" bin_dir.mkdir() stat_shim = bin_dir / "stat" stat_shim.write_text( f"#!{sys.executable}\nimport sys\n" f"if sys.argv[-1] == '/tmp': print({temporary_root_info!r})\n" "else: raise SystemExit(91)\n", encoding="utf-8", ) stat_shim.chmod(0o755) archive = ( f"/tmp/itworx-controller-artifact-promoter-{REVISION[:12]}.AAAAAA/controller.tar" if modern_call == "missing-image" else f"/mnt/cache/appdata/gitea-deploy-runners/.controller-promoter-{REVISION[:12]}/controller.tar" ) args = [ "--archive", archive, "--archive-sha256", "a" * 64, "--source-repository", "jens/unraid-appops-gateway", "--source-revision", REVISION, "--source-tree", TREE, "--controller-version", "2.1.14", "--confirm", "UPGRADE_DEPLOYMENT_CONTROLLER_V2", ] if modern_call == "shared-archive": args.extend(["--image-id", CONTROLLER_IMAGE_ID]) result = subprocess.run( [bash, str(ROOT / "scripts/upgrade_deployment_controller.sh"), *args], cwd=ROOT, env={**os.environ, "PATH": f"{bin_dir}{os.pathsep}{os.environ['PATH']}"}, capture_output=True, text=True, check=False, ) assert result.returncode != 0 if modern_call == "shared-archive": if temporary_root_info == "777:0:0": assert "helper temporary root is unsafe" in result.stderr else: assert "archive is outside the helper private temporary directory" in result.stderr if modern_call == "missing-image": assert "image ID" in result.stderr