Publish curated Unraid-AppOps-Gateway source from ba66029223865607deddc234e283c11127324eb8
This commit is contained in:
1 parent
820244dbb3
commit
8dd4b40252
52 files changed
+11814
-288
No files matched your search
@@ -1,12 +1,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -24,6 +27,10 @@ CONTROLLER_IMAGE_ID = "sha256:c474d435a4f55ac5205570da0caa47e103f89a6c2ef96ea458
|
||||
CONTROLLER_IMAGE_REF = "docker.gitea.com/runner-images:ubuntu-latest"
|
||||
CONTROLLER_REVISION = "034c9f844d2455850ba37b31379b7edaa786583e"
|
||||
CONTROLLER_TREE = "6b8e92942e6e5eca8a9aa9da0a1509b2019d7b7b"
|
||||
MODERN_CONTROLLER_CONTAINER_ID = "9093c8e29d9064f9948918e5884f9adc5db6b740991e2934902f93c6ea813e01"
|
||||
MODERN_CONTROLLER_REVISION = "e854334923cca574482d22b8424ba3ce46ec6aa1"
|
||||
MODERN_CONTROLLER_TREE = "cf2a117f9222b952deb9902c646116612f3a7ea0"
|
||||
MODERN_CONTROLLER_ARTIFACT_SHA256 = "e88739bf091c8bac77ea0db5b97300666a1baa8ba984bbf6ae91e3bf25719e08"
|
||||
REVISION = "a" * 40
|
||||
TREE = "b" * 40
|
||||
CALLER_ID = "c" * 64
|
||||
@@ -247,15 +254,39 @@ def _modern_controller_mounts(source_revision: str) -> list[dict[str, Any]]:
|
||||
]
|
||||
|
||||
|
||||
def _modern_controller_inspect(source_revision: str) -> dict[str, Any]:
|
||||
def _modern_controller_source_mounts(source_revision: str) -> list[dict[str, Any]]:
|
||||
mounts = [dict(mount) for mount in _modern_controller_mounts(source_revision)]
|
||||
mounts = [mount for mount in mounts if mount["Destination"] != "/forge-master-key-backup"]
|
||||
for mount in mounts:
|
||||
if mount["Destination"] in {"/opt/gitea-deploy", "/controller-config"}:
|
||||
mount["Propagation"] = "rslave"
|
||||
return mounts
|
||||
|
||||
|
||||
def _modern_controller_inspect(
|
||||
source_revision: str,
|
||||
*,
|
||||
identifier: str = MODERN_CONTROLLER_CONTAINER_ID,
|
||||
) -> dict[str, Any]:
|
||||
controller = _controller_inspect()
|
||||
controller.update({"Id": "e" * 64, "Image": CONTROLLER_IMAGE_ID})
|
||||
controller.update({"Id": identifier, "Image": CONTROLLER_IMAGE_ID})
|
||||
controller["State"].update({"Dead": False, "Paused": False, "Restarting": False})
|
||||
controller["Config"]["Image"] = CONTROLLER_IMAGE_ID
|
||||
controller["Config"]["Labels"] = _modern_controller_labels()
|
||||
controller["Mounts"] = _modern_controller_mounts(source_revision)
|
||||
return controller
|
||||
|
||||
|
||||
def _modern_controller_source_inspect(
|
||||
source_revision: str,
|
||||
*,
|
||||
identifier: str = MODERN_CONTROLLER_CONTAINER_ID,
|
||||
) -> dict[str, Any]:
|
||||
controller = _modern_controller_inspect(source_revision, identifier=identifier)
|
||||
controller["Mounts"] = _modern_controller_source_mounts(source_revision)
|
||||
return controller
|
||||
|
||||
|
||||
def _appops_inspect() -> dict[str, Any]:
|
||||
labels = _appops_labels()
|
||||
return {
|
||||
@@ -480,7 +511,9 @@ class FakeDocker:
|
||||
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)
|
||||
self.containers["gitea-deploy-control"] = _modern_controller_inspect(
|
||||
source_revision, identifier="e" * 64
|
||||
)
|
||||
|
||||
def remove_retired_failed_container(self, identifier: str) -> None:
|
||||
assert identifier == RETIRED_HELPER_ID
|
||||
@@ -505,6 +538,8 @@ class FakeProtectedGit:
|
||||
self.main_checks = 0
|
||||
self.fail_current_main_at: int | None = None
|
||||
self.retired_tree_valid = True
|
||||
self.controller_release_checks: list[bootstrap.ControllerBaseline] = []
|
||||
self.invalid_controller_release: bootstrap.ControllerBaseline | None = None
|
||||
|
||||
def verify_current_main(self, revision: str, tree: str) -> None:
|
||||
self.main_checks += 1
|
||||
@@ -517,6 +552,11 @@ class FakeProtectedGit:
|
||||
if (revision, tree) != (LEGACY_REVISION, LEGACY_TREE):
|
||||
raise bootstrap.BootstrapError("controller_promoter_legacy_provenance_invalid")
|
||||
|
||||
def verify_controller_release(self, baseline: bootstrap.ControllerBaseline) -> None:
|
||||
self.controller_release_checks.append(baseline)
|
||||
if baseline not in bootstrap.CONTROLLER_BASELINES or baseline == self.invalid_controller_release:
|
||||
raise bootstrap.BootstrapError("controller_promoter_controller_provenance_invalid")
|
||||
|
||||
def verify_retired_modern_revision(self, revision: str, tree: str) -> None:
|
||||
if (revision, tree) != (
|
||||
bootstrap.RETIRED_FAILED_MODERN_REVISION,
|
||||
@@ -570,6 +610,15 @@ def _health(
|
||||
}
|
||||
|
||||
|
||||
def _modern_baseline_health() -> dict[str, Any]:
|
||||
return _health(
|
||||
revision=MODERN_CONTROLLER_REVISION,
|
||||
tree=MODERN_CONTROLLER_TREE,
|
||||
version="2.1.14",
|
||||
artifact_sha256=MODERN_CONTROLLER_ARTIFACT_SHA256,
|
||||
)
|
||||
|
||||
|
||||
def _run_bootstrap(
|
||||
docker: FakeDocker,
|
||||
git: FakeProtectedGit,
|
||||
@@ -610,6 +659,176 @@ def _run_bootstrap(
|
||||
)
|
||||
|
||||
|
||||
def test_protected_git_reproduces_pinned_release_with_exact_handoff_crlf_conversion(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
):
|
||||
git_executable = shutil.which("git")
|
||||
if git_executable is None:
|
||||
pytest.skip("git is required to validate the pinned source archives")
|
||||
|
||||
probe = subprocess.run(
|
||||
[git_executable, "cat-file", "-e", f"{CONTROLLER_REVISION}^{{commit}}"],
|
||||
cwd=ROOT,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
timeout=15,
|
||||
check=False,
|
||||
)
|
||||
if probe.returncode != 0:
|
||||
pytest.skip("the checkout does not expose the retained historical commits to Git")
|
||||
|
||||
def local_git_output(arguments: list[str], *, limit: int = bootstrap.MAX_GIT_BLOB_BYTES) -> bytes:
|
||||
result = subprocess.run(
|
||||
[git_executable, "-c", f"safe.directory={ROOT}", *arguments],
|
||||
cwd=ROOT,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
timeout=15,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise bootstrap.BootstrapError("controller_promoter_protected_git_invalid")
|
||||
if len(result.stdout) > limit:
|
||||
raise bootstrap.BootstrapError("controller_promoter_protected_git_invalid")
|
||||
return result.stdout
|
||||
|
||||
monkeypatch.setattr(bootstrap, "_git_output", local_git_output)
|
||||
repository = bootstrap.ProtectedGitRepository()
|
||||
for baseline in (
|
||||
bootstrap.LEGACY_CONTROLLER_BASELINE,
|
||||
bootstrap.MODERN_CONTROLLER_BASELINE,
|
||||
):
|
||||
repository.verify_controller_release(baseline)
|
||||
with pytest.raises(
|
||||
bootstrap.BootstrapError,
|
||||
match="controller_promoter_controller_provenance_invalid",
|
||||
):
|
||||
repository.verify_controller_release(
|
||||
replace(baseline, tree="f" * 40)
|
||||
)
|
||||
archive_paths = [
|
||||
"scripts/run_repopulse_adoption.py"
|
||||
if name == "run_repopulse_adoption.py"
|
||||
else f"ops/gitea-deploy-broker/{name}"
|
||||
for name in baseline.artifact_files
|
||||
]
|
||||
archive = local_git_output(
|
||||
["archive", "--format=tar", baseline.revision, "--", *archive_paths],
|
||||
limit=bootstrap.MAX_CONTROLLER_ARTIFACT_ARCHIVE_BYTES,
|
||||
)
|
||||
assert bootstrap._controller_artifact_archive_digest(
|
||||
archive, baseline
|
||||
) == baseline.artifact_sha256
|
||||
|
||||
with pytest.raises(
|
||||
bootstrap.BootstrapError,
|
||||
match="controller_promoter_controller_provenance_invalid",
|
||||
):
|
||||
bootstrap._controller_artifact_source_bytes(
|
||||
replace(baseline, revision="f" * 40),
|
||||
bootstrap.CONTROLLER_CRLF_HANDOFF,
|
||||
b"source",
|
||||
)
|
||||
|
||||
corrupted_archive = _rewrite_controller_archive(
|
||||
archive,
|
||||
mutate_path="ops/gitea-deploy-broker/deploy.py",
|
||||
mutate=lambda content: content + b"# changed\n",
|
||||
)
|
||||
assert bootstrap._controller_artifact_archive_digest(
|
||||
corrupted_archive, baseline
|
||||
) != baseline.artifact_sha256
|
||||
|
||||
renamed_archive = _rewrite_controller_archive(
|
||||
archive,
|
||||
rename_path="scripts/run_repopulse_adoption.py",
|
||||
new_path="scripts/unreviewed.py",
|
||||
)
|
||||
with pytest.raises(
|
||||
bootstrap.BootstrapError,
|
||||
match="controller_promoter_controller_provenance_invalid",
|
||||
):
|
||||
bootstrap._controller_artifact_archive_digest(renamed_archive, baseline)
|
||||
|
||||
handoff_path = "scripts/run_repopulse_adoption.py"
|
||||
corrupted_handoff = _rewrite_controller_archive(
|
||||
archive,
|
||||
mutate_path=handoff_path,
|
||||
mutate=lambda content: content + b"# changed\n",
|
||||
)
|
||||
with pytest.raises(
|
||||
bootstrap.BootstrapError,
|
||||
match="controller_promoter_controller_provenance_invalid",
|
||||
):
|
||||
bootstrap._controller_artifact_archive_digest(corrupted_handoff, baseline)
|
||||
|
||||
crlf_source_archive = _rewrite_controller_archive(
|
||||
archive,
|
||||
mutate_path=handoff_path,
|
||||
mutate=lambda content: content.replace(b"\n", b"\r\r\n"),
|
||||
)
|
||||
with pytest.raises(
|
||||
bootstrap.BootstrapError,
|
||||
match="controller_promoter_controller_provenance_invalid",
|
||||
):
|
||||
bootstrap._controller_artifact_archive_digest(crlf_source_archive, baseline)
|
||||
|
||||
|
||||
def _rewrite_controller_archive(
|
||||
archive: bytes,
|
||||
*,
|
||||
rename_path: str | None = None,
|
||||
new_path: str | None = None,
|
||||
mutate_path: str | None = None,
|
||||
mutate: Any | None = None,
|
||||
) -> bytes:
|
||||
output = io.BytesIO()
|
||||
with tarfile.open(fileobj=io.BytesIO(archive), mode="r:") as original:
|
||||
with tarfile.open(fileobj=output, mode="w:") as rewritten:
|
||||
for member in original.getmembers():
|
||||
if member.isdir():
|
||||
rewritten.addfile(member)
|
||||
continue
|
||||
body = original.extractfile(member)
|
||||
assert body is not None
|
||||
content = body.read()
|
||||
if member.name == rename_path:
|
||||
assert new_path is not None
|
||||
member.name = new_path
|
||||
if member.name == mutate_path:
|
||||
assert mutate is not None
|
||||
content = mutate(content)
|
||||
member.size = len(content)
|
||||
rewritten.addfile(member, io.BytesIO(content))
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
def test_controller_source_provenance_failure_precedes_health_and_side_effects():
|
||||
helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
docker = FakeDocker(_legacy_helper_spec())
|
||||
git = FakeProtectedGit(helper_source)
|
||||
git.invalid_controller_release = bootstrap.MODERN_CONTROLLER_BASELINE
|
||||
|
||||
with pytest.raises(
|
||||
bootstrap.BootstrapError,
|
||||
match="controller_promoter_controller_provenance_invalid",
|
||||
):
|
||||
_run_bootstrap(
|
||||
docker,
|
||||
git,
|
||||
health_sequence=[_modern_baseline_health()],
|
||||
)
|
||||
|
||||
assert git.controller_release_checks == [bootstrap.MODERN_CONTROLLER_BASELINE]
|
||||
assert docker.removed == []
|
||||
assert docker.created == []
|
||||
assert docker.started == []
|
||||
|
||||
|
||||
def _retired_failed_child(helper_source: str) -> dict[str, Any]:
|
||||
spec = bootstrap.render_modern_helper_spec(
|
||||
helper_source,
|
||||
@@ -678,6 +897,87 @@ def test_initial_controller_runtime_accepts_exact_current_compose_container():
|
||||
)
|
||||
|
||||
|
||||
def test_modern_source_baseline_accepts_its_fixed_source_mount_profile():
|
||||
docker = FakeDocker(_legacy_helper_spec())
|
||||
docker.containers["gitea-deploy-control"] = _modern_controller_source_inspect(
|
||||
MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
base_image_labels = bootstrap._inspect_controller_base_image(docker)
|
||||
|
||||
assert (
|
||||
bootstrap._verify_controller_runtime(
|
||||
docker,
|
||||
health_payload=_modern_baseline_health(),
|
||||
base_image_labels=base_image_labels,
|
||||
)
|
||||
== base_image_labels
|
||||
)
|
||||
|
||||
|
||||
def test_modern_source_mount_profile_is_distinct_from_postpromotion_topology():
|
||||
source_mounts = _modern_controller_source_mounts(MODERN_CONTROLLER_REVISION)
|
||||
desired_mounts = _modern_controller_mounts(MODERN_CONTROLLER_REVISION)
|
||||
|
||||
assert len(source_mounts) == 10
|
||||
assert len(desired_mounts) == 11
|
||||
assert bootstrap._modern_controller_source_mounts_match(
|
||||
source_mounts, MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
assert not bootstrap._modern_controller_source_mounts_match(
|
||||
desired_mounts, MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
assert not bootstrap._modern_controller_source_mounts_match(
|
||||
source_mounts, "9" * 40
|
||||
)
|
||||
assert bootstrap._modern_controller_mounts_match(
|
||||
desired_mounts, MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
|
||||
docker = FakeDocker(_legacy_helper_spec())
|
||||
docker.containers["gitea-deploy-control"] = _modern_controller_source_inspect(
|
||||
MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
assert not bootstrap._verified_live_controller(
|
||||
docker, source_revision=MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"mutation",
|
||||
[
|
||||
"missing_mount",
|
||||
"extra_backup_mount",
|
||||
"artifact_propagation",
|
||||
"config_propagation",
|
||||
"integer_rw",
|
||||
"duplicate_mount",
|
||||
],
|
||||
)
|
||||
def test_modern_source_mount_profile_rejects_topology_drift(mutation: str):
|
||||
mounts = _modern_controller_source_mounts(MODERN_CONTROLLER_REVISION)
|
||||
if mutation == "missing_mount":
|
||||
mounts.pop()
|
||||
elif mutation == "extra_backup_mount":
|
||||
backup = next(
|
||||
mount
|
||||
for mount in _modern_controller_mounts(MODERN_CONTROLLER_REVISION)
|
||||
if mount["Destination"] == "/forge-master-key-backup"
|
||||
)
|
||||
mounts.append(backup)
|
||||
elif mutation == "artifact_propagation":
|
||||
mounts[3]["Propagation"] = "rprivate"
|
||||
elif mutation == "config_propagation":
|
||||
mounts[9]["Propagation"] = "rprivate"
|
||||
elif mutation == "integer_rw":
|
||||
mounts[1]["RW"] = 0
|
||||
elif mutation == "duplicate_mount":
|
||||
mounts.append(dict(mounts[0]))
|
||||
|
||||
assert not bootstrap._modern_controller_source_mounts_match(
|
||||
mounts, MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
|
||||
|
||||
def test_immutable_legacy_helper_source_fixture_passes_bootstrap_identity_guard():
|
||||
legacy_spec = _legacy_helper_spec()
|
||||
docker = FakeDocker(legacy_spec)
|
||||
@@ -1002,7 +1302,13 @@ def test_exact_failed_historical_child_is_removed_before_current_helper_launch()
|
||||
result = _run_bootstrap(
|
||||
docker,
|
||||
git,
|
||||
health_sequence=[legacy_health, legacy_health, legacy_health, _health()],
|
||||
health_sequence=[
|
||||
legacy_health,
|
||||
legacy_health,
|
||||
legacy_health,
|
||||
legacy_health,
|
||||
_health(),
|
||||
],
|
||||
)
|
||||
|
||||
assert result["status"] == "promoted"
|
||||
@@ -1015,6 +1321,174 @@ def test_exact_failed_historical_child_is_removed_before_current_helper_launch()
|
||||
) == 1
|
||||
|
||||
|
||||
def test_current_214_baseline_is_verified_and_bound_to_new_helper():
|
||||
helper_source = (ROOT / "backend/app/services/controller_artifact_helper_spec.py").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
docker = FakeDocker(_legacy_helper_spec())
|
||||
docker.containers["gitea-deploy-control"] = _modern_controller_source_inspect(
|
||||
MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
git = FakeProtectedGit(helper_source)
|
||||
|
||||
result = _run_bootstrap(
|
||||
docker,
|
||||
git,
|
||||
health_sequence=[_modern_baseline_health(), _health(), _health()],
|
||||
)
|
||||
|
||||
assert result["status"] == "promoted"
|
||||
assert git.controller_release_checks == [bootstrap.MODERN_CONTROLLER_BASELINE]
|
||||
assert docker.created[0]["Labels"] == {
|
||||
"itworx.controller.promoter": "true",
|
||||
"itworx.source.repository": "jens/unraid-appops-gateway",
|
||||
"itworx.source.revision": REVISION,
|
||||
"itworx.controller.version": "2.1.14",
|
||||
"itworx.controller.bootstrap.target.id": MODERN_CONTROLLER_CONTAINER_ID,
|
||||
"itworx.controller.bootstrap.target.image": CONTROLLER_IMAGE_ID,
|
||||
"itworx.controller.bootstrap.target.revision": MODERN_CONTROLLER_REVISION,
|
||||
"itworx.controller.bootstrap.target.tree": MODERN_CONTROLLER_TREE,
|
||||
"itworx.controller.bootstrap.target.version": "2.1.14",
|
||||
}
|
||||
|
||||
|
||||
def test_retired_failed_child_is_removed_only_after_current_214_profile_rechecks():
|
||||
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)
|
||||
docker.containers["gitea-deploy-control"] = _modern_controller_source_inspect(
|
||||
MODERN_CONTROLLER_REVISION
|
||||
)
|
||||
git = FakeProtectedGit(helper_source)
|
||||
|
||||
result = _run_bootstrap(
|
||||
docker,
|
||||
git,
|
||||
health_sequence=[
|
||||
_modern_baseline_health(),
|
||||
_modern_baseline_health(),
|
||||
_modern_baseline_health(),
|
||||
_modern_baseline_health(),
|
||||
_health(),
|
||||
_health(),
|
||||
],
|
||||
)
|
||||
|
||||
assert result["status"] == "promoted"
|
||||
assert docker.removed == [RETIRED_HELPER_ID]
|
||||
assert docker.created[0]["Labels"]["itworx.controller.bootstrap.target.id"] == (
|
||||
MODERN_CONTROLLER_CONTAINER_ID
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"mutation",
|
||||
[
|
||||
"container_id",
|
||||
"image_id",
|
||||
"source_revision",
|
||||
"source_tree",
|
||||
"artifact_digest",
|
||||
"version",
|
||||
"compose_label",
|
||||
"extra_label",
|
||||
"missing_label",
|
||||
"extra_mount",
|
||||
"missing_mount",
|
||||
"writable_mount",
|
||||
"integer_rw_mount",
|
||||
"missing_rw_mount",
|
||||
"malformed_mount_source",
|
||||
"malformed_mount_name",
|
||||
"mount_alias",
|
||||
"dead_controller",
|
||||
"paused_controller",
|
||||
"restarting_controller",
|
||||
"missing_state_flag",
|
||||
"health_revision",
|
||||
"health_tree",
|
||||
"health_artifact",
|
||||
"health_version",
|
||||
],
|
||||
)
|
||||
def test_retired_cleanup_refuses_any_current_214_identity_or_topology_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)
|
||||
controller = _modern_controller_source_inspect(MODERN_CONTROLLER_REVISION)
|
||||
health = _modern_baseline_health()
|
||||
if mutation == "container_id":
|
||||
controller["Id"] = "9" * 64
|
||||
elif mutation == "image_id":
|
||||
controller["Image"] = "sha256:" + "9" * 64
|
||||
elif mutation == "source_revision":
|
||||
controller["Mounts"][3]["Source"] = (
|
||||
"/var/lib/docker/volumes/itworx-controller-hub-releases/_data/"
|
||||
+ "9" * 40
|
||||
)
|
||||
elif mutation == "source_tree":
|
||||
health["artifact"]["sourceTree"] = "9" * 40
|
||||
elif mutation == "artifact_digest":
|
||||
health["artifact"]["artifactSha256"] = "9" * 64
|
||||
elif mutation == "version":
|
||||
controller["Config"]["Labels"]["org.opencontainers.image.version"] = "forged"
|
||||
elif mutation == "compose_label":
|
||||
controller["Config"]["Labels"]["com.docker.compose.service"] = "other"
|
||||
elif mutation == "extra_label":
|
||||
controller["Config"]["Labels"]["unexpected"] = "value"
|
||||
elif mutation == "missing_label":
|
||||
del controller["Config"]["Labels"]["tech.itworx.role"]
|
||||
elif mutation == "extra_mount":
|
||||
controller["Mounts"].append(dict(controller["Mounts"][0]))
|
||||
controller["Mounts"][-1]["Destination"] = "/unexpected"
|
||||
elif mutation == "missing_mount":
|
||||
controller["Mounts"].pop()
|
||||
elif mutation == "writable_mount":
|
||||
controller["Mounts"][1]["RW"] = True
|
||||
elif mutation == "integer_rw_mount":
|
||||
controller["Mounts"][1]["RW"] = 0
|
||||
elif mutation == "missing_rw_mount":
|
||||
del controller["Mounts"][1]["RW"]
|
||||
elif mutation == "malformed_mount_source":
|
||||
controller["Mounts"][1]["Source"] = 123
|
||||
elif mutation == "malformed_mount_name":
|
||||
controller["Mounts"][8]["Name"] = None
|
||||
elif mutation == "mount_alias":
|
||||
controller["Mounts"][9]["Source"] = "/tmp/controller-config"
|
||||
elif mutation == "dead_controller":
|
||||
controller["State"]["Dead"] = True
|
||||
elif mutation == "paused_controller":
|
||||
controller["State"]["Paused"] = True
|
||||
elif mutation == "restarting_controller":
|
||||
controller["State"]["Restarting"] = True
|
||||
elif mutation == "missing_state_flag":
|
||||
del controller["State"]["Dead"]
|
||||
elif mutation == "health_revision":
|
||||
health["artifact"]["sourceRevision"] = "9" * 40
|
||||
elif mutation == "health_tree":
|
||||
health["artifact"]["sourceTree"] = "9" * 40
|
||||
elif mutation == "health_artifact":
|
||||
health["artifact"]["artifactSha256"] = "9" * 64
|
||||
elif mutation == "health_version":
|
||||
health["artifact"]["controllerVersion"] = "2.1.13"
|
||||
docker.containers["gitea-deploy-control"] = controller
|
||||
|
||||
with pytest.raises(bootstrap.BootstrapError):
|
||||
_run_bootstrap(
|
||||
docker,
|
||||
FakeProtectedGit(helper_source),
|
||||
health_sequence=[health, health, health],
|
||||
)
|
||||
|
||||
assert docker.removed == []
|
||||
assert docker.created == []
|
||||
assert docker.started == []
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"mutation",
|
||||
[
|
||||
|
||||
Reference in new issue
Block a user