From dcf53043450039c57e8f4462969596f25fef343a Mon Sep 17 00:00:00 2001 From: Codex Date: Sun, 23 Aug 2026 11:58:58 +0300 Subject: [PATCH] feat(deploy): register Device Core telemetry releases --- infra/deploy-runner/nodedc-deploy | 1277 ++++++++++++++++- .../test_device_plane_registry.py | 251 ++++ 2 files changed, 1488 insertions(+), 40 deletions(-) diff --git a/infra/deploy-runner/nodedc-deploy b/infra/deploy-runner/nodedc-deploy index 99bc70d..b4fb292 100755 --- a/infra/deploy-runner/nodedc-deploy +++ b/infra/deploy-runner/nodedc-deploy @@ -281,6 +281,18 @@ DEVICE_PLANE_MANAGER_RELEASE_V8_REL = ( DEVICE_PLANE_MANAGER_RELEASE_V9_REL = ( "deployment/device-manager-release-v9.json" ) +DEVICE_PLANE_MANAGER_RELEASE_V10_REL = ( + "deployment/device-manager-release-v10.json" +) +DEVICE_PLANE_MANAGER_RELEASE_V11_REL = ( + "deployment/device-manager-release-v11.json" +) +DEVICE_PLANE_MANAGER_RELEASE_V12_REL = ( + "deployment/device-manager-release-v12.json" +) +DEVICE_PLANE_MANAGER_RELEASE_V13_REL = ( + "deployment/device-manager-release-v13.json" +) DEVICE_PLANE_MANAGER_COMPOSE_REL = "docker-compose.device-manager.yml" DEVICE_PLANE_EDGE_CORE_CHANNEL_BOOTSTRAP_REL = ( "deployment/device-edge-core-channel-bootstrap-v1.json" @@ -303,6 +315,9 @@ DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL = ( DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL = ( "deployment/device-control-core-release-v3.json" ) +DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL = ( + "deployment/device-control-core-release-v4.json" +) DEVICE_PLANE_EDGE_CORE_CHANNEL_COMPOSE_REL = ( "docker-compose.device-edge-core-channel.yml" ) @@ -383,6 +398,16 @@ DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES = ( "services/device-control-core", DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL, ) +DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES = ( + ".dockerignore", + "package.json", + "package-lock.json", + "packages/device-protocol-contract", + "packages/device-edge-channel-contract", + "packages/infrastructure-telemetry-contract", + "services/device-control-core", + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL, +) DEVICE_PLANE_CONTROL_CORE_V3_RECONCILIATION_REL = ( "deployment/device-control-core-release-v3-reconciliation-v1.json" ) @@ -505,6 +530,39 @@ DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_ARTIFACT = ( DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_ARTIFACT_SHA256 = ( "46000c76977fb583fc7c9cf74ecf624efd8b404f7b8d0322e0270e7b8ac6e450" ) +DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_BACKUP_ID = ( + "device-plane-device-control-core-migration-replay-checkpoint-recovery-" + "20260822-046-20260823-002004" +) +DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_BACKUP_SHA256 = { + "manifest.env": ( + "ca0d880ad8488a8e9d3b846f0f0b8e2d0a3e0df356b834ffdc1fedec65bb1acc" + ), + "files.txt": ( + "34cf294a6f5cc68ee7470356bac847221aa8457f8c90312b2eda17ec59b30155" + ), + "existing-files.txt": ( + "65452d59b095f05d550cafe9573ab8cad180d5c4799a353d67e6ac8e6366c7ad" + ), + "missing-files.txt": ( + "b15be462f0b40badb4a3c8b5922bd23fd93cb896b80394252f217c2b1102f1f3" + ), + "source-before.tgz": ( + "e27a9705c63a03158116530095466dcee8024ab1dee5ef26628e5691001d651e" + ), + "runtime-before.json": ( + "f74bfdb74efb85c7b57cb5e4032d161985b6da8e0208276e218dd7013d1bea6f" + ), +} +DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_PATCH_ID = ( + "device-control-core-release-v4-20260823-047" +) +DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_PATCH_ID = ( + "device-control-core-release-v2-20260822-038" +) +DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_ARTIFACT_SHA256 = ( + "e2d062b82b022dba662522b5d6e192026ac964d78950d903295ca3cbbc95ab28" +) DEVICE_PLANE_CONTROL_CORE_MIGRATION_014_PREDECESSOR_SHA256 = ( "751accf346b34d2774cc7b9572640d2c25fdb0b1db793ac32183b56f48e26508" ) @@ -710,6 +768,54 @@ DEVICE_PLANE_MANAGER_RELEASE_V9_CONTROL_CORE_PREDECESSOR_PATCH_ID = ( DEVICE_PLANE_MANAGER_RELEASE_V9_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 = ( "08448a56cdf391076f92c5242e368fd0033b420167874645eebfa1f22184ee92" ) +DEVICE_PLANE_MANAGER_RELEASE_V10_PREDECESSOR_PATCH_ID = ( + "device-manager-release-v8-20260822-039" +) +DEVICE_PLANE_MANAGER_RELEASE_V10_PREDECESSOR_ARTIFACT_SHA256 = ( + "30a83d4c6b5c029c96c4af19fa558e0ae75e4b60bc897881457304bd17e0e465" +) +DEVICE_PLANE_MANAGER_RELEASE_V10_CONTROL_CORE_PREDECESSOR_PATCH_ID = ( + "device-control-core-release-v4-20260823-047" +) +DEVICE_PLANE_MANAGER_RELEASE_V10_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 = ( + "4aecceeb8d400fdd3dbec8fc86b151691f389d165e72677f396f8994823b6b5c" +) +DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_PATCH_ID = ( + "device-manager-release-v10-20260823-048" +) +DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_ARTIFACT_SHA256 = ( + "e6b983a314db4f8c27d89062dfedf5ed0523cc30421170799d181a19e2d85d4c" +) +DEVICE_PLANE_MANAGER_RELEASE_V11_CONTROL_CORE_PREDECESSOR_PATCH_ID = ( + "device-control-core-release-v4-20260823-047" +) +DEVICE_PLANE_MANAGER_RELEASE_V11_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 = ( + "4aecceeb8d400fdd3dbec8fc86b151691f389d165e72677f396f8994823b6b5c" +) +DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_PATCH_ID = ( + "device-manager-release-v11-20260823-049" +) +DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_ARTIFACT_SHA256 = ( + "c1e2056b50bfbb0d03d077461d0c27cc56cc52967c3f5620be14871c8a6d5cf0" +) +DEVICE_PLANE_MANAGER_RELEASE_V12_CONTROL_CORE_PREDECESSOR_PATCH_ID = ( + "device-control-core-release-v4-20260823-047" +) +DEVICE_PLANE_MANAGER_RELEASE_V12_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 = ( + "4aecceeb8d400fdd3dbec8fc86b151691f389d165e72677f396f8994823b6b5c" +) +DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_PATCH_ID = ( + "device-manager-release-v12-20260823-050" +) +DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_ARTIFACT_SHA256 = ( + "1a49839140e5f2e49763d78f24ee47d946e244bcfde15a9c38266e8bd14c0d49" +) +DEVICE_PLANE_MANAGER_RELEASE_V13_CONTROL_CORE_PREDECESSOR_PATCH_ID = ( + "device-control-core-release-v4-20260823-047" +) +DEVICE_PLANE_MANAGER_RELEASE_V13_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 = ( + "4aecceeb8d400fdd3dbec8fc86b151691f389d165e72677f396f8994823b6b5c" +) DEVICE_PLANE_EDGE_CORE_CHANNEL_BOOTSTRAP_PREDECESSOR_PATCH_ID = ( "device-edge-core-channel-bootstrap-20260812-018" ) @@ -786,6 +892,18 @@ DEVICE_PLANE_MANAGER_RELEASE_V8_COMPOSE_SHA256 = ( DEVICE_PLANE_MANAGER_RELEASE_V9_COMPOSE_SHA256 = ( DEVICE_PLANE_MANAGER_RELEASE_V8_COMPOSE_SHA256 ) +DEVICE_PLANE_MANAGER_RELEASE_V10_COMPOSE_SHA256 = ( + DEVICE_PLANE_MANAGER_RELEASE_V9_COMPOSE_SHA256 +) +DEVICE_PLANE_MANAGER_RELEASE_V11_COMPOSE_SHA256 = ( + DEVICE_PLANE_MANAGER_RELEASE_V10_COMPOSE_SHA256 +) +DEVICE_PLANE_MANAGER_RELEASE_V12_COMPOSE_SHA256 = ( + DEVICE_PLANE_MANAGER_RELEASE_V11_COMPOSE_SHA256 +) +DEVICE_PLANE_MANAGER_RELEASE_V13_COMPOSE_SHA256 = ( + DEVICE_PLANE_MANAGER_RELEASE_V12_COMPOSE_SHA256 +) DEVICE_PLANE_MANAGER_FAVICON_SHA256 = { "favicon.ico": ( "f8933114a85646335ea5c94944f56d3cd8c48a6032016719f7905ff244ec0aa2" @@ -917,6 +1035,26 @@ DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES = ( "services/device-manager", DEVICE_PLANE_MANAGER_RELEASE_V9_REL, ) +DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES = ( + DEVICE_PLANE_MANAGER_COMPOSE_REL, + "services/device-manager", + DEVICE_PLANE_MANAGER_RELEASE_V10_REL, +) +DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES = ( + DEVICE_PLANE_MANAGER_COMPOSE_REL, + "services/device-manager", + DEVICE_PLANE_MANAGER_RELEASE_V11_REL, +) +DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES = ( + DEVICE_PLANE_MANAGER_COMPOSE_REL, + "services/device-manager", + DEVICE_PLANE_MANAGER_RELEASE_V12_REL, +) +DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES = ( + DEVICE_PLANE_MANAGER_COMPOSE_REL, + "services/device-manager", + DEVICE_PLANE_MANAGER_RELEASE_V13_REL, +) DEVICE_PLANE_MANAGER_RECONCILIATION_REL = ( "deployment/device-manager-control-plane-reconciliation-v1.json" ) @@ -4961,6 +5099,10 @@ def allowed_payload_path(component, rel): DEVICE_PLANE_MANAGER_RELEASE_V7_REL, DEVICE_PLANE_MANAGER_RELEASE_V8_REL, DEVICE_PLANE_MANAGER_RELEASE_V9_REL, + DEVICE_PLANE_MANAGER_RELEASE_V10_REL, + DEVICE_PLANE_MANAGER_RELEASE_V11_REL, + DEVICE_PLANE_MANAGER_RELEASE_V12_REL, + DEVICE_PLANE_MANAGER_RELEASE_V13_REL, DEVICE_PLANE_EDGE_CORE_CHANNEL_BOOTSTRAP_REL, DEVICE_PLANE_EDGE_CORE_CHANNEL_UPGRADE_REL, DEVICE_PLANE_EDGE_CORE_CHANNEL_UPGRADE_V2_REL, @@ -4968,6 +5110,7 @@ def allowed_payload_path(component, rel): DEVICE_PLANE_CONTROL_CORE_RELEASE_REL, DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL, DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL, DEVICE_PLANE_CONTROL_CORE_V3_RECONCILIATION_REL, DEVICE_PLANE_CONTROL_CORE_INCIDENT_AUDIT_REL, DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_AUDIT_REL, @@ -11963,6 +12106,10 @@ def is_device_plane_manager_control_plane_slice(component, entries): DEVICE_PLANE_MANAGER_RELEASE_V7_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V8_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES, ) ) @@ -12031,6 +12178,38 @@ def is_device_plane_manager_release_v9_slice(component, entries): ) +def is_device_plane_manager_release_v10_slice(component, entries): + return ( + component == "device-plane" + and entries is not None + and tuple(entries) == DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES + ) + + +def is_device_plane_manager_release_v11_slice(component, entries): + return ( + component == "device-plane" + and entries is not None + and tuple(entries) == DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES + ) + + +def is_device_plane_manager_release_v12_slice(component, entries): + return ( + component == "device-plane" + and entries is not None + and tuple(entries) == DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES + ) + + +def is_device_plane_manager_release_v13_slice(component, entries): + return ( + component == "device-plane" + and entries is not None + and tuple(entries) == DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES + ) + + def is_device_plane_manager_persistent_release_slice(component, entries): return ( is_device_plane_manager_release_v4_slice(component, entries) @@ -12039,6 +12218,10 @@ def is_device_plane_manager_persistent_release_slice(component, entries): or is_device_plane_manager_release_v7_slice(component, entries) or is_device_plane_manager_release_v8_slice(component, entries) or is_device_plane_manager_release_v9_slice(component, entries) + or is_device_plane_manager_release_v10_slice(component, entries) + or is_device_plane_manager_release_v11_slice(component, entries) + or is_device_plane_manager_release_v12_slice(component, entries) + or is_device_plane_manager_release_v13_slice(component, entries) ) @@ -12098,6 +12281,7 @@ def is_device_plane_control_core_release_slice(component, entries): DEVICE_PLANE_CONTROL_CORE_RELEASE_ENTRIES, DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES, ) ) @@ -12118,6 +12302,14 @@ def is_device_plane_control_core_release_v3_slice(component, entries): ) +def is_device_plane_control_core_release_v4_slice(component, entries): + return ( + component == "device-plane" + and entries is not None + and tuple(entries) == DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES + ) + + def is_device_plane_control_core_v3_reconciliation_slice(component, entries): return ( component == "device-plane" @@ -12446,6 +12638,128 @@ def expected_device_plane_manager_release_v9_boundaries(): } +def expected_device_plane_manager_release_v10_boundaries(): + return { + **expected_device_plane_manager_release_v9_boundaries(), + "predecessor": { + "kind": "release", + "patchId": DEVICE_PLANE_MANAGER_RELEASE_V10_PREDECESSOR_PATCH_ID, + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V10_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "controlCorePredecessor": { + "patchId": ( + DEVICE_PLANE_MANAGER_RELEASE_V10_CONTROL_CORE_PREDECESSOR_PATCH_ID + ), + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V10_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + } + + +def expected_device_plane_manager_release_v11_boundaries(): + return { + **expected_device_plane_manager_release_v10_boundaries(), + "predecessor": { + "kind": "release", + "patchId": DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_PATCH_ID, + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "controlCorePredecessor": { + "patchId": ( + DEVICE_PLANE_MANAGER_RELEASE_V11_CONTROL_CORE_PREDECESSOR_PATCH_ID + ), + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V11_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "designSystem": "nodedc-canonical-components-and-tokens-v1", + "missionCoreReference": "compute-modules-workspace-71c8b04", + "infrastructureWorkspaceLayout": ( + "mission-core-system-workspace-v1" + ), + "hostInventoryComposition": "mission-core-compute-host-list-v1", + "telemetryWorkspace": ( + "mission-core-compute-module-visual-parity-v2" + ), + "telemetrySurface": "borderless-soft-surface-v1", + "telemetryStatus": "mission-core-dot-status-v1", + "telemetryScroll": "reset-on-workspace-transition-v1", + } + + +def expected_device_plane_manager_release_v12_boundaries(): + return { + **expected_device_plane_manager_release_v11_boundaries(), + "predecessor": { + "kind": "release", + "patchId": DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_PATCH_ID, + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "controlCorePredecessor": { + "patchId": ( + DEVICE_PLANE_MANAGER_RELEASE_V12_CONTROL_CORE_PREDECESSOR_PATCH_ID + ), + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V12_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "telemetryWorkspace": ( + "mission-core-compute-module-adaptive-window-v3" + ), + "telemetryGraphScale": ( + "adaptive-observed-window-explicit-domain-v1" + ), + "telemetryCpuMinimumSpan": "five-percentage-points", + "telemetryMemoryMinimumSpan": "four-percentage-points", + "telemetryNetworkMissingSemantics": ( + "missing-counters-never-zero-v1" + ), + } + + +def expected_device_plane_manager_release_v13_boundaries(): + return { + **expected_device_plane_manager_release_v12_boundaries(), + "predecessor": { + "kind": "release", + "patchId": DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_PATCH_ID, + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "controlCorePredecessor": { + "patchId": ( + DEVICE_PLANE_MANAGER_RELEASE_V13_CONTROL_CORE_PREDECESSOR_PATCH_ID + ), + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V13_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "infrastructureWorkspaceLayout": "mission-core-system-workspace-v2", + "hostInventoryComposition": "mission-core-compute-host-accordion-v2", + "hostInventoryOverviewSurface": ( + "separate-summary-soft-surface-v1" + ), + "hostInventoryCollectionSurface": ( + "separate-host-collection-soft-surface-v1" + ), + "hostInventoryRow": "compact-centered-accordion-v1", + "hostInventoryFreshness": "dot-only-v1", + "hostInventoryRelations": ( + "host-scoped-endpoint-deployment-service-v1" + ), + "hostInventoryDefaultExpansion": "collapsed", + "hostInventoryScaleTarget": "five-hundred-collapsed-rows-v1", + } + + def expected_device_plane_manager_release_boundaries(): # Compatibility name for the current release builder/tests. Immutable v1 # predecessors always use expected_device_plane_manager_release_v1_boundaries. @@ -12692,12 +13006,12 @@ def expected_device_plane_control_core_release_descriptor( "edgeRegistrations": "preserved", "commandTransport": ( "typed-service-ping-v1" - if schema_version in ("v2", "v3") + if schema_version in ("v2", "v3", "v4") else "disabled" ), "gelios": ( "untouched-legacy-only" - if schema_version in ("v2", "v3") + if schema_version in ("v2", "v3", "v4") else "untouched" ), "preservedServices": [ @@ -12712,7 +13026,7 @@ def expected_device_plane_control_core_release_descriptor( ), "rollback": "restore-preapply-source-and-core-runtime", } - if schema_version in ("v2", "v3"): + if schema_version in ("v2", "v3", "v4"): descriptor["commandCatalog"] = ( "allowlisted-adapter-typed-commands-only" ) @@ -12720,7 +13034,7 @@ def expected_device_plane_control_core_release_descriptor( "transient-core-memory-then-single-pinned-mtls-command-envelope-" "to-edge-never-persisted-never-logged-never-returned" ) - if schema_version == "v3": + if schema_version in ("v3", "v4"): descriptor.update({ "telemetryTransport": ( "edge-channel-host-telemetry-observed-v1" @@ -12738,6 +13052,20 @@ def expected_device_plane_control_core_release_descriptor( "fifteen-seconds-missing-stale-not-unhealthy" ), }) + if schema_version == "v4": + descriptor.update({ + "recoveryPredecessor": ( + "terminal-applied-046-exact-source-runtime-database" + ), + "databasePreflight": ( + "final-migration-016-validated-and-host-telemetry-table-absent" + ), + "databaseRowMutation": "none-before-core-startup-migrations", + "databaseSchemaOutcome": ( + "migration-017-host-telemetry-table-present" + ), + "runtimePredecessor": "healthy-recovery-046-core-generation", + }) return descriptor @@ -13220,6 +13548,7 @@ def validate_device_plane_manager_release_payload_contract( "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", ): required_compose.extend(( "NODEDC_DEVICE_MANAGER_PRESENTATION_PATH: " @@ -13428,6 +13757,78 @@ def validate_device_plane_manager_release_v9_payload( return descriptor +def validate_device_plane_manager_release_v10_payload( + payload_dir, + *, + expected_release_id=None, +): + descriptor = validate_device_plane_manager_release_payload_contract( + payload_dir, + descriptor_rel=DEVICE_PLANE_MANAGER_RELEASE_V10_REL, + schema_version="nodedc.device-plane.device-manager-release.v10", + boundaries=expected_device_plane_manager_release_v10_boundaries(), + compose_sha256=DEVICE_PLANE_MANAGER_RELEASE_V10_COMPOSE_SHA256, + edge_channel=False, + expected_release_id=expected_release_id, + ) + validate_device_plane_manager_v6_favicon_bundle(payload_dir) + return descriptor + + +def validate_device_plane_manager_release_v11_payload( + payload_dir, + *, + expected_release_id=None, +): + descriptor = validate_device_plane_manager_release_payload_contract( + payload_dir, + descriptor_rel=DEVICE_PLANE_MANAGER_RELEASE_V11_REL, + schema_version="nodedc.device-plane.device-manager-release.v11", + boundaries=expected_device_plane_manager_release_v11_boundaries(), + compose_sha256=DEVICE_PLANE_MANAGER_RELEASE_V11_COMPOSE_SHA256, + edge_channel=False, + expected_release_id=expected_release_id, + ) + validate_device_plane_manager_v6_favicon_bundle(payload_dir) + return descriptor + + +def validate_device_plane_manager_release_v12_payload( + payload_dir, + *, + expected_release_id=None, +): + descriptor = validate_device_plane_manager_release_payload_contract( + payload_dir, + descriptor_rel=DEVICE_PLANE_MANAGER_RELEASE_V12_REL, + schema_version="nodedc.device-plane.device-manager-release.v12", + boundaries=expected_device_plane_manager_release_v12_boundaries(), + compose_sha256=DEVICE_PLANE_MANAGER_RELEASE_V12_COMPOSE_SHA256, + edge_channel=False, + expected_release_id=expected_release_id, + ) + validate_device_plane_manager_v6_favicon_bundle(payload_dir) + return descriptor + + +def validate_device_plane_manager_release_v13_payload( + payload_dir, + *, + expected_release_id=None, +): + descriptor = validate_device_plane_manager_release_payload_contract( + payload_dir, + descriptor_rel=DEVICE_PLANE_MANAGER_RELEASE_V13_REL, + schema_version="nodedc.device-plane.device-manager-release.v13", + boundaries=expected_device_plane_manager_release_v13_boundaries(), + compose_sha256=DEVICE_PLANE_MANAGER_RELEASE_V13_COMPOSE_SHA256, + edge_channel=False, + expected_release_id=expected_release_id, + ) + validate_device_plane_manager_v6_favicon_bundle(payload_dir) + return descriptor + + def validate_device_plane_manager_release_payload( payload_dir, *, @@ -13442,12 +13843,38 @@ def validate_device_plane_manager_release_payload( v7 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V7_REL v8 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V8_REL v9 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V9_REL + v10 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V10_REL + v11 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V11_REL + v12 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V12_REL + v13 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V13_REL present = [ - path for path in (v1, v2, v3, v4, v5, v6, v7, v8, v9) + path for path in ( + v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 + ) if path.exists() or path.is_symlink() ] if len(present) != 1: die("Device Manager release descriptor cardinality mismatch") + if present[0] == v13: + return validate_device_plane_manager_release_v13_payload( + payload_dir, + expected_release_id=expected_release_id, + ) + if present[0] == v12: + return validate_device_plane_manager_release_v12_payload( + payload_dir, + expected_release_id=expected_release_id, + ) + if present[0] == v11: + return validate_device_plane_manager_release_v11_payload( + payload_dir, + expected_release_id=expected_release_id, + ) + if present[0] == v10: + return validate_device_plane_manager_release_v10_payload( + payload_dir, + expected_release_id=expected_release_id, + ) if present[0] == v9: return validate_device_plane_manager_release_v9_payload( payload_dir, @@ -13616,10 +14043,19 @@ def validate_device_plane_control_core_release_payload( v1 = payload_dir / DEVICE_PLANE_CONTROL_CORE_RELEASE_REL v2 = payload_dir / DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL v3 = payload_dir / DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL - present = [path for path in (v1, v2, v3) if path.exists() or path.is_symlink()] + v4 = payload_dir / DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL + present = [ + path for path in (v1, v2, v3, v4) + if path.exists() or path.is_symlink() + ] if len(present) != 1: die("Device Control Core release descriptor cardinality mismatch") - schema_version = "v3" if present[0] == v3 else "v2" if present[0] == v2 else "v1" + schema_version = ( + "v4" if present[0] == v4 + else "v3" if present[0] == v3 + else "v2" if present[0] == v2 + else "v1" + ) descriptor = read_strict_json( present[0], "Device Control Core release descriptor", @@ -13633,6 +14069,7 @@ def validate_device_plane_control_core_release_payload( and predecessor.get("kind") in ( "edge-core-channel-upgrade-v4", "release", + "migration-replay-checkpoint-recovery", ) and isinstance(predecessor.get("patchId"), str) and re.fullmatch( @@ -13652,16 +14089,25 @@ def validate_device_plane_control_core_release_payload( == DEVICE_PLANE_CONTROL_CORE_RELEASE_FIRST_PREDECESSOR_ARTIFACT_SHA256 ) elif valid_predecessor: - valid_predecessor = ( - predecessor["patchId"].startswith( - "device-control-core-release-" + if predecessor["kind"] == "migration-replay-checkpoint-recovery": + valid_predecessor = ( + schema_version == "v4" + and predecessor["patchId"] + == DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_PATCH_ID + and predecessor["artifactSha256"] + == DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_ARTIFACT_SHA256 + ) + else: + valid_predecessor = ( + predecessor["patchId"].startswith( + "device-control-core-release-" + ) + and predecessor["patchId"] != release_id ) - and predecessor["patchId"] != release_id - ) if ( not isinstance(release_id, str) or not re.fullmatch( - r"device-control-core-release(?:-v[23])?-[A-Za-z0-9._-]{1,67}", + r"device-control-core-release(?:-v[234])?-[A-Za-z0-9._-]{1,67}", release_id, ) or ( @@ -13669,6 +14115,10 @@ def validate_device_plane_control_core_release_payload( and release_id != expected_release_id ) or not valid_predecessor + or ( + schema_version == "v4" + and release_id != DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_PATCH_ID + ) or descriptor != expected_device_plane_control_core_release_descriptor( release_id, @@ -13762,6 +14212,62 @@ def installed_device_plane_manager_compose_sha256(): v7 = root / DEVICE_PLANE_MANAGER_RELEASE_V7_REL v8 = root / DEVICE_PLANE_MANAGER_RELEASE_V8_REL v9 = root / DEVICE_PLANE_MANAGER_RELEASE_V9_REL + v10 = root / DEVICE_PLANE_MANAGER_RELEASE_V10_REL + v11 = root / DEVICE_PLANE_MANAGER_RELEASE_V11_REL + v12 = root / DEVICE_PLANE_MANAGER_RELEASE_V12_REL + v13 = root / DEVICE_PLANE_MANAGER_RELEASE_V13_REL + + if v13.exists() or v13.is_symlink(): + descriptor = read_strict_json( + v13, + "installed Device Manager release v13 descriptor", + max_bytes=16 * 1024, + ) + validate_device_plane_manager_release_descriptor( + descriptor, + schema_version="nodedc.device-plane.device-manager-release.v13", + boundaries=expected_device_plane_manager_release_v13_boundaries(), + ) + return DEVICE_PLANE_MANAGER_RELEASE_V13_COMPOSE_SHA256 + + if v12.exists() or v12.is_symlink(): + descriptor = read_strict_json( + v12, + "installed Device Manager release v12 descriptor", + max_bytes=16 * 1024, + ) + validate_device_plane_manager_release_descriptor( + descriptor, + schema_version="nodedc.device-plane.device-manager-release.v12", + boundaries=expected_device_plane_manager_release_v12_boundaries(), + ) + return DEVICE_PLANE_MANAGER_RELEASE_V12_COMPOSE_SHA256 + + if v11.exists() or v11.is_symlink(): + descriptor = read_strict_json( + v11, + "installed Device Manager release v11 descriptor", + max_bytes=16 * 1024, + ) + validate_device_plane_manager_release_descriptor( + descriptor, + schema_version="nodedc.device-plane.device-manager-release.v11", + boundaries=expected_device_plane_manager_release_v11_boundaries(), + ) + return DEVICE_PLANE_MANAGER_RELEASE_V11_COMPOSE_SHA256 + + if v10.exists() or v10.is_symlink(): + descriptor = read_strict_json( + v10, + "installed Device Manager release v10 descriptor", + max_bytes=16 * 1024, + ) + validate_device_plane_manager_release_descriptor( + descriptor, + schema_version="nodedc.device-plane.device-manager-release.v10", + boundaries=expected_device_plane_manager_release_v10_boundaries(), + ) + return DEVICE_PLANE_MANAGER_RELEASE_V10_COMPOSE_SHA256 if v9.exists() or v9.is_symlink(): descriptor = read_strict_json( @@ -15470,6 +15976,7 @@ def validate_device_plane_manager_v3_active_baseline(descriptor): "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", ): return None @@ -15534,6 +16041,7 @@ def validate_device_plane_manager_v3_active_baseline(descriptor): or core_entry_set not in ( DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES, ) ): die("Device Manager v3 Device Control Core predecessor type mismatch") @@ -15543,11 +16051,12 @@ def validate_device_plane_manager_v3_active_baseline(descriptor): expected_release_id=core_reference["patchId"], ) ) - installed_core_descriptor_rel = ( - DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL - if core_entry_set == DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES - else DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL - ) + installed_core_descriptor_rel = { + DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES: + DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES: + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL, + }.get(core_entry_set, DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL) installed_core_descriptor = read_strict_json( DEVICE_PLANE_ROOT / installed_core_descriptor_rel, "installed Device Manager v3 Device Control Core predecessor", @@ -15631,6 +16140,7 @@ def device_plane_manager_preserved_runtime_health_services(descriptor): "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", ): return ( "device-control-core", @@ -15768,6 +16278,10 @@ def validate_device_plane_manager_activation_predecessor( DEVICE_PLANE_MANAGER_RELEASE_V7_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V8_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES, ) ): die("Device Manager release predecessor type mismatch") @@ -15794,6 +16308,14 @@ def validate_device_plane_manager_activation_predecessor( DEVICE_PLANE_MANAGER_RELEASE_V8_REL, DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES: DEVICE_PLANE_MANAGER_RELEASE_V9_REL, + DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES: + DEVICE_PLANE_MANAGER_RELEASE_V10_REL, + DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES: + DEVICE_PLANE_MANAGER_RELEASE_V11_REL, + DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES: + DEVICE_PLANE_MANAGER_RELEASE_V12_REL, + DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES: + DEVICE_PLANE_MANAGER_RELEASE_V13_REL, }.get( tuple(predecessor_entries), DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL, @@ -16311,6 +16833,220 @@ def validate_device_plane_control_core_preserved_runtime_health(descriptor): return services +def validate_device_plane_control_core_release_v4_recovery_backup(): + backup_dir = ( + BACKUPS_DIR + / DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_BACKUP_ID + ) + try: + backup_stat = backup_dir.lstat() + except FileNotFoundError: + die("Device Control Core recovery 046 backup is missing") + if stat.S_ISLNK(backup_stat.st_mode) or not stat.S_ISDIR( + backup_stat.st_mode + ): + die("Device Control Core recovery 046 backup is unsafe") + expected = ( + DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_BACKUP_SHA256 + ) + if {child.name for child in backup_dir.iterdir()} != set(expected): + die("Device Control Core recovery 046 backup file set mismatch") + for name, expected_sha256 in expected.items(): + path = backup_dir / name + path_stat = path.lstat() + if ( + stat.S_ISLNK(path_stat.st_mode) + or not stat.S_ISREG(path_stat.st_mode) + or sha256_file(path) != expected_sha256 + ): + die( + "Device Control Core recovery 046 backup drift detected: " + f"{name}" + ) + existing = tuple( + read_backup_path_list(backup_dir / "existing-files.txt") + ) + missing = tuple( + read_backup_path_list(backup_dir / "missing-files.txt") + ) + validate_backup_partition( + DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_ENTRIES, + existing, + missing, + "Device Control Core recovery 046", + ) + if ( + existing != (DEVICE_PLANE_CONTROL_CORE_MIGRATION_014_REL,) + or missing + != ( + DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_REL, + ) + ): + die("Device Control Core recovery 046 backup partition mismatch") + return backup_dir + + +def validate_device_plane_control_core_release_v4_recovered_source( + recovery_payload, +): + base_artifact_name = ( + "nodedc-device-plane-" + f"{DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_PATCH_ID}.tgz" + ) + base_artifact = APPLIED_DIR / base_artifact_name + if ( + not base_artifact.is_file() + or base_artifact.is_symlink() + or sha256_file(base_artifact) + != DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_ARTIFACT_SHA256 + ): + die("Device Control Core release v4 recovered base artifact mismatch") + base_records = [ + row for row in load_state(STATE_FILE) + if row.get("id") == DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_PATCH_ID + and row.get("sha256") + == DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_ARTIFACT_SHA256 + ] + if ( + len(base_records) != 1 + or base_records[0].get("status") != "ok" + or base_records[0].get("component") != "device-plane" + or base_records[0].get("artifact") != base_artifact_name + ): + die("Device Control Core release v4 recovered base journal mismatch") + + with tempfile.TemporaryDirectory( + prefix="device-control-core-release-v4-base-", + dir=TMP_DIR, + ) as directory: + base_manifest, base_entries, base_payload = load_artifact( + base_artifact, + Path(directory), + ) + if ( + base_manifest.get("id") + != DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_PATCH_ID + or base_manifest.get("component") != "device-plane" + or base_manifest.get("type") != "app-overlay" + or tuple(base_entries) + != DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES + ): + die("Device Control Core release v4 recovered base type mismatch") + validate_device_plane_control_core_release_payload( + base_payload, + expected_release_id=( + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_BASE_PATCH_ID + ), + ) + expected_source = collect_exact_files( + base_payload, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, + "Device Control Core release v4 recovered base source", + ) + recovery_source = collect_exact_files( + recovery_payload, + DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_ENTRIES, + "Device Control Core release v4 recovery overlay", + ) + expected_source.update(recovery_source) + live_entries = ( + *DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, + DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_REL, + ) + actual_source = collect_exact_files( + DEVICE_PLANE_ROOT, + live_entries, + "installed Device Control Core release v4 recovered source", + ) + if actual_source != expected_source: + die("installed Device Control Core recovery 046 source drift detected") + for rel in ( + "packages/infrastructure-telemetry-contract", + DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL, + DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_RECOVERY_REL, + DEVICE_PLANE_CONTROL_CORE_V3_RECONCILIATION_REL, + ): + path = DEVICE_PLANE_ROOT / rel + if path.exists() or path.is_symlink(): + die( + "Device Control Core recovery 046 candidate-only source " + f"appeared: {rel}" + ) + installed_recovery = read_strict_json( + ( + DEVICE_PLANE_ROOT + / DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_REL + ), + "installed Device Control Core recovery 046 descriptor", + max_bytes=16 * 1024, + ) + if ( + installed_recovery + != expected_device_plane_control_core_migration_replay_checkpoint_recovery_descriptor() + ): + die("installed Device Control Core recovery 046 descriptor mismatch") + return base_artifact + + +def validate_device_plane_control_core_release_v4_recovered_runtime( + backup_dir, +): + before = read_strict_json( + backup_dir / "runtime-before.json", + "Device Control Core recovery 046 pre-apply runtime inventory", + max_bytes=64 * 1024, + ) + expected_services = { + "device-control-core", + "device-manager", + "device-gateway", + "device-postgres", + "device-backhaul-target", + } + if set(device_plane_inventory_service_names(before)) != expected_services: + die("Device Control Core recovery 046 runtime evidence mismatch") + current = device_plane_runtime_inventory(tuple(sorted(expected_services))) + if set(device_plane_inventory_service_names(current)) != expected_services: + die("Device Control Core recovery 046 live runtime is incomplete") + expected = {item["service"]: item for item in before["services"]} + observed = {item["service"]: item for item in current["services"]} + for service in expected_services - {"device-control-core"}: + item = observed[service] + if ( + item["containerId"] != expected[service]["containerId"] + or item["imageId"] != expected[service]["imageId"] + or item["status"] != "running" + or item["running"] is not True + or item["health"] != "healthy" + or item["restartCount"] != expected[service]["restartCount"] + ): + die( + "Device Control Core recovery 046 changed preserved service: " + f"{service}" + ) + core = observed["device-control-core"] + local_image_id = inspect_optional_local_image( + DEVICE_PLANE_CONTROL_CORE_IMAGE, + "Device Control Core recovery 046 active local image", + ) + if ( + core["containerId"] == expected["device-control-core"]["containerId"] + or core["imageId"] == DEVICE_PLANE_CONTROL_CORE_V3_PREAPPLY_IMAGE_ID + or core["imageId"] != local_image_id + or core["status"] != "running" + or core["running"] is not True + or core["health"] != "healthy" + or core["restartCount"] != 0 + ): + die("Device Control Core recovery 046 runtime mismatch") + validate_device_manager_control_plane_runtime( + require_edge_channel=True, + core_network_mode="private-egress", + ) + return {"before": before, "current": current, "core": core} + + def validate_device_plane_control_core_release_predecessor( payload_dir, *, @@ -16352,6 +17088,14 @@ def validate_device_plane_control_core_release_predecessor( installed_v3_release = ( DEVICE_PLANE_ROOT / DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL ) + installed_v4_release = ( + DEVICE_PLANE_ROOT / DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL + ) + source_entries = None + expected_source = None + recovery_backup = None + recovery_runtime = None + recovery_database = None with tempfile.TemporaryDirectory( prefix="device-control-core-release-predecessor-", dir=TMP_DIR, @@ -16387,8 +17131,25 @@ def validate_device_plane_control_core_release_predecessor( or installed_v2_release.is_symlink() or installed_v3_release.exists() or installed_v3_release.is_symlink() + or installed_v4_release.exists() + or installed_v4_release.is_symlink() ): die("Device Control Core first release is already installed") + elif predecessor["kind"] == "migration-replay-checkpoint-recovery": + if ( + tuple(entries) + != DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_ENTRIES + ): + die( + "Device Control Core release v4 recovery predecessor " + "type mismatch" + ) + validate_device_plane_control_core_migration_replay_checkpoint_recovery_payload( + predecessor_payload + ) + validate_device_plane_control_core_release_v4_recovered_source( + predecessor_payload + ) else: if tuple(entries) == DEVICE_PLANE_CONTROL_CORE_RELEASE_ENTRIES: installed_release = installed_v1_release @@ -16399,6 +17160,9 @@ def validate_device_plane_control_core_release_predecessor( elif tuple(entries) == DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES: installed_release = installed_v3_release source_entries = DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES + elif tuple(entries) == DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES: + installed_release = installed_v4_release + source_entries = DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES else: die("Device Control Core release predecessor type mismatch") expected_installed = validate_device_plane_control_core_release_payload( @@ -16412,26 +17176,44 @@ def validate_device_plane_control_core_release_predecessor( ) if installed_descriptor != expected_installed: die("Device Control Core release predecessor is not current") - expected_source = collect_exact_files( - predecessor_payload, + if source_entries is not None: + expected_source = collect_exact_files( + predecessor_payload, + source_entries, + "Device Control Core release predecessor source", + ) + if source_entries is not None: + actual_source = collect_exact_files( + DEVICE_PLANE_ROOT, source_entries, - "Device Control Core release predecessor source", + "installed Device Control Core release predecessor source", ) - actual_source = collect_exact_files( - DEVICE_PLANE_ROOT, - source_entries, - "installed Device Control Core release predecessor source", - ) - if actual_source != expected_source: - die("installed Device Control Core release source drift detected") + if actual_source != expected_source: + die("installed Device Control Core release source drift detected") component_compose_files("device-plane") identity_state = inspect_device_edge_channel_core_identity_state() if identity_state != "valid-reuse-at-apply": die("Device Control Core release requires the valid active identity") - selected_runtime = ( - validate_device_plane_control_core_selected_predecessor_runtime() - ) + if predecessor["kind"] == "migration-replay-checkpoint-recovery": + recovery_backup = ( + validate_device_plane_control_core_release_v4_recovery_backup() + ) + recovery_runtime = ( + validate_device_plane_control_core_release_v4_recovered_runtime( + recovery_backup + ) + ) + recovery_database = ( + collect_device_plane_control_core_migration_replay_database_evidence( + expected_state="final", + ) + ) + selected_runtime = recovery_runtime["core"] + else: + selected_runtime = ( + validate_device_plane_control_core_selected_predecessor_runtime() + ) preserved_runtime_health = "deferred-to-apply" if preflight_phase == "apply": preserved_runtime_health = ( @@ -16444,12 +17226,20 @@ def validate_device_plane_control_core_release_predecessor( core_network_mode="private-egress", ) return { - "mode": "active-device-control-core-forward-release", + "mode": ( + "recovery-046-host-telemetry-forward-release" + if predecessor["kind"] + == "migration-replay-checkpoint-recovery" + else "active-device-control-core-forward-release" + ), "descriptor": descriptor, "identityState": identity_state, "predecessorArtifact": artifact, "selectedRuntime": selected_runtime, "preservedRuntimeHealth": preserved_runtime_health, + "recoveryBackup": recovery_backup, + "recoveryRuntime": recovery_runtime, + "recoveryDatabase": recovery_database, } @@ -17387,6 +18177,230 @@ select ( return evidence +def collect_device_plane_control_core_host_telemetry_database_evidence(): + postgres_ids = device_plane_service_container_ids("device-postgres") + if len(postgres_ids) != 1: + die("Device Control Core host telemetry PostgreSQL is missing") + query = """ +select ( + to_regclass( + 'public.device_infrastructure_host_telemetry_samples' + ) is not null + )::text, + ( + select count(*) + from information_schema.columns + where table_schema = 'public' + and table_name = + 'device_infrastructure_host_telemetry_samples' + )::text, + ( + select count(*) + from pg_indexes + where schemaname = 'public' + and indexname in ( + 'device_host_telemetry_project_host_time_idx', + 'device_host_telemetry_retention_idx' + ) + )::text, + coalesce(( + select bool_and(convalidated)::text + from pg_constraint + where conrelid = + 'public.device_infrastructure_host_telemetry_samples'::regclass + ), 'false'), + ( + select count(*) + from device_infrastructure_host_telemetry_samples + )::text, + ( + select count(*) + from device_infrastructure_host_telemetry_samples + where observed_at < now() - interval '7 days' + )::text +""".strip() + result = subprocess.run( + [ + str(DOCKER), + "exec", + postgres_ids[0], + "psql", + "-X", + "-qAt", + "-v", + "ON_ERROR_STOP=1", + "-U", + "device_plane", + "-d", + "device_plane", + "-F", + "\t", + "-c", + query, + ], + check=False, + capture_output=True, + text=True, + timeout=30, + ) + values = result.stdout.strip().split("\t") + if ( + result.returncode != 0 + or result.stderr.strip() + or len(values) != 6 + or values[0] not in ("true", "false") + or not all(re.fullmatch(r"[0-9]+", value) for value in values[1:3]) + or values[3] not in ("true", "false") + or not all(re.fullmatch(r"[0-9]+", value) for value in values[4:6]) + ): + die("Device Control Core host telemetry database read failed") + evidence = { + "tablePresent": values[0] == "true", + "columnCount": int(values[1]), + "indexCount": int(values[2]), + "constraintsValidated": values[3] == "true", + "sampleCount": int(values[4]), + "expiredSampleCount": int(values[5]), + "query": query, + } + if ( + not evidence["tablePresent"] + or evidence["columnCount"] != 19 + or evidence["indexCount"] != 2 + or not evidence["constraintsValidated"] + or evidence["expiredSampleCount"] != 0 + ): + die("Device Control Core host telemetry database invariant mismatch") + return evidence + + +def accept_device_plane_control_core_release_v4( + payload_dir, + runtime_before, +): + descriptor = validate_device_plane_control_core_release_payload( + payload_dir, + expected_release_id=DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_PATCH_ID, + ) + if ( + descriptor.get("schemaVersion") + != "nodedc.device-plane.device-control-core-release.v4" + ): + die("Device Control Core release v4 acceptance descriptor mismatch") + expected_source = collect_exact_files( + payload_dir, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES, + "Device Control Core release v4 candidate source", + ) + actual_source = collect_exact_files( + DEVICE_PLANE_ROOT, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES, + "installed Device Control Core release v4 source", + ) + if actual_source != expected_source: + die("installed Device Control Core release v4 source mismatch") + installed_descriptor = read_strict_json( + DEVICE_PLANE_ROOT / DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_REL, + "installed Device Control Core release v4 descriptor", + max_bytes=16 * 1024, + ) + if installed_descriptor != descriptor: + die("installed Device Control Core release v4 descriptor mismatch") + installed_recovery = read_strict_json( + ( + DEVICE_PLANE_ROOT + / DEVICE_PLANE_CONTROL_CORE_MIGRATION_REPLAY_CHECKPOINT_RECOVERY_REL + ), + "preserved Device Control Core recovery 046 descriptor", + max_bytes=16 * 1024, + ) + if ( + installed_recovery + != expected_device_plane_control_core_migration_replay_checkpoint_recovery_descriptor() + ): + die("Device Control Core release v4 lost recovery 046 evidence") + legacy_v3 = DEVICE_PLANE_ROOT / DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL + if legacy_v3.exists() or legacy_v3.is_symlink(): + die("failed Device Control Core release v3 descriptor appeared") + + expected_services = { + "device-control-core", + "device-manager", + "device-gateway", + "device-postgres", + "device-backhaul-target", + } + if set(device_plane_inventory_service_names(runtime_before)) != expected_services: + die("Device Control Core release v4 predecessor inventory mismatch") + current = device_plane_runtime_inventory(tuple(sorted(expected_services))) + if set(device_plane_inventory_service_names(current)) != expected_services: + die("Device Control Core release v4 runtime is incomplete") + before = {item["service"]: item for item in runtime_before["services"]} + observed = {item["service"]: item for item in current["services"]} + for service in expected_services - {"device-control-core"}: + item = observed[service] + if ( + item["containerId"] != before[service]["containerId"] + or item["imageId"] != before[service]["imageId"] + or item["status"] != "running" + or item["running"] is not True + or item["health"] != "healthy" + or item["restartCount"] != before[service]["restartCount"] + ): + die( + "Device Control Core release v4 changed preserved service: " + f"{service}" + ) + core = observed["device-control-core"] + local_image_id = inspect_local_image( + DEVICE_PLANE_CONTROL_CORE_IMAGE, + "Device Control Core release v4 local image", + ) + if ( + core["containerId"] == before["device-control-core"]["containerId"] + or core["imageId"] == before["device-control-core"]["imageId"] + or core["imageId"] != local_image_id + or core["status"] != "running" + or core["running"] is not True + or core["health"] != "healthy" + or core["restartCount"] != 0 + ): + die("Device Control Core release v4 did not converge") + + database = ( + collect_device_plane_control_core_migration_replay_database_evidence( + enforce_recovery_invariants=False, + ) + ) + if ( + database["invalidCommandKindCount"] != 0 + or database["triggeringReceiptCount"] < 1 + or not database["constraintValidated"] + or not database["constraintCoversFinalKinds"] + or database["constraintPhase"] != "final-016" + or database["hostTelemetryTableAbsent"] + ): + emit_device_plane_control_core_migration_replay_database_evidence( + database + ) + die("Device Control Core release v4 database invariant mismatch") + telemetry = ( + collect_device_plane_control_core_host_telemetry_database_evidence() + ) + validate_device_manager_control_plane_runtime( + require_edge_channel=True, + core_network_mode="private-egress", + ) + assert_loopback_tcp_port_open(9921) + return { + "descriptor": descriptor, + "runtime": current, + "core": core, + "database": database, + "telemetry": telemetry, + } + + def validate_device_plane_control_core_migration_replay_audit_evidence( payload_dir, ): @@ -19472,6 +20486,10 @@ def validate_device_manager_control_plane_runtime( require_edge_channel = edge_descriptor.is_file() and not edge_descriptor.is_symlink() if require_persistent_data is None: data_descriptors = ( + DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V13_REL, + DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V12_REL, + DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V11_REL, + DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V10_REL, DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V9_REL, DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V8_REL, DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V7_REL, @@ -32280,6 +33298,10 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ) else "build+recreate:device-control-core,device-manager" ) @@ -32299,6 +33321,10 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ) else "preserved:device-gateway,device-postgres," "device-backhaul-target" @@ -32314,6 +33340,10 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ) print( "device_manager_health_gate=" @@ -32339,6 +33369,10 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ): print( "device_manager_overview_layout=" @@ -32349,12 +33383,20 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ): print("device_manager_favicon_set=nodedc-adaptive-v1") if manager_schema in ( "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ): print("device_manager_command_form=aligned-control-row-v1") print("device_manager_secondary_empty_text=help-text-sm-v1") @@ -32372,6 +33414,10 @@ def plan_artifact(artifact): if manager_schema in ( "nodedc.device-plane.device-manager-release.v8", "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ): print( "device_manager_infrastructure_hosts=" @@ -32401,12 +33447,18 @@ def plan_artifact(artifact): "device_manager_interactive_shell=" "disabled-pending-managed-session-boundary" ) - if manager_schema == ( - "nodedc.device-plane.device-manager-release.v9" + if manager_schema in ( + "nodedc.device-plane.device-manager-release.v9", + "nodedc.device-plane.device-manager-release.v10", + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", ): print( "device_manager_host_telemetry_workspace=" - "mission-core-compute-module-parity-v1" + + device_plane_manager_activation_preflight[ + "descriptor" + ]["telemetryWorkspace"] ) print( "device_manager_host_telemetry_navigation=" @@ -32417,6 +33469,101 @@ def plan_artifact(artifact): "device_manager_host_telemetry_freshness=" "fifteen-seconds-missing-stale-not-unhealthy" ) + if manager_schema in ( + "nodedc.device-plane.device-manager-release.v11", + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", + ): + manager_descriptor = ( + device_plane_manager_activation_preflight["descriptor"] + ) + print( + "device_manager_design_system=" + + manager_descriptor["designSystem"] + ) + print( + "device_manager_mission_core_reference=" + + manager_descriptor["missionCoreReference"] + ) + print( + "device_manager_infrastructure_workspace=" + + manager_descriptor["infrastructureWorkspaceLayout"] + ) + print( + "device_manager_host_inventory=" + + manager_descriptor["hostInventoryComposition"] + ) + print( + "device_manager_host_telemetry_surface=" + + manager_descriptor["telemetrySurface"] + ) + print( + "device_manager_host_telemetry_status=" + + manager_descriptor["telemetryStatus"] + ) + print( + "device_manager_host_telemetry_scroll=" + + manager_descriptor["telemetryScroll"] + ) + if manager_schema in ( + "nodedc.device-plane.device-manager-release.v12", + "nodedc.device-plane.device-manager-release.v13", + ): + manager_descriptor = ( + device_plane_manager_activation_preflight["descriptor"] + ) + print( + "device_manager_host_telemetry_graph_scale=" + + manager_descriptor["telemetryGraphScale"] + ) + print( + "device_manager_host_telemetry_cpu_minimum_span=" + + manager_descriptor["telemetryCpuMinimumSpan"] + ) + print( + "device_manager_host_telemetry_memory_minimum_span=" + + manager_descriptor["telemetryMemoryMinimumSpan"] + ) + print( + "device_manager_host_telemetry_network_missing=" + + manager_descriptor[ + "telemetryNetworkMissingSemantics" + ] + ) + if manager_schema == ( + "nodedc.device-plane.device-manager-release.v13" + ): + manager_descriptor = ( + device_plane_manager_activation_preflight["descriptor"] + ) + print( + "device_manager_host_inventory_overview_surface=" + + manager_descriptor["hostInventoryOverviewSurface"] + ) + print( + "device_manager_host_inventory_collection_surface=" + + manager_descriptor["hostInventoryCollectionSurface"] + ) + print( + "device_manager_host_inventory_row=" + + manager_descriptor["hostInventoryRow"] + ) + print( + "device_manager_host_inventory_freshness=" + + manager_descriptor["hostInventoryFreshness"] + ) + print( + "device_manager_host_inventory_relations=" + + manager_descriptor["hostInventoryRelations"] + ) + print( + "device_manager_host_inventory_default_expansion=" + + manager_descriptor["hostInventoryDefaultExpansion"] + ) + print( + "device_manager_host_inventory_scale_target=" + + manager_descriptor["hostInventoryScaleTarget"] + ) if device_plane_manager_activation_preflight["descriptor"].get( "commandTransport" ) == "typed-service-ping-v1": @@ -32474,8 +33621,9 @@ def plan_artifact(artifact): "device_edge_channel_commands=" f"{descriptor['commandTransport']}" ) - if descriptor.get("schemaVersion") == ( - "nodedc.device-plane.device-control-core-release.v3" + if descriptor.get("schemaVersion") in ( + "nodedc.device-plane.device-control-core-release.v3", + "nodedc.device-plane.device-control-core-release.v4", ): print( "device_host_telemetry_transport=" @@ -32493,6 +33641,32 @@ def plan_artifact(artifact): "device_host_telemetry_freshness=" "fifteen-seconds-missing-stale-not-unhealthy" ) + if descriptor.get("schemaVersion") == ( + "nodedc.device-plane.device-control-core-release.v4" + ): + recovery_database = core_release["recoveryDatabase"] + recovery_runtime = core_release["recoveryRuntime"] + print( + "device_control_core_recovery_backup=" + f"{core_release['recoveryBackup'].name}" + ) + print( + "device_control_core_current_runtime=" + f"{recovery_runtime['core']['containerId']}:" + f"{recovery_runtime['core']['imageId']}:" + f"{recovery_runtime['core']['status']}:" + f"{recovery_runtime['core']['health']}:" + f"restarts={recovery_runtime['core']['restartCount']}" + ) + emit_device_plane_control_core_migration_replay_database_evidence( + recovery_database + ) + print("device_postgres_row_mutation=none") + print("device_postgres_telemetry_table=absent") + print( + "device_control_core_target_telemetry_table=" + "migration-017:present" + ) print("device_manager=preserved:active") print("device_manager_public_route=unchanged:active") print("device_gateway_tcp_9921=preserved:loopback-only") @@ -35211,9 +36385,19 @@ def component_healthchecks(component, entries=None, services=None): "managementApi": "enabled", "commandTransport": ( "typed-service-ping-v1" - if is_device_plane_control_core_release_v2_slice( - component, - entries, + if ( + is_device_plane_control_core_release_v2_slice( + component, + entries, + ) + or is_device_plane_control_core_release_v3_slice( + component, + entries, + ) + or is_device_plane_control_core_release_v4_slice( + component, + entries, + ) ) else "disabled" ), @@ -37939,6 +39123,19 @@ def apply_artifact(artifact): ): die("Engine L2 closed-loop app generation was not recreated") run_healthchecks(component, entries, services) + if is_device_plane_control_core_release_v4_slice( + component, + entries, + ): + if device_plane_runtime_before is None: + die( + "Device Control Core release v4 predecessor " + "runtime inventory is missing" + ) + accept_device_plane_control_core_release_v4( + payload_dir, + device_plane_runtime_before, + ) if is_device_plane_b2_discovery_ingress_slice( component, entries, diff --git a/infra/deploy-runner/test_device_plane_registry.py b/infra/deploy-runner/test_device_plane_registry.py index 2854a6c..bac3a7f 100644 --- a/infra/deploy-runner/test_device_plane_registry.py +++ b/infra/deploy-runner/test_device_plane_registry.py @@ -55,6 +55,154 @@ class DevicePlaneRegistryTest(unittest.TestCase): ("device-manager",), ) + def test_manager_v10_pins_successful_v8_and_control_core_v4(self): + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_PREDECESSOR_PATCH_ID, + "device-manager-release-v8-20260822-039", + ) + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_CONTROL_CORE_PREDECESSOR_PATCH_ID, + "device-control-core-release-v4-20260823-047", + ) + boundaries = RUNNER.expected_device_plane_manager_release_v10_boundaries() + self.assertEqual( + boundaries["controlCorePredecessor"]["artifactSha256"], + "4aecceeb8d400fdd3dbec8fc86b151691f389d165e72677f396f8994823b6b5c", + ) + self.assertEqual( + boundaries["telemetryWorkspace"], + "mission-core-compute-module-parity-v1", + ) + self.assertTrue( + RUNNER.is_device_plane_manager_release_v10_slice( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES, + ) + ) + self.assertEqual( + RUNNER.component_services( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES, + ), + ("device-manager",), + ) + + def test_manager_v11_pins_v10_and_mission_core_visual_parity(self): + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_PATCH_ID, + "device-manager-release-v10-20260823-048", + ) + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_ARTIFACT_SHA256, + "e6b983a314db4f8c27d89062dfedf5ed0523cc30421170799d181a19e2d85d4c", + ) + boundaries = RUNNER.expected_device_plane_manager_release_v11_boundaries() + self.assertEqual( + boundaries["missionCoreReference"], + "compute-modules-workspace-71c8b04", + ) + self.assertEqual( + boundaries["telemetryWorkspace"], + "mission-core-compute-module-visual-parity-v2", + ) + self.assertEqual( + boundaries["telemetrySurface"], + "borderless-soft-surface-v1", + ) + self.assertEqual( + boundaries["telemetryScroll"], + "reset-on-workspace-transition-v1", + ) + self.assertTrue( + RUNNER.is_device_plane_manager_release_v11_slice( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES, + ) + ) + self.assertEqual( + RUNNER.component_services( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES, + ), + ("device-manager",), + ) + + def test_manager_v12_pins_v11_and_adaptive_telemetry_graphs(self): + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_PATCH_ID, + "device-manager-release-v11-20260823-049", + ) + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_ARTIFACT_SHA256, + "c1e2056b50bfbb0d03d077461d0c27cc56cc52967c3f5620be14871c8a6d5cf0", + ) + boundaries = RUNNER.expected_device_plane_manager_release_v12_boundaries() + self.assertEqual( + boundaries["telemetryWorkspace"], + "mission-core-compute-module-adaptive-window-v3", + ) + self.assertEqual( + boundaries["telemetryGraphScale"], + "adaptive-observed-window-explicit-domain-v1", + ) + self.assertEqual( + boundaries["telemetryNetworkMissingSemantics"], + "missing-counters-never-zero-v1", + ) + self.assertTrue( + RUNNER.is_device_plane_manager_release_v12_slice( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES, + ) + ) + self.assertEqual( + RUNNER.component_services( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES, + ), + ("device-manager",), + ) + + def test_manager_v13_pins_v12_and_host_inventory_accordion(self): + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_PATCH_ID, + "device-manager-release-v12-20260823-050", + ) + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_ARTIFACT_SHA256, + "1a49839140e5f2e49763d78f24ee47d946e244bcfde15a9c38266e8bd14c0d49", + ) + boundaries = RUNNER.expected_device_plane_manager_release_v13_boundaries() + self.assertEqual( + boundaries["hostInventoryComposition"], + "mission-core-compute-host-accordion-v2", + ) + self.assertEqual( + boundaries["hostInventoryRow"], + "compact-centered-accordion-v1", + ) + self.assertEqual( + boundaries["hostInventoryRelations"], + "host-scoped-endpoint-deployment-service-v1", + ) + self.assertEqual( + boundaries["telemetryWorkspace"], + "mission-core-compute-module-adaptive-window-v3", + ) + self.assertTrue( + RUNNER.is_device_plane_manager_release_v13_slice( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES, + ) + ) + self.assertEqual( + RUNNER.component_services( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES, + ), + ("device-manager",), + ) + def test_control_core_v3_registers_telemetry_contract_slice(self): predecessor = { "kind": "release", @@ -83,6 +231,109 @@ class DevicePlaneRegistryTest(unittest.TestCase): ) ) + def test_control_core_v4_pins_terminal_recovery_046(self): + predecessor = { + "kind": "migration-replay-checkpoint-recovery", + "patchId": ( + "device-control-core-migration-replay-checkpoint-recovery-" + "20260822-046" + ), + "artifactSha256": ( + "46000c76977fb583fc7c9cf74ecf624efd8b404f7b8d0322e0270e7b8ac6e450" + ), + } + descriptor = RUNNER.expected_device_plane_control_core_release_descriptor( + RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_PATCH_ID, + predecessor, + schema_version="v4", + ) + self.assertEqual( + descriptor["schemaVersion"], + "nodedc.device-plane.device-control-core-release.v4", + ) + self.assertEqual( + descriptor["recoveryPredecessor"], + "terminal-applied-046-exact-source-runtime-database", + ) + self.assertEqual( + descriptor["databaseSchemaOutcome"], + "migration-017-host-telemetry-table-present", + ) + self.assertTrue( + RUNNER.is_device_plane_control_core_release_v4_slice( + "device-plane", + RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES, + ) + ) + self.assertEqual( + RUNNER.component_services( + "device-plane", + RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES, + ), + ("device-control-core",), + ) + + def test_control_core_v4_health_contract_keeps_typed_commands(self): + checks = RUNNER.component_healthchecks( + "device-plane", + RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES, + ("device-control-core",), + ) + self.assertEqual( + checks[0]["expected_json"]["commandTransport"], + "typed-service-ping-v1", + ) + + def test_control_core_v4_telemetry_database_acceptance(self): + result = mock.Mock( + returncode=0, + stdout="true\t19\t2\ttrue\t0\t0\n", + stderr="", + ) + with ( + mock.patch.object( + RUNNER, + "device_plane_service_container_ids", + return_value=("a" * 64,), + ), + mock.patch.object( + RUNNER.subprocess, + "run", + return_value=result, + ), + ): + evidence = ( + RUNNER.collect_device_plane_control_core_host_telemetry_database_evidence() + ) + self.assertTrue(evidence["tablePresent"]) + self.assertEqual(evidence["columnCount"], 19) + self.assertEqual(evidence["indexCount"], 2) + self.assertEqual(evidence["expiredSampleCount"], 0) + + def test_control_core_v4_telemetry_database_rejects_missing_index(self): + result = mock.Mock( + returncode=0, + stdout="true\t19\t1\ttrue\t0\t0\n", + stderr="", + ) + with ( + mock.patch.object( + RUNNER, + "device_plane_service_container_ids", + return_value=("a" * 64,), + ), + mock.patch.object( + RUNNER.subprocess, + "run", + return_value=result, + ), + self.assertRaisesRegex( + RUNNER.DeployError, + "host telemetry database invariant mismatch", + ), + ): + RUNNER.collect_device_plane_control_core_host_telemetry_database_evidence() + def test_manager_v8_pins_production_v6_and_canonical_ontology_runtime(self): self.assertEqual( RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V8_PREDECESSOR_PATCH_ID,