Publish curated Unraid-AppOps-Gateway source from 828f7274cafede223d37b2697367bfd89357b079
This commit is contained in:
1 parent
551de41e9f
commit
e49634bb10
7 files changed
+449
-24
No files matched your search
@@ -95,20 +95,24 @@ def _legacy_model(module, image_ref: str | None = None) -> dict[str, object]:
|
||||
}
|
||||
|
||||
|
||||
def _mock_legacy_docker(monkeypatch, module):
|
||||
environment = _legacy_environment()
|
||||
def _mock_legacy_docker(monkeypatch, module, *, candidate=False):
|
||||
environment = _environment() if candidate else _legacy_environment()
|
||||
raw_env = [f"{key}={value}" for key, value in environment.items()]
|
||||
raw_env.append(f"APPOPS_FRONTEND_ORIGIN={environment['APPOPS_FRONTEND_ORIGIN']}")
|
||||
if not candidate:
|
||||
raw_env.append(f"APPOPS_FRONTEND_ORIGIN={environment['APPOPS_FRONTEND_ORIGIN']}")
|
||||
revision = REVISION if candidate else module.SELF_UPGRADE_OLD_REVISION
|
||||
tree = TREE if candidate else module.SELF_UPGRADE_OLD_TREE
|
||||
image_id = IMAGE_ID if candidate else module.SELF_UPGRADE_IMAGE_ID
|
||||
image_labels = {
|
||||
"org.opencontainers.image.source": module.SELF_UPGRADE_REPOSITORY,
|
||||
"org.opencontainers.image.revision": module.SELF_UPGRADE_OLD_REVISION,
|
||||
"org.opencontainers.image.source-tree": module.SELF_UPGRADE_OLD_TREE,
|
||||
"org.opencontainers.image.version": module.SELF_UPGRADE_OLD_REVISION[:12],
|
||||
"org.opencontainers.image.created": "2026-09-01T00:00:00.000Z",
|
||||
"org.opencontainers.image.revision": revision,
|
||||
"org.opencontainers.image.source-tree": tree,
|
||||
"org.opencontainers.image.version": revision[:12],
|
||||
"org.opencontainers.image.created": BUILT_AT if candidate else "2026-09-01T00:00:00.000Z",
|
||||
}
|
||||
container = {
|
||||
"Id": CONTAINER_ID,
|
||||
"Image": module.SELF_UPGRADE_IMAGE_ID,
|
||||
"Image": image_id,
|
||||
"State": {"Running": True, "Health": {"Status": "healthy"}},
|
||||
"Mounts": [
|
||||
{"Type": "bind", "Source": module.SELF_UPGRADE_DATA_PATH,
|
||||
@@ -117,7 +121,7 @@ def _mock_legacy_docker(monkeypatch, module):
|
||||
"Destination": module.SELF_UPGRADE_MODELS_TARGET, "RW": True},
|
||||
],
|
||||
"Config": {
|
||||
"Image": module.SELF_UPGRADE_OLD_IMAGE_REF,
|
||||
"Image": module.SELF_UPGRADE_PRODUCTION_IMAGE_REF if candidate else module.SELF_UPGRADE_OLD_IMAGE_REF,
|
||||
"Hostname": "unraid-appops-gateway",
|
||||
"Env": raw_env,
|
||||
"Labels": {
|
||||
@@ -137,7 +141,7 @@ def _mock_legacy_docker(monkeypatch, module):
|
||||
},
|
||||
},
|
||||
}
|
||||
image = {"Id": module.SELF_UPGRADE_IMAGE_ID, "Config": {"Labels": image_labels}}
|
||||
image = {"Id": image_id, "Config": {"Labels": image_labels}}
|
||||
|
||||
def fake_run(args, **_kwargs):
|
||||
if args[:3] == ["docker", "ps", "-a"]:
|
||||
@@ -308,6 +312,43 @@ def test_live_model_records_are_content_addressed_and_activate_only_from_state(
|
||||
assert module._self_upgrade_active_compose_model(state, project=project) is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("drift", [None, "host", "port", "extra-port", "environment", "image", "unsigned", "stopped", "not-restarting"])
|
||||
def test_signed_crashloop_candidate_can_be_identified_only_for_rollback(controller, monkeypatch, drift):
|
||||
module, _root, _secret = controller
|
||||
if os.name == "nt" or os.geteuid() != 0:
|
||||
pytest.skip("The signed model store requires root-owned POSIX files")
|
||||
pointer = module._self_upgrade_store_compose_model(RECOVERY_ID, REVISION, _model())
|
||||
assert isinstance(pointer, dict)
|
||||
loaded = module._self_upgrade_load_compose_model(RECOVERY_ID, REVISION)
|
||||
container = _mock_legacy_docker(monkeypatch, module, candidate=True)
|
||||
container["Config"]["Labels"]["com.docker.compose.project.config_files"] = loaded["path"]
|
||||
container["State"].update({"Restarting": True, "Status": "restarting", "Health": {"Status": "unhealthy"}})
|
||||
container["NetworkSettings"]["Ports"] = {}
|
||||
container["HostConfig"]["PortBindings"] = {
|
||||
"1216/tcp": [{"HostIp": module.SELF_UPGRADE_PUBLISH_HOST, "HostPort": "1216"}],
|
||||
}
|
||||
if drift == "host":
|
||||
container["HostConfig"]["PortBindings"]["1216/tcp"][0]["HostIp"] = "0.0.0.0"
|
||||
elif drift == "port":
|
||||
container["HostConfig"]["PortBindings"]["1216/tcp"][0]["HostPort"] = "9999"
|
||||
elif drift == "extra-port":
|
||||
container["HostConfig"]["PortBindings"]["22/tcp"] = [{"HostIp": "0.0.0.0", "HostPort": "22"}]
|
||||
elif drift == "environment":
|
||||
container["Config"]["Env"].append("UNREVIEWED_SETTING=drift")
|
||||
elif drift == "image":
|
||||
container["Image"] = "sha256:" + "0" * 64
|
||||
elif drift == "unsigned":
|
||||
pointer = {**pointer, "modelRecordSha256": "0" * 64}
|
||||
elif drift == "stopped":
|
||||
container["State"]["Running"] = False
|
||||
elif drift == "not-restarting":
|
||||
container["State"]["Restarting"] = False
|
||||
assert module._self_upgrade_runtime() is None
|
||||
assert module._self_upgrade_model_runtime_identity(pointer, IMAGE_ID) is None
|
||||
identity = module._self_upgrade_model_runtime_identity(pointer, IMAGE_ID, require_healthy=False)
|
||||
assert (identity is not None) == (drift is None)
|
||||
|
||||
|
||||
def test_candidate_binding_rechecks_exact_model_receipt_and_runtime(
|
||||
controller, monkeypatch,
|
||||
):
|
||||
@@ -554,8 +595,9 @@ def test_fixed_555_production_reference_is_valid_only_under_signed_legacy_model(
|
||||
assert restored_runtime["imageRef"] == module.SELF_UPGRADE_PRODUCTION_IMAGE_REF
|
||||
|
||||
|
||||
@pytest.mark.parametrize("cache", ["registered-builds", "registered-builds-v2"])
|
||||
def test_recovery_transaction_binds_authorized_555_preimage_and_holds_pending_state(
|
||||
controller, monkeypatch,
|
||||
controller, monkeypatch, cache,
|
||||
):
|
||||
module, recovery_root, secret = controller
|
||||
if os.name == "nt" or os.geteuid() != 0:
|
||||
@@ -747,6 +789,24 @@ def test_recovery_transaction_binds_authorized_555_preimage_and_holds_pending_st
|
||||
assert module._self_upgrade_update_model_transaction(
|
||||
transaction_id, "0" * 64, "recovery_required",
|
||||
) is None
|
||||
candidate_pointer = module._self_upgrade_store_compose_model(RECOVERY_ID, REVISION, _model())
|
||||
assert isinstance(candidate_pointer, dict)
|
||||
context = f"{module.EXACT_SOURCE_ROOT.as_posix()}/{cache}/unraid-appops-gateway/{REVISION}/tree"
|
||||
for bad_context in (
|
||||
context + "/../outside", context.replace(REVISION, "f" * 40),
|
||||
context.replace(cache, "registered-builds-v3"), context + "-outside",
|
||||
):
|
||||
assert module._self_upgrade_update_model_transaction(
|
||||
transaction_id, created["sha256"], "source_staged",
|
||||
{"candidateModelPointer": candidate_pointer, "buildContexts": {"appops-gateway": bad_context}},
|
||||
) is None
|
||||
staged = module._self_upgrade_update_model_transaction(
|
||||
transaction_id, created["sha256"], "source_staged",
|
||||
{"candidateModelPointer": candidate_pointer, "buildContexts": {"appops-gateway": context}},
|
||||
)
|
||||
assert isinstance(staged, dict)
|
||||
assert module._self_upgrade_load_model_transaction(transaction_id) == staged
|
||||
created = staged
|
||||
held = module._self_upgrade_update_model_transaction(
|
||||
transaction_id, created["sha256"], "recovery_required",
|
||||
)
|
||||
@@ -802,6 +862,21 @@ def test_recovery_transaction_binds_authorized_555_preimage_and_holds_pending_st
|
||||
)
|
||||
rollback_before = module._self_upgrade_load_model_transaction(rollback_transaction_id)
|
||||
assert isinstance(rollback_before, dict)
|
||||
intent = {
|
||||
"schema": "appops-fixed-555-rollback-resume.v1", "stateSha256": "a" * 64,
|
||||
"candidateRuntimeSha256": "b" * 64, "candidateStableRuntimeSha256": "d" * 64,
|
||||
"snapshotSha256": hashlib.sha256(module._self_upgrade_canonical_json(rollback_payload["priorSnapshot"])).hexdigest(),
|
||||
}
|
||||
resumed = module._self_upgrade_update_model_transaction(
|
||||
rollback_transaction_id, rollback_before["sha256"], "rolling_back", {"rollbackResumeIntent": intent},
|
||||
)
|
||||
assert isinstance(resumed, dict)
|
||||
assert module._self_upgrade_load_model_transaction(rollback_transaction_id) == resumed
|
||||
assert module._self_upgrade_update_model_transaction(
|
||||
rollback_transaction_id, resumed["sha256"], "rolling_back",
|
||||
{"rollbackResumeIntent": {**intent, "stateSha256": "c" * 64}},
|
||||
) is None
|
||||
rollback_before = resumed
|
||||
rolled_back = module._self_upgrade_update_model_transaction(
|
||||
rollback_transaction_id,
|
||||
rollback_before["sha256"],
|
||||
|
||||
Reference in new issue
Block a user