feat(worker): compose installed LAB V1 profile
This commit is contained in:
@@ -0,0 +1,625 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
from dataclasses import dataclass, replace
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.observatory import portable_lab_v1_local_runners as local_runners
|
||||
from k1link.observatory import portable_lab_v1_worker_service as service_module
|
||||
from k1link.observatory.portable_lab_v1_executor import (
|
||||
PortableLabV1ExecutorSeal,
|
||||
PortableLabV1ReleaseInspection,
|
||||
)
|
||||
from k1link.observatory.portable_lab_v1_worker import (
|
||||
PORTABLE_LAB_V1_RUNTIME_PHASES,
|
||||
)
|
||||
from k1link.observatory.portable_lab_v1_worker import (
|
||||
PortableLabV1RunnerInstallation as PortableLabV1ReleaseInstallation,
|
||||
)
|
||||
from k1link.observatory.portable_result_contract import (
|
||||
OBSERVATION_ONLY_AUTHORITY,
|
||||
canonical_json,
|
||||
)
|
||||
from k1link.observatory.portable_run_definitions import (
|
||||
PortableExecutorAvailability,
|
||||
PortableRunDefinition,
|
||||
PortableRunDefinitionRegistry,
|
||||
canonical_sha256,
|
||||
)
|
||||
from k1link.observatory.portable_worker_runtime import (
|
||||
PORTABLE_WORKER_RUNTIME_CANDIDATE_SCHEMA,
|
||||
PortableWorkerAssetRequirement,
|
||||
PortableWorkerAssetVerification,
|
||||
PortableWorkerExecutorSeal,
|
||||
PortableWorkerResultPublisher,
|
||||
PortableWorkerRuntimeAdmission,
|
||||
PortableWorkerRuntimeCandidate,
|
||||
PortableWorkerRuntimePhase,
|
||||
PortableWorkerSourceMaterializer,
|
||||
)
|
||||
from k1link.observatory.worker_service import ObservatoryWorkerExecutorBuildContext
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFINITIONS_PATH = REPOSITORY_ROOT / "config/observatory-portable-run-definitions.json"
|
||||
RELEASE_ID = "lab-v1-eomt-ddrnet-v1"
|
||||
RELEASE_SHA256 = "7" * 64
|
||||
IMAGE_SHA256 = "8" * 64
|
||||
RELEASE_CANDIDATE_SHA256 = "9" * 64
|
||||
SOURCE_REVISION = "a" * 40
|
||||
BASE_IMAGE_SHA256S = {
|
||||
"eomt": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794",
|
||||
"ddrnet": "591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _ReleaseAsset:
|
||||
kind: str
|
||||
sha256: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _Release:
|
||||
definition_contract_sha256: str
|
||||
result_contract_sha256: str
|
||||
candidate_sha256: str = RELEASE_CANDIDATE_SHA256
|
||||
executor_image_sha256: str = IMAGE_SHA256
|
||||
assets: tuple[_ReleaseAsset, ...] = ()
|
||||
declared_blockers: tuple[str, ...] = ()
|
||||
|
||||
def bind_definition(self, definition: PortableRunDefinition) -> None:
|
||||
if (
|
||||
definition.setup_id != service_module.PORTABLE_LAB_V1_SETUP_ID
|
||||
or definition.definition_id != "lab-v1-eomt-ddrnet-portable"
|
||||
or definition.version != 2
|
||||
or definition.executable_contract_sha256
|
||||
!= self.definition_contract_sha256
|
||||
or definition.result_contract.contract_sha256
|
||||
!= self.result_contract_sha256
|
||||
):
|
||||
raise ValueError("release belongs to another definition")
|
||||
|
||||
def seal(
|
||||
self,
|
||||
inspection: PortableLabV1ReleaseInspection,
|
||||
) -> PortableLabV1ExecutorSeal:
|
||||
if (
|
||||
inspection.candidate_sha256 != self.candidate_sha256
|
||||
or not inspection.ready
|
||||
or inspection.blockers
|
||||
):
|
||||
raise ValueError("release inspection is not ready")
|
||||
return PortableLabV1ExecutorSeal(
|
||||
release_id=RELEASE_ID,
|
||||
candidate_sha256=self.candidate_sha256,
|
||||
definition_contract_sha256=self.definition_contract_sha256,
|
||||
executor_image_sha256=self.executor_image_sha256,
|
||||
release_sha256=RELEASE_SHA256,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class _ReleaseInstallation:
|
||||
release: _Release
|
||||
inspection: PortableLabV1ReleaseInspection
|
||||
|
||||
|
||||
class _Launcher:
|
||||
def __call__(self, launch: local_runners.PortableLabV1DockerLaunch) -> None:
|
||||
del launch
|
||||
raise AssertionError("composition must not launch a component")
|
||||
|
||||
|
||||
def _ready_definition() -> PortableRunDefinition:
|
||||
base = PortableRunDefinitionRegistry.from_file(DEFINITIONS_PATH).resolve(
|
||||
service_module.PORTABLE_LAB_V1_SETUP_ID,
|
||||
"3692d41cec3949f348a36eb60a501fb2cd483fed1645679b0ec58061a2fc6dc2",
|
||||
)
|
||||
executor = PortableExecutorAvailability(
|
||||
contour_id=base.executor.contour_id,
|
||||
state="ready",
|
||||
release_id=RELEASE_ID,
|
||||
release_sha256=RELEASE_SHA256,
|
||||
image_sha256=IMAGE_SHA256,
|
||||
reason_code=None,
|
||||
reason=None,
|
||||
)
|
||||
identity = base.identity_document()
|
||||
identity["executor"] = executor.identity_document()
|
||||
return replace(
|
||||
base,
|
||||
executor=executor,
|
||||
definition_sha256=canonical_sha256(identity),
|
||||
)
|
||||
|
||||
|
||||
def _runtime_candidate(
|
||||
definition: PortableRunDefinition,
|
||||
receipt: service_module.PortableLabV1WorkerInstallationReceipt,
|
||||
) -> PortableWorkerRuntimeCandidate:
|
||||
executor = PortableWorkerExecutorSeal(
|
||||
release_id=RELEASE_ID,
|
||||
release_sha256=RELEASE_SHA256,
|
||||
image_sha256=IMAGE_SHA256,
|
||||
)
|
||||
phases = tuple(
|
||||
PortableWorkerRuntimePhase(phase_id=phase_id, state="implemented")
|
||||
for phase_id in PORTABLE_LAB_V1_RUNTIME_PHASES
|
||||
)
|
||||
receipt_requirement = PortableWorkerAssetRequirement(
|
||||
asset_id=service_module.PORTABLE_LAB_V1_WORKER_INSTALLATION_RECEIPT_ASSET_ID,
|
||||
kind="local-file",
|
||||
sha256=receipt.file_sha256,
|
||||
byte_length=receipt.file_byte_length,
|
||||
component_id=None,
|
||||
model_release_id=None,
|
||||
model_artifact_role=None,
|
||||
)
|
||||
identity = {
|
||||
"schema_version": PORTABLE_WORKER_RUNTIME_CANDIDATE_SCHEMA,
|
||||
"adapter_id": service_module.PORTABLE_LAB_V1_ADAPTER_ID,
|
||||
"setup_id": definition.setup_id,
|
||||
"definition_id": definition.definition_id,
|
||||
"definition_version": definition.version,
|
||||
"definition_sha256": definition.definition_sha256,
|
||||
"source_adapter_sha256": definition.source_adapter.contract_sha256,
|
||||
"model_manifest_sha256": definition.model_manifest_sha256,
|
||||
"resource_profile_sha256": definition.resource_profile.profile_sha256,
|
||||
"result_contract_sha256": definition.result_contract.contract_sha256,
|
||||
"state": "ready",
|
||||
"executor": executor.as_dict(),
|
||||
"reusable_assets": [receipt_requirement.as_dict()],
|
||||
"phases": [phase.as_dict() for phase in phases],
|
||||
"blockers": [],
|
||||
"authority": dict(OBSERVATION_ONLY_AUTHORITY),
|
||||
}
|
||||
return PortableWorkerRuntimeCandidate(
|
||||
adapter_id=service_module.PORTABLE_LAB_V1_ADAPTER_ID,
|
||||
setup_id=definition.setup_id,
|
||||
definition_id=definition.definition_id,
|
||||
definition_version=definition.version,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
source_adapter_sha256=definition.source_adapter.contract_sha256,
|
||||
model_manifest_sha256=definition.model_manifest_sha256,
|
||||
resource_profile_sha256=definition.resource_profile.profile_sha256,
|
||||
result_contract_sha256=definition.result_contract.contract_sha256,
|
||||
state="ready",
|
||||
executor=executor,
|
||||
reusable_assets=(receipt_requirement,),
|
||||
phases=phases,
|
||||
blockers=(),
|
||||
candidate_sha256=canonical_sha256(identity),
|
||||
)
|
||||
|
||||
|
||||
def _assets(
|
||||
component: local_runners.PortableLabV1Component,
|
||||
) -> tuple[local_runners.PortableLabV1HostAsset, ...]:
|
||||
return tuple(
|
||||
local_runners.PortableLabV1HostAsset(
|
||||
asset_id=asset_id,
|
||||
host_path=f"D:\\NDC_MISSIONCORE\\assets\\{asset_id}",
|
||||
container_path=container_path,
|
||||
kind="tree" if component == "eomt" else "file",
|
||||
verification="identity-sha256" if component == "eomt" else "sha256",
|
||||
identity_sha256=local_runners._EXPECTED_FIXED_ASSET_IDENTITIES.get(
|
||||
asset_id,
|
||||
hashlib.sha256(asset_id.encode()).hexdigest(),
|
||||
),
|
||||
byte_length=local_runners._EXPECTED_ASSET_LENGTHS.get(asset_id),
|
||||
)
|
||||
for asset_id, container_path in sorted(
|
||||
local_runners._EXPECTED_ASSET_TARGETS[component].items()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _component(
|
||||
component: local_runners.PortableLabV1Component,
|
||||
) -> local_runners.PortableLabV1ComponentInstallation:
|
||||
return local_runners.PortableLabV1ComponentInstallation.seal(
|
||||
component=component,
|
||||
image_sha256="b" * 64 if component == "eomt" else "c" * 64,
|
||||
entrypoint=local_runners._EXPECTED_ENTRYPOINTS[component],
|
||||
command=local_runners._EXPECTED_COMMANDS[component],
|
||||
assets=_assets(component),
|
||||
timeout_seconds=3600.0,
|
||||
memory_bytes=16 * 1024**3,
|
||||
nano_cpus=4_000_000_000,
|
||||
)
|
||||
|
||||
|
||||
def _image_build(
|
||||
component: local_runners.PortableLabV1Component,
|
||||
*,
|
||||
derived_image_sha256: str,
|
||||
) -> service_module.PortableLabV1ComponentImageBuildSeal:
|
||||
return service_module.PortableLabV1ComponentImageBuildSeal.seal(
|
||||
component=component,
|
||||
base_image_sha256=BASE_IMAGE_SHA256S[component],
|
||||
derived_image_sha256=derived_image_sha256,
|
||||
dockerfile_sha256=hashlib.sha256(
|
||||
f"{component}:dockerfile".encode()
|
||||
).hexdigest(),
|
||||
shared_adapter_sha256=hashlib.sha256(b"shared-adapter").hexdigest(),
|
||||
component_adapter_sha256=hashlib.sha256(
|
||||
f"{component}:adapter".encode()
|
||||
).hexdigest(),
|
||||
)
|
||||
|
||||
|
||||
def _runner_installation(
|
||||
tmp_path: Path,
|
||||
*,
|
||||
definition_sha256: str,
|
||||
) -> local_runners.PortableLabV1RunnerInstallation:
|
||||
controller_root = tmp_path / "worker006-runtime"
|
||||
controller_root.mkdir(exist_ok=True)
|
||||
return local_runners.PortableLabV1RunnerInstallation.seal(
|
||||
definition_sha256=definition_sha256,
|
||||
release_candidate_sha256=RELEASE_CANDIDATE_SHA256,
|
||||
work_root=local_runners.PortableLabV1WorkRootBinding(
|
||||
controller_root=controller_root,
|
||||
engine_host_root="D:\\NDC_MISSIONCORE\\runtime",
|
||||
),
|
||||
eomt=_component("eomt"),
|
||||
ddrnet=_component("ddrnet"),
|
||||
)
|
||||
|
||||
|
||||
def _write_receipt(
|
||||
tmp_path: Path,
|
||||
*,
|
||||
definition_sha256: str,
|
||||
path_name: str = "lab-v1-worker-installation-receipt.json",
|
||||
source_revision: str = SOURCE_REVISION,
|
||||
) -> Path:
|
||||
runner = _runner_installation(
|
||||
tmp_path,
|
||||
definition_sha256=definition_sha256,
|
||||
)
|
||||
document = service_module.portable_lab_v1_worker_receipt_document(
|
||||
source_revision=source_revision,
|
||||
release_candidate_sha256=RELEASE_CANDIDATE_SHA256,
|
||||
runner_installation=runner,
|
||||
eomt_image_build=_image_build(
|
||||
"eomt",
|
||||
derived_image_sha256=runner.eomt.image_sha256,
|
||||
),
|
||||
ddrnet_image_build=_image_build(
|
||||
"ddrnet",
|
||||
derived_image_sha256=runner.ddrnet.image_sha256,
|
||||
),
|
||||
)
|
||||
path = tmp_path / path_name
|
||||
path.write_bytes(canonical_json(document))
|
||||
return path
|
||||
|
||||
|
||||
def _composition_inputs(
|
||||
tmp_path: Path,
|
||||
) -> tuple[
|
||||
PortableRunDefinition,
|
||||
PortableWorkerRuntimeCandidate,
|
||||
service_module.PortableLabV1WorkerInstallationReceipt,
|
||||
PortableWorkerRuntimeAdmission,
|
||||
PortableLabV1ReleaseInstallation,
|
||||
]:
|
||||
definition = _ready_definition()
|
||||
receipt_path = _write_receipt(
|
||||
tmp_path,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
)
|
||||
receipt = service_module.load_portable_lab_v1_worker_installation_receipt(
|
||||
receipt_path
|
||||
)
|
||||
candidate = _runtime_candidate(definition, receipt)
|
||||
admission = PortableWorkerRuntimeAdmission(
|
||||
candidate_sha256=candidate.candidate_sha256,
|
||||
ready=True,
|
||||
blockers=(),
|
||||
assets=(
|
||||
PortableWorkerAssetVerification(
|
||||
asset_id=(
|
||||
service_module.PORTABLE_LAB_V1_WORKER_INSTALLATION_RECEIPT_ASSET_ID
|
||||
),
|
||||
state="matched",
|
||||
reason=None,
|
||||
),
|
||||
),
|
||||
)
|
||||
release = _Release(
|
||||
definition_contract_sha256=definition.executable_contract_sha256,
|
||||
result_contract_sha256=definition.result_contract.contract_sha256,
|
||||
assets=tuple(
|
||||
_ReleaseAsset(kind=kind, sha256=sha256)
|
||||
for kind, sha256 in sorted(
|
||||
{
|
||||
*(
|
||||
("container-image", digest)
|
||||
for build in (
|
||||
receipt.eomt_image_build,
|
||||
receipt.ddrnet_image_build,
|
||||
)
|
||||
for digest in (
|
||||
build.base_image_sha256,
|
||||
build.derived_image_sha256,
|
||||
)
|
||||
),
|
||||
*(
|
||||
("runtime-artifact", asset.identity_sha256)
|
||||
for component in (
|
||||
receipt.runner_installation.eomt,
|
||||
receipt.runner_installation.ddrnet,
|
||||
)
|
||||
for asset in component.assets
|
||||
),
|
||||
*(
|
||||
("runtime-artifact", digest)
|
||||
for build in (
|
||||
receipt.eomt_image_build,
|
||||
receipt.ddrnet_image_build,
|
||||
)
|
||||
for digest in (
|
||||
build.dockerfile_sha256,
|
||||
build.shared_adapter_sha256,
|
||||
build.component_adapter_sha256,
|
||||
build.seal_sha256,
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
),
|
||||
)
|
||||
inspection = PortableLabV1ReleaseInspection(
|
||||
candidate_sha256=release.candidate_sha256,
|
||||
matched_assets=(),
|
||||
blockers=(),
|
||||
ready=True,
|
||||
)
|
||||
installation = cast(
|
||||
PortableLabV1ReleaseInstallation,
|
||||
_ReleaseInstallation(release=release, inspection=inspection),
|
||||
)
|
||||
return definition, candidate, receipt, admission, installation
|
||||
|
||||
|
||||
def test_receipt_loader_round_trips_full_runner_identity(tmp_path: Path) -> None:
|
||||
definition = _ready_definition()
|
||||
path = _write_receipt(
|
||||
tmp_path,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
)
|
||||
|
||||
receipt = service_module.load_portable_lab_v1_worker_installation_receipt(path)
|
||||
|
||||
assert receipt.path == path
|
||||
assert receipt.release_candidate_sha256 == RELEASE_CANDIDATE_SHA256
|
||||
assert receipt.eomt_image_build.derived_image_sha256 == (
|
||||
receipt.runner_installation.eomt.image_sha256
|
||||
)
|
||||
assert receipt.ddrnet_image_build.network == "none"
|
||||
assert receipt.runner_installation.definition_sha256 == (
|
||||
definition.definition_sha256
|
||||
)
|
||||
assert tuple(asset.asset_id for asset in receipt.runner_installation.eomt.assets) == (
|
||||
tuple(sorted(local_runners._EXPECTED_ASSET_TARGETS["eomt"]))
|
||||
)
|
||||
assert receipt.file_sha256 == hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def test_receipt_loader_rejects_noncanonical_links_and_oversized_files(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
definition = _ready_definition()
|
||||
path = _write_receipt(
|
||||
tmp_path,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
)
|
||||
path.write_bytes(path.read_bytes() + b"\n")
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="not canonical JSON",
|
||||
):
|
||||
service_module.load_portable_lab_v1_worker_installation_receipt(path)
|
||||
|
||||
valid = _write_receipt(
|
||||
tmp_path,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
path_name="valid.json",
|
||||
)
|
||||
link = tmp_path / "linked.json"
|
||||
link.symlink_to(valid)
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="unavailable",
|
||||
):
|
||||
service_module.load_portable_lab_v1_worker_installation_receipt(link)
|
||||
|
||||
oversized = tmp_path / "oversized.json"
|
||||
oversized.write_bytes(b"x" * (256 * 1024 + 1))
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="size is invalid",
|
||||
):
|
||||
service_module.load_portable_lab_v1_worker_installation_receipt(oversized)
|
||||
|
||||
|
||||
def test_receipt_loader_rejects_duplicate_keys_and_identity_tampering(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
duplicate = tmp_path / "duplicate.json"
|
||||
duplicate.write_bytes(b'{"schema_version":"a","schema_version":"b"}')
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="not canonical JSON",
|
||||
):
|
||||
service_module.load_portable_lab_v1_worker_installation_receipt(duplicate)
|
||||
|
||||
definition = _ready_definition()
|
||||
path = _write_receipt(
|
||||
tmp_path,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
)
|
||||
payload = path.read_bytes().replace(SOURCE_REVISION.encode(), ("f" * 40).encode())
|
||||
path.write_bytes(payload)
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="identity changed",
|
||||
):
|
||||
service_module.load_portable_lab_v1_worker_installation_receipt(path)
|
||||
|
||||
|
||||
def test_compose_returns_exact_lab_v1_builder_registration(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
definition, candidate, receipt, admission, installation = _composition_inputs(
|
||||
tmp_path
|
||||
)
|
||||
captured: dict[str, object] = {}
|
||||
adapter = object()
|
||||
|
||||
def _compose_adapter(**kwargs: object) -> object:
|
||||
captured.update(kwargs)
|
||||
return adapter
|
||||
|
||||
monkeypatch.setattr(
|
||||
service_module,
|
||||
"compose_lab_v1_portable_executor_adapter",
|
||||
_compose_adapter,
|
||||
)
|
||||
builder = service_module.compose_installed_lab_v1_executor_builder(
|
||||
receipt=receipt,
|
||||
definition=definition,
|
||||
candidate=candidate,
|
||||
admission=admission,
|
||||
release_installation=installation,
|
||||
launcher=_Launcher(),
|
||||
created_at_utc=lambda: "2026-08-31T12:00:00Z",
|
||||
)
|
||||
|
||||
context = ObservatoryWorkerExecutorBuildContext(
|
||||
definition=definition,
|
||||
candidate=candidate,
|
||||
source_transport=cast(PortableWorkerSourceMaterializer, object()),
|
||||
result_transport=cast(PortableWorkerResultPublisher, object()),
|
||||
work_root=tmp_path,
|
||||
)
|
||||
registration = builder.builder(context)
|
||||
|
||||
assert builder.setup_id == service_module.PORTABLE_LAB_V1_SETUP_ID
|
||||
assert registration.identity == candidate.executor_identity()
|
||||
assert registration.adapter is adapter
|
||||
assert captured["candidate"] is candidate
|
||||
assert captured["definition"] is definition
|
||||
assert captured["admission"] is admission
|
||||
assert captured["installation"] is installation
|
||||
assert captured["source_output_parent"] == (
|
||||
tmp_path / "lab-v1-portable/source-output"
|
||||
)
|
||||
assert captured["eomt_runner"].__class__.__name__ == (
|
||||
"InstalledPortableLabV1EomtRunner"
|
||||
)
|
||||
assert captured["ddrnet_runner"].__class__.__name__ == (
|
||||
"InstalledPortableLabV1DdrnetRunner"
|
||||
)
|
||||
|
||||
|
||||
def test_compose_fails_closed_on_changed_receipt_or_incomplete_admission(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
definition, candidate, receipt, admission, installation = _composition_inputs(
|
||||
tmp_path
|
||||
)
|
||||
changed_path = _write_receipt(
|
||||
tmp_path,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
source_revision="f" * 40,
|
||||
)
|
||||
assert changed_path == receipt.path
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="changed after it was loaded",
|
||||
):
|
||||
service_module.compose_installed_lab_v1_executor_builder(
|
||||
receipt=receipt,
|
||||
definition=definition,
|
||||
candidate=candidate,
|
||||
admission=admission,
|
||||
release_installation=installation,
|
||||
launcher=_Launcher(),
|
||||
)
|
||||
|
||||
current = service_module.load_portable_lab_v1_worker_installation_receipt(
|
||||
changed_path
|
||||
)
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="does not seal its installation receipt",
|
||||
):
|
||||
service_module.compose_installed_lab_v1_executor_builder(
|
||||
receipt=current,
|
||||
definition=definition,
|
||||
candidate=candidate,
|
||||
admission=admission,
|
||||
release_installation=installation,
|
||||
launcher=_Launcher(),
|
||||
)
|
||||
|
||||
restored_path = _write_receipt(
|
||||
tmp_path,
|
||||
definition_sha256=definition.definition_sha256,
|
||||
)
|
||||
restored = service_module.load_portable_lab_v1_worker_installation_receipt(
|
||||
restored_path
|
||||
)
|
||||
release = cast(_Release, installation.release)
|
||||
uncovered_installation = cast(
|
||||
PortableLabV1ReleaseInstallation,
|
||||
_ReleaseInstallation(
|
||||
release=replace(release, assets=()),
|
||||
inspection=installation.inspection,
|
||||
),
|
||||
)
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="does not cover its installed component identities",
|
||||
):
|
||||
service_module.compose_installed_lab_v1_executor_builder(
|
||||
receipt=restored,
|
||||
definition=definition,
|
||||
candidate=candidate,
|
||||
admission=admission,
|
||||
release_installation=uncovered_installation,
|
||||
launcher=_Launcher(),
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="has no complete local asset admission",
|
||||
):
|
||||
service_module.compose_installed_lab_v1_executor_builder(
|
||||
receipt=restored,
|
||||
definition=definition,
|
||||
candidate=candidate,
|
||||
admission=PortableWorkerRuntimeAdmission(
|
||||
candidate_sha256=candidate.candidate_sha256,
|
||||
ready=False,
|
||||
blockers=("asset-missing",),
|
||||
assets=(
|
||||
PortableWorkerAssetVerification(
|
||||
asset_id=(
|
||||
service_module.PORTABLE_LAB_V1_WORKER_INSTALLATION_RECEIPT_ASSET_ID
|
||||
),
|
||||
state="missing",
|
||||
reason="not-bound",
|
||||
),
|
||||
),
|
||||
),
|
||||
release_installation=installation,
|
||||
launcher=_Launcher(),
|
||||
)
|
||||
Reference in New Issue
Block a user