95 lines
3.2 KiB
Python
95 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
IN_PLACE = ROOT / "scripts" / "upgrade_gitea_runner_3_5.sh"
|
|
FLEET_BOOTSTRAP = ROOT / "scripts" / "upgrade_ci_runner_fleet.sh"
|
|
|
|
OLD_IMAGE = (
|
|
"gitea/runner@sha256:"
|
|
"3ebc3ad9654552377896e1edbc77ac420025bb01ff1736bc43232d0bf2866f5a"
|
|
)
|
|
NEW_IMAGE = (
|
|
"gitea/runner:3.5.0@sha256:"
|
|
"66b7da94dc7dcadb2e076bec6928221336a9a637196399281c4b766fe1288242"
|
|
)
|
|
|
|
|
|
def test_runner_3_5_upgrade_script_is_syntactically_valid() -> None:
|
|
for script in (IN_PLACE, FLEET_BOOTSTRAP):
|
|
subprocess.run(
|
|
["bash", "-n"],
|
|
input=script.read_bytes().replace(b"\r\n", b"\n"),
|
|
check=True,
|
|
)
|
|
|
|
|
|
def test_future_fleet_bootstrap_uses_reviewed_runner_3_5_pin() -> None:
|
|
text = FLEET_BOOTSTRAP.read_text(encoding="utf-8")
|
|
assert NEW_IMAGE in text
|
|
assert OLD_IMAGE not in text
|
|
assert "gitea/runner:latest" not in text
|
|
assert "# v3.5.0" in text
|
|
|
|
|
|
def test_in_place_upgrade_is_fixed_target_and_fail_closed() -> None:
|
|
text = IN_PLACE.read_text(encoding="utf-8")
|
|
|
|
assert 'readonly PRIMARY="itworx-security-runner"' in text
|
|
assert 'readonly SECONDARY="itworx-validation-runner-2"' in text
|
|
assert 'readonly DIND="gitea-runner-dind"' in text
|
|
assert OLD_IMAGE in text
|
|
assert NEW_IMAGE in text
|
|
assert 'readonly CONFIRMATION="UPGRADE_GITEA_RUNNER_3_5"' in text
|
|
|
|
assert 'active DinD workload prevents runner upgrade' in text
|
|
assert 'grep -F -c "$OLD_IMAGE"' in text
|
|
assert 'runner image baseline is not the reviewed 2.0.0 pin' in text
|
|
assert 'docker pull "$NEW_IMAGE"' in text
|
|
assert 'gitea-runner "$NEW_IMAGE" --version' in text
|
|
|
|
assert 'set_runner_disabled "$PRIMARY" true' in text
|
|
assert 'set_runner_disabled "$SECONDARY" true' in text
|
|
assert 'set_runner_disabled "$PRIMARY" false' in text
|
|
assert 'set_runner_disabled "$SECONDARY" false' in text
|
|
|
|
assert (
|
|
'up -d --no-deps --force-recreate "$PRIMARY_SERVICE" "$SECONDARY_SERVICE"'
|
|
in text
|
|
)
|
|
assert 'docker compose --project-directory "$ROOT"' in text
|
|
assert 'cp -a -- "$backup/docker-compose.yml" "$COMPOSE_FILE"' in text
|
|
|
|
# The maintenance path may inspect DinD for active CI work, but must never
|
|
# recreate or prune the daemon, Gitea, unrelated networks, or host storage.
|
|
forbidden = (
|
|
'force-recreate "$DIND"',
|
|
'force-recreate "$GITEA_CONTAINER"',
|
|
"docker system prune",
|
|
"docker network prune",
|
|
"docker volume prune",
|
|
'docker rm --force "$DIND"',
|
|
'docker rm --force "$GITEA_CONTAINER"',
|
|
)
|
|
for token in forbidden:
|
|
assert token not in text
|
|
|
|
|
|
def test_runner_3_5_upgrade_preserves_separate_runner_credentials() -> None:
|
|
text = IN_PLACE.read_text(encoding="utf-8")
|
|
assert (
|
|
'PRIMARY_RUNNER_FILE="/mnt/cache/appdata/itworx-security-runner/.runner"'
|
|
in text
|
|
)
|
|
assert (
|
|
'SECONDARY_RUNNER_FILE="/mnt/cache/appdata/itworx-validation-runner-2/.runner"'
|
|
in text
|
|
)
|
|
assert (
|
|
'readlink -f "$PRIMARY_RUNNER_FILE"'
|
|
in text
|
|
and 'readlink -f "$SECONDARY_RUNNER_FILE"' in text
|
|
)
|