diff --git a/config/deployment/mission-core-worker-shadow-v1.template.json b/config/deployment/mission-core-worker-shadow-v1.template.json index cff73b7..18c83c9 100644 --- a/config/deployment/mission-core-worker-shadow-v1.template.json +++ b/config/deployment/mission-core-worker-shadow-v1.template.json @@ -14,6 +14,7 @@ }, "code_revision": "__CODE_REVISION__", "component": "mission-core-worker", + "dependency_tree_identity": "relative-path-tab-size-tab-file-sha256-lf/v1", "container": { "cap_drop": [ "ALL" @@ -39,7 +40,7 @@ "includes": [ "." ], - "tree_sha256": "301f5b9334703f838edc1b682c25ed81086bf3fe81a838c4c849e08e75bcf8e2" + "tree_sha256": "e32a03544dfc575ac9a002dd6a57765481b0c2b42bf76ab638441bbfe4f459b3" }, { "bytes": 155757763, @@ -50,7 +51,7 @@ "includes": [ "." ], - "tree_sha256": "abdc6cb3989e38b0c2f4c1a050efb3d6a474e90f68b05ba39a42f001ddf87dbe" + "tree_sha256": "c46cbf1ffd1bf4f88dc34c1763f7605cfd1d23b143190fd305142f9c23473c79" }, { "bytes": 21052467, @@ -63,7 +64,7 @@ "pillow-12.3.0.dist-info", "pillow.libs" ], - "tree_sha256": "e8ad3f69443be6c9f6233703aedf793fba40a7b54c1f7845702cce7e47699711" + "tree_sha256": "31e052e7790f0b3a3d495171070e97244d3da43aaef45d7f10e7d2d671b9ea23" } ], "host": { @@ -132,6 +133,10 @@ "name": "m4-recorded-realtime-baseline-v1.json", "sha256": "ea10359339e6cce31b5780a2710299771cab7cc0c1c2a2b56a1621f786b31fa8" }, + "runner": { + "name": "Invoke-M4DetectorShadow.ps1", + "sha256": "__RUNNER_SHA256__" + }, "wheel": { "name": "nodedc_mission_core-0.1.0-py3-none-any.whl", "sha256": "df756938f2c212fb6d1770c83569e5366c708d70d74e0434895a21be55a16102" diff --git a/scripts/Invoke-M4DetectorShadow.ps1 b/scripts/Invoke-M4DetectorShadow.ps1 index 960f0ab..7d36798 100644 --- a/scripts/Invoke-M4DetectorShadow.ps1 +++ b/scripts/Invoke-M4DetectorShadow.ps1 @@ -161,6 +161,9 @@ if ( $descriptor.component -cne "mission-core-worker" -or $descriptor.artifact_type -cne "shadow-release" -or $descriptor.transition -cne "m4-detector-shadow-v1" -or + $descriptor.dependency_tree_identity -cne ( + "relative-path-tab-size-tab-file-sha256-lf/v1" + ) -or $descriptor.boundary.repository -cne "NODEDC_MISSION_CORE" -or $descriptor.boundary.nodedc_platform_repository -ne $false -or $descriptor.boundary.external_deploy_registry -ne $false -or @@ -177,6 +180,11 @@ if ( $wheelName = Get-RequiredText $descriptor.release.wheel.name "Wheel name" $baselineName = Get-RequiredText $descriptor.release.baseline.name "Baseline name" +$runnerName = Get-RequiredText $descriptor.release.runner.name "Runner name" +if ($runnerName -cne "Invoke-M4DetectorShadow.ps1") { + throw "M4 runner name changed" +} +$null = Assert-FileSha256 $PSCommandPath $descriptor.release.runner.sha256 "M4 local runner" $wheelPath = Assert-FileSha256 ( Join-Path $payload $wheelName ) $descriptor.release.wheel.sha256 "M4 runtime wheel" @@ -188,6 +196,7 @@ foreach ($entry in $descriptor.inputs.PSObject.Properties) { $input = $entry.Value $null = Assert-FileSha256 $input.host_path $input.sha256 ("M4 input {0}" -f $entry.Name) } +$dependencyFailures = @() foreach ($dependency in $descriptor.dependencies) { $root = Resolve-DDirectory $dependency.host_path ("M4 dependency {0}" -f $dependency.id) $false $files = @() @@ -217,7 +226,7 @@ foreach ($dependency in $descriptor.dependencies) { $bytes -ne [int64]$dependency.bytes -or $treeSha256 -cne [string]$dependency.tree_sha256 ) { - throw ( + $dependencyFailures += ( "M4 dependency {0} inventory changed: expected {1}/{2}/{3}, observed {4}/{5}/{6}" -f $dependency.id, $dependency.file_count, @@ -229,6 +238,10 @@ foreach ($dependency in $descriptor.dependencies) { ) } } +if ($dependencyFailures.Count -gt 0) { + $dependencyFailures | ForEach-Object { Write-Output ("DEPENDENCY_MISMATCH={0}" -f $_) } + throw "M4 dependency inventory changed" +} $imageRef = Get-RequiredText $descriptor.container.image_ref "Container image reference" & docker image inspect $imageRef *> $null diff --git a/scripts/build_m4_worker_shadow_artifact.py b/scripts/build_m4_worker_shadow_artifact.py index c69aa44..174b63b 100644 --- a/scripts/build_m4_worker_shadow_artifact.py +++ b/scripts/build_m4_worker_shadow_artifact.py @@ -84,9 +84,17 @@ def build_wheel(output: Path) -> Path: def render_descriptor(patch_id: str, revision: str) -> bytes: template = DESCRIPTOR_TEMPLATE.read_text("utf-8") - if template.count("__PATCH_ID__") != 1 or template.count("__CODE_REVISION__") != 1: + if ( + template.count("__PATCH_ID__") != 1 + or template.count("__CODE_REVISION__") != 1 + or template.count("__RUNNER_SHA256__") != 1 + ): raise ArtifactBuildError("descriptor template placeholders changed") - rendered = template.replace("__PATCH_ID__", patch_id).replace("__CODE_REVISION__", revision) + rendered = ( + template.replace("__PATCH_ID__", patch_id) + .replace("__CODE_REVISION__", revision) + .replace("__RUNNER_SHA256__", sha256_file(RUNNER)) + ) document = json.loads(rendered) if document["patch_id"] != patch_id or document["code_revision"] != revision: raise ArtifactBuildError("descriptor identity rendering failed") diff --git a/tests/test_m4_worker_shadow_artifact.py b/tests/test_m4_worker_shadow_artifact.py index 2cada0d..ba66210 100644 --- a/tests/test_m4_worker_shadow_artifact.py +++ b/tests/test_m4_worker_shadow_artifact.py @@ -76,6 +76,13 @@ def test_worker_shadow_artifact_is_deterministic_narrow_and_self_contained( "repository": "NODEDC_MISSION_CORE", "server_docker_runtime": False, } + assert descriptor["dependency_tree_identity"] == ( + "relative-path-tab-size-tab-file-sha256-lf/v1" + ) + assert descriptor["release"]["runner"] == { + "name": BUILDER.RUNNER_NAME, + "sha256": _sha256(BUILDER.RUNNER.read_bytes()), + } assert descriptor["container"]["public_ports"] is False assert descriptor["rollback"] == { "durable_worker_action": "none",