From c5c4e69acb255cf018d95256c92f498af7fd6aef Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 22 Aug 2026 17:29:46 +0300 Subject: [PATCH] feat(deploy): register VPS telemetry releases --- infra/deploy-runner/nodedc-deploy | 239 +++++++++++++++++- .../test_device_plane_registry.py | 54 ++++ 2 files changed, 280 insertions(+), 13 deletions(-) diff --git a/infra/deploy-runner/nodedc-deploy b/infra/deploy-runner/nodedc-deploy index 9c162ef..9e21203 100755 --- a/infra/deploy-runner/nodedc-deploy +++ b/infra/deploy-runner/nodedc-deploy @@ -278,6 +278,9 @@ DEVICE_PLANE_MANAGER_RELEASE_V7_REL = ( DEVICE_PLANE_MANAGER_RELEASE_V8_REL = ( "deployment/device-manager-release-v8.json" ) +DEVICE_PLANE_MANAGER_RELEASE_V9_REL = ( + "deployment/device-manager-release-v9.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" @@ -297,6 +300,9 @@ DEVICE_PLANE_CONTROL_CORE_RELEASE_REL = ( DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL = ( "deployment/device-control-core-release-v2.json" ) +DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL = ( + "deployment/device-control-core-release-v3.json" +) DEVICE_PLANE_EDGE_CORE_CHANNEL_COMPOSE_REL = ( "docker-compose.device-edge-core-channel.yml" ) @@ -367,6 +373,16 @@ DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES = ( "services/device-control-core", DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL, ) +DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_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_V3_REL, +) DEVICE_PLANE_CONTROL_CORE_RELEASE_FIRST_PREDECESSOR_PATCH_ID = ( "device-edge-core-channel-upgrade-v4-20260812-023" ) @@ -427,6 +443,18 @@ DEVICE_PLANE_MANAGER_RELEASE_V8_CONTROL_CORE_PREDECESSOR_PATCH_ID = ( DEVICE_PLANE_MANAGER_RELEASE_V8_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 = ( "e2d062b82b022dba662522b5d6e192026ac964d78950d903295ca3cbbc95ab28" ) +DEVICE_PLANE_MANAGER_RELEASE_V9_PREDECESSOR_PATCH_ID = ( + "device-manager-release-v8-20260822-039" +) +DEVICE_PLANE_MANAGER_RELEASE_V9_PREDECESSOR_ARTIFACT_SHA256 = ( + "30a83d4c6b5c029c96c4af19fa558e0ae75e4b60bc897881457304bd17e0e465" +) +DEVICE_PLANE_MANAGER_RELEASE_V9_CONTROL_CORE_PREDECESSOR_PATCH_ID = ( + "device-control-core-release-v3-20260822-040" +) +DEVICE_PLANE_MANAGER_RELEASE_V9_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 = ( + "08448a56cdf391076f92c5242e368fd0033b420167874645eebfa1f22184ee92" +) DEVICE_PLANE_EDGE_CORE_CHANNEL_BOOTSTRAP_PREDECESSOR_PATCH_ID = ( "device-edge-core-channel-bootstrap-20260812-018" ) @@ -500,6 +528,9 @@ DEVICE_PLANE_MANAGER_RELEASE_V7_COMPOSE_SHA256 = ( DEVICE_PLANE_MANAGER_RELEASE_V8_COMPOSE_SHA256 = ( DEVICE_PLANE_MANAGER_RELEASE_V6_COMPOSE_SHA256 ) +DEVICE_PLANE_MANAGER_RELEASE_V9_COMPOSE_SHA256 = ( + DEVICE_PLANE_MANAGER_RELEASE_V8_COMPOSE_SHA256 +) DEVICE_PLANE_MANAGER_FAVICON_SHA256 = { "favicon.ico": ( "f8933114a85646335ea5c94944f56d3cd8c48a6032016719f7905ff244ec0aa2" @@ -626,6 +657,11 @@ DEVICE_PLANE_MANAGER_RELEASE_V8_ENTRIES = ( "services/device-manager", DEVICE_PLANE_MANAGER_RELEASE_V8_REL, ) +DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES = ( + DEVICE_PLANE_MANAGER_COMPOSE_REL, + "services/device-manager", + DEVICE_PLANE_MANAGER_RELEASE_V9_REL, +) DEVICE_PLANE_MANAGER_RECONCILIATION_REL = ( "deployment/device-manager-control-plane-reconciliation-v1.json" ) @@ -4669,16 +4705,19 @@ def allowed_payload_path(component, rel): DEVICE_PLANE_MANAGER_RELEASE_V6_REL, DEVICE_PLANE_MANAGER_RELEASE_V7_REL, DEVICE_PLANE_MANAGER_RELEASE_V8_REL, + DEVICE_PLANE_MANAGER_RELEASE_V9_REL, DEVICE_PLANE_EDGE_CORE_CHANNEL_BOOTSTRAP_REL, DEVICE_PLANE_EDGE_CORE_CHANNEL_UPGRADE_REL, DEVICE_PLANE_EDGE_CORE_CHANNEL_UPGRADE_V2_REL, DEVICE_PLANE_EDGE_CORE_CHANNEL_UPGRADE_V4_REL, DEVICE_PLANE_CONTROL_CORE_RELEASE_REL, DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL, DEVICE_PLANE_MANAGER_RECONCILIATION_REL, DEVICE_PLANE_MANAGER_V2_RECONCILIATION_REL, "packages/device-protocol-contract", "packages/device-edge-channel-contract", + "packages/infrastructure-telemetry-contract", "packages/arusnavi-b2-adapter", "services/device-control-core", "services/device-gateway", @@ -4690,6 +4729,7 @@ def allowed_payload_path(component, rel): if rel.startswith(( "packages/device-protocol-contract/", "packages/device-edge-channel-contract/", + "packages/infrastructure-telemetry-contract/", "packages/arusnavi-b2-adapter/", "services/device-control-core/", "services/device-gateway/", @@ -11611,6 +11651,7 @@ def is_device_plane_manager_control_plane_slice(component, entries): DEVICE_PLANE_MANAGER_RELEASE_V6_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V7_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V8_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES, ) ) @@ -11671,6 +11712,14 @@ def is_device_plane_manager_release_v8_slice(component, entries): ) +def is_device_plane_manager_release_v9_slice(component, entries): + return ( + component == "device-plane" + and entries is not None + and tuple(entries) == DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES + ) + + def is_device_plane_manager_persistent_release_slice(component, entries): return ( is_device_plane_manager_release_v4_slice(component, entries) @@ -11678,6 +11727,7 @@ def is_device_plane_manager_persistent_release_slice(component, entries): or is_device_plane_manager_release_v6_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) ) @@ -11736,6 +11786,7 @@ def is_device_plane_control_core_release_slice(component, entries): and tuple(entries) in ( DEVICE_PLANE_CONTROL_CORE_RELEASE_ENTRIES, DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES, ) ) @@ -11748,6 +11799,14 @@ def is_device_plane_control_core_release_v2_slice(component, entries): ) +def is_device_plane_control_core_release_v3_slice(component, entries): + return ( + component == "device-plane" + and entries is not None + and tuple(entries) == DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES + ) + + def is_device_plane_manager_v2_control_plane_slice(component, entries): return ( component == "device-plane" @@ -11992,6 +12051,37 @@ def expected_device_plane_manager_release_v8_boundaries(): } +def expected_device_plane_manager_release_v9_boundaries(): + return { + **expected_device_plane_manager_release_v8_boundaries(), + "predecessor": { + "kind": "release", + "patchId": DEVICE_PLANE_MANAGER_RELEASE_V9_PREDECESSOR_PATCH_ID, + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V9_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "controlCorePredecessor": { + "patchId": ( + DEVICE_PLANE_MANAGER_RELEASE_V9_CONTROL_CORE_PREDECESSOR_PATCH_ID + ), + "artifactSha256": ( + DEVICE_PLANE_MANAGER_RELEASE_V9_CONTROL_CORE_PREDECESSOR_ARTIFACT_SHA256 + ), + }, + "telemetryWorkspace": "mission-core-compute-module-parity-v1", + "telemetryNavigation": "full-workspace-back-navigation-v1", + "telemetryPollInterval": "three-seconds", + "telemetryFreshness": ( + "fifteen-seconds-missing-stale-not-unhealthy" + ), + "telemetryOntologyProjection": ( + "observation-observed-property-provenance-freshness-v1" + ), + "telemetryAgent": "telegraf-host-observer-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. @@ -12238,12 +12328,12 @@ def expected_device_plane_control_core_release_descriptor( "edgeRegistrations": "preserved", "commandTransport": ( "typed-service-ping-v1" - if schema_version == "v2" + if schema_version in ("v2", "v3") else "disabled" ), "gelios": ( "untouched-legacy-only" - if schema_version == "v2" + if schema_version in ("v2", "v3") else "untouched" ), "preservedServices": [ @@ -12258,7 +12348,7 @@ def expected_device_plane_control_core_release_descriptor( ), "rollback": "restore-preapply-source-and-core-runtime", } - if schema_version == "v2": + if schema_version in ("v2", "v3"): descriptor["commandCatalog"] = ( "allowlisted-adapter-typed-commands-only" ) @@ -12266,6 +12356,24 @@ 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": + descriptor.update({ + "telemetryTransport": ( + "edge-channel-host-telemetry-observed-v1" + ), + "telemetryContract": ( + "nodedc.infrastructure.host-telemetry.v1" + ), + "telemetryStorage": ( + "device-control-core-postgres-seven-day-retention" + ), + "ontologyProjection": ( + "observation-observed-property-provenance-freshness-v1" + ), + "telemetryFreshness": ( + "fifteen-seconds-missing-stale-not-unhealthy" + ), + }) return descriptor @@ -12512,6 +12620,7 @@ def validate_device_plane_manager_release_payload_contract( "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ): required_compose.extend(( "NODEDC_DEVICE_MANAGER_PRESENTATION_PATH: " @@ -12702,6 +12811,24 @@ def validate_device_plane_manager_release_v8_payload( return descriptor +def validate_device_plane_manager_release_v9_payload( + payload_dir, + *, + expected_release_id=None, +): + descriptor = validate_device_plane_manager_release_payload_contract( + payload_dir, + descriptor_rel=DEVICE_PLANE_MANAGER_RELEASE_V9_REL, + schema_version="nodedc.device-plane.device-manager-release.v9", + boundaries=expected_device_plane_manager_release_v9_boundaries(), + compose_sha256=DEVICE_PLANE_MANAGER_RELEASE_V9_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, *, @@ -12715,12 +12842,18 @@ def validate_device_plane_manager_release_payload( v6 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V6_REL 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 present = [ - path for path in (v1, v2, v3, v4, v5, v6, v7, v8) + path for path in (v1, v2, v3, v4, v5, v6, v7, v8, v9) if path.exists() or path.is_symlink() ] if len(present) != 1: die("Device Manager release descriptor cardinality mismatch") + if present[0] == v9: + return validate_device_plane_manager_release_v9_payload( + payload_dir, + expected_release_id=expected_release_id, + ) if present[0] == v8: return validate_device_plane_manager_release_v8_payload( payload_dir, @@ -12883,10 +13016,11 @@ 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 - present = [path for path in (v1, v2) if path.exists() or path.is_symlink()] + 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()] if len(present) != 1: die("Device Control Core release descriptor cardinality mismatch") - schema_version = "v2" if present[0] == v2 else "v1" + schema_version = "v3" if present[0] == v3 else "v2" if present[0] == v2 else "v1" descriptor = read_strict_json( present[0], "Device Control Core release descriptor", @@ -12928,7 +13062,7 @@ def validate_device_plane_control_core_release_payload( if ( not isinstance(release_id, str) or not re.fullmatch( - r"device-control-core-release(?:-v2)?-[A-Za-z0-9._-]{1,67}", + r"device-control-core-release(?:-v[23])?-[A-Za-z0-9._-]{1,67}", release_id, ) or ( @@ -13028,6 +13162,20 @@ def installed_device_plane_manager_compose_sha256(): v6 = root / DEVICE_PLANE_MANAGER_RELEASE_V6_REL v7 = root / DEVICE_PLANE_MANAGER_RELEASE_V7_REL v8 = root / DEVICE_PLANE_MANAGER_RELEASE_V8_REL + v9 = root / DEVICE_PLANE_MANAGER_RELEASE_V9_REL + + if v9.exists() or v9.is_symlink(): + descriptor = read_strict_json( + v9, + "installed Device Manager release v9 descriptor", + max_bytes=16 * 1024, + ) + validate_device_plane_manager_release_descriptor( + descriptor, + schema_version="nodedc.device-plane.device-manager-release.v9", + boundaries=expected_device_plane_manager_release_v9_boundaries(), + ) + return DEVICE_PLANE_MANAGER_RELEASE_V9_COMPOSE_SHA256 # A successful v2 overlay intentionally leaves the immutable v1 release # descriptor as predecessor evidence. Prefer the highest installed @@ -14621,6 +14769,7 @@ def validate_device_plane_manager_v3_active_baseline(descriptor): "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ): return None @@ -14677,11 +14826,15 @@ def validate_device_plane_manager_v3_active_baseline(descriptor): core_artifact, Path(directory), ) + core_entry_set = tuple(core_entries) if ( core_manifest.get("id") != core_reference["patchId"] or core_manifest.get("component") != "device-plane" or core_manifest.get("type") != "app-overlay" - or tuple(core_entries) != DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES + or core_entry_set not in ( + DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, + DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES, + ) ): die("Device Manager v3 Device Control Core predecessor type mismatch") expected_core_descriptor = ( @@ -14690,8 +14843,13 @@ 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 = read_strict_json( - DEVICE_PLANE_ROOT / DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL, + DEVICE_PLANE_ROOT / installed_core_descriptor_rel, "installed Device Manager v3 Device Control Core predecessor", max_bytes=16 * 1024, ) @@ -14699,12 +14857,12 @@ def validate_device_plane_manager_v3_active_baseline(descriptor): die("Device Manager v3 Device Control Core predecessor is not current") expected_core_source = collect_exact_files( core_payload, - DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, + core_entry_set, "Device Manager v3 Device Control Core predecessor source", ) actual_core_source = collect_exact_files( DEVICE_PLANE_ROOT, - DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES, + core_entry_set, "installed Device Manager v3 Device Control Core predecessor source", ) if actual_core_source != expected_core_source: @@ -14772,6 +14930,7 @@ def device_plane_manager_preserved_runtime_health_services(descriptor): "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ): return ( "device-control-core", @@ -14908,6 +15067,7 @@ def validate_device_plane_manager_activation_predecessor( DEVICE_PLANE_MANAGER_RELEASE_V6_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V7_ENTRIES, DEVICE_PLANE_MANAGER_RELEASE_V8_ENTRIES, + DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES, ) ): die("Device Manager release predecessor type mismatch") @@ -14932,6 +15092,8 @@ def validate_device_plane_manager_activation_predecessor( DEVICE_PLANE_MANAGER_RELEASE_V7_REL, DEVICE_PLANE_MANAGER_RELEASE_V8_ENTRIES: DEVICE_PLANE_MANAGER_RELEASE_V8_REL, + DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES: + DEVICE_PLANE_MANAGER_RELEASE_V9_REL, }.get( tuple(predecessor_entries), DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL, @@ -15487,6 +15649,9 @@ def validate_device_plane_control_core_release_predecessor( installed_v2_release = ( DEVICE_PLANE_ROOT / DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_REL ) + installed_v3_release = ( + DEVICE_PLANE_ROOT / DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_REL + ) with tempfile.TemporaryDirectory( prefix="device-control-core-release-predecessor-", dir=TMP_DIR, @@ -15520,6 +15685,8 @@ def validate_device_plane_control_core_release_predecessor( or installed_v1_release.is_symlink() or installed_v2_release.exists() or installed_v2_release.is_symlink() + or installed_v3_release.exists() + or installed_v3_release.is_symlink() ): die("Device Control Core first release is already installed") else: @@ -15529,6 +15696,9 @@ def validate_device_plane_control_core_release_predecessor( elif tuple(entries) == DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES: installed_release = installed_v2_release source_entries = DEVICE_PLANE_CONTROL_CORE_RELEASE_V2_ENTRIES + elif tuple(entries) == DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES: + installed_release = installed_v3_release + source_entries = DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES else: die("Device Control Core release predecessor type mismatch") expected_installed = validate_device_plane_control_core_release_payload( @@ -17311,6 +17481,7 @@ 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_V9_REL, DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V8_REL, DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V7_REL, DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_RELEASE_V6_REL, @@ -29966,6 +30137,7 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ) else "build+recreate:device-control-core,device-manager" ) @@ -29984,6 +30156,7 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ) else "preserved:device-gateway,device-postgres," "device-backhaul-target" @@ -29998,6 +30171,7 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ) print( "device_manager_health_gate=" @@ -30022,6 +30196,7 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ): print( "device_manager_overview_layout=" @@ -30031,11 +30206,13 @@ def plan_artifact(artifact): "nodedc.device-plane.device-manager-release.v6", "nodedc.device-plane.device-manager-release.v7", "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ): 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", ): print("device_manager_command_form=aligned-control-row-v1") print("device_manager_secondary_empty_text=help-text-sm-v1") @@ -30050,8 +30227,9 @@ def plan_artifact(artifact): "device_manager_host_ontology=" "candidate-not-canonical" ) - if manager_schema == ( - "nodedc.device-plane.device-manager-release.v8" + if manager_schema in ( + "nodedc.device-plane.device-manager-release.v8", + "nodedc.device-plane.device-manager-release.v9", ): print( "device_manager_infrastructure_hosts=" @@ -30081,6 +30259,22 @@ def plan_artifact(artifact): "device_manager_interactive_shell=" "disabled-pending-managed-session-boundary" ) + if manager_schema == ( + "nodedc.device-plane.device-manager-release.v9" + ): + print( + "device_manager_host_telemetry_workspace=" + "mission-core-compute-module-parity-v1" + ) + print( + "device_manager_host_telemetry_navigation=" + "full-workspace-back-navigation-v1" + ) + print("device_manager_host_telemetry_poll=three-seconds") + print( + "device_manager_host_telemetry_freshness=" + "fifteen-seconds-missing-stale-not-unhealthy" + ) if device_plane_manager_activation_preflight["descriptor"].get( "commandTransport" ) == "typed-service-ping-v1": @@ -30138,6 +30332,25 @@ def plan_artifact(artifact): "device_edge_channel_commands=" f"{descriptor['commandTransport']}" ) + if descriptor.get("schemaVersion") == ( + "nodedc.device-plane.device-control-core-release.v3" + ): + print( + "device_host_telemetry_transport=" + "edge-channel-host-telemetry-observed-v1" + ) + print( + "device_host_telemetry_storage=" + "device-control-core-postgres-seven-day-retention" + ) + print( + "device_host_telemetry_ontology=" + "observation-observed-property-provenance-freshness-v1" + ) + print( + "device_host_telemetry_freshness=" + "fifteen-seconds-missing-stale-not-unhealthy" + ) print("device_manager=preserved:active") print("device_manager_public_route=unchanged:active") print("device_gateway_tcp_9921=preserved:loopback-only") diff --git a/infra/deploy-runner/test_device_plane_registry.py b/infra/deploy-runner/test_device_plane_registry.py index b8ba4fc..c048689 100644 --- a/infra/deploy-runner/test_device_plane_registry.py +++ b/infra/deploy-runner/test_device_plane_registry.py @@ -29,6 +29,60 @@ RUNNER = load_runner() class DevicePlaneRegistryTest(unittest.TestCase): + def test_manager_v9_pins_v8_and_host_telemetry_workspace(self): + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V9_PREDECESSOR_PATCH_ID, + "device-manager-release-v8-20260822-039", + ) + self.assertEqual( + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V9_CONTROL_CORE_PREDECESSOR_PATCH_ID, + "device-control-core-release-v3-20260822-040", + ) + boundaries = RUNNER.expected_device_plane_manager_release_v9_boundaries() + self.assertEqual( + boundaries["telemetryWorkspace"], + "mission-core-compute-module-parity-v1", + ) + self.assertEqual( + boundaries["telemetryFreshness"], + "fifteen-seconds-missing-stale-not-unhealthy", + ) + self.assertEqual( + RUNNER.component_services( + "device-plane", + RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V9_ENTRIES, + ), + ("device-manager",), + ) + + def test_control_core_v3_registers_telemetry_contract_slice(self): + predecessor = { + "kind": "release", + "patchId": "device-control-core-release-v2-20260822-038", + "artifactSha256": ( + "e2d062b82b022dba662522b5d6e192026ac964d78950d903295ca3cbbc95ab28" + ), + } + descriptor = RUNNER.expected_device_plane_control_core_release_descriptor( + "device-control-core-release-v3-20260822-040", + predecessor, + schema_version="v3", + ) + self.assertEqual( + descriptor["telemetryTransport"], + "edge-channel-host-telemetry-observed-v1", + ) + self.assertEqual( + descriptor["telemetryStorage"], + "device-control-core-postgres-seven-day-retention", + ) + self.assertTrue( + RUNNER.is_device_plane_control_core_release_v3_slice( + "device-plane", + RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V3_ENTRIES, + ) + ) + def test_manager_v8_pins_production_v6_and_canonical_ontology_runtime(self): self.assertEqual( RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V8_PREDECESSOR_PATCH_ID,