Publish curated Unraid-AppOps-Gateway source from 0f89e1506065c5365c48968b1d7845d666a18be8
This commit is contained in:
1 parent
3eed2d6657
commit
a0d1a1224a
8 files changed
+152
-31
No files matched your search
@@ -13,6 +13,113 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def controller_only_source(specialize, tmp_path, monkeypatch):
|
||||
image = "sha256:" + "a" * 64
|
||||
code = f"{specialize.RELEASE_VOLUME_HOST_ROOT}/controller-artifacts/{'b' * 40}"
|
||||
model = {"name": specialize.PROJECT, "services": {"deploy-control": {
|
||||
"container_name": specialize.CONTROLLER_CONTAINER, "image": image,
|
||||
"volumes": [{"type": "bind", "source": code, "target": "/opt/gitea-deploy",
|
||||
"read_only": True, "bind": {"propagation": "rslave"}}],
|
||||
}}}
|
||||
compose = tmp_path / "docker-compose.yml"
|
||||
archive_dir = tmp_path / "backups" / "controller-v2-fixed"
|
||||
archive_dir.mkdir(parents=True, mode=0o700)
|
||||
archive_dir.parent.chmod(0o700)
|
||||
archive = archive_dir / "compose-before.bin"
|
||||
archived = b"reviewed complete legacy v1 source"
|
||||
archive.write_bytes(archived)
|
||||
archive.chmod(0o600)
|
||||
monkeypatch.setattr(specialize, "ROOT", tmp_path)
|
||||
monkeypatch.setattr(specialize, "COMPOSE_PATH", compose)
|
||||
monkeypatch.setattr(specialize, "LEGACY_V1_COMPOSE_BACKUP", archive)
|
||||
monkeypatch.setattr(specialize, "_KNOWN_V1_COMPOSE_SHA256", hashlib.sha256(archived).hexdigest())
|
||||
monkeypatch.setattr(specialize, "_controller_runtime_identity", lambda: (image, code))
|
||||
return model, compose, archive, archived
|
||||
|
||||
|
||||
def test_controller_only_source_uses_fixed_pinned_archive_without_rewriting_live_compose(
|
||||
specialize, controller_only_source,
|
||||
):
|
||||
model, compose, _archive, archived = controller_only_source
|
||||
current = json.dumps(model).encode()
|
||||
compose.write_bytes(current)
|
||||
result, current_sha = specialize._read_fleet_base_compose()
|
||||
assert result == archived
|
||||
assert current_sha == hashlib.sha256(current).hexdigest()
|
||||
assert compose.read_bytes() == current
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mutation", ["project", "image", "container", "code", "writable", "shared", "extra_code"])
|
||||
def test_controller_only_source_rejects_identity_and_code_binding_drift(
|
||||
specialize, controller_only_source, mutation,
|
||||
):
|
||||
model, compose, _archive, _archived = controller_only_source
|
||||
controller = model["services"]["deploy-control"]
|
||||
mount = controller["volumes"][0]
|
||||
if mutation == "project":
|
||||
model["name"] = "other"
|
||||
elif mutation == "image":
|
||||
controller["image"] = "sha256:" + "c" * 64
|
||||
elif mutation == "container":
|
||||
controller["container_name"] = "other"
|
||||
elif mutation == "code":
|
||||
mount["source"] = "/mnt/shared/code"
|
||||
elif mutation == "writable":
|
||||
mount["read_only"] = False
|
||||
elif mutation == "shared":
|
||||
mount["bind"]["propagation"] = "rshared"
|
||||
else:
|
||||
controller["volumes"].append(copy.deepcopy(mount))
|
||||
compose.write_text(json.dumps(model))
|
||||
with pytest.raises(specialize.RunnerFleetSpecializeError, match="controller_compose_untrusted"):
|
||||
specialize._read_fleet_base_compose()
|
||||
|
||||
|
||||
def test_controller_only_source_rejects_modified_fixed_archive(specialize, controller_only_source):
|
||||
model, compose, archive, _archived = controller_only_source
|
||||
compose.write_text(json.dumps(model))
|
||||
archive.write_bytes(b"different legacy source")
|
||||
with pytest.raises(specialize.RunnerFleetSpecializeError, match="legacy_backup_changed"):
|
||||
specialize._read_fleet_base_compose()
|
||||
|
||||
|
||||
def test_legacy_live_source_does_not_open_promotion_backup(specialize, monkeypatch):
|
||||
current = b"services:\n runner: {}\n"
|
||||
monkeypatch.setattr(specialize, "_read_source_file", lambda *_args, **_kwargs: current)
|
||||
monkeypatch.setattr(specialize, "_trusted_private_file", lambda *_args, **_kwargs: pytest.fail("archive must remain unopened"))
|
||||
assert specialize._read_fleet_base_compose() == (current, None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("separate_controller_hash", [False, True])
|
||||
def test_controller_compose_cas_blocks_rollback_before_any_lifecycle_action(
|
||||
specialize, controller_only_source, monkeypatch, separate_controller_hash,
|
||||
):
|
||||
model, compose, _archive, _archived = controller_only_source
|
||||
compose.write_text(json.dumps(model))
|
||||
original_sha = hashlib.sha256(compose.read_bytes()).hexdigest()
|
||||
journal = {"baseComposeSha256": original_sha}
|
||||
if separate_controller_hash:
|
||||
journal["controllerComposeSha256"] = original_sha
|
||||
compose.write_bytes(compose.read_bytes() + b"\n")
|
||||
monkeypatch.setattr(specialize, "_guard_state_unchanged", lambda *_args: pytest.fail("must reject before lifecycle"))
|
||||
assert specialize._rollback_before_activation(journal) is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("reviewed,user,image,expected", [
|
||||
(True, None, True, "rootless"), (False, None, True, None),
|
||||
(True, "root", True, "root"), (True, None, False, None),
|
||||
])
|
||||
def test_only_byte_pinned_v1_pinned_image_default_user_is_normalized(
|
||||
specialize, monkeypatch, reviewed, user, image, expected,
|
||||
):
|
||||
source = b"reviewed v1 source" if reviewed else b"different source"
|
||||
monkeypatch.setattr(specialize, "_KNOWN_V1_COMPOSE_SHA256", hashlib.sha256(b"reviewed v1 source").hexdigest())
|
||||
model = {"services": {"runner-dind": {"image": specialize._DIND_IMAGE_REFERENCE if image else "unknown", "user": user}}}
|
||||
monkeypatch.setattr(specialize, "_run_compose", lambda *_args: json.dumps(model))
|
||||
assert specialize._compose_model(source)["services"]["runner-dind"]["user"] == expected
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
BROKER = ROOT / "ops" / "gitea-deploy-broker"
|
||||
SPECIALIZE_PATH = BROKER / "runner_fleet_specialize.py"
|
||||
@@ -2145,6 +2252,10 @@ def test_migration_failure_restores_captured_dind_state_before_cgroup_cleanup(
|
||||
|
||||
base_bytes = json.dumps(base_model, sort_keys=True, separators=(",", ":")).encode()
|
||||
specialized_bytes = json.dumps(specialized_model, sort_keys=True, separators=(",", ":")).encode()
|
||||
live_compose = tmp_path / "docker-compose.yml"
|
||||
live_compose.write_bytes(base_bytes)
|
||||
monkeypatch.setattr(specialize, "COMPOSE_PATH", live_compose)
|
||||
journal["baseComposeSha256"] = hashlib.sha256(base_bytes).hexdigest()
|
||||
monkeypatch.setattr(specialize, "_guard_state_unchanged", lambda _journal: True)
|
||||
monkeypatch.setattr(
|
||||
specialize,
|
||||
|
||||
Reference in new issue
Block a user