fix(deploy): version device manager edge release contract

This commit is contained in:
Codex
2026-08-11 20:43:07 +03:00
parent 724e745628
commit 0f44cca27d
5 changed files with 283 additions and 59 deletions
+177 -44
View File
@@ -240,10 +240,19 @@ DEVICE_PLANE_MANAGER_V2_CONTROL_PLANE_REL = (
DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL = (
"deployment/device-manager-release-v1.json"
)
DEVICE_PLANE_MANAGER_RELEASE_V2_REL = (
"deployment/device-manager-release-v2.json"
)
DEVICE_PLANE_MANAGER_COMPOSE_REL = "docker-compose.device-manager.yml"
DEVICE_PLANE_MANAGER_COMPOSE_SHA256 = (
DEVICE_PLANE_MANAGER_RELEASE_V1_COMPOSE_SHA256 = (
"4954120aaddc999798b64c304d8cf692b79714feb727d873117bd1f3434e865e"
)
DEVICE_PLANE_MANAGER_RELEASE_V2_COMPOSE_SHA256 = (
"369a2acf9c1a1030b9e1c6c366144b1eaf8900aef0ee59bb6bf23250b7b371b9"
)
DEVICE_PLANE_MANAGER_COMPOSE_SHA256 = (
DEVICE_PLANE_MANAGER_RELEASE_V2_COMPOSE_SHA256
)
DEVICE_PLANE_MANAGER_FAILED_CONTROL_PLANE_ENTRIES = (
".dockerignore",
"package.json",
@@ -283,6 +292,19 @@ DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES = (
"services/device-manager",
DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL,
)
DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES = (
".dockerignore",
"package.json",
"package-lock.json",
DEVICE_PLANE_MANAGER_COMPOSE_REL,
"packages/device-protocol-contract",
"packages/arusnavi-b2-adapter",
"services/device-control-core",
"services/device-gateway/package.json",
"services/device-edge-relay/package.json",
"services/device-manager",
DEVICE_PLANE_MANAGER_RELEASE_V2_REL,
)
DEVICE_PLANE_MANAGER_RECONCILIATION_REL = (
"deployment/device-manager-control-plane-reconciliation-v1.json"
)
@@ -3505,6 +3527,7 @@ def allowed_payload_path(component, rel):
DEVICE_PLANE_BACKHAUL_TARGET_REL,
DEVICE_PLANE_MANAGER_FAILED_CONTROL_PLANE_REL,
DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL,
DEVICE_PLANE_MANAGER_RELEASE_V2_REL,
DEVICE_PLANE_MANAGER_RECONCILIATION_REL,
DEVICE_PLANE_MANAGER_V2_RECONCILIATION_REL,
"packages/device-protocol-contract",
@@ -8551,7 +8574,7 @@ def load_artifact(artifact, work_dir):
manifest["component"],
entries,
):
validate_device_plane_manager_control_plane_payload(
validate_device_plane_manager_release_payload(
payload_dir,
expected_release_id=manifest["id"],
)
@@ -9023,7 +9046,18 @@ def is_device_plane_manager_control_plane_slice(component, entries):
return (
component == "device-plane"
and entries is not None
and tuple(entries) == DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES
and tuple(entries) in (
DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES,
DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES,
)
)
def is_device_plane_manager_release_v2_slice(component, entries):
return (
component == "device-plane"
and entries is not None
and tuple(entries) == DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES
)
@@ -9083,12 +9117,22 @@ def expected_platform_device_manager_public_route_descriptor():
}
def expected_device_plane_manager_release_boundaries():
def expected_device_plane_manager_release_v1_boundaries():
return {
"service": "device-manager",
"publicIngress": "reverse-proxy-only",
"deviceCoreManagementApi": "file-token-authenticated",
"launcherTrust": "file-token-scoped-to-device-core-handoff",
"healthGate": "bounded-container-grace+core-contract",
"commandTransport": "disabled",
"gelios": "untouched",
"rollback": "restore-preapply-snapshot",
}
def expected_device_plane_manager_release_v2_boundaries():
return {
**expected_device_plane_manager_release_v1_boundaries(),
"edgeChannel": (
"core-initiated-pinned-mtls-enabled-zero-or-more-registered-edges"
),
@@ -9099,13 +9143,15 @@ def expected_device_plane_manager_release_boundaries():
"dedicated-core-only-bridge-no-host-ingress-"
"public-ipv4-tcp-8443-registration-policy"
),
"healthGate": "bounded-container-grace+core-contract",
"commandTransport": "disabled",
"gelios": "untouched",
"rollback": "restore-preapply-snapshot",
}
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.
return expected_device_plane_manager_release_v2_boundaries()
def expected_device_plane_manager_failed_control_plane_descriptor():
return {
"schemaVersion": (
@@ -9231,29 +9277,28 @@ def validate_platform_device_manager_public_route_payload(payload_dir):
return descriptor
def validate_device_plane_manager_control_plane_payload(
payload_dir,
def validate_device_plane_manager_release_descriptor(
descriptor,
*,
schema_version,
boundaries,
expected_release_id=None,
):
descriptor = read_strict_json(
payload_dir / DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL,
"Device Manager release descriptor",
max_bytes=16 * 1024,
)
required_keys = {
"schemaVersion",
"releaseId",
"action",
"predecessor",
*expected_device_plane_manager_release_boundaries(),
*boundaries,
}
if set(descriptor) != required_keys:
die("Device Manager release descriptor key set mismatch")
if (
descriptor.get("schemaVersion")
!= "nodedc.device-plane.device-manager-release.v1"
):
missing = ",".join(sorted(required_keys - set(descriptor))) or "none"
extra = ",".join(sorted(set(descriptor) - required_keys)) or "none"
die(
"Device Manager release descriptor key set mismatch: "
f"schema={schema_version} missing={missing} extra={extra}"
)
if descriptor.get("schemaVersion") != schema_version:
die("Device Manager release descriptor schema mismatch")
release_id = descriptor.get("releaseId")
if (
@@ -9284,16 +9329,39 @@ def validate_device_plane_manager_control_plane_payload(
!= (predecessor["kind"] == "reconciliation")
):
die("Device Manager release predecessor mismatch")
boundaries = expected_device_plane_manager_release_boundaries()
if any(descriptor.get(key) != value for key, value in boundaries.items()):
die("Device Manager release security boundary mismatch")
return descriptor
def validate_device_plane_manager_release_payload_contract(
payload_dir,
*,
descriptor_rel,
schema_version,
boundaries,
compose_sha256,
edge_channel,
expected_release_id=None,
):
descriptor = read_strict_json(
payload_dir / descriptor_rel,
"Device Manager release descriptor",
max_bytes=16 * 1024,
)
validate_device_plane_manager_release_descriptor(
descriptor,
schema_version=schema_version,
boundaries=boundaries,
expected_release_id=expected_release_id,
)
compose_path = payload_dir / DEVICE_PLANE_MANAGER_COMPOSE_REL
if sha256_file(compose_path) != DEVICE_PLANE_MANAGER_COMPOSE_SHA256:
if sha256_file(compose_path) != compose_sha256:
die("Device Manager control-plane Compose mismatch")
compose = compose_path.read_text(
encoding="utf-8"
)
for required in (
required_compose = [
"device-manager:",
'DEVICE_MANAGEMENT_API_ENABLED: "true"',
"DEVICE_MANAGEMENT_CORE_TOKEN_FILE: "
@@ -9302,22 +9370,26 @@ def validate_device_plane_manager_control_plane_payload(
"/run/nodedc-secrets/device-core-internal-token",
"NODEDC_DEVICE_CORE_TOKEN_FILE: "
"/run/nodedc-secrets/management-core-token",
'DEVICE_EDGE_CHANNEL_ENABLED: "true"',
"DEVICE_EDGE_CHANNEL_CORE_KEY_FILE: "
"/run/nodedc-secrets/device-edge-channel/core-private-key.pem",
"DEVICE_EDGE_CHANNEL_CORE_CERTIFICATE_FILE: "
"/run/nodedc-secrets/device-edge-channel/core-certificate.pem",
"DEVICE_EDGE_CHANNEL_TRUST_ROOT: "
"/run/nodedc-secrets/device-edge-channel/peers",
"source: /volume1/docker/nodedc-device-plane/secrets/"
"device-edge-channel/core-private-key.pem",
"source: /volume1/docker/nodedc-device-plane/secrets/"
"device-edge-channel/core-certificate.pem",
"source: /volume1/docker/nodedc-device-plane/secrets/"
"device-edge-channel/peers",
"name: nodedc-device-plane-egress",
"name: nodedc-platform_edge",
):
]
if edge_channel:
required_compose.extend((
'DEVICE_EDGE_CHANNEL_ENABLED: "true"',
"DEVICE_EDGE_CHANNEL_CORE_KEY_FILE: "
"/run/nodedc-secrets/device-edge-channel/core-private-key.pem",
"DEVICE_EDGE_CHANNEL_CORE_CERTIFICATE_FILE: "
"/run/nodedc-secrets/device-edge-channel/core-certificate.pem",
"DEVICE_EDGE_CHANNEL_TRUST_ROOT: "
"/run/nodedc-secrets/device-edge-channel/peers",
"source: /volume1/docker/nodedc-device-plane/secrets/"
"device-edge-channel/core-private-key.pem",
"source: /volume1/docker/nodedc-device-plane/secrets/"
"device-edge-channel/core-certificate.pem",
"source: /volume1/docker/nodedc-device-plane/secrets/"
"device-edge-channel/peers",
"name: nodedc-device-plane-egress",
))
for required in required_compose:
if required not in compose:
die(f"Device Manager control-plane boundary missing: {required}")
for forbidden in (
@@ -9330,6 +9402,59 @@ def validate_device_plane_manager_control_plane_payload(
return descriptor
def validate_device_plane_manager_control_plane_payload(
payload_dir,
*,
expected_release_id=None,
):
return validate_device_plane_manager_release_payload_contract(
payload_dir,
descriptor_rel=DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL,
schema_version="nodedc.device-plane.device-manager-release.v1",
boundaries=expected_device_plane_manager_release_v1_boundaries(),
compose_sha256=DEVICE_PLANE_MANAGER_RELEASE_V1_COMPOSE_SHA256,
edge_channel=False,
expected_release_id=expected_release_id,
)
def validate_device_plane_manager_release_v2_payload(
payload_dir,
*,
expected_release_id=None,
):
return validate_device_plane_manager_release_payload_contract(
payload_dir,
descriptor_rel=DEVICE_PLANE_MANAGER_RELEASE_V2_REL,
schema_version="nodedc.device-plane.device-manager-release.v2",
boundaries=expected_device_plane_manager_release_v2_boundaries(),
compose_sha256=DEVICE_PLANE_MANAGER_RELEASE_V2_COMPOSE_SHA256,
edge_channel=True,
expected_release_id=expected_release_id,
)
def validate_device_plane_manager_release_payload(
payload_dir,
*,
expected_release_id=None,
):
v1 = payload_dir / DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL
v2 = payload_dir / DEVICE_PLANE_MANAGER_RELEASE_V2_REL
present = [path for path in (v1, v2) if path.exists() or path.is_symlink()]
if len(present) != 1:
die("Device Manager release descriptor cardinality mismatch")
if present[0] == v2:
return validate_device_plane_manager_release_v2_payload(
payload_dir,
expected_release_id=expected_release_id,
)
return validate_device_plane_manager_control_plane_payload(
payload_dir,
expected_release_id=expected_release_id,
)
def validate_device_plane_manager_failed_control_plane_payload(payload_dir):
descriptor = read_strict_json(
payload_dir / DEVICE_PLANE_MANAGER_FAILED_CONTROL_PLANE_REL,
@@ -10802,7 +10927,7 @@ def validate_device_plane_manager_reconciliation_evidence(payload_dir):
def validate_device_plane_manager_activation_predecessor(payload_dir):
descriptor = validate_device_plane_manager_control_plane_payload(
descriptor = validate_device_plane_manager_release_payload(
payload_dir
)
predecessor = descriptor["predecessor"]
@@ -10904,18 +11029,26 @@ def validate_device_plane_manager_activation_predecessor(payload_dir):
else:
if (
descriptor["action"] != "upgrade"
or tuple(predecessor_entries)
!= DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES
or tuple(predecessor_entries) not in (
DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES,
DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES,
)
):
die("Device Manager release predecessor type mismatch")
predecessor_descriptor = (
validate_device_plane_manager_control_plane_payload(
validate_device_plane_manager_release_payload(
predecessor_payload,
expected_release_id=patch_id,
)
)
predecessor_descriptor_rel = (
DEVICE_PLANE_MANAGER_RELEASE_V2_REL
if tuple(predecessor_entries)
== DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES
else DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL
)
installed_descriptor = read_strict_json(
root / DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL,
root / predecessor_descriptor_rel,
"installed Device Manager release predecessor",
max_bytes=16 * 1024,
)