fix(deploy): track installed device manager release generation

This commit is contained in:
Codex
2026-08-11 21:01:37 +03:00
parent 0f44cca27d
commit 35c37de586
3 changed files with 121 additions and 2 deletions
@@ -12,7 +12,7 @@ const devicePlaneRoot = resolve(platformRoot, "device-plane");
const designRoot = resolve(process.env.NODEDC_DEVICE_MANAGER_SOURCE_ROOT || resolve(platformRoot, "../NODEDC_DESIGN_GUIDELINE"));
const managerRoot = resolve(designRoot, "apps/device-manager");
const artifactDir = resolve(process.env.NODEDC_DEPLOY_ARTIFACT_DIR || resolve(scriptDir, "../deploy-artifacts"));
const [patchId = "device-manager-release-20260811-012", ...extra] = process.argv.slice(2);
const [patchId = "device-manager-release-20260811-013", ...extra] = process.argv.slice(2);
if (extra.length || !/^[A-Za-z0-9._-]{1,96}$/.test(patchId)) throw new Error("usage: build-device-manager-control-plane-artifact.mjs [patch-id]");
const entries = [
+44 -1
View File
@@ -9455,6 +9455,46 @@ def validate_device_plane_manager_release_payload(
)
def installed_device_plane_manager_compose_sha256():
root = DEVICE_PLANE_ROOT
v1 = root / DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL
v2 = root / DEVICE_PLANE_MANAGER_RELEASE_V2_REL
# A successful v2 overlay intentionally leaves the immutable v1 release
# descriptor as predecessor evidence. Prefer the highest installed
# generation, but validate its exact schema/security boundary before using
# the generation-specific Compose digest. During a failed v2 apply,
# rollback removes the candidate-only v2 descriptor before rebuilding the
# restored v1 runtime, so the same lookup follows the restored source.
if v2.exists() or v2.is_symlink():
descriptor = read_strict_json(
v2,
"installed Device Manager release v2 descriptor",
max_bytes=16 * 1024,
)
validate_device_plane_manager_release_descriptor(
descriptor,
schema_version="nodedc.device-plane.device-manager-release.v2",
boundaries=expected_device_plane_manager_release_v2_boundaries(),
)
return DEVICE_PLANE_MANAGER_RELEASE_V2_COMPOSE_SHA256
if v1.exists() or v1.is_symlink():
descriptor = read_strict_json(
v1,
"installed Device Manager release v1 descriptor",
max_bytes=16 * 1024,
)
validate_device_plane_manager_release_descriptor(
descriptor,
schema_version="nodedc.device-plane.device-manager-release.v1",
boundaries=expected_device_plane_manager_release_v1_boundaries(),
)
return DEVICE_PLANE_MANAGER_RELEASE_V1_COMPOSE_SHA256
die("installed Device Manager release descriptor is missing")
def validate_device_plane_manager_failed_control_plane_payload(payload_dir):
descriptor = read_strict_json(
payload_dir / DEVICE_PLANE_MANAGER_FAILED_CONTROL_PLANE_REL,
@@ -14829,11 +14869,14 @@ def component_compose_files(
if component == "device-plane":
manager_overlay = DEVICE_PLANE_ROOT / DEVICE_PLANE_MANAGER_COMPOSE_REL
if manager_overlay.exists() or manager_overlay.is_symlink():
expected_manager_compose_sha256 = (
installed_device_plane_manager_compose_sha256()
)
if (
manager_overlay.is_symlink()
or not manager_overlay.is_file()
or sha256_file(manager_overlay)
!= DEVICE_PLANE_MANAGER_COMPOSE_SHA256
!= expected_manager_compose_sha256
):
die("installed Device Manager Compose drift detected")
files = (*files, manager_overlay)
@@ -278,6 +278,82 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
),
)
def test_installed_manager_compose_follows_release_generation(self):
with tempfile.TemporaryDirectory(
prefix="nodedc-device-manager-compose-generation-",
) as directory:
root = Path(directory)
deployment = root / "deployment"
deployment.mkdir()
compose = root / RUNNER.DEVICE_PLANE_MANAGER_COMPOSE_REL
compose.write_text("services: {}\n", encoding="utf-8")
v1 = root / RUNNER.DEVICE_PLANE_MANAGER_CONTROL_PLANE_REL
v2 = root / RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V2_REL
v1.write_text(
json.dumps(device_manager_release_v1_descriptor()),
encoding="utf-8",
)
with (
mock.patch.object(RUNNER, "DEVICE_PLANE_ROOT", root),
mock.patch.dict(
RUNNER.COMPONENTS["device-plane"],
{"compose_files": ()},
),
mock.patch.object(
RUNNER,
"sha256_file",
return_value=(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V1_COMPOSE_SHA256
),
),
):
self.assertEqual(
RUNNER.component_compose_files("device-plane"),
(compose,),
)
v2.write_text(
json.dumps(device_manager_release_descriptor()),
encoding="utf-8",
)
with (
mock.patch.object(RUNNER, "DEVICE_PLANE_ROOT", root),
mock.patch.dict(
RUNNER.COMPONENTS["device-plane"],
{"compose_files": ()},
),
mock.patch.object(
RUNNER,
"sha256_file",
return_value=(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V2_COMPOSE_SHA256
),
),
):
self.assertEqual(
RUNNER.component_compose_files("device-plane"),
(compose,),
)
with (
mock.patch.object(RUNNER, "DEVICE_PLANE_ROOT", root),
mock.patch.dict(
RUNNER.COMPONENTS["device-plane"],
{"compose_files": ()},
),
mock.patch.object(
RUNNER,
"sha256_file",
return_value="0" * 64,
),
):
with self.assertRaisesRegex(
RUNNER.DeployError,
"installed Device Manager Compose drift detected",
):
RUNNER.component_compose_files("device-plane")
def test_public_route_artifact_is_last_and_proxy_only(self):
manifest, entries, _names, result = self.assert_deterministic_artifact(
"build-platform-device-manager-route-artifact.mjs",