fix(worker): seal coordinator Python dependencies
This commit is contained in:
@@ -255,6 +255,14 @@ def _write_installation_evidence(tmp_path: Path) -> tuple[Path, Path]:
|
||||
),
|
||||
"embedded_snapshot_manifest_sha256": "a" * 64,
|
||||
"embedded_snapshot_manifest_byte_length": 8192,
|
||||
"python_dependency_bundle_sha256": "b" * 64,
|
||||
"python_dependency_bundle_file_count": 128,
|
||||
"python_dependency_bundle_byte_length": 16_384,
|
||||
"python_dependency_bundle_canonicalization": (
|
||||
"utf8-path-nul-length-nul-sha256-lf-v1"
|
||||
),
|
||||
"python_dependency_bundle_manifest_sha256": "c" * 64,
|
||||
"python_dependency_bundle_manifest_byte_length": 4096,
|
||||
},
|
||||
"base_image_sha256": (
|
||||
"58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
|
||||
@@ -278,6 +286,10 @@ def _write_installation_evidence(tmp_path: Path) -> tuple[Path, Path]:
|
||||
},
|
||||
"runtime_contract": {
|
||||
"workdir": "/opt/nodedc/mission-core",
|
||||
"pythonpath": (
|
||||
"/opt/nodedc/mission-core/src:"
|
||||
"/opt/nodedc/mission-core/deps"
|
||||
),
|
||||
"entrypoint": [
|
||||
"python3",
|
||||
"-m",
|
||||
@@ -287,6 +299,7 @@ def _write_installation_evidence(tmp_path: Path) -> tuple[Path, Path]:
|
||||
"authority": "observation-only",
|
||||
"models": "external",
|
||||
"runtime_registries": "external-read-only",
|
||||
"python_dependencies": "embedded-sealed-build-input",
|
||||
},
|
||||
"smoke": {
|
||||
"network": "none",
|
||||
@@ -294,6 +307,10 @@ def _write_installation_evidence(tmp_path: Path) -> tuple[Path, Path]:
|
||||
"staged_source_bytes": "matched",
|
||||
"embedded_context_bytes": "matched",
|
||||
"embedded_snapshot_manifest": "matched",
|
||||
"python_dependency_bundle_bytes": "matched",
|
||||
"python_dependency_bundle_manifest": "matched",
|
||||
"required_dependency_versions": "matched",
|
||||
"offline_dependency_operations": "passed",
|
||||
"result": "passed",
|
||||
},
|
||||
}
|
||||
@@ -433,3 +450,87 @@ def test_promotion_rejects_bare_or_mismatched_installer_claims(
|
||||
match="does not bind the promotion",
|
||||
):
|
||||
promotion.load_promotion_input(mismatched)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("section", "field", "replacement"),
|
||||
[
|
||||
("provenance", "python_dependency_bundle_sha256", "not-a-digest"),
|
||||
("provenance", "python_dependency_bundle_file_count", 0),
|
||||
("provenance", "python_dependency_bundle_byte_length", 0),
|
||||
(
|
||||
"provenance",
|
||||
"python_dependency_bundle_canonicalization",
|
||||
"different-canonicalization",
|
||||
),
|
||||
(
|
||||
"provenance",
|
||||
"python_dependency_bundle_manifest_sha256",
|
||||
"not-a-digest",
|
||||
),
|
||||
("provenance", "python_dependency_bundle_manifest_byte_length", 0),
|
||||
("runtime_contract", "pythonpath", "/tmp/unsealed-dependencies"),
|
||||
("runtime_contract", "python_dependencies", "host-mounted"),
|
||||
("smoke", "python_dependency_bundle_bytes", "unchecked"),
|
||||
("smoke", "python_dependency_bundle_manifest", "unchecked"),
|
||||
("smoke", "required_dependency_versions", "unchecked"),
|
||||
("smoke", "offline_dependency_operations", "unchecked"),
|
||||
],
|
||||
)
|
||||
def test_promotion_rejects_invalid_coordinator_dependency_evidence(
|
||||
tmp_path: Path,
|
||||
section: str,
|
||||
field: str,
|
||||
replacement: object,
|
||||
) -> None:
|
||||
work_root = tmp_path / "work"
|
||||
work_root.mkdir()
|
||||
component_receipt, coordinator_receipt = _write_installation_evidence(tmp_path)
|
||||
coordinator = cast(
|
||||
dict[str, object],
|
||||
json.loads(coordinator_receipt.read_text(encoding="utf-8")),
|
||||
)
|
||||
nested = cast(dict[str, object], coordinator[section])
|
||||
nested[field] = replacement
|
||||
coordinator_receipt.write_text(json.dumps(coordinator), encoding="utf-8")
|
||||
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",
|
||||
)
|
||||
|
||||
with pytest.raises(promotion.PortableLabV1PromotionError, match="coordinator"):
|
||||
promotion.load_promotion_input(input_path)
|
||||
|
||||
|
||||
def test_promotion_rejects_symlinked_installation_evidence_path(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
work_root = tmp_path / "work"
|
||||
work_root.mkdir()
|
||||
component_receipt, coordinator_receipt = _write_installation_evidence(tmp_path)
|
||||
coordinator_link = tmp_path / "coordinator-image-installation-link.json"
|
||||
coordinator_link.symlink_to(coordinator_receipt)
|
||||
input_path = tmp_path / "promotion-input.json"
|
||||
input_path.write_text(
|
||||
json.dumps(
|
||||
_input_document(
|
||||
work_root,
|
||||
component_receipt=component_receipt,
|
||||
coordinator_receipt=coordinator_link,
|
||||
)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
promotion.PortableLabV1PromotionError,
|
||||
match="not a regular file",
|
||||
):
|
||||
promotion.load_promotion_input(input_path)
|
||||
|
||||
@@ -30,6 +30,22 @@ RUNTIME_REGISTRY_CONTAINER_PATH = (
|
||||
)
|
||||
LAB_V1_RECEIPT_CONTAINER_PATH = "/release/lab-v1-worker-installation-receipt.json"
|
||||
LAB_V1_RELEASE_CANDIDATE_CONTAINER_PATH = "/release/lab-v1-executor-release.json"
|
||||
DEPENDENCY_IMAGE_PATH = "/opt/nodedc/mission-core/deps"
|
||||
DEPENDENCY_MANIFEST_IMAGE_PATH = (
|
||||
"/opt/nodedc/mission-core/release/"
|
||||
"worker-006-agent-python-dependency-bundle.json"
|
||||
)
|
||||
DEPENDENCY_MANIFEST_SCHEMA = (
|
||||
"missioncore.observatory-worker-agent-python-dependency-bundle/v1"
|
||||
)
|
||||
DEPENDENCY_CANONICALIZATION = "utf8-path-nul-length-nul-sha256-lf-v1"
|
||||
DEPENDENCY_VERSIONS = {
|
||||
"httpx": "0.28.1",
|
||||
"lz4": "4.4.5",
|
||||
"paho-mqtt": "2.1.0",
|
||||
"PyYAML": "6.0.3",
|
||||
"typing_extensions": "4.16.0",
|
||||
}
|
||||
|
||||
|
||||
def _document(path: Path) -> dict[str, object]:
|
||||
@@ -44,19 +60,39 @@ def test_agent_dockerfile_is_offline_commit_bound_and_model_free() -> None:
|
||||
assert payload.splitlines()[0] == f"FROM {BASE_REFERENCE}"
|
||||
assert "ARG NODEDC_SOURCE_REVISION" in payload
|
||||
assert "ARG NODEDC_BUILD_CONTEXT_SHA256" in payload
|
||||
assert "ARG NODEDC_PYTHON_DEPENDENCY_BUNDLE_SHA256" in payload
|
||||
assert "ARG NODEDC_PYTHON_DEPENDENCY_BUNDLE_FILE_COUNT" in payload
|
||||
assert "ARG NODEDC_PYTHON_DEPENDENCY_BUNDLE_BYTE_LENGTH" in payload
|
||||
assert "ARG NODEDC_PYTHON_DEPENDENCY_BUNDLE_MANIFEST_SHA256" in payload
|
||||
assert "ARG NODEDC_PYTHON_DEPENDENCY_BUNDLE_MANIFEST_BYTE_LENGTH" in payload
|
||||
assert "ARG SOURCE_DATE_EPOCH" in payload
|
||||
assert "org.opencontainers.image.revision=\"${NODEDC_SOURCE_REVISION}\"" in payload
|
||||
assert "com.nodedc.build-context.sha256=\"${NODEDC_BUILD_CONTEXT_SHA256}\"" in payload
|
||||
assert "com.nodedc.models=\"external\"" in payload
|
||||
assert "PYTHONPATH=/opt/nodedc/mission-core/src" in payload
|
||||
assert f"PYTHONPATH=/opt/nodedc/mission-core/src:{DEPENDENCY_IMAGE_PATH}" in payload
|
||||
assert 'ENTRYPOINT ["python3", "-m", "k1link.observatory.m49_worker_container_main"]' in payload
|
||||
assert "CMD []" in payload
|
||||
assert "COPY src/k1link ./src/k1link" in payload
|
||||
assert "COPY python-dependency-bundle ./deps" in payload
|
||||
assert "COPY worker-006-agent-python-dependency-bundle.json" in payload
|
||||
assert "separately sealed build-only dependency bundle" in payload
|
||||
assert "must never be supplied as runtime bind mounts" in payload
|
||||
assert DEPENDENCY_MANIFEST_IMAGE_PATH.removeprefix(
|
||||
"/opt/nodedc/mission-core/"
|
||||
) in payload
|
||||
assert "COPY config" not in payload
|
||||
assert "COPY . ." not in payload
|
||||
assert "mkdir -p /run/nodedc/registries" in payload
|
||||
assert "chmod 0555 /run/nodedc /run/nodedc/registries" in payload
|
||||
assert 'com.nodedc.runtime-registries="external-read-only"' in payload
|
||||
for label in (
|
||||
"sha256",
|
||||
"file-count",
|
||||
"byte-length",
|
||||
"manifest.sha256",
|
||||
"manifest.byte-length",
|
||||
):
|
||||
assert f"com.nodedc.python-dependency-bundle.{label}" in payload
|
||||
assert re.search(r"(?im)^\s*(?:run\s+)?(?:apt|apk|yum|dnf|pip|uv)\b", payload) is None
|
||||
assert "curl " not in payload.lower()
|
||||
assert "wget " not in payload.lower()
|
||||
@@ -66,7 +102,7 @@ def test_agent_dockerfile_is_offline_commit_bound_and_model_free() -> None:
|
||||
def test_build_context_is_only_repo_source_configuration_and_artifact_contract() -> None:
|
||||
document = _document(CONTEXT_MANIFEST)
|
||||
|
||||
assert document["schema_version"] == "missioncore.observatory-worker-agent-build-context/v1"
|
||||
assert document["schema_version"] == "missioncore.observatory-worker-agent-build-context/v2"
|
||||
assert document["worker_id"] == "worker-006"
|
||||
assert document["authority"] == AUTHORITY
|
||||
source = cast(dict[str, object], document["source_contract"])
|
||||
@@ -107,6 +143,31 @@ def test_build_context_is_only_repo_source_configuration_and_artifact_contract()
|
||||
)
|
||||
|
||||
assert document["embedded_configuration"] == []
|
||||
build_inputs = cast(list[dict[str, object]], document["external_build_inputs"])
|
||||
assert build_inputs == [
|
||||
{
|
||||
"role": "python-dependency-bundle",
|
||||
"required": True,
|
||||
"provenance_requirement": (
|
||||
"already-proven Python dependency tree from the previously "
|
||||
"accepted Worker; build input only"
|
||||
),
|
||||
"source_path": None,
|
||||
"image_path": DEPENDENCY_IMAGE_PATH,
|
||||
"manifest_image_path": DEPENDENCY_MANIFEST_IMAGE_PATH,
|
||||
"manifest_schema_version": DEPENDENCY_MANIFEST_SCHEMA,
|
||||
"identity": {
|
||||
"algorithm": "sha256",
|
||||
"subject": "complete regular-file inventory",
|
||||
"canonicalization": DEPENDENCY_CANONICALIZATION,
|
||||
"path_order": "ordinal",
|
||||
},
|
||||
"included_in_git_archive": False,
|
||||
"copied_into_image": True,
|
||||
"runtime_bind_allowed": False,
|
||||
"network_install_allowed": False,
|
||||
}
|
||||
]
|
||||
assert "required_configuration" not in document
|
||||
external_runtime_files = cast(
|
||||
list[dict[str, object]], document["external_runtime_files"]
|
||||
@@ -133,9 +194,14 @@ def test_build_context_is_only_repo_source_configuration_and_artifact_contract()
|
||||
]
|
||||
runtime = cast(dict[str, object], document["runtime"])
|
||||
assert runtime["entrypoint"] == ENTRYPOINT
|
||||
assert runtime["pythonpath"] == (
|
||||
f"/opt/nodedc/mission-core/src:{DEPENDENCY_IMAGE_PATH}"
|
||||
)
|
||||
assert runtime["composition_module"] == "k1link.observatory.m49_worker_service"
|
||||
assert runtime["configuration_baked_into_image"] is False
|
||||
assert runtime["runtime_registries_baked_into_image"] is False
|
||||
assert runtime["python_dependency_bundle_baked_into_image"] is True
|
||||
assert runtime["python_dependency_bundle_runtime_bind_allowed"] is False
|
||||
assert runtime["models_baked_into_image"] is False
|
||||
assert runtime["executor_releases_baked_into_image"] is False
|
||||
forbidden = cast(list[str], document["forbidden_context"])
|
||||
@@ -146,7 +212,7 @@ def test_build_context_is_only_repo_source_configuration_and_artifact_contract()
|
||||
def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receipt() -> None:
|
||||
document = _document(INSTALL_PLAN)
|
||||
|
||||
assert document["schema_version"] == "missioncore.observatory-worker-agent-install-plan/v1"
|
||||
assert document["schema_version"] == "missioncore.observatory-worker-agent-install-plan/v2"
|
||||
assert document["state"] == "planned-not-built"
|
||||
build = cast(dict[str, object], document["build"])
|
||||
assert build["source_revision"] is None
|
||||
@@ -181,6 +247,31 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
assert staged["embedded_manifest"] == (
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-staged-snapshot.json"
|
||||
)
|
||||
dependency = cast(dict[str, object], build["python_dependency_bundle"])
|
||||
assert dependency == {
|
||||
"state": "required-external-build-input",
|
||||
"provenance_requirement": (
|
||||
"already-proven Python dependency tree from the previously accepted "
|
||||
"Worker; build input only"
|
||||
),
|
||||
"source_root": None,
|
||||
"expected_sha256": None,
|
||||
"identity_subject": "complete regular-file inventory",
|
||||
"canonicalization": DEPENDENCY_CANONICALIZATION,
|
||||
"path_order": "ordinal",
|
||||
"reject_reparse_points": True,
|
||||
"reject_unexpected_non_regular_entries": True,
|
||||
"verify_timing": [
|
||||
"before temporary container creation",
|
||||
"after dependency installation and before image commit",
|
||||
],
|
||||
"image_path": DEPENDENCY_IMAGE_PATH,
|
||||
"manifest_schema_version": DEPENDENCY_MANIFEST_SCHEMA,
|
||||
"embedded_manifest": DEPENDENCY_MANIFEST_IMAGE_PATH,
|
||||
"included_in_git_archive": False,
|
||||
"runtime_bind_allowed": False,
|
||||
"network_install_allowed": False,
|
||||
}
|
||||
base = cast(dict[str, object], build["base_image"])
|
||||
assert base["reference"] == BASE_REFERENCE
|
||||
assert base["sha256"] == BASE_SHA256
|
||||
@@ -199,8 +290,10 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"maximum_thin_layer_bytes": 33554432,
|
||||
"embedded_payload": [
|
||||
"/opt/nodedc/mission-core/src/k1link",
|
||||
DEPENDENCY_IMAGE_PATH,
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-build-context.json",
|
||||
"/opt/nodedc/mission-core/release/worker-006-agent-staged-snapshot.json",
|
||||
DEPENDENCY_MANIFEST_IMAGE_PATH,
|
||||
],
|
||||
}
|
||||
assert build["required_preflight"] == [
|
||||
@@ -211,6 +304,9 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"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",
|
||||
"Python dependency bundle is an external build input with an exact expected SHA-256",
|
||||
"Python dependency bundle SHA-256 matches its ordinal canonical regular-file inventory",
|
||||
"Python dependency bundle contains the five exact smoke-tested package versions",
|
||||
]
|
||||
|
||||
acceptance = cast(dict[str, object], document["build_acceptance"])
|
||||
@@ -225,6 +321,21 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
assert labels["com.nodedc.embedded-snapshot-manifest.sha256"] == (
|
||||
"<embedded-snapshot-manifest-sha256>"
|
||||
)
|
||||
assert labels["com.nodedc.python-dependency-bundle.sha256"] == (
|
||||
"<python-dependency-bundle-sha256>"
|
||||
)
|
||||
assert labels["com.nodedc.python-dependency-bundle.file-count"] == (
|
||||
"<python-dependency-bundle-file-count>"
|
||||
)
|
||||
assert labels["com.nodedc.python-dependency-bundle.byte-length"] == (
|
||||
"<python-dependency-bundle-byte-length>"
|
||||
)
|
||||
assert labels["com.nodedc.python-dependency-bundle.manifest.sha256"] == (
|
||||
"<python-dependency-bundle-manifest-sha256>"
|
||||
)
|
||||
assert labels["com.nodedc.python-dependency-bundle.manifest.byte-length"] == (
|
||||
"<python-dependency-bundle-manifest-byte-length>"
|
||||
)
|
||||
assert labels["com.nodedc.build-method"] == "docker-commit-exact-layer-v1"
|
||||
rootfs = cast(dict[str, object], acceptance["rootfs"])
|
||||
assert rootfs == {
|
||||
@@ -237,6 +348,12 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
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"])
|
||||
assert "mandatory external build input" in cast(
|
||||
str, embedded["python_dependency_bundle"]
|
||||
)
|
||||
assert "every embedded dependency file" in cast(
|
||||
str, embedded["python_dependency_bundle_manifest"]
|
||||
)
|
||||
smoke = cast(dict[str, object], acceptance["smoke"])
|
||||
assert smoke["network"] == "none"
|
||||
assert smoke["read_only_rootfs"] is True
|
||||
@@ -244,6 +361,12 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
assert smoke["staged_source_bytes"] == "matched"
|
||||
assert smoke["embedded_context_bytes"] == "matched"
|
||||
assert smoke["embedded_snapshot_manifest"] == "matched"
|
||||
assert smoke["embedded_python_dependency_bundle"] == "matched"
|
||||
assert smoke["embedded_python_dependency_bundle_manifest"] == "matched"
|
||||
assert smoke["pythonpath"] == (
|
||||
f"/opt/nodedc/mission-core/src:{DEPENDENCY_IMAGE_PATH}"
|
||||
)
|
||||
assert smoke["python_package_versions"] == DEPENDENCY_VERSIONS
|
||||
assert smoke["expected_result"] == "exit-0"
|
||||
|
||||
runtime = cast(dict[str, object], document["runtime"])
|
||||
@@ -367,6 +490,10 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
assert external["baked_into_agent_image"] is False
|
||||
assert external["queued_jobs_may_select_paths_or_commands"] is False
|
||||
mounts = cast(list[dict[str, object]], runtime["mounts"])
|
||||
assert not any(
|
||||
cast(str, row["container_path"]).startswith(DEPENDENCY_IMAGE_PATH)
|
||||
for row in mounts
|
||||
)
|
||||
registry_mounts = [
|
||||
row
|
||||
for row in mounts
|
||||
@@ -400,10 +527,13 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"owns_component_image_identities": True,
|
||||
"queued_jobs_may_override_component_images": False,
|
||||
}
|
||||
external = cast(dict[str, object], runtime["external_assets"])
|
||||
assert external["python_dependency_bundle_baked_into_agent_image"] is True
|
||||
assert external["python_dependency_bundle_runtime_bind_allowed"] is False
|
||||
|
||||
receipt = cast(dict[str, object], document["receipt_skeleton"])
|
||||
assert receipt["schema_version"] == (
|
||||
"missioncore.observatory-worker-agent-image-installation/v1"
|
||||
"missioncore.observatory-worker-agent-image-installation/v2"
|
||||
)
|
||||
assert receipt["status"] is None
|
||||
assert receipt["build_method"] == "docker-commit-exact-layer-v1"
|
||||
@@ -420,6 +550,12 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
),
|
||||
"embedded_snapshot_manifest_sha256": None,
|
||||
"embedded_snapshot_manifest_byte_length": None,
|
||||
"python_dependency_bundle_sha256": None,
|
||||
"python_dependency_bundle_file_count": None,
|
||||
"python_dependency_bundle_byte_length": None,
|
||||
"python_dependency_bundle_canonicalization": DEPENDENCY_CANONICALIZATION,
|
||||
"python_dependency_bundle_manifest_sha256": None,
|
||||
"python_dependency_bundle_manifest_byte_length": None,
|
||||
}
|
||||
assert receipt["base_image_sha256"] == BASE_SHA256
|
||||
assert receipt["derived_image_sha256"] is None
|
||||
@@ -442,9 +578,11 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"workdir": "/opt/nodedc/mission-core",
|
||||
"entrypoint": ENTRYPOINT,
|
||||
"command": [],
|
||||
"pythonpath": f"/opt/nodedc/mission-core/src:{DEPENDENCY_IMAGE_PATH}",
|
||||
"authority": "observation-only",
|
||||
"models": "external",
|
||||
"runtime_registries": "external-read-only",
|
||||
"python_dependencies": "embedded-sealed-build-input",
|
||||
}
|
||||
receipt_smoke = cast(dict[str, object], receipt["smoke"])
|
||||
assert receipt_smoke == {
|
||||
@@ -453,5 +591,9 @@ def test_install_plan_requires_offline_build_hardening_smoke_and_unfilled_receip
|
||||
"staged_source_bytes": None,
|
||||
"embedded_context_bytes": None,
|
||||
"embedded_snapshot_manifest": None,
|
||||
"python_dependency_bundle_bytes": None,
|
||||
"python_dependency_bundle_manifest": None,
|
||||
"required_dependency_versions": None,
|
||||
"offline_dependency_operations": None,
|
||||
"result": "not-run",
|
||||
}
|
||||
|
||||
@@ -73,6 +73,10 @@ def test_worker_agent_installer_separates_archive_and_staged_identities() -> Non
|
||||
)
|
||||
assert "Dockerfile.worker-006-agent" in script
|
||||
assert "worker-006-agent-build-context.json" in script
|
||||
assert (
|
||||
'"missioncore.observatory-worker-agent-build-context/v2"'
|
||||
in script
|
||||
)
|
||||
assert (
|
||||
'schema_version = "missioncore.observatory-worker-agent-embedded-snapshot/v1"'
|
||||
in script
|
||||
@@ -81,6 +85,32 @@ def test_worker_agent_installer_separates_archive_and_staged_identities() -> Non
|
||||
assert "embedded_snapshot_manifest_sha256" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_uses_ordinal_snapshot_path_order() -> None:
|
||||
script = _script()
|
||||
|
||||
assert "$rowsByPath = @{}" in script
|
||||
assert "$rowsByPath.ContainsKey($relative)" in script
|
||||
assert "$orderedPaths = [string[]]@($rowsByPath.Keys)" in script
|
||||
assert "[Array]::Sort($orderedPaths, [StringComparer]::Ordinal)" in script
|
||||
assert "$orderedPaths | ForEach-Object { $rowsByPath[$_] }" in script
|
||||
assert "Sort-Object -Property path -CaseSensitive" not in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_reports_observed_snapshot_sha_on_mismatch() -> None:
|
||||
script = _script()
|
||||
|
||||
mismatch_guard = (
|
||||
"if ([string]$snapshotBefore.sha256 -cne "
|
||||
"$ExpectedStagedSnapshotSha256)"
|
||||
)
|
||||
diagnostic = (
|
||||
'"Worker 006 staged snapshot identity changed: observed=" +\n'
|
||||
" [string]$snapshotBefore.sha256"
|
||||
)
|
||||
assert mismatch_guard in script
|
||||
assert diagnostic in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_copies_only_runtime_source_and_contract() -> None:
|
||||
script = _script()
|
||||
|
||||
@@ -106,6 +136,60 @@ def test_worker_agent_installer_copies_only_runtime_source_and_contract() -> Non
|
||||
assert "chmod 0555 /run/nodedc /run/nodedc/registries" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_seals_offline_python_dependency_bundle() -> None:
|
||||
script = _script()
|
||||
|
||||
assert "[string]$DependencyBundleRoot" in script
|
||||
assert "[string]$ExpectedDependencyBundleSha256" in script
|
||||
assert script.count("Get-DependencyBundleInspection $DependencyBundleRoot") == 2
|
||||
assert "Python dependency bundle contains a reparse point" in script
|
||||
assert "Python dependency bundle contains a non-file entry" in script
|
||||
assert "Python dependency bundle contains no regular files" in script
|
||||
assert "[Array]::Sort($orderedPaths, [StringComparer]::Ordinal)" in script
|
||||
assert 'canonicalization = "utf8-path-nul-length-nul-sha256-lf-v1"' in script
|
||||
assert (
|
||||
'schema_version = "missioncore.observatory-worker-agent-python-'
|
||||
'dependency-bundle/v1"'
|
||||
) in script
|
||||
assert "required_distributions = [ordered]@{" in script
|
||||
assert "$externalBuildInputs = @($context.external_build_inputs)" in script
|
||||
assert '[string]$dependencyInput.identity.path_order -cne "ordinal"' in script
|
||||
assert "[bool]$dependencyInput.runtime_bind_allowed" in script
|
||||
assert "[bool]$dependencyInput.network_install_allowed" in script
|
||||
assert "cp -a /nodedc-build-deps/. /opt/nodedc/mission-core/deps/" in script
|
||||
assert "target=/nodedc-build-deps,readonly" in script
|
||||
assert "target=/nodedc-verify-deps,readonly" in script
|
||||
assert "target=/opt/nodedc/mission-core/deps" not in script
|
||||
assert "find /nodedc-build-deps -type f" in script
|
||||
assert "find /opt/nodedc/mission-core/deps -type f" in script
|
||||
assert "find /nodedc-verify-deps -type f" in script
|
||||
assert (
|
||||
"/opt/nodedc/mission-core/release/"
|
||||
"worker-006-agent-python-dependency-bundle.json"
|
||||
) in script
|
||||
|
||||
|
||||
def test_worker_agent_dependency_smoke_is_exact_and_networkless() -> None:
|
||||
script = _script()
|
||||
|
||||
for distribution, version in (
|
||||
("httpx", "0.28.1"),
|
||||
("lz4", "4.4.5"),
|
||||
("paho-mqtt", "2.1.0"),
|
||||
("PyYAML", "6.0.3"),
|
||||
("typing_extensions", "4.16.0"),
|
||||
):
|
||||
assert f'`"{distribution}`":`"{version}`"' in script
|
||||
assert "import httpx; import lz4.frame; import paho.mqtt.client as mqtt" in script
|
||||
assert "lz4.frame.decompress(lz4.frame.compress(payload))" in script
|
||||
assert "httpx.Request(`\"GET`\",url)" in script
|
||||
assert "yaml.safe_load(yaml.safe_dump(document))" in script
|
||||
assert "mqtt.Client(callback_api_version=mqtt.CallbackAPIVersion.VERSION2)" in script
|
||||
assert "typing_extensions.TypeAlias is not None" in script
|
||||
assert "compose_installed_m49_worker_service" in script
|
||||
assert "--network none" in script
|
||||
|
||||
|
||||
def test_worker_agent_installer_proves_base_chain_and_embedded_bytes() -> None:
|
||||
script = _script()
|
||||
|
||||
@@ -131,20 +215,20 @@ def test_worker_agent_installer_removes_failed_new_image_and_temporary_files() -
|
||||
script = _script()
|
||||
|
||||
assert "$imageCommitted = $true" in script
|
||||
assert (
|
||||
"Invoke-InstalledImageSmoke $tag $StagedSnapshotRoot $embeddedManifest"
|
||||
in script
|
||||
)
|
||||
assert "Invoke-InstalledImageSmoke `" in script
|
||||
assert "$DependencyBundleRoot `" in script
|
||||
assert "$dependencyManifest" 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
|
||||
assert "Remove-Item -LiteralPath $dependencyManifest.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",
|
||||
"PYTHONPATH=/opt/nodedc/mission-core/src:/opt/nodedc/mission-core/deps",
|
||||
"PYTHONNOUSERSITE=1",
|
||||
"PYTHONDONTWRITEBYTECODE=1",
|
||||
"PYTHONUNBUFFERED=1",
|
||||
@@ -165,8 +249,25 @@ def test_worker_agent_installer_seals_runtime_contract_smoke_and_output() -> Non
|
||||
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"'
|
||||
'schema_version = "missioncore.observatory-worker-agent-image-installation/v2"'
|
||||
in script
|
||||
)
|
||||
for label in (
|
||||
"com.nodedc.python-dependency-bundle.sha256",
|
||||
"com.nodedc.python-dependency-bundle.file-count",
|
||||
"com.nodedc.python-dependency-bundle.byte-length",
|
||||
"com.nodedc.python-dependency-bundle.manifest.sha256",
|
||||
"com.nodedc.python-dependency-bundle.manifest.byte-length",
|
||||
):
|
||||
assert label in script
|
||||
for field in (
|
||||
"python_dependency_bundle_sha256",
|
||||
"python_dependency_bundle_file_count",
|
||||
"python_dependency_bundle_byte_length",
|
||||
"python_dependency_bundle_canonicalization",
|
||||
"python_dependency_bundle_manifest_sha256",
|
||||
"python_dependency_bundle_manifest_byte_length",
|
||||
):
|
||||
assert field 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