Publish curated Unraid-AppOps-Gateway source from 84f2a9958b2bdf1c1c13553925026d75f32703c3

This commit is contained in:
Public Source Publisher committed 2026-10-07 05:27:40 +00:00
1 parent 8dd4b40252
commit 5d598db0df
14 files changed
+2765 -46

No files matched your search

+25 -5
View File
@@ -14,6 +14,7 @@ from typing import Any
from mcp.server import MCPServer
from mcp.server.transport_security import TransportSecuritySettings
from mcp.types import ToolAnnotations
from pydantic import StrictBool
from sqlmodel import Session, select
from .api.routes_actions import action_registry as build_action_registry
@@ -679,13 +680,32 @@ def appops_hub_component_status(plan_id: str) -> dict[str, Any]:
return _json(DeploymentBrokerClient().hub_component_operation("status", {"planId": plan_id}))
@appops_mcp.tool(name="appops.registered_legacy.observe", annotations=_READ_ONLY_TOOL)
@_tracked("appops.registered_legacy.observe")
def appops_registered_legacy_observe(repository: str) -> dict[str, Any]:
"""Read registered runtime fingerprints, container/image IDs and mismatched key names only."""
return _json(DeploymentBrokerClient().registered_legacy_operation("observe", {"repository": repository}))
@appops_mcp.tool(name="appops.registered_legacy.baseline", annotations=_READ_ONLY_TOOL)
@_tracked("appops.registered_legacy.baseline")
def appops_registered_legacy_baseline(repository: str, request_id: str) -> dict[str, Any]:
"""Observe an existing registered runtime whose previous source revision is unknown."""
return _json(DeploymentBrokerClient().registered_legacy_operation("baseline", {
"repository": repository, "requestId": request_id,
}))
def appops_registered_legacy_baseline(
repository: str, request_id: str, capture_runtime_environment: StrictBool = False,
expected_source_fingerprint: str | None = None, expected_runtime_fingerprint: str | None = None,
expected_state_sha256: str | None = None, expected_container_ids: dict[str, str] | None = None,
) -> dict[str, Any]:
"""Seal a registered baseline; opt-in runtime environment capture requires all observed CAS bindings. Values stay private."""
bindings = {"expectedSourceFingerprint": expected_source_fingerprint,
"expectedRuntimeFingerprint": expected_runtime_fingerprint,
"expectedStateSha256": expected_state_sha256, "expectedContainerIds": expected_container_ids}
if type(capture_runtime_environment) is not bool or (
not capture_runtime_environment and any(value is not None for value in bindings.values())
) or (capture_runtime_environment and any(value is None for value in bindings.values())):
raise ValueError("registered capture contract is invalid")
payload: dict[str, Any] = {"repository": repository, "requestId": request_id}
if capture_runtime_environment:
payload.update(captureRuntimeEnvironment=True, **bindings)
return _json(DeploymentBrokerClient().registered_legacy_operation("baseline", payload))
@appops_mcp.tool(name="appops.registered_legacy.preflight", annotations=_READ_ONLY_TOOL)
+22 -1
View File
@@ -296,8 +296,29 @@ class DeploymentBrokerClient:
def registered_legacy_operation(self, stage: str, payload: dict[str, Any]) -> dict[str, Any]:
"""Signed fixed-stage metadata adoption/recovery for existing registered targets."""
if stage not in {"baseline", "preflight", "adopt", "status", "rollback", "delivery"}:
if stage not in {"observe", "baseline", "preflight", "adopt", "status", "rollback", "delivery"}:
raise ValueError("registered legacy stage is invalid")
if stage == "observe" and (not isinstance(payload, dict) or set(payload) != {"repository"}):
raise ValueError("registered observation contract is invalid")
if stage == "baseline":
if not isinstance(payload, dict):
raise ValueError("registered capture contract is invalid")
base = {"repository", "requestId"}
bindings = {"expectedSourceFingerprint", "expectedRuntimeFingerprint", "expectedStateSha256", "expectedContainerIds"}
capture = payload.get("captureRuntimeEnvironment", False)
if (type(capture) is not bool or (capture is False and
set(payload) not in (base, base | {"captureRuntimeEnvironment"}))):
raise ValueError("registered capture contract is invalid")
if capture is True:
if (set(payload) != base | bindings | {"captureRuntimeEnvironment"}
or any(not isinstance(payload.get(field), str) or not re.fullmatch(r"[0-9a-f]{64}", payload[field])
for field in bindings - {"expectedContainerIds"})
or not isinstance(payload.get("expectedContainerIds"), dict)
or not 1 <= len(payload["expectedContainerIds"]) <= 64
or any(not isinstance(name, str) or not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}", name)
or not isinstance(value, str) or not re.fullmatch(r"[0-9a-f]{64}", value)
for name, value in payload["expectedContainerIds"].items())):
raise ValueError("registered capture contract is invalid")
return self._request(
"POST", f"/v1/appops/registered-legacy/{stage}",
secret=settings.mcp_token or "", payload=payload,
@@ -24,6 +24,81 @@ spec.loader.exec_module(module)
REVISION = "a" * 40
IMAGE_ID = "sha256:" + "b" * 64
ORIGINAL_COMPOSE = b"name: gitea-deploy-runners\nservices:\n deploy-control:\n image: immutable-controller\n"
@pytest.fixture(autouse=True)
def compose_config_root(monkeypatch, tmp_path):
# Production uses the fixed /tmp tmpfs; tests never write outside their tree.
monkeypatch.setattr(module, "_COMPOSE_CONFIG_ROOT", tmp_path)
@pytest.mark.parametrize("operation", ["config", "stop", "up"])
def test_compose_uses_private_ephemeral_cli_config_with_readonly_root(
monkeypatch, tmp_path, operation,
):
directories = []
def run(arguments, *, data=None, diagnostic_action=None, **kwargs):
# Compose up attempts to mkdir /root/.docker even for a dry run. The
# controller promoter must keep its root filesystem read-only.
assert arguments[:2] == ["docker", "--config"]
directory = Path(arguments[2])
directories.append(directory)
assert directory.parent == tmp_path
assert directory.name.startswith("itworx-controller-compose-")
assert directory.is_dir() and not directory.is_symlink()
if os.name == "posix":
assert stat.S_IMODE(directory.stat().st_mode) == 0o700
assert directory.stat().st_uid == os.getuid()
assert arguments[3] == "compose"
assert diagnostic_action == f"compose_{operation}"
assert data == b"captured-private-compose"
assert arguments[-1] == operation
assert arguments[arguments.index("--env-file") + 1] == "/dev/null"
assert arguments[arguments.index("-f") + 1] == "-"
(directory / "generated-client-state").write_bytes(b"private-client-state")
return b"ok"
monkeypatch.setattr(module, "_run", run)
for _ in range(2):
assert module._compose([operation], b"captured-private-compose") == b"ok"
assert directories[0] != directories[1]
assert all(not directory.exists() for directory in directories)
def test_compose_private_cli_config_is_cleaned_after_command_failure(monkeypatch):
directories = []
failure = module.VolumeTransitionError(
"controller_volume_command_failed",
command_failure=("compose_up", "exit_1", "mount_read_only"),
)
def run(arguments, **kwargs):
directory = Path(arguments[2])
directories.append(directory)
(directory / "client-state").write_bytes(b"private-state")
raise failure
monkeypatch.setattr(module, "_run", run)
with pytest.raises(module.VolumeTransitionError) as caught:
module._compose(["up"], b"private-compose")
assert caught.value is failure
assert directories and all(not directory.exists() for directory in directories)
@pytest.mark.parametrize("action", ["compose_config", "compose_stop", "compose_up", "image_inspect"])
def test_compose_boundary_diagnostic_never_discloses_stderr(monkeypatch, action):
stderr = b"mkdir /root/.docker: read-only file system fixture-secret-token"
monkeypatch.setattr(module.subprocess, "run", lambda *args, **kwargs: types.SimpleNamespace(
returncode=1, stdout=b"", stderr=stderr,
))
with pytest.raises(module.VolumeTransitionError) as caught:
module._run(["docker", "fixed-operation"], diagnostic_action=action)
diagnostic = module._safe_command_diagnostic(caught.value.command_failure)
assert diagnostic == f"{action}_exit_1_mount_read_only"
assert "fixture-secret-token" not in diagnostic
assert "/root/.docker" not in diagnostic
# Frozen historical layouts; these fixtures must not follow the acceptance registry.
HISTORICAL_PRE_CANDIDATE_RELEASE_FILES = (
"controller_paths.py", "mcp_hub_release_store.py", "deploy.py",
@@ -716,7 +791,8 @@ def test_compose_uses_protected_code_and_two_config_views(compose):
assert rendered["services"]["runner"] == baseline["services"]["runner"]
assert service["environment"] == baseline["services"]["deploy-control"]["environment"]
compose_calls = [
arguments for arguments, _ in calls if arguments[:2] == ["docker", "compose"]
arguments for arguments, _ in calls
if arguments[:2] == ["docker", "--config"] and arguments[3] == "compose"
]
assert compose_calls
assert all("--project-directory" in arguments for arguments in compose_calls)
@@ -1696,14 +1772,17 @@ def test_promoted_compose_actions_use_only_stdin_and_fixed_service(compose):
assert baseline["name"] == "gitea-deploy-runners"
def test_rollback_uses_no_dependencies_when_recreating_prior_controller(compose, tmp_path, monkeypatch):
@pytest.mark.parametrize("phase", ["stopped", "activated"])
def test_rollback_uses_no_dependencies_when_recreating_prior_controller(
compose, tmp_path, monkeypatch, phase,
):
_baseline, calls = compose
previous = REVISION
target = "c" * 40
previous_compose = module.rendered_model(ORIGINAL_COMPOSE, previous, IMAGE_ID)
target_compose = module.rendered_model(ORIGINAL_COMPOSE, target, IMAGE_ID)
receipt = {
"phase": "activated",
"phase": phase,
"imageId": IMAGE_ID,
"previousRevision": previous,
"sourceRevision": target,
@@ -1739,7 +1818,7 @@ def test_rollback_uses_no_dependencies_when_recreating_prior_controller(compose,
up_calls = [
arguments for arguments, _data in calls
if arguments[:2] == ["docker", "compose"] and "up" in arguments
if arguments[:2] == ["docker", "--config"] and arguments[3] == "compose" and "up" in arguments
]
assert len(up_calls) == 1
up_index = up_calls[0].index("up")
@@ -262,9 +262,14 @@ def original_git_reader(tmp_path, monkeypatch, request):
def test_original_descriptor_cannot_be_substituted_by_git_replacement(original_git_reader):
bare, original, namespace = original_git_reader
# A real replacement keeps the branch SHA but swaps the source descriptor.
replacement_env = {
key: value for key, value in os.environ.items()
if key != "GIT_NO_REPLACE_OBJECTS"
}
replaced = subprocess.check_output(
["git", f"--git-dir={bare}", "show", f"{original}:.itworx/deployment-target.json"],
text=True,
env=replacement_env,
)
assert json.loads(replaced)["verifyOciRevision"] is True
result = namespace["git_run"]("show", f"{original}:.itworx/deployment-target.json")
+11 -3
View File
@@ -338,9 +338,17 @@ def test_git_replacement_refs_cannot_substitute_other_commit_contents(inputs):
other = git(checkout, "rev-parse", "HEAD")
git(bare, "fetch", "--quiet", str(checkout), "HEAD")
git(bare, "replace", protected, other)
assert (
git(bare, "show", f"{protected}:server/main.py") == "replacement commit bytes"
)
# The positive attack demonstration must not inherit the production guard.
replacement_env = {
key: value for key, value in os.environ.items()
if key != "GIT_NO_REPLACE_OBJECTS"
}
replaced = subprocess.check_output(
["git", "-C", str(bare), "show", f"{protected}:server/main.py"],
text=True,
env=replacement_env,
).strip()
assert replaced == "replacement commit bytes"
tree = Path(prepare(load(), inputs)["quiz-server"])
assert tree.joinpath("server/main.py").read_text() == "print('protected')\n"
@@ -0,0 +1,397 @@
from __future__ import annotations
import asyncio
import json
import sys
from types import SimpleNamespace
import pytest
from mcp import Client
from test_hub_component_transport import call
from test_hub_component_transport import controller as _controller_fixture
from backend.app import mcp_server
from backend.app.services.deployment_broker import DeploymentBrokerClient
REPOSITORY = "jens/registered-app"
REQUEST_ID = "11111111-1111-4111-8111-111111111111"
PREFIX = "/v1/appops/registered-legacy/"
CAS = {
"expectedSourceFingerprint": "a" * 64,
"expectedRuntimeFingerprint": "b" * 64,
"expectedStateSha256": "c" * 64,
"expectedContainerIds": {"registered-app": "d" * 64},
}
@pytest.fixture
def controller(monkeypatch):
return _controller_fixture.__wrapped__(monkeypatch)
@pytest.fixture
def broker_calls(monkeypatch):
calls = []
monkeypatch.setattr(
DeploymentBrokerClient,
"_request",
lambda _self, method, path, **kwargs: calls.append((method, path, kwargs))
or {"status": "observed"},
)
return calls
def test_observe_client_accepts_only_repository_and_exact_route(broker_calls):
result = DeploymentBrokerClient().registered_legacy_operation(
"observe", {"repository": REPOSITORY}
)
assert result == {"status": "observed"}
assert broker_calls[0][0:2] == ("POST", PREFIX + "observe")
assert broker_calls[0][2]["payload"] == {"repository": REPOSITORY}
with pytest.raises(ValueError):
DeploymentBrokerClient().registered_legacy_operation(
"observe", {"repository": REPOSITORY, "environment": {"secret": "value"}}
)
assert len(broker_calls) == 1
def test_baseline_default_mcp_preserves_original_payload(broker_calls):
mcp_server.appops_registered_legacy_baseline(REPOSITORY, REQUEST_ID)
assert broker_calls[0][2]["payload"] == {
"repository": REPOSITORY,
"requestId": REQUEST_ID,
}
def test_baseline_capture_mcp_forwards_exact_optin_bindings(broker_calls):
mcp_server.appops_registered_legacy_baseline(
REPOSITORY,
REQUEST_ID,
capture_runtime_environment=True,
expected_source_fingerprint=CAS["expectedSourceFingerprint"],
expected_runtime_fingerprint=CAS["expectedRuntimeFingerprint"],
expected_state_sha256=CAS["expectedStateSha256"],
expected_container_ids=CAS["expectedContainerIds"],
)
assert broker_calls[0][2]["payload"] == {
"repository": REPOSITORY,
"requestId": REQUEST_ID,
"captureRuntimeEnvironment": True,
**CAS,
}
@pytest.mark.parametrize(
"capture,bindings", [(False, CAS), (True, {}), (1, CAS), ("true", CAS)]
)
def test_capture_contract_rejects_before_broker(capture, bindings, broker_calls):
with pytest.raises(ValueError):
mcp_server.appops_registered_legacy_baseline(
REPOSITORY,
REQUEST_ID,
capture_runtime_environment=capture,
expected_source_fingerprint=bindings.get("expectedSourceFingerprint"),
expected_runtime_fingerprint=bindings.get("expectedRuntimeFingerprint"),
expected_state_sha256=bindings.get("expectedStateSha256"),
expected_container_ids=bindings.get("expectedContainerIds"),
)
assert not broker_calls
def test_observe_http_has_no_request_id_or_mutating_helpers(controller, monkeypatch):
module, _projects = controller
calls = []
def observe(payload, **kwargs):
calls.append((payload, kwargs))
return {
"status": "observed",
"repository": REPOSITORY,
"sourceFingerprint": CAS["expectedSourceFingerprint"],
"runtimeFingerprint": CAS["expectedRuntimeFingerprint"],
"expectedStateSha256": CAS["expectedStateSha256"],
"containerIDs": CAS["expectedContainerIds"],
"imageIDs": {"registered-app": "sha256:" + "e" * 64},
"environmentMismatchKeys": ["registered-app/SERVER_URL"],
}
fake = SimpleNamespace(
observe=observe,
read_registered_state=lambda *_args: ({}, None),
RegisteredLegacyAdoptionError=ValueError,
)
monkeypatch.setitem(sys.modules, "registered_legacy_adoption", fake)
monkeypatch.setattr(
module,
"_registered_legacy_api_reader",
lambda *_args: pytest.fail("observe must not read Git API"),
)
code, result = call(
module,
PREFIX + "observe",
{"repository": REPOSITORY},
"_handle_registered_legacy",
)
assert code == 200 and result["status"] == "observed"
assert calls[0][0] == {"repository": REPOSITORY}
assert "environment" not in result
assert result["environmentMismatchKeys"] == ["registered-app/SERVER_URL"]
assert (
call(
module,
PREFIX + "observe",
{"repository": REPOSITORY, "requestId": REQUEST_ID},
"_handle_registered_legacy",
)[0]
== 400
)
def test_observe_mcp_schema_is_readonly_and_calls_no_other_operation(broker_calls):
async def exercise():
async with Client(mcp_server.appops_mcp) as client:
listing = await client.list_tools()
tool = next(
item
for item in listing.tools
if item.name == "appops.registered_legacy.observe"
)
assert tool.annotations.read_only_hint is True
assert tool.annotations.destructive_hint is False
assert set(tool.input_schema["properties"]) == {"repository"}
result = await client.call_tool(tool.name, {"repository": REPOSITORY})
assert not result.is_error
asyncio.run(exercise())
assert len(broker_calls) == 1
assert broker_calls[0][2]["payload"] == {"repository": REPOSITORY}
@pytest.mark.parametrize(
"changes",
[
{"captureRuntimeEnvironment": "true", **CAS},
{"captureRuntimeEnvironment": 1, **CAS},
{"captureRuntimeEnvironment": False, **CAS},
{"captureRuntimeEnvironment": True},
{"captureRuntimeEnvironment": True, **CAS, "expectedSourceFingerprint": "bad"},
{"captureRuntimeEnvironment": True, **CAS, "expectedContainerIds": {}},
{"environment": {"PRIVATE_KEY": "caller-value-must-not-escape"}},
],
)
def test_broker_baseline_rejects_incomplete_or_untyped_capture_before_transport(
changes, broker_calls
):
with pytest.raises(ValueError) as error:
DeploymentBrokerClient().registered_legacy_operation(
"baseline", {"repository": REPOSITORY, "requestId": REQUEST_ID, **changes}
)
assert "caller-value-must-not-escape" not in str(error.value)
assert not broker_calls
@pytest.mark.parametrize(
"changes",
[
{"captureRuntimeEnvironment": "true", **CAS},
{"captureRuntimeEnvironment": 1, **CAS},
{"captureRuntimeEnvironment": False, **CAS},
{"captureRuntimeEnvironment": True},
{
"captureRuntimeEnvironment": True,
**CAS,
"environment": {"PRIVATE_KEY": "caller-value-must-not-escape"},
},
],
)
def test_http_capture_shape_rejects_before_registry_or_core(
changes, controller, monkeypatch
):
module, _ = controller
monkeypatch.setattr(
module,
"_load_projects",
lambda: pytest.fail("invalid capture must not reach core"),
)
code, result = call(
module,
PREFIX + "baseline",
{"repository": REPOSITORY, "requestId": REQUEST_ID, **changes},
"_handle_registered_legacy",
)
assert code == 400 and result == {"status": "invalid_contract"}
assert "caller-value-must-not-escape" not in json.dumps(result)
def test_http_observation_extra_environment_is_rejected_without_value_echo(
controller, monkeypatch
):
module, _ = controller
fake = SimpleNamespace(
observe=lambda *_args, **_kwargs: {
"status": "observed",
"environment": {"PRIVATE_KEY": "private-observer-value"},
},
read_registered_state=lambda *_args: ({}, None),
RegisteredLegacyAdoptionError=type("AdoptionError", (RuntimeError,), {}),
)
monkeypatch.setitem(sys.modules, "registered_legacy_adoption", fake)
code, result = call(
module,
PREFIX + "observe",
{"repository": REPOSITORY},
"_handle_registered_legacy",
)
assert code == 503 and result == {"status": "registered_controller_unavailable"}
assert "private-observer-value" not in json.dumps(result)
def test_observe_requires_existing_signed_controller_authentication(
controller, monkeypatch
):
module, _ = controller
monkeypatch.setattr(
module, "_self_upgrade_signature_is_valid", lambda *_args, **_kwargs: False
)
monkeypatch.setattr(
module,
"_load_projects",
lambda: pytest.fail("unauthenticated observe must not read registry"),
)
assert (
call(
module,
PREFIX + "observe",
{"repository": REPOSITORY},
"_handle_registered_legacy",
)[0]
== 403
)
def test_mcp_wire_capture_boolean_does_not_coerce_strings_or_numbers(broker_calls):
async def exercise():
async with Client(mcp_server.appops_mcp) as client:
listing = await client.list_tools()
tool = next(
item
for item in listing.tools
if item.name == "appops.registered_legacy.baseline"
)
assert tool.annotations.read_only_hint is True
assert (
tool.input_schema["properties"]["capture_runtime_environment"][
"default"
]
is False
)
for value in ("true", 1):
result = await client.call_tool(
tool.name,
{
"repository": REPOSITORY,
"request_id": REQUEST_ID,
"capture_runtime_environment": value,
},
)
assert result.is_error
asyncio.run(exercise())
assert not broker_calls
def test_http_observe_real_registered_runtime_returns_only_safe_keynames(
controller, monkeypatch, tmp_path
):
from contextlib import nullcontext
from functools import partial
from test_registered_legacy_runtime import (
_load_module,
production_runtime as runtime_fixture,
)
core = _load_module()
runtime, boundary, project, model = runtime_fixture.__wrapped__(
core, monkeypatch, tmp_path
)
repository = "jens/quiz-monitor"
model["services"]["quiz-server"]["environment"]["KNOWLEDGE_OPTIMIZER_MODEL"] = (
"intended-test-model"
)
boundary.live["Config"]["Env"].append(
"KNOWLEDGE_OPTIMIZER_MODEL=observed-test-model"
)
monkeypatch.setattr(core, "_global_deploy_lock", lambda: nullcontext())
# Reuse the adapter fixture's validated registration against its temporary source tree.
monkeypatch.setattr(
core,
"validate_registered_legacy_target",
lambda repo, target: target["slug"]
if repo == repository
else pytest.fail("wrong registered target"),
)
monkeypatch.setattr(
core, "read_registered_state", lambda *_args, **_kwargs: ({}, None)
)
monkeypatch.setattr(
core,
"observe",
partial(core.observe, runtime=runtime, state_root=tmp_path / "state"),
)
monkeypatch.setitem(sys.modules, "registered_legacy_adoption", core)
module, _projects = controller
monkeypatch.setattr(module, "_load_projects", lambda: {repository: project})
code, result = call(
module,
PREFIX + "observe",
{"repository": repository},
"_handle_registered_legacy",
)
assert code == 200 and result["status"] == "observed"
assert result["environmentMismatchKeys"] == [
"quiz-server/KNOWLEDGE_OPTIMIZER_MODEL"
]
assert result["expectedStateSha256"] == core._state_digest(None)
assert set(result) == {
"status",
"repository",
"expectedStateSha256",
"sourceFingerprint",
"runtimeFingerprint",
"containerIDs",
"imageIDs",
"environmentMismatchKeys",
}
serialized = json.dumps(result)
assert "intended-test-model" not in serialized
assert "observed-test-model" not in serialized
assert "198.19.1.2" not in serialized
def test_http_complete_capture_forwards_no_environment_values(controller, monkeypatch):
module, _projects = controller
called = []
fake = SimpleNamespace(
baseline=lambda payload, **_kwargs: called.append(payload)
or {
"status": "baseline_captured",
"capturedEnvironmentKeys": ["service/SETTING"],
},
read_registered_state=lambda *_args: ({}, None),
RegisteredLegacyAdoptionError=ValueError,
)
monkeypatch.setitem(sys.modules, "registered_legacy_adoption", fake)
payload = {
"repository": REPOSITORY,
"requestId": REQUEST_ID,
"captureRuntimeEnvironment": True,
**CAS,
}
code, result = call(
module, PREFIX + "baseline", payload, "_handle_registered_legacy"
)
assert code == 200 and called == [payload]
assert result == {
"status": "baseline_captured",
"capturedEnvironmentKeys": ["service/SETTING"],
}
@@ -0,0 +1,466 @@
from __future__ import annotations
import copy
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
MODULE_PATH = ROOT / "ops" / "gitea-deploy-broker" / "registered_legacy_adoption.py"
REPOSITORY = "jens/quiz-monitor"
REQUEST_ID = "6f9619ff-8b86-4011-b42d-00c04fc964ff"
KEY = b"k" * 32
IMAGE_ID = "sha256:" + "b" * 64
CONTAINER_ID = "c" * 64
MISMATCH_KEY = "quiz-server/KNOWLEDGE_OPTIMIZER_MODEL"
STATE = b'{"schema_version":3,"live_status":"unknown"}\n'
SOURCE_FP = "1" * 64
RUNTIME_FP = "2" * 64
SECRET_VALUE = "gpt-6-luna"
def _load_module():
spec = importlib.util.spec_from_file_location("registered_legacy_capture_test", MODULE_PATH)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
@pytest.fixture
def module():
return _load_module()
@pytest.fixture
def project(tmp_path):
return {
"slug": "quiz-monitor",
"branch": "main",
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": ["quiz-monitor-quiz-server-1"],
"source_dir": "/srv/example/appdata/quiz-monitor/source",
"verify_oci_revision": True,
}
def _observation():
container = {
"Id": CONTAINER_ID,
"Image": IMAGE_ID,
"Config": {
"Labels": {
"com.docker.compose.project": "quiz-monitor",
"com.docker.compose.service": "quiz-server",
},
"Env": ["KNOWLEDGE_OPTIMIZER_MODEL=" + SECRET_VALUE],
},
"State": {"Running": True},
}
return {
"status": "observed",
"sourceFingerprint": SOURCE_FP,
"runtimeFingerprint": RUNTIME_FP,
"imageIDs": {"quiz-monitor-quiz-server-1": IMAGE_ID},
"environmentMismatchKeys": [MISMATCH_KEY],
"capturedEnvironmentKeys": [MISMATCH_KEY],
"artifacts": {
"containers": {
"quiz-monitor-quiz-server-1": {
"container_id": CONTAINER_ID,
"image_id": IMAGE_ID,
"image_ref": "quiz-monitor:latest",
"service": "quiz-server",
"running": True,
},
},
},
"runtimeConfiguration": {
"composeModel": {
"services": {
"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": "gpt-5.5"},
},
},
},
"containers": {"quiz-monitor-quiz-server-1": container},
"imageEnvironments": {IMAGE_ID: {}},
},
}
class _CaptureRuntime:
def __init__(self, observation=None):
self.observation = observation or _observation()
self.strict_calls = 0
self.capture_calls = 0
def observe(self, repository, _project, *, receipt_key):
self.strict_calls += 1
assert repository == REPOSITORY
assert receipt_key == KEY
raise RuntimeError("strict observer rejects environment mismatch")
def observe_for_baseline_capture(self, repository, _project, *, receipt_key):
self.capture_calls += 1
assert repository == REPOSITORY
assert receipt_key == KEY
return copy.deepcopy(self.observation)
def _payload():
observation = _observation()
return {
"repository": REPOSITORY,
"requestId": REQUEST_ID,
"captureRuntimeEnvironment": True,
"expectedSourceFingerprint": observation["sourceFingerprint"],
"expectedRuntimeFingerprint": observation["runtimeFingerprint"],
"expectedStateSha256": hashlib.sha256(STATE).hexdigest(),
"expectedContainerIds": {
"quiz-monitor-quiz-server-1": CONTAINER_ID,
},
}
def _read_state(_repository, _project):
return {"live_status": "unknown"}, STATE
def test_observe_returns_only_cas_metadata_and_environment_key_names(module, project):
runtime = _CaptureRuntime()
result = module.observe(
{"repository": REPOSITORY},
registry={REPOSITORY: project},
read_state=_read_state,
runtime=runtime,
receipt_key=KEY,
)
assert result == {
"status": "observed",
"repository": REPOSITORY,
"expectedStateSha256": hashlib.sha256(STATE).hexdigest(),
"sourceFingerprint": SOURCE_FP,
"runtimeFingerprint": RUNTIME_FP,
"containerIDs": {"quiz-monitor-quiz-server-1": CONTAINER_ID},
"imageIDs": {"quiz-monitor-quiz-server-1": IMAGE_ID},
"environmentMismatchKeys": [MISMATCH_KEY],
}
encoded = json.dumps(result)
assert SECRET_VALUE not in encoded
assert "gpt-5.5" not in encoded
assert runtime.strict_calls == 0
assert runtime.capture_calls == 1
def test_explicit_capture_is_hmac_bound_private_and_idempotent(module, project, tmp_path):
runtime = _CaptureRuntime()
payload = _payload()
first = module.baseline(
payload,
registry={REPOSITORY: project},
read_state=_read_state,
runtime=runtime,
receipt_key=KEY,
state_root=tmp_path,
)
second = module.baseline(
payload,
registry={REPOSITORY: project},
read_state=_read_state,
runtime=runtime,
receipt_key=KEY,
state_root=tmp_path,
)
assert first == second
assert first["capturedEnvironmentKeys"] == [MISMATCH_KEY]
assert SECRET_VALUE not in json.dumps(first)
paths = module._paths(tmp_path, "quiz-monitor", REQUEST_ID)
signed, _ = module._load_signed(paths["baseline"], KEY, module.BASELINE_SCHEMA)
private, _ = module._load_private(paths["private"], KEY)
assert signed["environmentCapture"]["containerIDs"] == payload["expectedContainerIds"]
assert signed["environmentCapture"]["capturedEnvironmentKeys"] == [MISMATCH_KEY]
assert private["environmentCapture"] == signed["environmentCapture"]
assert SECRET_VALUE in json.dumps(private)
assert module._capture_runtime_environment(
private,
captured_environment_keys=signed["environmentCapture"]["capturedEnvironmentKeys"],
) == {"quiz-server": {"KNOWLEDGE_OPTIMIZER_MODEL": SECRET_VALUE}}
assert runtime.strict_calls == 0
@pytest.mark.skipif(os.name == "nt", reason="POSIX private-file mode enforcement")
def test_candidate_compose_proof_rejects_group_readable_environment_values(module, tmp_path):
paths = module._paths(tmp_path, "quiz-monitor", REQUEST_ID)
module._write_signed(
paths["candidate"],
{"schema": module.CANDIDATE_SCHEMA, "composeModel": {"secret": SECRET_VALUE}},
KEY,
)
paths["candidate"].chmod(0o640)
with pytest.raises(module.RegisteredLegacyAdoptionError):
module._load_signed(paths["candidate"], KEY, module.CANDIDATE_SCHEMA)
@pytest.mark.parametrize(
"captured_keys",
[[], ["quiz-server/UNDECLARED_RUNTIME_VALUE"]],
)
def test_candidate_environment_preservation_requires_exact_signed_capture_keys(
module, captured_keys
):
private = _observation()["runtimeConfiguration"]
with pytest.raises(module.RegisteredLegacyAdoptionError):
module._capture_runtime_environment(
{"runtimeConfiguration": private},
captured_environment_keys=captured_keys,
)
@pytest.mark.parametrize(
"mutate",
[
lambda payload: payload.pop("expectedContainerIds"),
lambda payload: payload.update(expectedSourceFingerprint="f" * 64),
lambda payload: payload.update(expectedRuntimeFingerprint="f" * 64),
lambda payload: payload.update(expectedStateSha256="f" * 64),
lambda payload: payload.update(expectedContainerIds={"quiz-monitor-quiz-server-1": "f" * 64}),
lambda payload: payload.update(unexpected="value"),
],
)
def test_capture_rejects_incomplete_or_changed_cas_without_writing(module, project, tmp_path, mutate):
payload = _payload()
mutate(payload)
with pytest.raises(module.RegisteredLegacyAdoptionError):
module.baseline(
payload,
registry={REPOSITORY: project},
read_state=_read_state,
runtime=_CaptureRuntime(),
receipt_key=KEY,
state_root=tmp_path,
)
assert not list(tmp_path.rglob("*.baseline.json"))
assert not list(tmp_path.rglob("*.private.json"))
@pytest.mark.parametrize(
"payload",
[
{"repository": REPOSITORY, "requestId": REQUEST_ID, "expectedRuntimeFingerprint": "2" * 64},
{"repository": REPOSITORY, "requestId": REQUEST_ID,
"captureRuntimeEnvironment": False, "expectedStateSha256": hashlib.sha256(STATE).hexdigest()},
],
)
def test_default_baseline_does_not_accept_capture_compare_fields(module, project, tmp_path, payload):
with pytest.raises(module.RegisteredLegacyAdoptionError):
module.baseline(
payload,
registry={REPOSITORY: project},
read_state=_read_state,
runtime=_CaptureRuntime(),
receipt_key=KEY,
state_root=tmp_path,
)
assert not list(tmp_path.rglob("*.baseline.json"))
def test_capture_reobserves_exact_runtime_and_container_preimage(module, project, tmp_path):
runtime = _CaptureRuntime()
module.baseline(
_payload(), registry={REPOSITORY: project}, read_state=_read_state,
runtime=runtime, receipt_key=KEY, state_root=tmp_path,
)
paths = module._paths(tmp_path, "quiz-monitor", REQUEST_ID)
with pytest.raises(module.RegisteredLegacyAdoptionError, match="source or runtime changed"):
changed = _observation()
changed["runtimeFingerprint"] = "f" * 64
module._load_current_baseline(
REPOSITORY, "quiz-monitor", REQUEST_ID, paths, KEY, _read_state,
project, tmp_path, _CaptureRuntime(changed),
)
changed_container = _observation()
changed_container["artifacts"]["containers"]["quiz-monitor-quiz-server-1"]["container_id"] = "d" * 64
with pytest.raises(module.RegisteredLegacyAdoptionError, match="container identity changed"):
module._load_current_baseline(
REPOSITORY, "quiz-monitor", REQUEST_ID, paths, KEY, _read_state,
project, tmp_path, _CaptureRuntime(changed_container),
)
def test_private_capture_record_requires_owner_only_read_permissions(module, tmp_path):
if module.os.name == "nt":
pytest.skip("POSIX file-mode enforcement is not available on Windows")
path = tmp_path / "private.json"
module._write_signed(
path,
{"schema": "itworx.registered-legacy-private/v1", "repository": REPOSITORY},
KEY,
)
path.chmod(0o640)
with pytest.raises(module.RegisteredLegacyAdoptionError, match="permissions"):
module._load_private(path, KEY)
path.chmod(0o600)
private, _ = module._load_private(path, KEY)
assert private["repository"] == REPOSITORY
def test_signed_capture_value_is_required_in_the_rendered_candidate_model(module, project):
captured_key = "quiz-server/KNOWLEDGE_OPTIMIZER_MODEL"
current_model = {"services": {"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": SECRET_VALUE},
"image": "quiz-server:current",
}}}
candidate_model = {"services": {"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": SECRET_VALUE},
"image": "quiz-server:candidate",
}}}
binding = {
"schema": "itworx.registered-legacy-env-capture/v1",
"sourceFingerprint": SOURCE_FP,
"runtimeFingerprint": RUNTIME_FP,
"expectedStateSha256": hashlib.sha256(STATE).hexdigest(),
"containerIDs": {"quiz-monitor-quiz-server-1": CONTAINER_ID},
"capturedEnvironmentKeys": [captured_key],
}
runtime_container = {
"Image": IMAGE_ID,
"Config": {
"Labels": {"com.docker.compose.service": "quiz-server"},
"Env": [f"KNOWLEDGE_OPTIMIZER_MODEL={SECRET_VALUE}"],
},
}
private = {
"environmentCapture": binding,
"runtimeConfiguration": {
"composeModel": current_model,
"containers": {"quiz-monitor-quiz-server-1": runtime_container},
"imageEnvironments": {IMAGE_ID: {}},
},
}
baseline = {"environmentCapture": binding, "sourceFingerprint": SOURCE_FP,
"runtimeFingerprint": RUNTIME_FP,
"expectedStateSha256": hashlib.sha256(STATE).hexdigest()}
normalized = module._candidate_runtime_comparison_model(
candidate_model, current_model, baseline, private, project,
)
assert normalized["services"]["quiz-server"]["environment"] == {
"KNOWLEDGE_OPTIMIZER_MODEL": SECRET_VALUE,
}
def test_raw_source_comparison_allows_only_the_signed_default_or_capture_value(module, project):
captured_key = "quiz-server/KNOWLEDGE_OPTIMIZER_MODEL"
baseline_model = {"services": {"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": SECRET_VALUE},
"image": "quiz-server:current",
}}}
binding = {
"schema": "itworx.registered-legacy-env-capture/v1",
"sourceFingerprint": SOURCE_FP,
"runtimeFingerprint": RUNTIME_FP,
"expectedStateSha256": hashlib.sha256(STATE).hexdigest(),
"containerIDs": {"quiz-monitor-quiz-server-1": CONTAINER_ID},
"capturedEnvironmentKeys": [captured_key],
}
runtime_container = {
"Image": IMAGE_ID,
"Config": {
"Labels": {"com.docker.compose.service": "quiz-server"},
"Env": [f"KNOWLEDGE_OPTIMIZER_MODEL={SECRET_VALUE}"],
},
}
private = {
"environmentCapture": binding,
"capturedEnvironmentSource": {"quiz-server": {"KNOWLEDGE_OPTIMIZER_MODEL": "gpt-5.5"}},
"runtimeConfiguration": {
"composeModel": baseline_model,
"containers": {"quiz-monitor-quiz-server-1": runtime_container},
"imageEnvironments": {IMAGE_ID: {}},
},
}
baseline = {"environmentCapture": binding, "sourceFingerprint": SOURCE_FP,
"runtimeFingerprint": RUNTIME_FP,
"expectedStateSha256": hashlib.sha256(STATE).hexdigest()}
for source_value in ("gpt-5.5", SECRET_VALUE):
candidate_model = {"services": {"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": source_value},
"image": "quiz-server:candidate",
}}}
normalized = module._candidate_source_comparison_model(
candidate_model, baseline_model, baseline, private, project,
)
assert normalized["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] == SECRET_VALUE
third_value_model = {"services": {"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": "gpt-6.2"},
"image": "quiz-server:candidate",
}}}
with pytest.raises(module.RegisteredLegacyAdoptionError):
module._candidate_source_comparison_model(
third_value_model, baseline_model, baseline, private, project,
)
def test_baseline_rechecks_controller_state_before_public_signed_commit(module, project, tmp_path):
calls = 0
def changing_state(_repository, _project):
nonlocal calls
calls += 1
if calls == 1:
return {"live_status": "unknown"}, STATE
return {"live_status": "changed"}, b'{"live_status":"changed"}\n'
with pytest.raises(module.RegisteredLegacyAdoptionError, match="state changed before"):
module.baseline(
_payload(), registry={REPOSITORY: project}, read_state=changing_state,
runtime=_CaptureRuntime(), receipt_key=KEY, state_root=tmp_path,
)
assert calls == 2
assert not list(tmp_path.rglob("*.baseline.json"))
assert not list(tmp_path.rglob("*.private.json"))
@pytest.mark.parametrize("drift", ["source", "runtime", "container", "mismatch_keys"])
def test_baseline_reobserves_exact_runtime_before_public_commit(module, project, tmp_path, drift):
class _DriftingRuntime(_CaptureRuntime):
def observe_for_baseline_capture(self, repository, target, *, receipt_key):
value = super().observe_for_baseline_capture(repository, target, receipt_key=receipt_key)
if self.capture_calls == 2:
if drift == "source":
value["sourceFingerprint"] = "f" * 64
elif drift == "runtime":
value["runtimeFingerprint"] = "f" * 64
elif drift == "container":
value["artifacts"]["containers"]["quiz-monitor-quiz-server-1"][
"container_id"
] = "d" * 64
else:
value["environmentMismatchKeys"] = []
value["capturedEnvironmentKeys"] = []
return value
with pytest.raises(module.RegisteredLegacyAdoptionError, match="runtime changed before baseline"):
module.baseline(
_payload(), registry={REPOSITORY: project}, read_state=_read_state,
runtime=_DriftingRuntime(), receipt_key=KEY, state_root=tmp_path,
)
assert not list(tmp_path.rglob("*.baseline.json"))
assert not list(tmp_path.rglob("*.private.json"))
@@ -0,0 +1,167 @@
from __future__ import annotations
from types import SimpleNamespace
import pytest
from test_deploy_broker import deploy_module as _deploy_fixture
from test_hub_component_transport import controller as _controller_fixture
@pytest.fixture
def deploy_module():
return _deploy_fixture.__wrapped__()
@pytest.fixture
def controller(monkeypatch):
return _controller_fixture.__wrapped__(monkeypatch)
@pytest.mark.parametrize("mode", ["deploy", "rollback"])
def test_original_git_objects_are_bound_before_identity_reads(deploy_module, monkeypatch, mode):
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.delenv("GIT_NO_REPLACE_OBJECTS", raising=False)
assert deploy_module.bind_registered_legacy_request({}, mode) == ""
assert deploy_module.os.environ["GIT_NO_REPLACE_OBJECTS"] == "1"
def test_known_registered_restore_keeps_verified_revision_and_fresh_runtime_ids(deploy_module):
prior_revision = "a" * 40
restored_artifacts = {
"containers": {
"quiz-monitor-quiz-server-1": {
"container_id": "b" * 64,
"image_id": "sha256:" + "c" * 64,
}
},
"registered_legacy": {"requestId": "restored-request", "revision": prior_revision},
}
restored_receipt = {"revision": prior_revision, "containers": restored_artifacts["containers"]}
project = {
"_registered_legacy_restored": True,
"_registered_legacy_restored_revision": prior_revision,
"_registered_legacy_restored_artifacts": restored_artifacts,
"_registered_legacy_restored_receipt": restored_receipt,
}
result = deploy_module.resolve_failed_live_state(
{"live_revision": prior_revision, "live_status": "healthy"},
{"revision": prior_revision, "legacy_provenance": False, "registered_legacy": {}},
project,
production_started=True,
failure_stage="verification",
restore_error=None,
)
assert result["live_revision"] == prior_revision
assert result["live_status"] == "healthy"
assert result["live_legacy_provenance"] is False
assert result["live_artifacts"] == restored_artifacts
assert result["live_receipt"] == restored_receipt
def test_failed_known_registered_restore_keeps_provenance_unknown(deploy_module):
prior_revision = "a" * 40
result = deploy_module.resolve_failed_live_state(
{"live_revision": prior_revision, "live_status": "healthy"},
{"revision": prior_revision, "legacy_provenance": False, "registered_legacy": {}},
{},
production_started=True,
failure_stage="verification",
restore_error=RuntimeError("restore failed"),
)
assert result["live_status"] == "unknown"
assert result["live_artifacts"] == {}
assert result["live_receipt"] == {}
def test_known_restore_source_callback_precedes_verification_and_state_binding(
deploy_module, monkeypatch
):
previous_revision = "a" * 40
failed_revision = "d" * 40
image_ids = {"quiz-monitor-quiz-server-1": "sha256:" + "c" * 64}
restored_receipt = {"revision": previous_revision, "build": {"revision": previous_revision}}
artifacts = {"containers": {}, "registered_legacy": {"revision": previous_revision}}
events = []
project = {}
def core_restore(_repository, _project, _snapshot, _failed, **kwargs):
events.append("signed-preimage-verified")
restore_source = kwargs.get("restore_source")
assert callable(restore_source), "known restore requires a source callback after signed validation"
restore_source(previous_revision)
events.append("restored-runtime-signed")
return {"revision": previous_revision, "artifacts": artifacts,
"imageIDs": image_ids, "liveReceipt": restored_receipt}
def source_restore(_project, _repository, revision):
assert revision == previous_revision
events.append("prior-source-restored")
def verify(_project, metadata, *, expected_image_ids):
assert metadata["revision"] == previous_revision
assert expected_image_ids == image_ids
events.append("health-and-provenance-verified")
return restored_receipt
monkeypatch.setattr(deploy_module, "read_state", lambda _project: {})
monkeypatch.setattr(deploy_module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(deploy_module, "_load_registered_legacy_adoption", lambda: SimpleNamespace(
restore_deployment_snapshot=core_restore
))
monkeypatch.setattr(deploy_module, "restore_live_source", source_restore)
monkeypatch.setattr(deploy_module, "validate_recorded_receipt", lambda *_args: restored_receipt["build"])
monkeypatch.setattr(deploy_module, "verify_deployment", verify)
deploy_module.restore_compose_snapshot(
project,
{"revision": previous_revision, "legacy_provenance": False, "registered_legacy": {}},
"jens/quiz-monitor", failed_revision,
)
assert events == ["signed-preimage-verified", "prior-source-restored",
"restored-runtime-signed", "health-and-provenance-verified"]
assert project["_registered_legacy_restored"] is True
assert project["_registered_legacy_restored_revision"] == previous_revision
assert project["_registered_legacy_restored_receipt"] == restored_receipt
@pytest.mark.parametrize("receipt_matches", [True, False])
def test_typed_rollback_worker_supplies_fixed_source_and_provenance_verifiers(
controller, monkeypatch, receipt_matches
):
import sys
module, projects = controller
repository = "jens/quiz-monitor"
previous_revision = "a" * 40
receipt = {"revision": previous_revision, "build": {"revision": previous_revision}}
project = {"slug": "quiz-monitor", "source_dir": "/fixed/source"}
projects[repository] = project
calls = []
def rollback(_payload, **kwargs):
kwargs["restore_source"](previous_revision)
kwargs["verify_restored"]({"revision": previous_revision, "liveReceipt": receipt,
"imageIDs": {"quiz": "immutable-image"}})
calls.append("state-cas")
monkeypatch.setitem(sys.modules, "registered_legacy_adoption", SimpleNamespace(
rollback=rollback, read_registered_state=lambda *_a: ({}, None),
RegisteredLegacyAdoptionError=ValueError,
record_worker_failure=lambda *_a, **_k: calls.append("failed"),
))
monkeypatch.setitem(sys.modules, "deploy", SimpleNamespace(
restore_live_source=lambda selected, repo, revision: calls.append(
("source", selected, repo, revision)),
validate_recorded_receipt=lambda selected, raw, repo, revision: receipt["build"],
verify_deployment=lambda selected, build, **kwargs: calls.append("verified") or (
receipt if receipt_matches else {**receipt, "revision": "f" * 40}
),
))
module._run_registered_legacy_rollback({
"repository": repository, "requestId": "11111111-1111-4111-8111-111111111111",
}, b"k" * 32)
assert calls == [("source", project, repository, previous_revision), "verified",
"state-cas" if receipt_matches else "failed"]
@@ -0,0 +1,398 @@
from __future__ import annotations
import copy
import importlib.util
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
RUNTIME_TEST_PATH = ROOT / "backend" / "tests" / "test_registered_legacy_runtime.py"
_runtime_spec = importlib.util.spec_from_file_location(
"registered_legacy_followup_runtime_support", RUNTIME_TEST_PATH,
)
assert _runtime_spec and _runtime_spec.loader
runtime_support = importlib.util.module_from_spec(_runtime_spec)
_runtime_spec.loader.exec_module(runtime_support)
REPOSITORY = runtime_support.REPOSITORY
REQUEST_ID = runtime_support.REQUEST_ID
TARGET_SHA = runtime_support.TARGET_SHA
KEY = runtime_support.KEY
CONTAINER_NAME = runtime_support.CONTAINER_NAME
CAPTURED_VALUE = "gpt-6-luna"
FOLLOWUP_SHA = "f" * 40
RETRY_SHA = "9" * 40
FOLLOWUP_IMAGE = "sha256:" + "d" * 64
def _validation_api(revision: str, run_id: int):
def api_json(path, *, query):
if path == f"/repos/{REPOSITORY}/branches/main":
return {"protected": True, "commit": {"id": revision}}
if path == f"/repos/{REPOSITORY}/actions/runs":
return {
"total_count": 1,
"workflow_runs": [{
"id": run_id,
"run_number": run_id + 10,
"run_attempt": 1,
"event": "push",
"head_sha": revision,
"head_branch": "main",
"path": "managed-validation.yml@refs/heads/main",
"created_at": "2026-10-07T12:00:00Z",
"status": "completed",
"conclusion": "success",
"repository": {"full_name": REPOSITORY},
"head_repository": {"full_name": REPOSITORY},
"pull_requests": [],
}],
}
if path == f"/repos/{REPOSITORY}/actions/runs/{run_id}/jobs":
return {
"total_count": 1,
"jobs": [{
"id": run_id + 100,
"name": "full",
"run_id": run_id,
"run_attempt": 1,
"head_sha": revision,
"head_branch": "main",
"status": "completed",
"conclusion": "success",
}],
}
raise AssertionError(f"unexpected fixed validation request: {path} {query}")
return api_json
def _persist_state(state_root: Path, state: dict) -> None:
raw = (json.dumps(state, sort_keys=True, separators=(",", ":")) + "\n").encode()
(state_root / "quiz-monitor.json").write_bytes(raw)
def _initial_active_case(tmp_path: Path, monkeypatch, *, capture: bool):
module = runtime_support._load_module()
source = tmp_path / "quiz-source"
source.mkdir()
(source / "docker-compose.yml").write_text("services: {}\n", encoding="utf-8")
project = runtime_support._project(source)
model = runtime_support._compose_model()
if capture:
model["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] = "gpt-5.5"
boundary = runtime_support._DockerComposeBoundary(module, project, model)
if capture:
boundary.live["Config"]["Env"].append(
f"KNOWLEDGE_OPTIMIZER_MODEL={CAPTURED_VALUE}"
)
boundary.install(monkeypatch)
runtime, registry, _read_state, state_bytes, unknown_snapshot = (
runtime_support._adopt_with_production_runtime(
module, boundary, project, tmp_path, monkeypatch,
capture_runtime_environment=capture,
)
)
live, deployed = runtime_support._candidate_deployment(
module, runtime, boundary, project, registry, state_bytes, tmp_path,
use_signed_overlay=capture,
)
live_containers = copy.deepcopy(deployed["live_artifacts"]["containers"])
standard_receipt = {
"schema": "gitea-deploy-receipt.v1",
"repository": REPOSITORY,
"revision": TARGET_SHA,
"containers": live_containers,
}
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": TARGET_SHA,
"live_revision": TARGET_SHA,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": copy.deepcopy(deployed["live_artifacts"]),
"live_receipt": standard_receipt,
"rollback_snapshot": copy.deepcopy(unknown_snapshot),
"status": "success",
}
_persist_state(tmp_path, state)
assert live["revision"] == TARGET_SHA
return module, runtime, boundary, project, model, registry, state, unknown_snapshot
def _prepare_followup_candidate(module, runtime, boundary, project, state_root,
revision: str, run_id: int):
state = json.loads((state_root / "quiz-monitor.json").read_text(encoding="utf-8"))
prepared = module.prepare_followup_deployment(
REPOSITORY, project, revision, state,
api_json=_validation_api(revision, run_id),
receipt_key=KEY, state_root=state_root, runtime=runtime,
)
request_id = prepared["requestId"]
project_bound = dict(project)
project_bound.update({
"_registered_legacy_request_id": request_id,
"_registered_legacy_repository": REPOSITORY,
})
source_model = copy.deepcopy(boundary.model)
# The current model is the live overlay; the next protected source carries
# the original declared model value, while the signed overlay restores the
# captured runtime value.
origin_request_id = prepared["originUnknownSnapshot"]["registered_legacy"]["requestId"]
origin_paths = module._paths(state_root, "quiz-monitor", origin_request_id)
origin_baseline, origin_private, _ = module._baseline_payload(
REPOSITORY, "quiz-monitor", origin_request_id, origin_paths, KEY,
)
origin_capture = module._capture_binding(origin_baseline, origin_private, project)
if origin_capture is not None:
source_model["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] = "gpt-5.5"
module.prepare_build_configuration(
REPOSITORY, project_bound, request_id, revision, source_model, {},
{
"APPOPS_SOURCE_REVISION": revision,
"APPOPS_SOURCE_REPOSITORY": REPOSITORY,
"APPOPS_SOURCE_TREE": "d" * 40,
"APPOPS_BUILD_ID": f"test-build-{run_id}",
"APPOPS_BUILD_DATE": "2026-10-07T12:00:00Z",
},
receipt_key=KEY, state_root=state_root,
)
overlay_path = module.validated_compose_overlay(
project_bound, receipt_key=KEY, state_root=state_root, repository=REPOSITORY,
)
assert overlay_path is not None
overlay = json.loads(overlay_path.read_text(encoding="utf-8"))
if origin_capture is not None:
assert overlay["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] == CAPTURED_VALUE
boundary.model = runtime_support._compose_config_resolve_escaped(overlay)
boundary.images["quiz-monitor:latest"] = {
"Id": FOLLOWUP_IMAGE,
"Config": {
"Labels": {
"org.opencontainers.image.revision": revision,
"org.opencontainers.image.source": REPOSITORY,
},
"Cmd": list(runtime_support.QUIZ_IMAGE_CMD),
"Entrypoint": None,
"WorkingDir": "/app",
},
}
boundary.images[FOLLOWUP_IMAGE] = copy.deepcopy(boundary.images["quiz-monitor:latest"])
module.prepare_candidate_deployment(
REPOSITORY, project_bound, request_id, revision,
receipt_key=KEY, state_root=state_root, runtime=runtime,
)
module.verify_candidate_pre_mutation(
REPOSITORY, project_bound, request_id, revision,
receipt_key=KEY, state_root=state_root, runtime=runtime,
api_json=_validation_api(revision, run_id),
)
boundary.live = runtime_support._container(
image_id=FOLLOWUP_IMAGE, container_id="9" * 64,
hostname="followup-generated-hostname", config_hash="followup-config",
)
boundary.live["Config"]["Image"] = "quiz-monitor:latest"
if origin_capture is not None:
boundary.live["Config"]["Env"].append(
f"KNOWLEDGE_OPTIMIZER_MODEL={CAPTURED_VALUE}"
)
module.record_candidate_runtime(
REPOSITORY, project_bound, request_id, revision,
receipt_key=KEY, state_root=state_root, runtime=runtime,
)
containers = {
CONTAINER_NAME: {"container_id": "9" * 64, "image_id": FOLLOWUP_IMAGE}
}
receipt = {
"schema": "gitea-deploy-receipt.v1", "repository": REPOSITORY,
"revision": revision, "containers": copy.deepcopy(containers),
}
live_binding = module.record_live_runtime(
REPOSITORY, project_bound, request_id, revision, receipt,
receipt_key=KEY, state_root=state_root, runtime=runtime,
)
state = {
"schema_version": 3, "repository": REPOSITORY,
"slug": "quiz-monitor", "compose_project": "quiz-monitor",
"revision": revision, "live_revision": revision,
"live_status": "healthy", "live_legacy_provenance": False,
"live_artifacts": {
"registered_legacy": {"requestId": request_id, "revision": revision, **live_binding},
"containers": containers,
},
"live_receipt": receipt,
"rollback_snapshot": prepared["rollbackSnapshot"],
"status": "success",
}
_persist_state(state_root, state)
return prepared, project_bound, state, origin_capture
def test_captured_runtime_survives_followup_known_restore_retry_and_unknown_fallback(
tmp_path, monkeypatch
):
module, runtime, boundary, project, _source_model, _registry, initial_state, _unknown = (
_initial_active_case(tmp_path, monkeypatch, capture=True)
)
original_lineage = module.active_lineage(
REPOSITORY, project, initial_state,
receipt_key=KEY, state_root=tmp_path, runtime=runtime,
)
assert original_lineage["capturedEnvironmentKeys"] == [
"quiz-server/KNOWLEDGE_OPTIMIZER_MODEL",
]
first, _first_project, first_state, _capture = _prepare_followup_candidate(
module, runtime, boundary, project, tmp_path, FOLLOWUP_SHA, 31,
)
assert first["originUnknownSnapshot"]["revision"] == "unknown"
active = module.active_lineage(
REPOSITORY, project, first_state,
receipt_key=KEY, state_root=tmp_path, runtime=runtime,
)
assert active["requestId"] == first["requestId"]
assert active["capturedEnvironmentKeys"] == original_lineage["capturedEnvironmentKeys"]
restored_source_model = copy.deepcopy(boundary.model)
def restore_source(revision):
assert revision == TARGET_SHA
original_overlay_project = dict(project)
original_overlay_project.update({
"_registered_legacy_request_id": REQUEST_ID,
"_registered_legacy_repository": REPOSITORY,
})
original_overlay = module.validated_compose_overlay(
original_overlay_project, receipt_key=KEY, state_root=tmp_path,
repository=REPOSITORY,
)
assert original_overlay is not None
boundary.model = runtime_support._compose_config_resolve_escaped(
json.loads(original_overlay.read_text(encoding="utf-8"))
)
original_docker_run = module._ProductionRuntime._run
def restore_exact_image(command, *, cwd=None, timeout=45, env=None):
result = original_docker_run(command, cwd=cwd, timeout=timeout, env=env)
if (command[:3] == ["docker", "compose", "--project-name"]
and command[-1:] == ["--force-recreate"]):
rollback_file = Path(command[command.index("--file") + 1])
restore_model = json.loads(rollback_file.read_text(encoding="utf-8"))
image_ref = restore_model["services"]["quiz-server"]["image"]
boundary.live["Image"] = boundary.images[image_ref]["Id"]
return result
monkeypatch.setattr(module._ProductionRuntime, "_run", staticmethod(restore_exact_image))
first_state_bytes = (tmp_path / "quiz-monitor.json").read_bytes()
followup_rollback_payload = {
"repository": REPOSITORY,
"requestId": first["requestId"],
"revision": FOLLOWUP_SHA,
"expectedStateSha256": module._state_digest(first_state_bytes),
}
accepted_followup_rollback = module.accept_rollback(
followup_rollback_payload, registry={REPOSITORY: project},
receipt_key=KEY, state_root=tmp_path,
)
assert accepted_followup_rollback["status"] == "rollback_pending"
verified_known_restores = []
def verify_known_restore(restored):
assert restored["revision"] == TARGET_SHA
assert restored["legacy_provenance"] is False
assert restored["runtimeReceipt"]["runtimeReceiptKind"] == "restored"
assert (tmp_path / "quiz-monitor.json").read_bytes() == first_state_bytes
verified_known_restores.append(restored["runtimeReceipt"]["runtimeReceiptSha256"])
completed_followup_rollback = module.rollback(
followup_rollback_payload, registry={REPOSITORY: project}, runtime=runtime,
receipt_key=KEY, state_root=tmp_path,
restore_source=restore_source, verify_restored=verify_known_restore,
)
assert completed_followup_rollback["status"] == "rolled-back"
assert len(verified_known_restores) == 1
restored_state = json.loads((tmp_path / "quiz-monitor.json").read_text(encoding="utf-8"))
followup_rollback_path = module._paths(
tmp_path, project["slug"], first["requestId"],
)["rollback"]
terminal_followup_rollback = followup_rollback_path.read_bytes()
assert json.loads(terminal_followup_rollback)["status"] == "rolled-back"
assert "KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna" in boundary.live["Config"]["Env"]
restored_lineage = module.active_lineage(
REPOSITORY, project, restored_state,
receipt_key=KEY, state_root=tmp_path, runtime=runtime,
)
assert restored_lineage["revision"] == TARGET_SHA
assert restored_lineage["runtimeReceiptKind"] == "restored"
assert restored_lineage["rollbackSnapshot"]["revision"] == "unknown"
retry = module.prepare_followup_deployment(
REPOSITORY, project, RETRY_SHA, restored_state,
api_json=_validation_api(RETRY_SHA, 41),
receipt_key=KEY, state_root=tmp_path, runtime=runtime,
)
assert retry["requestId"] != first["requestId"]
assert retry["originUnknownSnapshot"] == restored_lineage["originUnknownSnapshot"]
assert retry["rollbackSnapshot"]["revision"] == TARGET_SHA
assert restored_source_model["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] == CAPTURED_VALUE
_rollback_state, current_raw = module.read_registered_state(
REPOSITORY, project, state_root=tmp_path,
)
assert current_raw is not None
rollback_payload = {
"repository": REPOSITORY,
"requestId": REQUEST_ID,
"revision": TARGET_SHA,
"expectedStateSha256": module._state_digest(current_raw),
}
accepted = module.accept_rollback(
rollback_payload, registry={REPOSITORY: project},
receipt_key=KEY, state_root=tmp_path,
)
assert accepted["status"] == "rollback_pending"
completed = module.rollback(
rollback_payload, registry={REPOSITORY: project}, runtime=runtime,
receipt_key=KEY, state_root=tmp_path,
restore_source=restore_source, verify_restored=verify_known_restore,
)
assert completed["status"] == "rolled-back"
final_state = json.loads((tmp_path / "quiz-monitor.json").read_text(encoding="utf-8"))
assert final_state["live_revision"] == "unknown"
assert final_state["live_status"] == "unknown"
assert final_state["live_legacy_provenance"] is True
assert final_state["live_receipt"] == {}
assert boundary.live["Image"] == runtime_support.BASELINE_IMAGE
assert f"KNOWLEDGE_OPTIMIZER_MODEL={CAPTURED_VALUE}" in boundary.live["Config"]["Env"]
assert followup_rollback_path.read_bytes() == terminal_followup_rollback
def test_non_capture_registered_lineage_remains_strict_and_can_follow_up(
tmp_path, monkeypatch
):
module, runtime, _boundary, project, _source_model, _registry, state, _unknown = (
_initial_active_case(tmp_path, monkeypatch, capture=False)
)
active = module.active_lineage(
REPOSITORY, project, state,
receipt_key=KEY, state_root=tmp_path, runtime=runtime,
)
assert active["capturedEnvironmentKeys"] == []
prepared = module.prepare_followup_deployment(
REPOSITORY, project, FOLLOWUP_SHA, state,
api_json=_validation_api(FOLLOWUP_SHA, 31),
receipt_key=KEY, state_root=tmp_path, runtime=runtime,
)
assert prepared["status"] == "followup-ready"
assert prepared["rollbackSnapshot"]["legacy_provenance"] is False
@@ -0,0 +1,845 @@
from __future__ import annotations
import importlib.util
import json
import sys
import types
from contextlib import contextmanager
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
MODULE_PATH = ROOT / "ops" / "gitea-deploy-broker" / "registered_legacy_adoption.py"
REPOSITORY = "jens/quiz-monitor"
CONTAINER = "quiz-monitor-quiz-server-1"
IMAGE_ID = "sha256:" + "a" * 64
SOURCE_FINGERPRINT = "b" * 64
RUNTIME_FINGERPRINT = "c" * 64
STATE_SHA256 = "d" * 64
CONTAINER_ID = "e" * 64
CAPTURED_VALUE = "gpt-6-luna"
CAPTURED_KEY = "quiz-server/KNOWLEDGE_OPTIMIZER_MODEL"
def _load_module():
spec = importlib.util.spec_from_file_location(
"registered_legacy_followup_review", MODULE_PATH
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def _load_deploy_module():
if "fcntl" not in sys.modules:
fcntl = types.ModuleType("fcntl")
fcntl.LOCK_EX = 2
fcntl.LOCK_UN = 8
fcntl.flock = lambda *_args: None
sys.modules["fcntl"] = fcntl
name = "registered_legacy_followup_deploy_review"
spec = importlib.util.spec_from_file_location(
name, ROOT / "ops" / "gitea-deploy-broker" / "deploy.py"
)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
return module
def _captured_lineage(module):
binding = {
"schema": "itworx.registered-legacy-env-capture/v1",
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"expectedStateSha256": STATE_SHA256,
"containerIDs": {CONTAINER: CONTAINER_ID},
"capturedEnvironmentKeys": [CAPTURED_KEY],
}
runtime_container = {
"Image": IMAGE_ID,
"Config": {
"Labels": {"com.docker.compose.service": "quiz-server"},
"Env": [f"KNOWLEDGE_OPTIMIZER_MODEL={CAPTURED_VALUE}"],
},
}
baseline_model = {
"services": {
"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": CAPTURED_VALUE},
"image": "quiz-monitor:baseline",
}
}
}
private = {
"environmentCapture": binding,
"runtimeConfiguration": {
"composeModel": baseline_model,
"containers": {CONTAINER: runtime_container},
"imageEnvironments": {IMAGE_ID: {}},
},
}
baseline = {
"environmentCapture": binding,
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"expectedStateSha256": STATE_SHA256,
}
project = {"containers": [CONTAINER]}
return baseline_model, baseline, private, project
def test_followup_candidate_cannot_mask_a_wrong_captured_environment_overlay():
module = _load_module()
baseline_model, baseline, private, project = _captured_lineage(module)
candidate_model = {
"services": {
"quiz-server": {
"environment": {"KNOWLEDGE_OPTIMIZER_MODEL": "gpt-5.5"},
"image": "quiz-monitor:candidate",
}
}
}
with pytest.raises(module.RegisteredLegacyAdoptionError):
module._candidate_runtime_comparison_model(
candidate_model, baseline_model, baseline, private, project
)
def test_main_revalidates_active_lineage_before_same_sha_fast_return(
monkeypatch, tmp_path, capsys
):
"""A matching receipt cannot hide drift in a previously adopted runtime."""
module = _load_deploy_module()
revision = "f" * 40
receipt = {"repository": REPOSITORY, "revision": revision, "tree": "a" * 40}
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": revision,
"live_revision": revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {
"registered_legacy": {
"requestId": "6f9619ff-8b86-4011-b42d-00c04fc964ff",
"revision": revision,
"runtimeReceiptSha256": "b" * 64,
"runtimeFingerprint": "c" * 64,
}
},
"live_receipt": receipt,
"status": "success",
}
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"exact_source": True,
"verify_oci_revision": True,
"health_timeout": 180,
}
projects = {REPOSITORY: project}
metadata = {"revision": revision, "tree": "a" * 40}
events = []
@contextmanager
def project_lock(_path, _description):
events.append("project-lock")
yield
class Adoption:
def active_lineage(self, repository, observed_project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state["live_legacy_provenance"] is False
events.append("active-lineage")
raise module.DeployError("registered live runtime drift")
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: projects)
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: (
events.append("same-sha-receipt") or receipt
))
monkeypatch.setattr(module, "_load_registered_legacy_adoption", Adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(module, "sync_source", lambda *_args, **_kwargs: (
events.append("sync-source"), pytest.fail("drift must be rejected before source sync")
))
with pytest.raises(module.DeployError, match="registered live runtime drift"):
module.main()
assert "active-lineage" in events
assert "same-sha-receipt" not in events
assert "sync-source" not in events
assert json.loads(capsys.readouterr().out or "{}") == {}
def test_main_defers_active_followup_source_sync_until_global_lock(
monkeypatch, tmp_path
):
"""Ordinary main deployment must attest the active target before source sync."""
module = _load_deploy_module()
previous_revision = "1" * 40
revision = "2" * 40
state_sha = "d" * 64
request_id = "6f9619ff-8b86-4011-b42d-00c04fc964ff"
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": previous_revision,
"live_revision": previous_revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {"registered_legacy": {"requestId": request_id}},
"live_receipt": {"repository": REPOSITORY, "revision": previous_revision},
"status": "success",
}
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"exact_source": True,
"verify_oci_revision": True,
"health_timeout": 180,
}
projects = {REPOSITORY: project}
metadata = {"revision": revision, "tree": "a" * 40}
events = []
lock_state = {"global": False}
@contextmanager
def project_lock(_path, _description):
events.append("project-lock-enter")
yield
@contextmanager
def global_lock():
events.append("global-lock-enter")
lock_state["global"] = True
try:
yield
finally:
lock_state["global"] = False
events.append("global-lock-exit")
class Adoption:
def active_lineage(self, repository, observed_project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state == state
events.append("active-lineage")
return {
"requestId": request_id,
"revision": previous_revision,
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"runtimeReceiptSha256": "e" * 64,
"originUnknownSnapshot": {"revision": "unknown"},
}
def prepare_followup_deployment(self, repository, observed_project, target, observed_state, **_kwargs):
assert lock_state["global"]
assert repository == REPOSITORY
assert target == revision
assert observed_state == state
assert "sync-source" not in events
events.append("prepare-followup")
return {
"requestId": request_id,
"authorization": {
"requestId": request_id,
"revision": target,
"expectedStateSha256": state_sha,
},
"rollbackSnapshot": {"revision": previous_revision},
"originUnknownSnapshot": {"revision": "unknown"},
"expectedStateSha256": state_sha,
"validationEvidenceSha256": "f" * 64,
}
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: projects)
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "exclusive_global_deploy_lock", global_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: None)
monkeypatch.setattr(module, "validate_forward_revision", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, "_load_registered_legacy_adoption", Adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
def stop_after_sync(*_args, **_kwargs):
events.append("sync-source")
if not lock_state["global"]:
events.append("sync-before-global")
raise module.DeployError("test stop after source sync")
monkeypatch.setattr(module, "sync_source", stop_after_sync)
monkeypatch.setattr(module, "copy_environment", lambda *_args: None)
monkeypatch.setattr(module, "restore_live_source", lambda *_args: None)
monkeypatch.setattr(module, "write_state", lambda *_args, **_kwargs: None)
with pytest.raises(module.DeployError, match="test stop after source sync"):
module.main()
assert events.index("global-lock-enter") < events.index("active-lineage")
assert events.index("active-lineage") < events.index("prepare-followup")
assert events.index("prepare-followup") < events.index("sync-source")
assert "sync-before-global" not in events
def test_main_commits_followup_as_verified_not_legacy_and_keeps_unknown_origin(
monkeypatch, tmp_path, capsys
):
module = _load_deploy_module()
previous_revision = "1" * 40
revision = "2" * 40
state_sha = "d" * 64
request_id = "6f9619ff-8b86-4011-b42d-00c04fc964ff"
origin = {"revision": "unknown", "legacy_provenance": True, "opaque": "signed-origin"}
known_snapshot = {
"revision": previous_revision,
"legacy_provenance": False,
"registered_legacy": {"requestId": request_id},
"originUnknownSnapshot": origin,
}
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": previous_revision,
"live_revision": previous_revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {"registered_legacy": {"requestId": request_id}},
"live_receipt": {"repository": REPOSITORY, "revision": previous_revision},
"status": "success",
}
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"exact_source": True,
"verify_oci_revision": True,
"health_timeout": 180,
}
projects = {REPOSITORY: project}
metadata = {"revision": revision, "tree": "a" * 40}
events = []
writes = []
@contextmanager
def project_lock(_path, _description):
events.append("project-lock-enter")
yield
@contextmanager
def global_lock():
events.append("global-lock-enter")
yield
events.append("global-lock-exit")
class Adoption:
def active_lineage(self, repository, _observed_project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state == state
events.append("active-lineage")
return {
"requestId": request_id,
"revision": previous_revision,
"sourceFingerprint": SOURCE_FINGERPRINT,
"runtimeFingerprint": RUNTIME_FINGERPRINT,
"runtimeReceiptSha256": "e" * 64,
"originUnknownSnapshot": origin,
}
def prepare_followup_deployment(self, repository, _observed_project, target, observed_state, **_kwargs):
assert repository == REPOSITORY and target == revision
assert observed_state == state
events.append("prepare-followup")
return {
"requestId": request_id,
"authorization": {
"requestId": request_id,
"revision": target,
"expectedStateSha256": state_sha,
},
"rollbackSnapshot": known_snapshot,
"originUnknownSnapshot": origin,
"expectedStateSha256": state_sha,
"validationEvidenceSha256": "f" * 64,
}
def record_live_runtime(self, repository, observed_project, observed_request, target, _receipt, **_kwargs):
assert repository == REPOSITORY and observed_request == request_id
assert target == revision
assert observed_project["_registered_legacy_request_id"] == request_id
events.append("record-live")
return {
"requestId": request_id,
"revision": target,
"runtimeReceiptSha256": "9" * 64,
"runtimeFingerprint": "8" * 64,
"originUnknownSnapshot": origin,
}
receipt = {"repository": REPOSITORY, "revision": revision, "tree": metadata["tree"]}
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: projects)
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "exclusive_global_deploy_lock", global_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: None)
monkeypatch.setattr(module, "validate_forward_revision", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, "revalidate_deployment_candidate", lambda *_args: None)
monkeypatch.setattr(module, "_load_registered_legacy_adoption", Adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(module, "_registered_legacy_validation_reader", lambda _repository: lambda *_args: {})
monkeypatch.setattr(module, "sync_source", lambda *_args, **_kwargs: events.append("sync-source"))
monkeypatch.setattr(module, "copy_environment", lambda *_args: None)
monkeypatch.setattr(module, "_mcp_hub_assert_no_unresolved_migration_intent", lambda *_args: None)
monkeypatch.setattr(module, "ensure_docker_capacity", lambda: None)
monkeypatch.setattr(module, "deploy_project", lambda observed_project, _revision, _repository: (
events.append("deploy"),
pytest.fail("follow-up deploy lost its derived internal request ID")
if observed_project.get("_registered_legacy_request_id") != request_id else None,
))
monkeypatch.setattr(module, "verify_deployment", lambda *_args: receipt)
monkeypatch.setattr(module, "finish_compose_migration", lambda *_args: None)
monkeypatch.setattr(module, "write_state", lambda *_args, **kwargs: writes.append(kwargs))
monkeypatch.setattr(module, "cleanup_rollback_image_tags", lambda *_args, **_kwargs: None)
assert module.main() == 0
assert events.index("global-lock-enter") < events.index("active-lineage")
assert events.index("prepare-followup") < events.index("sync-source") < events.index("deploy")
assert "record-live" in events
assert len(writes) == 1
assert writes[0]["live_legacy_provenance"] is False
assert writes[0]["rollback_snapshot"] == known_snapshot
assert writes[0]["expected_state_sha256"] == state_sha
assert json.loads(capsys.readouterr().out)["status"] == "deployed"
def test_main_known_current_rollback_restores_source_then_allows_retry(
monkeypatch, tmp_path, capsys
):
"""A failed candidate returns to the verified prior revision and can retry."""
module = _load_deploy_module()
previous_revision = "1" * 40
revision = "2" * 40
state_sha = "d" * 64
request_ids = [
"6f9619ff-8b86-4011-b42d-00c04fc964ff",
"8c7b4a2d-3d70-4a49-93e4-41d42aeecc03",
]
origin = {
"revision": "unknown",
"legacy_provenance": True,
"registered_legacy": {"schema": "origin-unknown-snapshot"},
}
state = {
"schema_version": 3,
"repository": REPOSITORY,
"slug": "quiz-monitor",
"compose_project": "quiz-monitor",
"revision": previous_revision,
"live_revision": previous_revision,
"live_status": "healthy",
"live_legacy_provenance": False,
"live_artifacts": {"registered_legacy": {"requestId": request_ids[0]}},
"live_receipt": {"schema": "gitea-deploy-receipt.v1", "repository": REPOSITORY,
"revision": previous_revision},
"rollback_snapshot": {"revision": "unknown", "legacy_provenance": True,
"registered_legacy": {"schema": "origin-unknown-snapshot"}},
"status": "success",
}
project_template = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": str(tmp_path / "source"),
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
"verify_oci_revision": True,
"health_timeout": 180,
}
metadata = {"revision": revision, "tree": "a" * 40}
known_snapshot = {
"revision": previous_revision,
"legacy_provenance": False,
"receipt": {"schema": "gitea-deploy-receipt.v1", "repository": REPOSITORY,
"revision": previous_revision},
"containers": [{"name": CONTAINER, "image_id": IMAGE_ID}],
"registered_legacy": {"schema": "known-current-snapshot"},
"originUnknownSnapshot": origin,
}
restored_artifacts = {
"registered_legacy": {"requestId": request_ids[0], "revision": previous_revision},
"containers": {CONTAINER: {"container_id": CONTAINER_ID, "image_id": IMAGE_ID}},
}
restored_receipt = known_snapshot["receipt"]
events = []
candidate_attempts = 0
writes = []
@contextmanager
def project_lock(_path, _description):
yield
@contextmanager
def global_lock():
events.append("global-lock-enter")
try:
yield
finally:
events.append("global-lock-exit")
class Adoption:
def active_lineage(self, repository, _project, observed_state, **_kwargs):
assert repository == REPOSITORY
assert observed_state["live_revision"] == previous_revision
assert observed_state["live_status"] == "healthy"
events.append(f"active-lineage:{observed_state['status']}")
return {
"requestId": request_ids[0],
"revision": previous_revision,
"originUnknownSnapshot": origin,
}
def prepare_followup_deployment(self, repository, _project, target, _state, **_kwargs):
assert repository == REPOSITORY and target == revision
request_id = request_ids[len([e for e in events if e == "prepare-followup"]) ]
events.append("prepare-followup")
return {
"requestId": request_id,
"authorization": {
"requestId": request_id,
"revision": target,
"expectedStateSha256": state_sha,
},
"rollbackSnapshot": known_snapshot,
"originUnknownSnapshot": origin,
"expectedStateSha256": state_sha,
"validationEvidenceSha256": "f" * 64,
}
def restore_deployment_snapshot(self, repository, _project, snapshot, failed, **kwargs):
assert repository == REPOSITORY and failed == revision
assert snapshot == known_snapshot
callback = kwargs.get("restore_source")
assert callable(callback)
events.append("rollback-core-accepted-snapshot")
callback(previous_revision)
events.append("prior-source-restored")
return {
"revision": previous_revision,
"artifacts": restored_artifacts,
"imageIDs": {CONTAINER: IMAGE_ID},
"liveReceipt": restored_receipt,
}
def verify_current_live_runtime(self, *_args, **_kwargs):
pytest.fail("candidate failure must not claim the failed revision is healthy")
def record_live_runtime(self, repository, project, request_id, target, _receipt, **_kwargs):
assert repository == REPOSITORY and target == revision
assert project["_registered_legacy_request_id"] == request_id
events.append("record-live")
return {
"requestId": request_id,
"revision": target,
"runtimeReceiptSha256": "9" * 64,
"runtimeFingerprint": "8" * 64,
"originRequestId": request_ids[0],
"originUnknownSnapshot": origin,
}
adoption = Adoption()
candidate_receipt = {
"schema": "gitea-deploy-receipt.v1", "repository": REPOSITORY,
"revision": revision, "tree": metadata["tree"],
}
monkeypatch.setattr(sys, "argv", ["deploy.py", "deploy", REPOSITORY, revision])
monkeypatch.delenv("APPOPS_REGISTERED_LEGACY_REQUEST_ID", raising=False)
monkeypatch.setattr(module, "load_projects", lambda: {REPOSITORY: dict(project_template)})
monkeypatch.setattr(module, "repository_path", lambda *_args: tmp_path)
monkeypatch.setattr(module, "validate_commit", lambda _repo, _branch, value: value)
monkeypatch.setattr(module, "deployment_metadata", lambda *_args: metadata)
monkeypatch.setattr(module, "validate_project_config", lambda *_args: None)
monkeypatch.setattr(module, "exclusive_lock", project_lock)
monkeypatch.setattr(module, "exclusive_global_deploy_lock", global_lock)
monkeypatch.setattr(module, "read_state", lambda _project: state)
monkeypatch.setattr(module, "verified_current_receipt", lambda *_args: None)
monkeypatch.setattr(module, "validate_forward_revision", lambda *_args, **_kwargs: None)
monkeypatch.setattr(module, "_load_registered_legacy_adoption", lambda: adoption)
monkeypatch.setattr(module, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(module, "_registered_legacy_validation_reader", lambda _repo: lambda *_args: {})
monkeypatch.setattr(module, "sync_source", lambda *_args, **_kwargs: events.append("sync-source"))
monkeypatch.setattr(module, "copy_environment", lambda *_args: None)
monkeypatch.setattr(module, "restore_live_source", lambda _project, _repo, rev: events.append(f"restore-source:{rev}"))
monkeypatch.setattr(module, "mcp_hub_prepare_current_receipt_model", lambda *_args: None)
monkeypatch.setattr(module, "_mcp_hub_assert_no_unresolved_migration_intent", lambda *_args: None)
monkeypatch.setattr(module, "revalidate_deployment_candidate", lambda *_args: None)
monkeypatch.setattr(module, "validate_managed_v1_runtime", lambda *_args: None)
monkeypatch.setattr(module, "capture_compose_rollback", lambda *_args: pytest.fail("signed rollback snapshot must be reused"))
monkeypatch.setattr(module, "ensure_docker_capacity", lambda: None)
monkeypatch.setattr(module, "finish_compose_migration", lambda *_args: None)
monkeypatch.setattr(module, "restore_compose_migration", lambda *_args: None)
monkeypatch.setattr(module, "validate_recorded_receipt", lambda *_args: {"revision": previous_revision})
monkeypatch.setattr(module, "verify_deployment", lambda _project, meta, **_kwargs: (
restored_receipt if meta.get("revision") == previous_revision else candidate_receipt
))
def deploy(_project, _revision, _repository):
nonlocal candidate_attempts
candidate_attempts += 1
events.append(f"deploy:{candidate_attempts}")
if candidate_attempts == 1:
raise module.DeployError("simulated candidate health failure")
monkeypatch.setattr(module, "deploy_project", deploy)
def write_state(_repository, project, target, status, message, **kwargs):
writes.append((target, status, dict(kwargs)))
state.update({
"repository": REPOSITORY,
"status": status,
"revision": kwargs.get("live_revision", state["live_revision"]),
"live_revision": kwargs.get("live_revision", state["live_revision"]),
"live_status": kwargs.get("live_status", state["live_status"]),
"live_legacy_provenance": kwargs.get(
"live_legacy_provenance", state["live_legacy_provenance"]
),
"live_artifacts": kwargs.get("live_artifacts", state["live_artifacts"]),
"live_receipt": kwargs.get("live_receipt", state["live_receipt"]),
"rollback_snapshot": kwargs.get("rollback_snapshot", state["rollback_snapshot"]),
"last_attempt": {"revision": target, "status": status, "message": message},
})
if status == "success":
state["live_revision"] = revision
state["revision"] = revision
state["live_status"] = "healthy"
state["live_legacy_provenance"] = False
state["live_receipt"] = candidate_receipt
state["live_artifacts"] = {
"registered_legacy": dict(project["_registered_legacy_runtime_binding"]),
"containers": restored_artifacts["containers"],
}
state["rollback_snapshot"] = known_snapshot
monkeypatch.setattr(module, "write_state", write_state)
monkeypatch.setattr(module, "cleanup_rollback_image_tags", lambda *_args, **_kwargs: None)
with pytest.raises(module.DeployError, match="simulated candidate health failure"):
module.main()
assert "rollback-core-accepted-snapshot" in events
assert events.index("rollback-core-accepted-snapshot") < events.index(
f"restore-source:{previous_revision}"
)
assert state["status"] == "failed"
assert state["live_revision"] == previous_revision
assert state["live_status"] == "healthy"
assert state["live_legacy_provenance"] is False
assert state["rollback_snapshot"]["revision"] == "unknown"
assert module.main() == 0
assert candidate_attempts == 2
assert len([event for event in events if event == "prepare-followup"]) == 2
assert state["status"] == "success"
assert state["live_revision"] == revision
assert state["live_legacy_provenance"] is False
assert state["live_artifacts"]["registered_legacy"]["originUnknownSnapshot"] == origin
assert state["rollback_snapshot"] == known_snapshot
assert any(event == "active-lineage:failed" for event in events)
assert json.loads(capsys.readouterr().out)["status"] == "deployed"
def test_unsigned_known_snapshot_cannot_mutate_source(monkeypatch, tmp_path):
"""A forged known-current rollback reference is rejected before source restore."""
deploy = _load_deploy_module()
adoption = _load_module()
project = {
"slug": "quiz-monitor",
"branch": "main",
"source_dir": "/srv/example/appdata/quiz-monitor/source",
"compose_project": "quiz-monitor",
"compose_files": ["docker-compose.yml"],
"containers": [CONTAINER],
}
previous_revision = "1" * 40
failed_revision = "2" * 40
request_id = "6f9619ff-8b86-4011-b42d-00c04fc964ff"
source_changes = []
forged_snapshot = {
"revision": previous_revision,
"legacy_provenance": False,
"registered_legacy": {
"schema": adoption.KNOWN_SNAPSHOT_SCHEMA,
"requestId": request_id,
"snapshotSha256": "0" * 64,
"targetRevision": failed_revision,
"previousRevision": previous_revision,
"originRequestId": request_id,
"originUnknownSnapshot": {},
},
}
monkeypatch.setattr(deploy, "STATE_ROOT", tmp_path)
monkeypatch.setattr(deploy, "read_state", lambda _project: {})
monkeypatch.setattr(deploy, "_load_registered_legacy_adoption", lambda: adoption)
monkeypatch.setattr(deploy, "_registered_legacy_receipt_key", lambda: b"k" * 32)
monkeypatch.setattr(
deploy, "restore_live_source",
lambda *_args: source_changes.append("source-restored"),
)
with pytest.raises(adoption.RegisteredLegacyAdoptionError):
deploy.restore_compose_snapshot(
project, forged_snapshot, REPOSITORY, failed_revision,
)
assert source_changes == []
def test_candidate_pre_mutation_rechecks_latest_canonical_main_run(
monkeypatch, tmp_path
):
"""A newly failed full run blocks a previously sealed candidate before Compose."""
support_path = ROOT / "backend" / "tests" / "test_registered_legacy_runtime.py"
support_spec = importlib.util.spec_from_file_location(
"registered_legacy_runtime_followup_review", support_path
)
assert support_spec and support_spec.loader
support = importlib.util.module_from_spec(support_spec)
support_spec.loader.exec_module(support)
module = support._load_module()
source = tmp_path / "source"
source.mkdir()
(source / "docker-compose.yml").write_text("services: {}\n", encoding="utf-8")
project = support._project(source)
model = support._compose_model()
boundary = support._DockerComposeBoundary(module, project, model)
boundary.install(monkeypatch)
runtime, _registry, _read_state, _state_bytes, _snapshot = (
support._adopt_with_production_runtime(
module, boundary, project, tmp_path, monkeypatch
)
)
bound_project = support._prepare_candidate_overlay(
module, boundary, project, tmp_path
)
module.prepare_candidate_deployment(
REPOSITORY,
bound_project,
support.REQUEST_ID,
support.TARGET_SHA,
receipt_key=support.KEY,
state_root=tmp_path,
runtime=runtime,
)
older_success = {
"id": 7,
"run_number": 19,
"run_attempt": 1,
"event": "push",
"head_sha": support.TARGET_SHA,
"head_branch": "main",
"path": "managed-validation.yml@refs/heads/main",
"created_at": "2026-10-07T12:00:00Z",
"status": "completed",
"conclusion": "success",
"repository": {"full_name": REPOSITORY},
"head_repository": {"full_name": REPOSITORY},
"pull_requests": [],
}
newer_failure = {
**older_success,
"id": 8,
"run_number": 20,
"created_at": "2026-10-07T12:01:00Z",
"status": "completed",
"conclusion": "failure",
}
queried = []
def failed_latest_api(path, *, query):
queried.append(path)
if path == f"/repos/{REPOSITORY}/actions/runs":
return {
"total_count": 2,
"workflow_runs": [older_success, newer_failure],
}
return support._validation_api(path, query=query)
with pytest.raises(module.RegisteredLegacyAdoptionError):
module.verify_candidate_pre_mutation(
REPOSITORY,
bound_project,
support.REQUEST_ID,
support.TARGET_SHA,
receipt_key=support.KEY,
state_root=tmp_path,
runtime=runtime,
api_json=failed_latest_api,
)
assert queried == [f"/repos/{REPOSITORY}/branches/main", f"/repos/{REPOSITORY}/actions/runs"]
assert boundary.compose_ups == 0
assert not any(
command[:3] == ["docker", "compose", "--project-name"]
and command[-1:] == ["--force-recreate"]
for command in boundary.commands
)
verified = module.verify_candidate_pre_mutation(
REPOSITORY,
bound_project,
support.REQUEST_ID,
support.TARGET_SHA,
receipt_key=support.KEY,
state_root=tmp_path,
runtime=runtime,
api_json=support._validation_api,
)
assert verified["status"] == "pre-mutation-ok"
assert verified["validationEvidenceSha256"]
assert boundary.compose_ups == 0
+269 -2
View File
@@ -264,6 +264,19 @@ class _DockerComposeBoundary:
config_hash="new-config-hash",
)
self.live["Config"]["Image"] = service["image"]
environment = service.get("environment") or {}
if isinstance(environment, dict):
current_env = {}
for entry in self.live["Config"]["Env"]:
name, value = entry.split("=", 1)
current_env[name] = value
current_env.update({
str(name): str(value).replace("$$", "$")
for name, value in environment.items()
})
self.live["Config"]["Env"] = [
f"{name}={value}" for name, value in current_env.items()
]
return ""
raise AssertionError(f"unexpected Docker command: {command}")
@@ -374,6 +387,107 @@ def test_production_observe_rejects_runtime_drift_before_baseline_capture(
with pytest.raises(module.RegisteredLegacyAdoptionError):
runtime.observe(REPOSITORY, project, receipt_key=KEY)
def test_capture_observer_allows_only_declared_environment_drift_and_reports_names(
production_runtime, module
):
runtime, boundary, project, model = production_runtime
model["services"]["quiz-server"]["environment"]["KNOWLEDGE_OPTIMIZER_MODEL"] = "gpt-5.5"
boundary.live["Config"]["Env"].append("KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna")
with pytest.raises(module.RegisteredLegacyAdoptionError):
runtime.observe(REPOSITORY, project, receipt_key=KEY)
observed = runtime.observe_for_baseline_capture(
REPOSITORY, project, receipt_key=KEY,
)
assert observed["status"] == "observed"
assert observed["environmentMismatchKeys"] == [
"quiz-server/KNOWLEDGE_OPTIMIZER_MODEL",
]
assert observed["capturedEnvironmentKeys"] == [
"quiz-server/KNOWLEDGE_OPTIMIZER_MODEL",
]
assert observed["sourceFingerprint"]
assert observed["runtimeFingerprint"]
assert observed["artifacts"]["containers"][CONTAINER_NAME]["container_id"] == "d" * 64
registry_project = copy.deepcopy(project)
registry_project["source_dir"] = "/srv/example/appdata/quiz-monitor/source"
class _CaptureOnlyRuntime:
def observe_for_baseline_capture(self, *_args, **_kwargs):
return copy.deepcopy(observed)
public = module.observe(
{"repository": REPOSITORY},
registry={REPOSITORY: registry_project},
read_state=lambda _repository, _project: ({}, b"{}\n"),
runtime=_CaptureOnlyRuntime(),
receipt_key=KEY,
state_root=runtime.state_root,
)
assert public["containerIDs"] == {CONTAINER_NAME: "d" * 64}
assert public["imageIDs"] == {CONTAINER_NAME: BASELINE_IMAGE}
assert public["environmentMismatchKeys"] == observed["environmentMismatchKeys"]
serialized = json.dumps(public)
assert "gpt-6-luna" not in serialized
assert "gpt-5.5" not in serialized
@pytest.mark.parametrize(
"entries",
[
["OPENRGB_HOST=198.19.1.2", "OPENRGB_HOST=attacker-value"],
["OPENRGB_HOST=198.19.1.2", "malformed-entry"],
],
)
def test_capture_observer_rejects_duplicate_or_malformed_inspected_environment(
production_runtime, module, entries
):
runtime, boundary, project, model = production_runtime
model["services"]["quiz-server"]["environment"]["KNOWLEDGE_OPTIMIZER_MODEL"] = "gpt-5.5"
boundary.live["Config"]["Env"].append("KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna")
boundary.live["Config"]["Env"] = entries + [
"QUIZ_TEMPLATE=" + TEMPLATE_VALUE,
"KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna",
]
with pytest.raises(module.RegisteredLegacyAdoptionError):
runtime.observe_for_baseline_capture(REPOSITORY, project, receipt_key=KEY)
def test_capture_observer_still_rejects_unrendered_unsafe_runtime_options(
production_runtime, module
):
runtime, boundary, project, model = production_runtime
model["services"]["quiz-server"]["environment"]["KNOWLEDGE_OPTIMIZER_MODEL"] = "gpt-5.5"
boundary.live["Config"]["Env"].append("KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna")
boundary.live["HostConfig"]["Privileged"] = True
with pytest.raises(module.RegisteredLegacyAdoptionError, match="privileged"):
runtime.observe_for_baseline_capture(REPOSITORY, project, receipt_key=KEY)
@pytest.mark.parametrize(
"model_key,model_value,actual_entry",
[
(None, None, "UNDECLARED_RUNTIME_VALUE=unexpected"),
("APPOPS_SOURCE_REVISION", "a" * 40, "APPOPS_SOURCE_REVISION=" + "b" * 40),
],
)
def test_capture_observer_rejects_undeclared_and_provenance_environment_drift(
production_runtime, module, model_key, model_value, actual_entry
):
runtime, boundary, project, model = production_runtime
model["services"]["quiz-server"]["environment"]["KNOWLEDGE_OPTIMIZER_MODEL"] = "gpt-5.5"
boundary.live["Config"]["Env"].append("KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna")
boundary.live["Config"]["Env"].append(actual_entry)
if model_key:
model["services"]["quiz-server"]["environment"][model_key] = model_value
with pytest.raises(module.RegisteredLegacyAdoptionError):
runtime.observe_for_baseline_capture(REPOSITORY, project, receipt_key=KEY)
assert boundary.compose_ups == 0
assert not any(
command[:3] == ["docker", "image", "tag"] for command in boundary.commands
@@ -574,7 +688,8 @@ def _validation_api(path, *, query):
raise AssertionError(f"unexpected validation request: {path} {query}")
def _adopt_with_production_runtime(module, boundary, project, state_root, monkeypatch):
def _adopt_with_production_runtime(module, boundary, project, state_root, monkeypatch,
*, capture_runtime_environment=False):
original_validate = module.validate_registered_legacy_target
def validate_temp_target(repository, registered_project):
@@ -600,8 +715,23 @@ def _adopt_with_production_runtime(module, boundary, project, state_root, monkey
runtime = module._ProductionRuntime(state_root)
registry = {REPOSITORY: project}
baseline_payload = {"repository": REPOSITORY, "requestId": REQUEST_ID}
if capture_runtime_environment:
observed = runtime.observe_for_baseline_capture(
REPOSITORY, project, receipt_key=KEY,
)
baseline_payload.update({
"captureRuntimeEnvironment": True,
"expectedSourceFingerprint": observed["sourceFingerprint"],
"expectedRuntimeFingerprint": observed["runtimeFingerprint"],
"expectedStateSha256": module._state_digest(state_bytes),
"expectedContainerIds": {
name: artifact["container_id"]
for name, artifact in observed["artifacts"]["containers"].items()
},
})
baseline = module.baseline(
{"repository": REPOSITORY, "requestId": REQUEST_ID},
baseline_payload,
registry=registry,
read_state=read_state,
runtime=runtime,
@@ -675,6 +805,75 @@ def adopted_production_runtime(production_runtime, tmp_path, monkeypatch):
)
def test_captured_environment_survives_signed_baseline_into_candidate_overlay(
production_runtime, tmp_path, monkeypatch, module
):
_runtime, boundary, project, model = production_runtime
model["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] = "gpt-5.5"
boundary.live["Config"]["Env"].append(
"KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna"
)
runtime, _registry, _read_state, _state_bytes, _snapshot = (
_adopt_with_production_runtime(
module, boundary, project, tmp_path, monkeypatch,
capture_runtime_environment=True,
)
)
bound_project = _prepare_candidate_overlay(module, boundary, project, tmp_path)
overlay_path = module.validated_compose_overlay(
bound_project, receipt_key=KEY, state_root=tmp_path, repository=REPOSITORY,
)
assert overlay_path is not None
overlay = json.loads(overlay_path.read_text(encoding="utf-8"))
assert overlay["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] == "gpt-6-luna"
assert runtime.observe_for_baseline_capture(
REPOSITORY, project, receipt_key=KEY,
)["environmentMismatchKeys"] == [
"quiz-server/KNOWLEDGE_OPTIMIZER_MODEL",
]
with pytest.raises(module.RegisteredLegacyAdoptionError):
runtime.observe(REPOSITORY, project, receipt_key=KEY)
def test_captured_environment_is_restored_from_signed_old_baseline(
production_runtime, tmp_path, monkeypatch, module
):
_runtime, boundary, project, model = production_runtime
model["services"]["quiz-server"]["environment"][
"KNOWLEDGE_OPTIMIZER_MODEL"
] = "gpt-5.5"
boundary.live["Config"]["Env"].append(
"KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna"
)
runtime, registry, _read_state, state_bytes, snapshot = (
_adopt_with_production_runtime(
module, boundary, project, tmp_path, monkeypatch,
capture_runtime_environment=True,
)
)
_candidate_deployment(
module, runtime, boundary, project, registry, state_bytes, tmp_path,
use_signed_overlay=True,
)
assert boundary.live["Image"] == CANDIDATE_IMAGE
restored = module.restore_deployment_snapshot(
REPOSITORY, project, snapshot, TARGET_SHA,
receipt_key=KEY, state_root=tmp_path, runtime=runtime,
)
assert restored["imageIDs"] == {CONTAINER_NAME: BASELINE_IMAGE}
assert boundary.compose_ups == 1
assert boundary.live["Image"] == BASELINE_IMAGE
assert "KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna" in boundary.live["Config"]["Env"]
runtime.observe(REPOSITORY, project, receipt_key=KEY)
def _prepare_candidate_overlay(module, boundary, project, tmp_path):
bound_project = dict(project)
bound_project.update(
@@ -714,8 +913,17 @@ def _candidate_deployment(
*,
receipt_container_id=None,
receipt_image_id=None,
use_signed_overlay=False,
):
bound_project = _prepare_candidate_overlay(module, boundary, project, tmp_path)
if use_signed_overlay:
overlay_path = module.validated_compose_overlay(
bound_project, receipt_key=KEY, state_root=tmp_path, repository=REPOSITORY,
)
assert overlay_path is not None
boundary.model = _compose_config_resolve_escaped(
json.loads(overlay_path.read_text(encoding="utf-8"))
)
sealed = module.prepare_candidate_deployment(
REPOSITORY,
bound_project,
@@ -733,6 +941,7 @@ def _candidate_deployment(
receipt_key=KEY,
state_root=tmp_path,
runtime=runtime,
api_json=_validation_api,
)
boundary.live = _container(
image_id=CANDIDATE_IMAGE,
@@ -741,6 +950,8 @@ def _candidate_deployment(
config_hash="candidate-config-hash",
)
boundary.live["Config"]["Image"] = "quiz-monitor:latest"
if use_signed_overlay:
boundary.live["Config"]["Env"].append("KNOWLEDGE_OPTIMIZER_MODEL=gpt-6-luna")
module.record_candidate_runtime(
REPOSITORY,
bound_project,
@@ -803,6 +1014,17 @@ def _candidate_deployment(
return live, controller_state
def _compose_config_resolve_escaped(value):
"""Model Compose config resolving dollars escaped in its saved input file."""
if isinstance(value, str):
return value.replace("$$", "$")
if isinstance(value, list):
return [_compose_config_resolve_escaped(item) for item in value]
if isinstance(value, dict):
return {key: _compose_config_resolve_escaped(item) for key, item in value.items()}
return value
def test_candidate_and_live_runtime_are_bound_to_exact_production_observation(
adopted_production_runtime, tmp_path
):
@@ -956,6 +1178,50 @@ def test_candidate_sealing_rejects_runtime_drift_after_build_before_mutation(
assert boundary.compose_ups == 0
def test_candidate_runtime_rejection_does_not_leak_environment_values(
adopted_production_runtime, monkeypatch, tmp_path, module, capsys
):
(
runtime,
_boundary,
project,
model,
_registry,
_read_state,
_state_bytes,
_snapshot,
) = adopted_production_runtime
sentinel = "fixture-secret-runtime-value-7c8f2f"
candidate_model = copy.deepcopy(model)
candidate_model["services"]["quiz-server"]["environment"]["QUIZ_TEMPLATE"] = sentinel
def prepare_candidate(*_args, **_kwargs):
return {
"composeModel": candidate_model,
"imageIDsByService": {"quiz-server": CANDIDATE_IMAGE},
}
monkeypatch.setattr(runtime, "prepare_candidate", prepare_candidate)
with pytest.raises(module.RegisteredLegacyAdoptionError) as caught:
# Use the public operation so logging/exception behavior is tested at
# the boundary where candidate runtime drift is rejected.
module.prepare_candidate_deployment(
REPOSITORY,
project,
REQUEST_ID,
TARGET_SHA,
receipt_key=KEY,
state_root=tmp_path,
runtime=runtime,
)
assert "captured runtime configuration" in str(caught.value)
assert sentinel not in str(caught.value)
output = capsys.readouterr()
assert sentinel not in output.out
assert sentinel not in output.err
def test_pre_mutation_cas_rejects_runtime_drift_after_candidate_is_sealed(
adopted_production_runtime, tmp_path
):
@@ -992,6 +1258,7 @@ def test_pre_mutation_cas_rejects_runtime_drift_after_candidate_is_sealed(
receipt_key=KEY,
state_root=tmp_path,
runtime=runtime,
api_json=_validation_api,
)
assert boundary.compose_ups == 0
@@ -42,13 +42,19 @@ def _sha(value: str) -> str:
return hashlib.sha256(value.encode("ascii")).hexdigest()
def _second_process_lock(path_text, started, locked):
def _second_process_lock(path_text, ready, attempt, attempting, acquired):
import fcntl
started.set()
# Separate process startup from the lock attempt. In particular, the
# parent must not hold the project lock while waiting for multiprocessing
# to import this test module (which can be slow under forkserver on /mnt/c).
ready.set()
if not attempt.wait(15):
return
attempting.set()
with open(path_text, "a+") as handle:
fcntl.flock(handle, fcntl.LOCK_EX)
locked.set()
acquired.set()
fcntl.flock(handle, fcntl.LOCK_UN)
@@ -807,20 +813,35 @@ def test_transaction_rejects_other_thread_store_calls_and_transactions(store_env
def test_project_flock_blocks_a_second_process(store_env):
module, store, _controller, _state = store_env
ready = multiprocessing.Event()
attempt = multiprocessing.Event()
attempting = multiprocessing.Event()
acquired = multiprocessing.Event()
with store.transaction():
assert store.load_intent() is None
child = multiprocessing.Process(
target=_second_process_lock,
args=(str(module.PROJECT_LOCK), ready, acquired),
)
child.start()
assert ready.wait(2)
assert not acquired.wait(0.1)
assert acquired.wait(2)
child.join(timeout=2)
assert child.exitcode == 0
child = multiprocessing.Process(
target=_second_process_lock,
args=(str(module.PROJECT_LOCK), ready, attempt, attempting, acquired),
)
child.start()
try:
# Give process creation/import a bounded interval while no lock is held.
assert ready.wait(15), f"lock contender did not start (exit={child.exitcode})"
with store.transaction():
assert store.load_intent() is None
attempt.set()
assert attempting.wait(5), (
f"lock contender did not attempt acquisition (exit={child.exitcode})"
)
assert not acquired.wait(0.1)
assert acquired.wait(5)
child.join(timeout=5)
assert child.exitcode == 0
finally:
# Let a child blocked on the handshake exit on assertion failures too.
attempt.set()
child.join(timeout=5)
if child.is_alive():
child.terminate()
child.join(timeout=5)
def _registered_recovery(store, restore, *, preserve=True, reason="target_health_failed"):