fix(deploy): version device manager edge release contract
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-011", ...extra] = process.argv.slice(2);
|
||||
const [patchId = "device-manager-release-20260811-012", ...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 = [
|
||||
@@ -26,7 +26,7 @@ const entries = [
|
||||
"services/device-gateway/package.json",
|
||||
"services/device-edge-relay/package.json",
|
||||
"services/device-manager",
|
||||
"deployment/device-manager-release-v1.json",
|
||||
"deployment/device-manager-release-v2.json",
|
||||
];
|
||||
const stage = await mkdtemp(join(tmpdir(), "nodedc-device-manager-control-plane-"));
|
||||
const payload = join(stage, "payload");
|
||||
@@ -40,7 +40,7 @@ try {
|
||||
if (build.status !== 0) throw new Error(`device_manager_build_failed:${build.stderr || build.stdout}`);
|
||||
await mkdir(payload, { recursive: true });
|
||||
for (const entry of entries) {
|
||||
if (entry === "deployment/device-manager-release-v1.json") {
|
||||
if (entry === "deployment/device-manager-release-v2.json") {
|
||||
const descriptor = JSON.parse(await readFile(resolve(devicePlaneRoot, entry), "utf8"));
|
||||
if (descriptor.releaseId !== "__PATCH_ID__") {
|
||||
throw new Error("device_manager_release_template_id_mismatch");
|
||||
@@ -109,12 +109,12 @@ try {
|
||||
if (compose.includes(forbidden)) throw new Error(`device_manager_compose_boundary_violation:${forbidden}`);
|
||||
}
|
||||
const descriptor = JSON.parse(await readFile(
|
||||
join(payload, "deployment/device-manager-release-v1.json"),
|
||||
join(payload, "deployment/device-manager-release-v2.json"),
|
||||
"utf8",
|
||||
));
|
||||
const predecessor = descriptor.predecessor;
|
||||
if (
|
||||
descriptor.schemaVersion !== "nodedc.device-plane.device-manager-release.v1"
|
||||
descriptor.schemaVersion !== "nodedc.device-plane.device-manager-release.v2"
|
||||
|| descriptor.releaseId !== patchId
|
||||
|| !["activate", "upgrade"].includes(descriptor.action)
|
||||
|| !predecessor
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -69,6 +69,27 @@ def device_manager_release_descriptor(
|
||||
predecessor_kind="reconciliation",
|
||||
predecessor_patch="device-manager-reconciliation-unit-001",
|
||||
predecessor_sha="a" * 64,
|
||||
):
|
||||
return {
|
||||
"schemaVersion": "nodedc.device-plane.device-manager-release.v2",
|
||||
"releaseId": release_id,
|
||||
"action": action,
|
||||
"predecessor": {
|
||||
"kind": predecessor_kind,
|
||||
"patchId": predecessor_patch,
|
||||
"artifactSha256": predecessor_sha,
|
||||
},
|
||||
**RUNNER.expected_device_plane_manager_release_v2_boundaries(),
|
||||
}
|
||||
|
||||
|
||||
def device_manager_release_v1_descriptor(
|
||||
release_id="device-manager-release-v1-unit-001",
|
||||
*,
|
||||
action="upgrade",
|
||||
predecessor_kind="release",
|
||||
predecessor_patch="device-manager-release-v1-unit-000",
|
||||
predecessor_sha="a" * 64,
|
||||
):
|
||||
return {
|
||||
"schemaVersion": "nodedc.device-plane.device-manager-release.v1",
|
||||
@@ -79,7 +100,7 @@ def device_manager_release_descriptor(
|
||||
"patchId": predecessor_patch,
|
||||
"artifactSha256": predecessor_sha,
|
||||
},
|
||||
**RUNNER.expected_device_plane_manager_release_boundaries(),
|
||||
**RUNNER.expected_device_plane_manager_release_v1_boundaries(),
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +185,7 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
|
||||
manifest, entries, names, result = self.assert_deterministic_artifact(
|
||||
"build-device-manager-control-plane-artifact.mjs",
|
||||
"device-manager-control-plane-unit-001",
|
||||
RUNNER.DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES,
|
||||
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES,
|
||||
)
|
||||
self.assertEqual(manifest["component"], "device-plane")
|
||||
self.assertEqual(
|
||||
@@ -205,6 +226,58 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
|
||||
self.assertEqual(checks[0]["expected_json"]["managementApi"], "enabled")
|
||||
self.assertEqual(checks[0]["expected_json"]["discoveryIngest"], "enabled")
|
||||
|
||||
def test_release_v2_keeps_release_v1_predecessor_contract_immutable(self):
|
||||
predecessor = device_manager_release_v1_descriptor(
|
||||
release_id="device-manager-release-20260811-010",
|
||||
)
|
||||
candidate = device_manager_release_descriptor(
|
||||
release_id="device-manager-release-20260811-012",
|
||||
action="upgrade",
|
||||
predecessor_kind="release",
|
||||
predecessor_patch="device-manager-release-20260811-010",
|
||||
predecessor_sha="d" * 64,
|
||||
)
|
||||
self.assertIs(
|
||||
RUNNER.validate_device_plane_manager_release_descriptor(
|
||||
predecessor,
|
||||
schema_version=(
|
||||
"nodedc.device-plane.device-manager-release.v1"
|
||||
),
|
||||
boundaries=(
|
||||
RUNNER.expected_device_plane_manager_release_v1_boundaries()
|
||||
),
|
||||
expected_release_id="device-manager-release-20260811-010",
|
||||
),
|
||||
predecessor,
|
||||
)
|
||||
self.assertIs(
|
||||
RUNNER.validate_device_plane_manager_release_descriptor(
|
||||
candidate,
|
||||
schema_version=(
|
||||
"nodedc.device-plane.device-manager-release.v2"
|
||||
),
|
||||
boundaries=(
|
||||
RUNNER.expected_device_plane_manager_release_v2_boundaries()
|
||||
),
|
||||
expected_release_id="device-manager-release-20260811-012",
|
||||
),
|
||||
candidate,
|
||||
)
|
||||
with self.assertRaisesRegex(
|
||||
RUNNER.DeployError,
|
||||
r"schema=.*release\.v2 missing=edgeChannel,edgeChannelEgress,"
|
||||
r"edgeChannelIdentity extra=none",
|
||||
):
|
||||
RUNNER.validate_device_plane_manager_release_descriptor(
|
||||
predecessor,
|
||||
schema_version=(
|
||||
"nodedc.device-plane.device-manager-release.v2"
|
||||
),
|
||||
boundaries=(
|
||||
RUNNER.expected_device_plane_manager_release_v2_boundaries()
|
||||
),
|
||||
)
|
||||
|
||||
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",
|
||||
@@ -239,7 +312,7 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
|
||||
):
|
||||
RUNNER.prepare_component_runtime(
|
||||
"device-plane",
|
||||
RUNNER.DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES,
|
||||
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES,
|
||||
)
|
||||
self.assertEqual(
|
||||
[call.args[0] for call in ensure.call_args_list],
|
||||
@@ -254,7 +327,7 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
|
||||
ensure_edge_identity.assert_called_once_with()
|
||||
|
||||
def test_apply_gate_checks_exact_services_core_contract_and_runtime_boundary(self):
|
||||
entries = RUNNER.DEVICE_PLANE_MANAGER_CONTROL_PLANE_ENTRIES
|
||||
entries = RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V2_ENTRIES
|
||||
services = ("device-control-core", "device-manager")
|
||||
with (
|
||||
mock.patch.object(
|
||||
@@ -341,7 +414,7 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
|
||||
),
|
||||
mock.patch.object(
|
||||
RUNNER,
|
||||
"validate_device_plane_manager_control_plane_payload",
|
||||
"validate_device_plane_manager_release_payload",
|
||||
return_value=descriptor,
|
||||
) as payload,
|
||||
mock.patch.object(
|
||||
@@ -441,7 +514,7 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
|
||||
artifact_bytes = b"arbitrary-reviewed-device-manager-release"
|
||||
artifact.write_bytes(artifact_bytes)
|
||||
artifact_sha = hashlib.sha256(artifact_bytes).hexdigest()
|
||||
predecessor_descriptor = device_manager_release_descriptor(
|
||||
predecessor_descriptor = device_manager_release_v1_descriptor(
|
||||
release_id=predecessor_patch,
|
||||
)
|
||||
candidate_descriptor = device_manager_release_descriptor(
|
||||
@@ -483,7 +556,7 @@ class DeviceManagerControlPlaneArtifactsTest(unittest.TestCase):
|
||||
),
|
||||
mock.patch.object(
|
||||
RUNNER,
|
||||
"validate_device_plane_manager_control_plane_payload",
|
||||
"validate_device_plane_manager_release_payload",
|
||||
side_effect=(
|
||||
candidate_descriptor,
|
||||
predecessor_descriptor,
|
||||
|
||||
Reference in New Issue
Block a user