fix(deploy): track installed device manager release generation
This commit is contained in:
@@ -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 = [
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user