feat(worker): install combined observatory profiles
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from k1link.observatory import m49_worker_service as service_module
|
||||
from k1link.observatory.portable_lab_v1_executor import (
|
||||
PortableLabV1ReleaseAsset,
|
||||
PortableLabV1ReleaseCandidate,
|
||||
)
|
||||
from k1link.observatory.portable_lab_v1_worker_service import (
|
||||
PortableLabV1WorkerInstallationReceipt,
|
||||
)
|
||||
from k1link.observatory.portable_run_definitions import PortableRunDefinition
|
||||
|
||||
|
||||
def _sha256(payload: bytes) -> str:
|
||||
return hashlib.sha256(payload).hexdigest()
|
||||
|
||||
|
||||
def _receipt(asset_sha256: str, byte_length: int) -> object:
|
||||
host_asset = SimpleNamespace(
|
||||
identity_sha256=asset_sha256,
|
||||
byte_length=byte_length,
|
||||
)
|
||||
eomt = SimpleNamespace(
|
||||
image_sha256="1" * 64,
|
||||
installation_sha256="c" * 64,
|
||||
assets=(host_asset,),
|
||||
)
|
||||
ddrnet = SimpleNamespace(
|
||||
image_sha256="2" * 64,
|
||||
installation_sha256="d" * 64,
|
||||
assets=(),
|
||||
)
|
||||
eomt_build = SimpleNamespace(
|
||||
base_image_sha256="3" * 64,
|
||||
derived_image_sha256=eomt.image_sha256,
|
||||
seal_sha256="4" * 64,
|
||||
identity_document=lambda: {
|
||||
"base_image_sha256": "3" * 64,
|
||||
"derived_image_sha256": eomt.image_sha256,
|
||||
"adapter_sha256": "5" * 64,
|
||||
},
|
||||
)
|
||||
ddrnet_build = SimpleNamespace(
|
||||
base_image_sha256="6" * 64,
|
||||
derived_image_sha256=ddrnet.image_sha256,
|
||||
seal_sha256="7" * 64,
|
||||
identity_document=lambda: {
|
||||
"base_image_sha256": "6" * 64,
|
||||
"derived_image_sha256": ddrnet.image_sha256,
|
||||
"adapter_sha256": "8" * 64,
|
||||
},
|
||||
)
|
||||
return SimpleNamespace(
|
||||
runner_installation=SimpleNamespace(
|
||||
eomt=eomt,
|
||||
ddrnet=ddrnet,
|
||||
receipt_sha256="e" * 64,
|
||||
),
|
||||
eomt_image_build=eomt_build,
|
||||
ddrnet_image_build=ddrnet_build,
|
||||
)
|
||||
|
||||
|
||||
def _release(
|
||||
tmp_path: Path,
|
||||
*,
|
||||
installed_asset_sha256: str,
|
||||
installed_asset_length: int,
|
||||
include_unknown: bool = False,
|
||||
include_worker_image: bool = True,
|
||||
executor_image_sha256: str = "b" * 64,
|
||||
) -> object:
|
||||
config_payload = b'{"profile":"lab-v1"}'
|
||||
config_path = tmp_path / "config.json"
|
||||
config_path.write_bytes(config_payload)
|
||||
assets = [
|
||||
PortableLabV1ReleaseAsset(
|
||||
asset_id="ddrnet-portable-config",
|
||||
kind="repository-file",
|
||||
sha256=_sha256(config_payload),
|
||||
byte_length=len(config_payload),
|
||||
repository_path="config.json",
|
||||
),
|
||||
PortableLabV1ReleaseAsset(
|
||||
asset_id="eomt-installed-tree",
|
||||
kind="runtime-artifact",
|
||||
sha256=installed_asset_sha256,
|
||||
byte_length=installed_asset_length,
|
||||
repository_path=None,
|
||||
),
|
||||
]
|
||||
if include_worker_image:
|
||||
assets.append(
|
||||
PortableLabV1ReleaseAsset(
|
||||
asset_id=service_module.LAB_V1_WORKER_AGENT_IMAGE_ASSET_ID,
|
||||
kind="container-image",
|
||||
sha256=executor_image_sha256,
|
||||
byte_length=None,
|
||||
repository_path=None,
|
||||
)
|
||||
)
|
||||
if include_unknown:
|
||||
assets.append(
|
||||
PortableLabV1ReleaseAsset(
|
||||
asset_id="unknown-runtime-artifact",
|
||||
kind="runtime-artifact",
|
||||
sha256="9" * 64,
|
||||
byte_length=None,
|
||||
repository_path=None,
|
||||
)
|
||||
)
|
||||
assets.sort(key=lambda asset: asset.asset_id)
|
||||
return SimpleNamespace(
|
||||
assets=tuple(assets),
|
||||
candidate_sha256="a" * 64,
|
||||
declared_blockers=(),
|
||||
executor_image_sha256=executor_image_sha256,
|
||||
repository_root=tmp_path,
|
||||
)
|
||||
|
||||
|
||||
def test_release_admission_requires_exact_files_or_installation_evidence(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
installed_payload = b"sealed EoMT tree identity"
|
||||
installed_sha256 = _sha256(installed_payload)
|
||||
receipt = _receipt(installed_sha256, len(installed_payload))
|
||||
release = _release(
|
||||
tmp_path,
|
||||
installed_asset_sha256=installed_sha256,
|
||||
installed_asset_length=len(installed_payload),
|
||||
)
|
||||
definition = SimpleNamespace(components=())
|
||||
|
||||
inspection = service_module._inspect_installed_lab_v1_release( # noqa: SLF001
|
||||
release=cast(PortableLabV1ReleaseCandidate, release),
|
||||
receipt=cast(PortableLabV1WorkerInstallationReceipt, receipt),
|
||||
definition=cast(PortableRunDefinition, definition),
|
||||
running_worker_image_sha256="b" * 64,
|
||||
)
|
||||
|
||||
assert inspection.ready
|
||||
assert inspection.matched_assets == (
|
||||
"ddrnet-portable-config",
|
||||
"eomt-installed-tree",
|
||||
service_module.LAB_V1_WORKER_AGENT_IMAGE_ASSET_ID,
|
||||
)
|
||||
|
||||
|
||||
def test_release_admission_rejects_uncovered_runtime_artifact(tmp_path: Path) -> None:
|
||||
installed_payload = b"sealed EoMT tree identity"
|
||||
installed_sha256 = _sha256(installed_payload)
|
||||
receipt = _receipt(installed_sha256, len(installed_payload))
|
||||
release = _release(
|
||||
tmp_path,
|
||||
installed_asset_sha256=installed_sha256,
|
||||
installed_asset_length=len(installed_payload),
|
||||
include_unknown=True,
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
service_module.M49WorkerCompositionError,
|
||||
match="absent from its installation receipt",
|
||||
):
|
||||
service_module._inspect_installed_lab_v1_release( # noqa: SLF001
|
||||
release=cast(PortableLabV1ReleaseCandidate, release),
|
||||
receipt=cast(PortableLabV1WorkerInstallationReceipt, receipt),
|
||||
definition=cast(
|
||||
PortableRunDefinition,
|
||||
SimpleNamespace(components=()),
|
||||
),
|
||||
running_worker_image_sha256="b" * 64,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("include_worker_image", [False, True])
|
||||
def test_release_admission_rejects_missing_or_changed_running_worker_image(
|
||||
tmp_path: Path,
|
||||
include_worker_image: bool,
|
||||
) -> None:
|
||||
receipt = _receipt("1" * 64, 1)
|
||||
release = _release(
|
||||
tmp_path,
|
||||
installed_asset_sha256="1" * 64,
|
||||
installed_asset_length=1,
|
||||
include_worker_image=include_worker_image,
|
||||
executor_image_sha256="b" * 64,
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
service_module.M49WorkerCompositionError,
|
||||
match="differs from the running Worker container",
|
||||
):
|
||||
service_module._inspect_installed_lab_v1_release( # noqa: SLF001
|
||||
release=cast(PortableLabV1ReleaseCandidate, release),
|
||||
receipt=cast(PortableLabV1WorkerInstallationReceipt, receipt),
|
||||
definition=cast(
|
||||
PortableRunDefinition,
|
||||
SimpleNamespace(components=()),
|
||||
),
|
||||
running_worker_image_sha256="c" * 64,
|
||||
)
|
||||
|
||||
|
||||
def test_running_worker_image_uses_exact_docker_container_inspection() -> None:
|
||||
container_id = "a" * 64
|
||||
image_sha256 = "b" * 64
|
||||
requested_paths: list[str] = []
|
||||
|
||||
def inspect(request: httpx.Request) -> httpx.Response:
|
||||
requested_paths.append(request.url.path)
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"Id": container_id,
|
||||
"Image": f"sha256:{image_sha256}",
|
||||
"State": {"Running": True},
|
||||
},
|
||||
)
|
||||
|
||||
result = service_module._inspect_running_worker_container_image_sha256( # noqa: SLF001
|
||||
container_hostname=container_id[:12],
|
||||
transport=httpx.MockTransport(inspect),
|
||||
)
|
||||
|
||||
assert result == image_sha256
|
||||
assert requested_paths == [
|
||||
f"/v1.47/containers/{container_id[:12]}/json"
|
||||
]
|
||||
@@ -38,7 +38,10 @@ from k1link.observatory.portable_worker_runtime import (
|
||||
PortableWorkerRuntimeRegistry,
|
||||
)
|
||||
from k1link.observatory.worker_http_transport import ObservatoryWorkerHttpGateway
|
||||
from k1link.observatory.worker_service import ObservatoryWorkerServiceConfiguration
|
||||
from k1link.observatory.worker_service import (
|
||||
ObservatoryWorkerExecutorBuilderRegistration,
|
||||
ObservatoryWorkerServiceConfiguration,
|
||||
)
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFINITIONS_PATH = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
|
||||
@@ -424,6 +427,62 @@ def test_fixed_m49_identity_allows_an_additional_ready_worker_profile(
|
||||
)
|
||||
|
||||
|
||||
def test_configured_lab_v1_builder_is_added_to_the_shared_worker_registry(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
configuration, _receipt = _fixture(tmp_path, monkeypatch)
|
||||
configuration = replace(
|
||||
configuration,
|
||||
lab_v1_installation_receipt_file=(
|
||||
service_module._FIXED_LAB_V1_INSTALLATION_RECEIPT_FILE # noqa: SLF001
|
||||
),
|
||||
lab_v1_release_candidate_file=(
|
||||
service_module._FIXED_LAB_V1_RELEASE_CANDIDATE_FILE # noqa: SLF001
|
||||
),
|
||||
)
|
||||
lab_builder = ObservatoryWorkerExecutorBuilderRegistration(
|
||||
setup_id="lab-v1-eomt-ddrnet-portable-v1",
|
||||
builder=lambda _context: pytest.fail("builder must not be called here"),
|
||||
)
|
||||
captured: dict[str, object] = {}
|
||||
sentinel = object()
|
||||
|
||||
def compose_lab_v1(**kwargs: object) -> ObservatoryWorkerExecutorBuilderRegistration:
|
||||
captured["lab_inputs"] = kwargs
|
||||
return lab_builder
|
||||
|
||||
def compose_worker(**kwargs: object) -> object:
|
||||
captured["worker_inputs"] = kwargs
|
||||
return sentinel
|
||||
|
||||
monkeypatch.setattr(
|
||||
service_module,
|
||||
"_compose_installed_lab_v1_builder",
|
||||
compose_lab_v1,
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
service_module,
|
||||
"compose_installed_observatory_worker_service_from_builders",
|
||||
compose_worker,
|
||||
)
|
||||
|
||||
composed = service_module.compose_installed_m49_worker_service(configuration)
|
||||
|
||||
assert composed is sentinel
|
||||
lab_inputs = cast(dict[str, object], captured["lab_inputs"])
|
||||
assert lab_inputs["configuration"] is configuration
|
||||
worker_inputs = cast(dict[str, object], captured["worker_inputs"])
|
||||
builders = cast(
|
||||
tuple[ObservatoryWorkerExecutorBuilderRegistration, ...],
|
||||
worker_inputs["builders"],
|
||||
)
|
||||
assert tuple(builder.setup_id for builder in builders) == (
|
||||
service_module.M49_WORKER_SETUP_ID,
|
||||
"lab-v1-eomt-ddrnet-portable-v1",
|
||||
)
|
||||
|
||||
|
||||
def test_fixed_m49_composition_rejects_receipt_asset_drift_before_gateway(
|
||||
tmp_path: Path,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
@@ -467,12 +526,35 @@ def test_entrypoint_environment_requires_all_absolute_fixed_files(tmp_path: Path
|
||||
service_module.M49_WORKER_DEFINITIONS_FILE_ENV: str(tmp_path / "definitions.json"),
|
||||
service_module.M49_WORKER_RUNTIME_REGISTRY_FILE_ENV: str(tmp_path / "runtime.json"),
|
||||
service_module.M49_WORKER_INSTALLATION_RECEIPT_FILE_ENV: str(tmp_path / "receipt.json"),
|
||||
service_module.LAB_V1_WORKER_INSTALLATION_RECEIPT_FILE_ENV: str(
|
||||
service_module._FIXED_LAB_V1_INSTALLATION_RECEIPT_FILE # noqa: SLF001
|
||||
),
|
||||
service_module.LAB_V1_WORKER_RELEASE_CANDIDATE_FILE_ENV: str(
|
||||
service_module._FIXED_LAB_V1_RELEASE_CANDIDATE_FILE # noqa: SLF001
|
||||
),
|
||||
}
|
||||
|
||||
configuration = service_module.M49WorkerEntrypointConfiguration.from_environment(environment)
|
||||
|
||||
assert configuration.worker.base_url == "http://127.0.0.1:18080"
|
||||
assert configuration.installation_receipt_file == tmp_path / "receipt.json"
|
||||
assert configuration.lab_v1_installation_receipt_file == (
|
||||
service_module._FIXED_LAB_V1_INSTALLATION_RECEIPT_FILE # noqa: SLF001
|
||||
)
|
||||
assert configuration.lab_v1_release_candidate_file == (
|
||||
service_module._FIXED_LAB_V1_RELEASE_CANDIDATE_FILE # noqa: SLF001
|
||||
)
|
||||
environment[service_module.LAB_V1_WORKER_RELEASE_CANDIDATE_FILE_ENV] = str(
|
||||
tmp_path / "lab-v1-release.json"
|
||||
)
|
||||
with pytest.raises(
|
||||
service_module.M49WorkerCompositionError,
|
||||
match="fixed /release files",
|
||||
):
|
||||
service_module.M49WorkerEntrypointConfiguration.from_environment(environment)
|
||||
environment[service_module.LAB_V1_WORKER_RELEASE_CANDIDATE_FILE_ENV] = str(
|
||||
service_module._FIXED_LAB_V1_RELEASE_CANDIDATE_FILE # noqa: SLF001
|
||||
)
|
||||
with pytest.raises(service_module.M49WorkerCompositionError, match="is required"):
|
||||
service_module.M49WorkerEntrypointConfiguration.from_environment(
|
||||
{
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.observatory.portable_lab_v1_executor import (
|
||||
PortableLabV1ReleaseCandidate,
|
||||
)
|
||||
from k1link.observatory.portable_run_definitions import (
|
||||
PortableRunDefinitionRegistry,
|
||||
)
|
||||
from k1link.observatory.portable_worker_runtime import (
|
||||
PortableWorkerRuntimeRegistry,
|
||||
)
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFINITIONS = REPOSITORY_ROOT / "config" / "observatory-portable-run-definitions.json"
|
||||
RUNTIME = REPOSITORY_ROOT / "config" / "observatory-worker-runtime-candidates.json"
|
||||
PORTABLE_CONFIG = (
|
||||
REPOSITORY_ROOT / "config" / "perception" / "lab-v1-eomt-ddrnet-portable-v2.json"
|
||||
)
|
||||
PROMOTION_SCRIPT = (
|
||||
REPOSITORY_ROOT
|
||||
/ "experiments"
|
||||
/ "perception"
|
||||
/ "worker"
|
||||
/ "observatory_portable"
|
||||
/ "promote_portable_lab_v1_ready.py"
|
||||
)
|
||||
_SPEC = importlib.util.spec_from_file_location(
|
||||
"observatory_portable_lab_v1_promotion_test",
|
||||
PROMOTION_SCRIPT,
|
||||
)
|
||||
assert _SPEC is not None and _SPEC.loader is not None
|
||||
promotion = importlib.util.module_from_spec(_SPEC)
|
||||
sys.modules[_SPEC.name] = promotion
|
||||
_SPEC.loader.exec_module(promotion)
|
||||
|
||||
|
||||
def _asset(
|
||||
asset_id: str,
|
||||
container_path: str,
|
||||
identity_sha256: str,
|
||||
byte_length: int | None,
|
||||
*,
|
||||
tree: bool,
|
||||
) -> dict[str, object]:
|
||||
return {
|
||||
"asset_id": asset_id,
|
||||
"host_path": (
|
||||
"D:\\NDC_MISSIONCORE\\runtime\\assets\\observatory-portable\\"
|
||||
+ asset_id
|
||||
),
|
||||
"container_path": container_path,
|
||||
"kind": "tree" if tree else "file",
|
||||
"verification": "identity-sha256" if tree else "sha256",
|
||||
"identity_sha256": identity_sha256,
|
||||
"byte_length": byte_length,
|
||||
}
|
||||
|
||||
|
||||
def _input_document(
|
||||
work_root: Path,
|
||||
*,
|
||||
component_receipt: Path,
|
||||
coordinator_receipt: Path,
|
||||
) -> dict[str, object]:
|
||||
eomt_assets = [
|
||||
_asset(
|
||||
"eomt-environment",
|
||||
"/environment",
|
||||
"8c8f343a5368ff17edbb58defa1669f6eccfba767aab897a23693872070ab9e0",
|
||||
211_776_082,
|
||||
tree=True,
|
||||
),
|
||||
_asset(
|
||||
"eomt-ffmpeg-runtime",
|
||||
"/opt/ffmpeg",
|
||||
"03651449fdcccec847a0f1241e1663a82cf374bd94e7470b4ddb0c0e46d88c69",
|
||||
256_208_352,
|
||||
tree=True,
|
||||
),
|
||||
_asset(
|
||||
"eomt-model-cache",
|
||||
"/cache",
|
||||
"064870e58814b97027d6a7ccd553bf51f5b8e6a8ad82a1cc703584d2dca5690c",
|
||||
2_552_355_458,
|
||||
tree=True,
|
||||
),
|
||||
_asset(
|
||||
"eomt-python-environment",
|
||||
"/opt/env",
|
||||
"b3f4efc53af491f174b1cff74b3ba03016e67c9c5c74257b49c6e7dd7d853f20",
|
||||
5_120_848_705,
|
||||
tree=True,
|
||||
),
|
||||
_asset(
|
||||
"eomt-runner-bundle",
|
||||
"/runner",
|
||||
"3bcfb73db5079deffe51173198f7a02e9e4c49f5fc5439d7976757a430fe91d3",
|
||||
144_128,
|
||||
tree=True,
|
||||
),
|
||||
_asset(
|
||||
"eomt-transformers-environment",
|
||||
"/opt/transformers",
|
||||
"f365de01426a33be51a310923c743655634d0868941bbf3f1aae1647fdeadfc9",
|
||||
225_272_284,
|
||||
tree=True,
|
||||
),
|
||||
_asset(
|
||||
"k1-valid-fov-root",
|
||||
"/valid-fov",
|
||||
"f4fc2053e4e6213bb364c8773979b755d5682a81b3946c25ff86274bc5f0031e",
|
||||
6_019,
|
||||
tree=True,
|
||||
),
|
||||
]
|
||||
ddrnet_assets = [
|
||||
_asset(
|
||||
"ddrnet-checkpoint",
|
||||
"/opt/nodedc/assets/ddrnet-checkpoint",
|
||||
"b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6",
|
||||
259_419_077,
|
||||
tree=False,
|
||||
),
|
||||
_asset(
|
||||
"ddrnet-goose-mapping",
|
||||
"/opt/nodedc/assets/ddrnet-goose-mapping",
|
||||
"88ae319ba5a3877dd3ae0773f693a6a5fdc283934140de9dfaff029108aefd7f",
|
||||
1_427,
|
||||
tree=False,
|
||||
),
|
||||
_asset(
|
||||
"ddrnet-goose-runner",
|
||||
"/opt/nodedc/assets/ddrnet-goose-runner",
|
||||
"b18ad60f277eea69a240a28f290611b94627fb9707faf1bb3e6e22102dad67c1",
|
||||
32_877,
|
||||
tree=False,
|
||||
),
|
||||
_asset(
|
||||
"vegetation-policy",
|
||||
"/opt/nodedc/assets/vegetation-policy",
|
||||
"b75c4ac841d7b4bcc57f7a9c8417ca2317d8ecfa499e72a9af8a8591a2ec0d35",
|
||||
3_022,
|
||||
tree=False,
|
||||
),
|
||||
_asset(
|
||||
"vegetation-provider-map",
|
||||
"/opt/nodedc/assets/vegetation-provider-map",
|
||||
"f2b69046b6a740fd9532d2d88e7fabae7c20fb662f783c9502adc9026406f352",
|
||||
2_756,
|
||||
tree=False,
|
||||
),
|
||||
]
|
||||
return {
|
||||
"schema_version": promotion.PROMOTION_INPUT_SCHEMA,
|
||||
"source_revision": "1" * 40,
|
||||
"component_source_revision": "2" * 40,
|
||||
"coordinator_image_sha256": "c" * 64,
|
||||
"work_root": {
|
||||
"controller_root": str(work_root),
|
||||
"engine_host_root": "D:\\NDC_MISSIONCORE\\runtime\\work",
|
||||
},
|
||||
"components": {
|
||||
"eomt": {
|
||||
"base_image_sha256": (
|
||||
"58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
|
||||
),
|
||||
"derived_image_sha256": "d" * 64,
|
||||
"dockerfile_sha256": "2" * 64,
|
||||
"installer_sha256": "3" * 64,
|
||||
"shared_adapter_sha256": "4" * 64,
|
||||
"component_adapter_sha256": "5" * 64,
|
||||
"assets": eomt_assets,
|
||||
},
|
||||
"ddrnet": {
|
||||
"base_image_sha256": (
|
||||
"591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"
|
||||
),
|
||||
"derived_image_sha256": "e" * 64,
|
||||
"dockerfile_sha256": "6" * 64,
|
||||
"installer_sha256": "3" * 64,
|
||||
"shared_adapter_sha256": "4" * 64,
|
||||
"component_adapter_sha256": "7" * 64,
|
||||
"assets": ddrnet_assets,
|
||||
},
|
||||
},
|
||||
"installation_evidence": {
|
||||
"component_image_installer_receipt_file": str(component_receipt),
|
||||
"coordinator_image_installer_receipt_file": str(coordinator_receipt),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _write_installation_evidence(tmp_path: Path) -> tuple[Path, Path]:
|
||||
source_revision = "1" * 40
|
||||
component_source_revision = "2" * 40
|
||||
component = tmp_path / "component-image-installation.json"
|
||||
component.write_text(
|
||||
json.dumps(
|
||||
[
|
||||
{
|
||||
"component": "eomt",
|
||||
"status": "already-installed",
|
||||
"tag": (
|
||||
"ndc/mission-core-lab-v1-eomt-adapter:"
|
||||
f"{component_source_revision[:12]}"
|
||||
),
|
||||
"base_image_sha256": (
|
||||
"58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
|
||||
),
|
||||
"derived_image_sha256": "d" * 64,
|
||||
"build_method": "docker-commit-exact-layer-v1",
|
||||
},
|
||||
{
|
||||
"component": "ddrnet",
|
||||
"status": "already-installed",
|
||||
"tag": (
|
||||
"ndc/mission-core-lab-v1-ddrnet-adapter:"
|
||||
f"{component_source_revision[:12]}"
|
||||
),
|
||||
"base_image_sha256": (
|
||||
"591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd"
|
||||
),
|
||||
"derived_image_sha256": "e" * 64,
|
||||
"build_method": "docker-commit-exact-layer-v1",
|
||||
},
|
||||
]
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
coordinator = tmp_path / "coordinator-image-installation.json"
|
||||
coordinator.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": promotion.WORKER_AGENT_IMAGE_INSTALLATION_SCHEMA,
|
||||
"status": "installed",
|
||||
"worker_id": "worker-006",
|
||||
"build_method": "docker-commit-exact-layer-v1",
|
||||
"source_revision": source_revision,
|
||||
"provenance": {
|
||||
"git_archive_sha256": "8" * 64,
|
||||
"git_archive_verification": "external-before-extract",
|
||||
"staged_snapshot_sha256": "9" * 64,
|
||||
"staged_snapshot_file_count": 42,
|
||||
"staged_snapshot_byte_length": 4096,
|
||||
"staged_snapshot_canonicalization": (
|
||||
"utf8-path-nul-length-nul-sha256-lf-v1"
|
||||
),
|
||||
"embedded_snapshot_manifest_sha256": "a" * 64,
|
||||
"embedded_snapshot_manifest_byte_length": 8192,
|
||||
},
|
||||
"base_image_sha256": (
|
||||
"58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
|
||||
),
|
||||
"derived_image_sha256": "c" * 64,
|
||||
"image": {
|
||||
"tag": (
|
||||
"ndc/mission-core-observatory-worker-agent:"
|
||||
f"{source_revision[:12]}"
|
||||
),
|
||||
"id": f"sha256:{'c' * 64}",
|
||||
"size_bytes": 1_000_000,
|
||||
"thin_layer_bytes": 10_000,
|
||||
"maximum_thin_layer_bytes": 33_554_432,
|
||||
"rootfs": {
|
||||
"base_layer_count": 42,
|
||||
"derived_layer_count": 1,
|
||||
"derived_layer_diff_id": f"sha256:{'b' * 64}",
|
||||
"pinned_base_is_exact_prefix": True,
|
||||
},
|
||||
},
|
||||
"runtime_contract": {
|
||||
"workdir": "/opt/nodedc/mission-core",
|
||||
"entrypoint": [
|
||||
"python3",
|
||||
"-m",
|
||||
"k1link.observatory.m49_worker_container_main",
|
||||
],
|
||||
"command": [],
|
||||
"authority": "observation-only",
|
||||
"models": "external",
|
||||
"runtime_registries": "external-read-only",
|
||||
},
|
||||
"smoke": {
|
||||
"network": "none",
|
||||
"read_only_rootfs": True,
|
||||
"staged_source_bytes": "matched",
|
||||
"embedded_context_bytes": "matched",
|
||||
"embedded_snapshot_manifest": "matched",
|
||||
"result": "passed",
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
return component, coordinator
|
||||
|
||||
|
||||
def _files(root: Path) -> dict[str, bytes]:
|
||||
return {
|
||||
path.relative_to(root).as_posix(): path.read_bytes()
|
||||
for path in root.rglob("*")
|
||||
if path.is_file()
|
||||
}
|
||||
|
||||
|
||||
def _row(document: dict[str, object], collection: str, setup_id: str) -> object:
|
||||
rows = cast(list[dict[str, object]], document[collection])
|
||||
return next(row for row in rows if row["setup_id"] == setup_id)
|
||||
|
||||
|
||||
def test_ready_promotion_is_deterministic_additive_and_round_trips(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
work_root = tmp_path / "work"
|
||||
work_root.mkdir()
|
||||
component_receipt, coordinator_receipt = _write_installation_evidence(tmp_path)
|
||||
input_path = tmp_path / "promotion-input.json"
|
||||
input_path.write_text(
|
||||
json.dumps(
|
||||
_input_document(
|
||||
work_root,
|
||||
component_receipt=component_receipt,
|
||||
coordinator_receipt=coordinator_receipt,
|
||||
)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
inputs = promotion.load_promotion_input(input_path)
|
||||
|
||||
first = promotion.generate_ready_lab_v1_artifacts(
|
||||
promotion=inputs,
|
||||
source_definition_registry=DEFINITIONS,
|
||||
source_runtime_registry=RUNTIME,
|
||||
ddrnet_portable_config=PORTABLE_CONFIG,
|
||||
output_root=tmp_path / "ready-a",
|
||||
)
|
||||
second = promotion.generate_ready_lab_v1_artifacts(
|
||||
promotion=inputs,
|
||||
source_definition_registry=DEFINITIONS,
|
||||
source_runtime_registry=RUNTIME,
|
||||
ddrnet_portable_config=PORTABLE_CONFIG,
|
||||
output_root=tmp_path / "ready-b",
|
||||
)
|
||||
|
||||
assert _files(first.root) == _files(second.root)
|
||||
source_definitions = json.loads(DEFINITIONS.read_text(encoding="utf-8"))
|
||||
ready_definitions_document = json.loads(
|
||||
first.definition_registry_path.read_text(encoding="utf-8")
|
||||
)
|
||||
assert _row(
|
||||
ready_definitions_document,
|
||||
"definitions",
|
||||
"m49-tgs-portable-v2",
|
||||
) == _row(source_definitions, "definitions", "m49-tgs-portable-v2")
|
||||
|
||||
definitions = PortableRunDefinitionRegistry.from_file(
|
||||
first.definition_registry_path
|
||||
)
|
||||
definition = definitions.resolve_setup(promotion.PORTABLE_LAB_V1_SETUP_ID)
|
||||
runtime = PortableWorkerRuntimeRegistry.from_file(
|
||||
first.runtime_registry_path,
|
||||
definitions=definitions,
|
||||
).resolve(definition.setup_id, definition.definition_sha256)
|
||||
release = PortableLabV1ReleaseCandidate.from_file(
|
||||
first.release_candidate_path,
|
||||
repository_root=first.root,
|
||||
)
|
||||
release.bind_definition(definition)
|
||||
|
||||
assert first.release_candidate_path.name == "lab-v1-executor-release.json"
|
||||
assert definition.executor.ready
|
||||
assert runtime.ready
|
||||
assert [
|
||||
asset.asset_id for asset in release.assets if asset.kind == "repository-file"
|
||||
] == [promotion.DDRNET_PORTABLE_CONFIG_ASSET_ID]
|
||||
assert promotion.PORTABLE_LAB_V1_WORKER_INSTALLATION_RECEIPT_ASSET_ID not in {
|
||||
asset.asset_id for asset in release.assets
|
||||
}
|
||||
receipt_requirement = next(
|
||||
asset
|
||||
for asset in runtime.reusable_assets
|
||||
if asset.asset_id
|
||||
== promotion.PORTABLE_LAB_V1_WORKER_INSTALLATION_RECEIPT_ASSET_ID
|
||||
)
|
||||
assert receipt_requirement.sha256 == first.installation_receipt_file_sha256
|
||||
assert first.release_candidate_sha256 == second.release_candidate_sha256
|
||||
assert first.release_sha256 == second.release_sha256
|
||||
assert first.definition_sha256 == second.definition_sha256
|
||||
assert first.runtime_candidate_sha256 == second.runtime_candidate_sha256
|
||||
|
||||
|
||||
def test_promotion_rejects_bare_or_mismatched_installer_claims(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
work_root = tmp_path / "work"
|
||||
work_root.mkdir()
|
||||
component_receipt, coordinator_receipt = _write_installation_evidence(tmp_path)
|
||||
bare = _input_document(
|
||||
work_root,
|
||||
component_receipt=component_receipt,
|
||||
coordinator_receipt=coordinator_receipt,
|
||||
)
|
||||
del bare["installation_evidence"]
|
||||
bare_path = tmp_path / "bare.json"
|
||||
bare_path.write_text(json.dumps(bare), encoding="utf-8")
|
||||
with pytest.raises(promotion.PortableLabV1PromotionError, match="fields are invalid"):
|
||||
promotion.load_promotion_input(bare_path)
|
||||
|
||||
rows = json.loads(component_receipt.read_text(encoding="utf-8"))
|
||||
rows[0]["derived_image_sha256"] = "f" * 64
|
||||
component_receipt.write_text(json.dumps(rows), encoding="utf-8")
|
||||
mismatched = tmp_path / "mismatched.json"
|
||||
mismatched.write_text(
|
||||
json.dumps(
|
||||
_input_document(
|
||||
work_root,
|
||||
component_receipt=component_receipt,
|
||||
coordinator_receipt=coordinator_receipt,
|
||||
)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
with pytest.raises(
|
||||
promotion.PortableLabV1PromotionError,
|
||||
match="does not bind the promotion",
|
||||
):
|
||||
promotion.load_promotion_input(mismatched)
|
||||
@@ -0,0 +1,95 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.observatory import portable_lab_v1_worker as worker_module
|
||||
from k1link.observatory.portable_lab_v1_worker import PortableLabV1WorkerError
|
||||
from k1link.observatory.portable_run_definitions import PortableRunDefinition
|
||||
from k1link.observatory.portable_worker_runtime import PortableWorkerRuntimeCandidate
|
||||
|
||||
|
||||
def _candidate(*, asset_id: str, kind: str = "local-file") -> object:
|
||||
return SimpleNamespace(
|
||||
setup_id="lab-v1-eomt-ddrnet-portable-v1",
|
||||
definition_id="lab-v1-eomt-ddrnet-portable",
|
||||
definition_version=2,
|
||||
definition_sha256="a" * 64,
|
||||
result_contract_sha256="b" * 64,
|
||||
phases=tuple(
|
||||
SimpleNamespace(phase_id=phase_id)
|
||||
for phase_id in worker_module.PORTABLE_LAB_V1_RUNTIME_PHASES
|
||||
),
|
||||
executor=SimpleNamespace(
|
||||
release_id="lab-v1-eomt-ddrnet-v1",
|
||||
release_sha256="c" * 64,
|
||||
image_sha256="d" * 64,
|
||||
),
|
||||
reusable_assets=(
|
||||
SimpleNamespace(
|
||||
asset_id=asset_id,
|
||||
kind=kind,
|
||||
sha256="e" * 64,
|
||||
byte_length=100,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _definition() -> object:
|
||||
return SimpleNamespace(
|
||||
definition_sha256="a" * 64,
|
||||
executable_contract_sha256="f" * 64,
|
||||
)
|
||||
|
||||
|
||||
def _installation() -> object:
|
||||
seal = SimpleNamespace(
|
||||
release_id="lab-v1-eomt-ddrnet-v1",
|
||||
release_sha256="c" * 64,
|
||||
executor_image_sha256="d" * 64,
|
||||
)
|
||||
release = SimpleNamespace(
|
||||
setup_id="lab-v1-eomt-ddrnet-portable-v1",
|
||||
definition_id="lab-v1-eomt-ddrnet-portable",
|
||||
definition_version=2,
|
||||
definition_contract_sha256="f" * 64,
|
||||
result_contract_sha256="b" * 64,
|
||||
assets=(),
|
||||
seal=lambda _inspection: seal,
|
||||
)
|
||||
return SimpleNamespace(release=release, inspection=object())
|
||||
|
||||
|
||||
def test_runtime_only_installation_receipt_anchor_is_not_required_in_release() -> None:
|
||||
worker_module._verify_candidate_release( # noqa: SLF001
|
||||
cast(PortableWorkerRuntimeCandidate, _candidate(
|
||||
asset_id="lab-v1-worker-installation-receipt"
|
||||
)),
|
||||
cast(worker_module.PortableLabV1RunnerInstallation, _installation()),
|
||||
cast(PortableRunDefinition, _definition()),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("asset_id", "kind"),
|
||||
[
|
||||
("unreviewed-runtime-anchor", "local-file"),
|
||||
("lab-v1-worker-installation-receipt", "container-image"),
|
||||
],
|
||||
)
|
||||
def test_no_other_runtime_asset_bypasses_release_coverage(
|
||||
asset_id: str,
|
||||
kind: str,
|
||||
) -> None:
|
||||
with pytest.raises(PortableLabV1WorkerError):
|
||||
worker_module._verify_candidate_release( # noqa: SLF001
|
||||
cast(
|
||||
PortableWorkerRuntimeCandidate,
|
||||
_candidate(asset_id=asset_id, kind=kind),
|
||||
),
|
||||
cast(worker_module.PortableLabV1RunnerInstallation, _installation()),
|
||||
cast(PortableRunDefinition, _definition()),
|
||||
)
|
||||
@@ -49,6 +49,7 @@ RELEASE_SHA256 = "7" * 64
|
||||
IMAGE_SHA256 = "8" * 64
|
||||
RELEASE_CANDIDATE_SHA256 = "9" * 64
|
||||
SOURCE_REVISION = "a" * 40
|
||||
INSTALLER_SHA256 = hashlib.sha256(b"component-image-installer").hexdigest()
|
||||
BASE_IMAGE_SHA256S = {
|
||||
"eomt": "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794",
|
||||
"ddrnet": "591cb382c099eeb05e7ec16e2371e0b2da54d2bb5c49ec0f4ac88dbf72b0f0cd",
|
||||
@@ -244,6 +245,10 @@ def _image_build(
|
||||
dockerfile_sha256=hashlib.sha256(
|
||||
f"{component}:dockerfile".encode()
|
||||
).hexdigest(),
|
||||
build_method=(
|
||||
service_module.PORTABLE_LAB_V1_COMPONENT_IMAGE_BUILD_METHOD
|
||||
),
|
||||
installer_sha256=INSTALLER_SHA256,
|
||||
shared_adapter_sha256=hashlib.sha256(b"shared-adapter").hexdigest(),
|
||||
component_adapter_sha256=hashlib.sha256(
|
||||
f"{component}:adapter".encode()
|
||||
@@ -293,6 +298,7 @@ def _write_receipt(
|
||||
"ddrnet",
|
||||
derived_image_sha256=runner.ddrnet.image_sha256,
|
||||
),
|
||||
installation_evidence_sha256="d" * 64,
|
||||
)
|
||||
path = tmp_path / path_name
|
||||
path.write_bytes(canonical_json(document))
|
||||
@@ -365,6 +371,7 @@ def _composition_inputs(
|
||||
)
|
||||
for digest in (
|
||||
build.dockerfile_sha256,
|
||||
build.installer_sha256,
|
||||
build.shared_adapter_sha256,
|
||||
build.component_adapter_sha256,
|
||||
build.seal_sha256,
|
||||
@@ -402,6 +409,11 @@ def test_receipt_loader_round_trips_full_runner_identity(tmp_path: Path) -> None
|
||||
receipt.runner_installation.eomt.image_sha256
|
||||
)
|
||||
assert receipt.ddrnet_image_build.network == "none"
|
||||
assert receipt.ddrnet_image_build.build_method == (
|
||||
"docker-commit-exact-layer-v1"
|
||||
)
|
||||
assert receipt.ddrnet_image_build.installer_sha256 == INSTALLER_SHA256
|
||||
assert receipt.installation_evidence_sha256 == "d" * 64
|
||||
assert receipt.runner_installation.definition_sha256 == (
|
||||
definition.definition_sha256
|
||||
)
|
||||
@@ -411,6 +423,37 @@ def test_receipt_loader_round_trips_full_runner_identity(tmp_path: Path) -> None
|
||||
assert receipt.file_sha256 == hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def test_component_image_build_seal_binds_actual_installer_method() -> None:
|
||||
seal = _image_build("eomt", derived_image_sha256="b" * 64)
|
||||
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="build provenance changed",
|
||||
):
|
||||
service_module.PortableLabV1ComponentImageBuildSeal.seal(
|
||||
component="eomt",
|
||||
base_image_sha256=BASE_IMAGE_SHA256S["eomt"],
|
||||
derived_image_sha256="b" * 64,
|
||||
dockerfile_sha256=seal.dockerfile_sha256,
|
||||
build_method="dockerfile-build-v1",
|
||||
installer_sha256=INSTALLER_SHA256,
|
||||
shared_adapter_sha256=seal.shared_adapter_sha256,
|
||||
component_adapter_sha256=seal.component_adapter_sha256,
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="image installer SHA-256 is invalid",
|
||||
):
|
||||
replace(seal, installer_sha256="invalid")
|
||||
|
||||
with pytest.raises(
|
||||
service_module.PortableLabV1WorkerCompositionError,
|
||||
match="build provenance changed",
|
||||
):
|
||||
replace(seal, installer_sha256="f" * 64)
|
||||
|
||||
|
||||
def test_receipt_loader_rejects_noncanonical_links_and_oversized_files(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
|
||||
@@ -29,6 +29,7 @@ RUNTIME_REGISTRY_CONTAINER_PATH = (
|
||||
"/run/nodedc/registries/observatory-worker-runtime-candidates.json"
|
||||
)
|
||||
LAB_V1_RECEIPT_CONTAINER_PATH = "/release/lab-v1-worker-installation-receipt.json"
|
||||
LAB_V1_RELEASE_CANDIDATE_CONTAINER_PATH = "/release/lab-v1-executor-release.json"
|
||||
|
||||
|
||||
def _document(path: Path) -> dict[str, object]:
|
||||
@@ -149,8 +150,13 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
assert document["state"] == "planned-not-built"
|
||||
build = cast(dict[str, object], document["build"])
|
||||
assert build["source_revision"] is None
|
||||
assert build["source_date_epoch"] is None
|
||||
assert build["build_context_sha256"] is None
|
||||
assert build["git_archive_sha256"] is None
|
||||
assert build["staged_snapshot_sha256"] is None
|
||||
assert build["installer"] == (
|
||||
"experiments/perception/worker/observatory_portable/"
|
||||
"Install-Worker006AgentImage.ps1"
|
||||
)
|
||||
assert "not executed" in cast(str, build["dockerfile_role"])
|
||||
materialization = cast(dict[str, object], build["materialization"])
|
||||
assert materialization == {
|
||||
"method": "git archive",
|
||||
@@ -158,25 +164,43 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"reject_dirty_worktree": True,
|
||||
"archive_format": "tar",
|
||||
"archive_paths_source": "context manifest context_entries in declared order",
|
||||
"build_context_sha256_subject": "exact git-archive tar bytes",
|
||||
"git_archive_sha256_subject": "exact git-archive tar bytes",
|
||||
"extraction_target": (
|
||||
"D:\\NDC_MISSIONCORE\\runtime\\staging\\"
|
||||
"observatory-worker-agent-<source-revision>"
|
||||
),
|
||||
}
|
||||
staged = cast(dict[str, object], build["staged_snapshot"])
|
||||
assert staged["canonicalization"] == "utf8-path-nul-length-nul-sha256-lf-v1"
|
||||
assert staged["reject_reparse_points"] is True
|
||||
assert staged["reject_unexpected_entries"] is True
|
||||
assert staged["verify_timing"] == [
|
||||
"before temporary container creation",
|
||||
"after source installation and before image commit",
|
||||
]
|
||||
assert staged["embedded_manifest"] == (
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-staged-snapshot.json"
|
||||
)
|
||||
base = cast(dict[str, object], build["base_image"])
|
||||
assert base["reference"] == BASE_REFERENCE
|
||||
assert base["sha256"] == BASE_SHA256
|
||||
assert base["must_exist_locally"] is True
|
||||
assert base["pull_allowed"] is False
|
||||
docker_build = cast(dict[str, object], build["docker_build"])
|
||||
assert docker_build == {
|
||||
image_materialization = cast(dict[str, object], build["image_materialization"])
|
||||
assert image_materialization == {
|
||||
"method": "docker-commit-exact-layer-v1",
|
||||
"dockerfile_executed": False,
|
||||
"network": "none",
|
||||
"pull": False,
|
||||
"no_cache": True,
|
||||
"provenance": False,
|
||||
"platform": "linux/amd64",
|
||||
"context_input": "exact git-archive tar bytes",
|
||||
"required_build_args": [
|
||||
"NODEDC_SOURCE_REVISION",
|
||||
"NODEDC_BUILD_CONTEXT_SHA256",
|
||||
"SOURCE_DATE_EPOCH",
|
||||
"source_input": "exact read-only staged snapshot",
|
||||
"base_container": "docker create by exact pinned base image ID",
|
||||
"commit": "docker commit --pause=true with fixed config changes",
|
||||
"maximum_thin_layer_bytes": 33554432,
|
||||
"embedded_payload": [
|
||||
"/opt/nodedc/mission-core/src/k1link",
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-build-context.json",
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-staged-snapshot.json",
|
||||
],
|
||||
}
|
||||
assert build["required_preflight"] == [
|
||||
@@ -185,6 +209,8 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"selected revision equals HEAD",
|
||||
"base image inspect ID equals the pinned SHA-256",
|
||||
"context archive contains exactly the context manifest entries",
|
||||
"git archive SHA-256 was verified externally before extraction",
|
||||
"staged snapshot SHA-256 matches the exact canonical regular-file inventory",
|
||||
]
|
||||
|
||||
acceptance = cast(dict[str, object], document["build_acceptance"])
|
||||
@@ -193,10 +219,31 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
assert labels["com.nodedc.base-image.sha256"] == BASE_SHA256
|
||||
assert labels["com.nodedc.models"] == "external"
|
||||
assert labels["com.nodedc.runtime-registries"] == "external-read-only"
|
||||
assert labels["com.nodedc.staged-snapshot.sha256"] == (
|
||||
"<staged-snapshot-sha256>"
|
||||
)
|
||||
assert labels["com.nodedc.embedded-snapshot-manifest.sha256"] == (
|
||||
"<embedded-snapshot-manifest-sha256>"
|
||||
)
|
||||
assert labels["com.nodedc.build-method"] == "docker-commit-exact-layer-v1"
|
||||
rootfs = cast(dict[str, object], acceptance["rootfs"])
|
||||
assert rootfs == {
|
||||
"base_layer_chain": "exact prefix of the pinned base image RootFS.Layers",
|
||||
"derived_layer_count": 1,
|
||||
"derived_layer_diff_id_required": True,
|
||||
"maximum_thin_layer_bytes": 33554432,
|
||||
}
|
||||
embedded = cast(dict[str, object], acceptance["embedded_identity"])
|
||||
assert "byte-for-byte" in cast(str, embedded["source_tree"])
|
||||
assert "byte-for-byte" in cast(str, embedded["context_manifest"])
|
||||
assert "every staged file" in cast(str, embedded["snapshot_manifest"])
|
||||
smoke = cast(dict[str, object], acceptance["smoke"])
|
||||
assert smoke["network"] == "none"
|
||||
assert smoke["read_only_rootfs"] is True
|
||||
assert smoke["platform"] == "linux/amd64"
|
||||
assert smoke["staged_source_bytes"] == "matched"
|
||||
assert smoke["embedded_context_bytes"] == "matched"
|
||||
assert smoke["embedded_snapshot_manifest"] == "matched"
|
||||
assert smoke["expected_result"] == "exit-0"
|
||||
|
||||
runtime = cast(dict[str, object], document["runtime"])
|
||||
@@ -215,6 +262,9 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"MISSIONCORE_OBSERVATORY_LAB_V1_INSTALLATION_RECEIPT_FILE": (
|
||||
LAB_V1_RECEIPT_CONTAINER_PATH
|
||||
),
|
||||
"MISSIONCORE_OBSERVATORY_LAB_V1_RELEASE_CANDIDATE_FILE": (
|
||||
LAB_V1_RELEASE_CANDIDATE_CONTAINER_PATH
|
||||
),
|
||||
}
|
||||
registry_files = cast(dict[str, object], runtime["runtime_registry_files"])
|
||||
assert registry_files["binding"] == "individual read-only bind files"
|
||||
@@ -339,19 +389,69 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"state": "external-installed-receipt-required",
|
||||
"environment_variable": "MISSIONCORE_OBSERVATORY_LAB_V1_INSTALLATION_RECEIPT_FILE",
|
||||
"container_path": LAB_V1_RECEIPT_CONTAINER_PATH,
|
||||
"release_candidate_environment_variable": (
|
||||
"MISSIONCORE_OBSERVATORY_LAB_V1_RELEASE_CANDIDATE_FILE"
|
||||
),
|
||||
"release_candidate_container_path": (
|
||||
LAB_V1_RELEASE_CANDIDATE_CONTAINER_PATH
|
||||
),
|
||||
"release_repository_root": "/release",
|
||||
"mode": "read-only",
|
||||
"owns_component_image_identities": True,
|
||||
"queued_jobs_may_override_component_images": False,
|
||||
}
|
||||
|
||||
receipt = cast(dict[str, object], document["receipt_skeleton"])
|
||||
assert receipt["receipt_state"] == "not-built"
|
||||
assert receipt["schema_version"] == (
|
||||
"missioncore.observatory-worker-agent-image-installation/v1"
|
||||
)
|
||||
assert receipt["status"] is None
|
||||
assert receipt["build_method"] == "docker-commit-exact-layer-v1"
|
||||
assert receipt["source_revision"] is None
|
||||
assert receipt["source_date_epoch"] is None
|
||||
assert receipt["build_context_sha256"] is None
|
||||
provenance = cast(dict[str, object], receipt["provenance"])
|
||||
assert provenance == {
|
||||
"git_archive_sha256": None,
|
||||
"git_archive_verification": "external-before-extract",
|
||||
"staged_snapshot_sha256": None,
|
||||
"staged_snapshot_file_count": None,
|
||||
"staged_snapshot_byte_length": None,
|
||||
"staged_snapshot_canonicalization": (
|
||||
"utf8-path-nul-length-nul-sha256-lf-v1"
|
||||
),
|
||||
"embedded_snapshot_manifest_sha256": None,
|
||||
"embedded_snapshot_manifest_byte_length": None,
|
||||
}
|
||||
assert receipt["base_image_sha256"] == BASE_SHA256
|
||||
assert receipt["derived_image_sha256"] is None
|
||||
image = cast(dict[str, object], receipt["image"])
|
||||
assert image == {"tag": None, "id": None, "size_bytes": None}
|
||||
assert receipt["models_baked_into_image"] is False
|
||||
assert receipt["runtime_registries_baked_into_image"] is False
|
||||
assert receipt["authority"] == AUTHORITY
|
||||
assert image == {
|
||||
"tag": None,
|
||||
"id": None,
|
||||
"size_bytes": None,
|
||||
"thin_layer_bytes": None,
|
||||
"maximum_thin_layer_bytes": 33554432,
|
||||
"rootfs": {
|
||||
"base_layer_count": None,
|
||||
"derived_layer_count": 1,
|
||||
"derived_layer_diff_id": None,
|
||||
"pinned_base_is_exact_prefix": True,
|
||||
},
|
||||
}
|
||||
runtime_contract = cast(dict[str, object], receipt["runtime_contract"])
|
||||
assert runtime_contract == {
|
||||
"workdir": "/opt/nodedc/mission-core",
|
||||
"entrypoint": ENTRYPOINT,
|
||||
"command": [],
|
||||
"authority": "observation-only",
|
||||
"models": "external",
|
||||
"runtime_registries": "external-read-only",
|
||||
}
|
||||
receipt_smoke = cast(dict[str, object], receipt["smoke"])
|
||||
assert receipt_smoke == {
|
||||
"network": "none",
|
||||
"read_only_rootfs": True,
|
||||
"staged_source_bytes": None,
|
||||
"embedded_context_bytes": None,
|
||||
"embedded_snapshot_manifest": None,
|
||||
"result": "not-run",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
INSTALLER = (
|
||||
REPOSITORY_ROOT
|
||||
/ "experiments/perception/worker/observatory_portable"
|
||||
/ "Install-Worker006AgentImage.ps1"
|
||||
)
|
||||
BASE_SHA256 = "58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
|
||||
|
||||
|
||||
def _script() -> str:
|
||||
return INSTALLER.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_worker_agent_installer_is_local_offline_and_non_buildkit() -> None:
|
||||
script = _script()
|
||||
|
||||
assert '"docker-commit-exact-layer-v1"' in script
|
||||
assert BASE_SHA256 in script
|
||||
assert '"staging\\observatory-worker-agent-$SourceRevision"' in script
|
||||
assert "docker create `" in script
|
||||
assert "--network none" in script
|
||||
assert "--cap-drop ALL" in script
|
||||
assert "--security-opt no-new-privileges" in script
|
||||
assert "target=/nodedc-build-source,readonly" in script
|
||||
assert "docker commit --pause=true @changes $containerId $tag" in script
|
||||
assert "docker rm -f $containerId" in script
|
||||
assert "docker rm -f $containerName" not in script
|
||||
|
||||
lowered = script.lower()
|
||||
assert "docker build" not in lowered
|
||||
assert "docker buildx" not in lowered
|
||||
assert "docker pull" not in lowered
|
||||
assert "--pull" not in lowered
|
||||
assert "invoke-webrequest" not in lowered
|
||||
assert "start-bitstransfer" not in lowered
|
||||
assert "curl " not in lowered
|
||||
assert "wget " not in lowered
|
||||
assert "smb" not in lowered
|
||||
|
||||
|
||||
def test_worker_agent_installer_separates_archive_and_staged_identities() -> None:
|
||||
script = _script()
|
||||
|
||||
assert "[string]$ExpectedGitArchiveSha256" in script
|
||||
assert "[string]$ExpectedStagedSnapshotSha256" in script
|
||||
assert script.count("Get-StagedSnapshotInspection $StagedSnapshotRoot") == 2
|
||||
assert '"com.nodedc.build-context.sha256" = $ExpectedGitArchiveSha256' in script
|
||||
assert (
|
||||
'"com.nodedc.staged-snapshot.sha256" = $ExpectedStagedSnapshotSha256'
|
||||
in script
|
||||
)
|
||||
assert "LABEL com.nodedc.build-context.sha256=$ExpectedGitArchiveSha256" in script
|
||||
assert (
|
||||
"LABEL com.nodedc.staged-snapshot.sha256=$ExpectedStagedSnapshotSha256"
|
||||
in script
|
||||
)
|
||||
assert 'git_archive_verification = "external-before-extract"' in script
|
||||
assert 'canonicalization = "utf8-path-nul-length-nul-sha256-lf-v1"' in script
|
||||
assert 'Assert-ExactDirectoryChildren $Root @("experiments", "src")' in script
|
||||
assert 'Assert-ExactDirectoryChildren $srcRoot @("k1link")' in script
|
||||
assert "Dockerfile.worker-006-agent" in script
|
||||
assert "worker-006-agent-build-context.json" in script
|
||||
assert (
|
||||
'schema_version = "missioncore.observatory-worker-agent-embedded-snapshot/v1"'
|
||||
in script
|
||||
)
|
||||
assert "files = @($SnapshotInspection.files)" in script
|
||||
assert "embedded_snapshot_manifest_sha256" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_copies_only_runtime_source_and_contract() -> None:
|
||||
script = _script()
|
||||
|
||||
assert (
|
||||
"cp -a /nodedc-build-source/src/k1link "
|
||||
"/opt/nodedc/mission-core/src/k1link"
|
||||
) in script
|
||||
assert (
|
||||
'"cp /nodedc-build-source/$ContextManifestRelativePath " +'
|
||||
in script
|
||||
)
|
||||
assert "/opt/nodedc/mission-core/release/worker-006-agent-build-context.json" in script
|
||||
assert (
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-staged-snapshot.json"
|
||||
in script
|
||||
)
|
||||
assert "cp -a /nodedc-build-source/experiments" not in script
|
||||
assert "cp /nodedc-build-source/$DockerfileRelativePath" not in script
|
||||
assert "test ! -e /opt/nodedc/mission-core/experiments" in script
|
||||
assert "cmp -s" in script
|
||||
assert "find /opt/nodedc/mission-core -type d -exec chmod 0555" in script
|
||||
assert "find /opt/nodedc/mission-core -type f -exec chmod 0444" in script
|
||||
assert "chmod 0555 /run/nodedc /run/nodedc/registries" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_proves_base_chain_and_embedded_bytes() -> None:
|
||||
script = _script()
|
||||
|
||||
assert "$baseLayers = @($BaseImage.RootFS.Layers)" in script
|
||||
assert "$imageLayers = @($Image.RootFS.Layers)" in script
|
||||
assert "$imageLayers.Count -ne ($baseLayers.Count + 1)" in script
|
||||
assert "RootFS does not extend the pinned base layer chain" in script
|
||||
assert "derived_layer_diff_id = $derivedLayerDiffId" in script
|
||||
assert "pinned_base_is_exact_prefix = $true" in script
|
||||
assert "target=/nodedc-verify-source,readonly" in script
|
||||
assert "target=/nodedc-verify-snapshot.json,readonly" in script
|
||||
assert "cmp -s /nodedc-verify-snapshot.json" in script
|
||||
assert (
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-staged-snapshot.json"
|
||||
in script
|
||||
)
|
||||
assert "find /nodedc-verify-source/src/k1link -type f" in script
|
||||
assert "find /opt/nodedc/mission-core/src/k1link -type f" in script
|
||||
assert "cmp -s /nodedc-verify-source/$ContextManifestRelativePath" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_removes_failed_new_image_and_temporary_files() -> None:
|
||||
script = _script()
|
||||
|
||||
assert "$imageCommitted = $true" in script
|
||||
assert (
|
||||
"Invoke-InstalledImageSmoke $tag $StagedSnapshotRoot $embeddedManifest"
|
||||
in script
|
||||
)
|
||||
assert "docker image rm --force $committedImageId" in script
|
||||
assert "image verification failed and committed image cleanup failed" in script
|
||||
assert "Remove-Item -LiteralPath $embeddedManifest.path -Force" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_seals_runtime_contract_smoke_and_output() -> None:
|
||||
script = _script()
|
||||
|
||||
for value in (
|
||||
"PYTHONPATH=/opt/nodedc/mission-core/src",
|
||||
"PYTHONNOUSERSITE=1",
|
||||
"PYTHONDONTWRITEBYTECODE=1",
|
||||
"PYTHONUNBUFFERED=1",
|
||||
"WORKDIR $ImageWorkdir",
|
||||
"USER 0:0",
|
||||
"ENTRYPOINT $ImageEntrypoint",
|
||||
"CMD $ImageCommand",
|
||||
"com.nodedc.authority=observation-only",
|
||||
"com.nodedc.models=external",
|
||||
"com.nodedc.runtime-registries=external-read-only",
|
||||
):
|
||||
assert value in script
|
||||
assert "--read-only" in script
|
||||
assert '"/tmp:rw,noexec,nosuid,size=16m"' in script
|
||||
assert "import k1link.observatory.m49_worker_container_main" in script
|
||||
assert "import k1link.observatory.m49_worker_service" in script
|
||||
assert "compose_installed_m49_worker_service" in script
|
||||
assert "$MaximumLayerBytes = [int64](32MB)" in script
|
||||
assert "image is not within the thin-layer bound" in script
|
||||
assert (
|
||||
'schema_version = "missioncore.observatory-worker-agent-image-installation/v1"'
|
||||
in script
|
||||
)
|
||||
assert "derived_image_sha256 = ([string]$Image.Id).Substring(7)" in script
|
||||
assert 'result = "passed"' in script
|
||||
Reference in New Issue
Block a user