fix(device-plane): reconcile failed manager activation
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"schemaVersion": "nodedc.device-plane.device-manager-control-plane-v2-reconciliation.v1",
|
||||
"mode": "failed-v2-control-plane-baseline-adoption",
|
||||
"failedPatchId": "device-manager-control-plane-20260811-003",
|
||||
"failedArtifactSha256": "ba29618ffbfed55448768794f28b18dda439ddb39a1d2a4f1dece19de7f29990",
|
||||
"backupId": "device-plane-device-manager-control-plane-20260811-003-20260811-012505",
|
||||
"failureClass": "deterministic-runtime-module-resolution",
|
||||
"missingModule": "/packages/external-provider-contract/src/credential-reference.mjs",
|
||||
"correctiveAction": "runtime-local-contract-adapter+staged-module-import-gate",
|
||||
"sourceAction": "publish-reconciliation-marker-only",
|
||||
"runtimeAction": "read-only-acceptance",
|
||||
"preservedServices": [
|
||||
"device-control-core",
|
||||
"device-gateway",
|
||||
"device-postgres",
|
||||
"device-backhaul-target"
|
||||
],
|
||||
"absentService": "device-manager",
|
||||
"databaseVolume": "nodedc-device-plane-postgres-data",
|
||||
"publicIngress": "disabled",
|
||||
"commandTransport": "disabled",
|
||||
"gelios": "untouched",
|
||||
"rollback": "marker-only-runtime-unchanged"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
export const NDC_CREDENTIAL_REFERENCE_OWNER = "ndc_l2_credentials";
|
||||
|
||||
const CREDENTIAL_REFERENCE_PATTERN =
|
||||
/^ndc-credref:[A-Za-z0-9][A-Za-z0-9._:-]{7,240}$/;
|
||||
|
||||
export function normalizeNdcCredentialReference(input) {
|
||||
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
||||
throw new TypeError("ndc_credential_reference_invalid");
|
||||
}
|
||||
for (const key of Object.keys(input)) {
|
||||
if (!new Set(["owner", "reference"]).has(key)) {
|
||||
throw new TypeError(`ndc_credential_reference_field_unexpected:${key}`);
|
||||
}
|
||||
}
|
||||
if (input.owner !== NDC_CREDENTIAL_REFERENCE_OWNER) {
|
||||
throw new TypeError("ndc_credential_reference_owner_invalid");
|
||||
}
|
||||
if (!isNdcCredentialReferenceValue(input.reference)) {
|
||||
throw new TypeError("ndc_credential_reference_value_invalid");
|
||||
}
|
||||
return Object.freeze({
|
||||
owner: NDC_CREDENTIAL_REFERENCE_OWNER,
|
||||
reference: input.reference,
|
||||
});
|
||||
}
|
||||
|
||||
export function isNdcCredentialReferenceValue(value) {
|
||||
return typeof value === "string" && CREDENTIAL_REFERENCE_PATTERN.test(value);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
normalizeNdcCredentialReference,
|
||||
} from "../../../../packages/external-provider-contract/src/credential-reference.mjs";
|
||||
} from "./credential-reference.mjs";
|
||||
|
||||
export const DEVICE_SENSITIVE_REFERENCE_COMMAND_KINDS = Object.freeze([
|
||||
"device_credential_binding.upsert",
|
||||
|
||||
+52
@@ -9,6 +9,12 @@ import {
|
||||
ALL_DEVICE_MANAGEMENT_COMMAND_KINDS,
|
||||
normalizeDeviceManagementCommand,
|
||||
} from "../src/management-command.mjs";
|
||||
import {
|
||||
normalizeNdcCredentialReference as normalizeRuntimeCredentialReference,
|
||||
} from "../src/credential-reference.mjs";
|
||||
import {
|
||||
normalizeNdcCredentialReference as normalizePlatformCredentialReference,
|
||||
} from "../../../../packages/external-provider-contract/src/credential-reference.mjs";
|
||||
|
||||
const projectRef = "project:11111111-1111-4111-8111-111111111111";
|
||||
const deviceRef = "device:22222222-2222-4222-8222-222222222222";
|
||||
@@ -65,6 +71,52 @@ test("credential binding accepts only the platform canonical opaque ref", () =>
|
||||
);
|
||||
});
|
||||
|
||||
test("runtime credential reference adapter matches the platform contract", () => {
|
||||
const accepted = [
|
||||
{
|
||||
owner: "ndc_l2_credentials",
|
||||
reference: "ndc-credref:pilot-command-0001",
|
||||
},
|
||||
{
|
||||
owner: "ndc_l2_credentials",
|
||||
reference: "ndc-credref:A1234567",
|
||||
},
|
||||
];
|
||||
for (const input of accepted) {
|
||||
assert.deepEqual(
|
||||
normalizeRuntimeCredentialReference(input),
|
||||
normalizePlatformCredentialReference(input),
|
||||
);
|
||||
}
|
||||
|
||||
const rejected = [
|
||||
null,
|
||||
[],
|
||||
{ owner: "device_core", reference: "ndc-credref:pilot-command-0001" },
|
||||
{ owner: "ndc_l2_credentials", reference: "secret:test" },
|
||||
{
|
||||
owner: "ndc_l2_credentials",
|
||||
reference: "ndc-credref:pilot-command-0001",
|
||||
token: "forbidden",
|
||||
},
|
||||
];
|
||||
for (const input of rejected) {
|
||||
let runtimeError;
|
||||
let platformError;
|
||||
try {
|
||||
normalizeRuntimeCredentialReference(input);
|
||||
} catch (error) {
|
||||
runtimeError = error;
|
||||
}
|
||||
try {
|
||||
normalizePlatformCredentialReference(input);
|
||||
} catch (error) {
|
||||
platformError = error;
|
||||
}
|
||||
assert.equal(runtimeError?.message, platformError?.message);
|
||||
}
|
||||
});
|
||||
|
||||
test("credential binding rejects raw secret-shaped fields", () => {
|
||||
for (const field of ["password", "token", "secretValue", "endpoint"]) {
|
||||
assert.throws(
|
||||
|
||||
Reference in New Issue
Block a user