Merge pull request #1897 from pikasTech/fix/issue-1888-migration-envelope
修复迁移 Job 失败 envelope 解包丢失 command
This commit is contained in:
@@ -333,6 +333,16 @@ describe("migrated CLI 执行 guard 与提示清理", () => {
|
||||
expect(failure.projection?.result).toBeUndefined();
|
||||
});
|
||||
|
||||
test("migration Job 解包 pretty JSON outer command 和 nested data.error", () => {
|
||||
const importFailure = JSON.stringify({ ok: false, command: "web-probe sentinel migration import --node NC01 --lane v03 --json", data: { ok: false, error: { code: "payload_hash_conflict", message: "payload hash conflicts with the existing import" } } }, null, 2);
|
||||
const probe = { failed: true, podPhase: "Failed", containers: [{ name: "import", phase: "terminated", exitCode: 1, reason: "Error", message: "-" }], containerLogs: { import: `import prefix\n${importFailure}` }, logsTail: importFailure };
|
||||
const payload = migrationJobPayloadFromProbe(probe);
|
||||
expect(payload).toMatchObject({ ok: false, command: "web-probe sentinel migration import --node NC01 --lane v03 --json", error: { code: "payload_hash_conflict", message: "payload hash conflicts with the existing import" } });
|
||||
const failure = migrationJobFailed("migration", { jobName: "job" }, "failed", payload, probe) as { projection?: Record<string, unknown> };
|
||||
expect(failure.projection?.failure).toMatchObject({ container: "import", code: "payload_hash_conflict" });
|
||||
expect(String((failure.projection?.failure as Record<string, unknown>)?.message)).toStartWith("payload hash conflicts with the existin");
|
||||
});
|
||||
|
||||
test("migration Job 成功固定读取 verify,缺结构化 verify 结果显式失败", () => {
|
||||
const importSuccess = JSON.stringify({ ok: true, data: { ok: true, command: "web-probe sentinel migration import" } }, null, 2);
|
||||
expect(migrationJobPayloadFromProbe({ succeeded: true, containers: [{ name: "verify", phase: "terminated", exitCode: 0 }], containerLogs: { import: importSuccess }, logsTail: importSuccess })).toMatchObject({ ok: false, error: { code: "monitor-migration-job-result-missing" } });
|
||||
|
||||
@@ -983,19 +983,27 @@ export function sentinelPayloadFromLogs(logsTail: string): Record<string, unknow
|
||||
const parsed = lastJsonObjectFromText(logsTail);
|
||||
if (parsed === null) return {};
|
||||
const data = isRecord(parsed.data) ? parsed.data : null;
|
||||
return data !== null && (data.ok === true || data.ok === false) ? data : parsed.ok === true || parsed.ok === false ? parsed : {};
|
||||
if (data !== null && (data.ok === true || data.ok === false)) {
|
||||
return data.command === undefined && typeof parsed.command === "string" ? { ...data, command: parsed.command } : data;
|
||||
}
|
||||
return parsed.ok === true || parsed.ok === false ? parsed : {};
|
||||
}
|
||||
|
||||
function lastJsonObjectFromText(text: string): Record<string, unknown> | null {
|
||||
let last: Record<string, unknown> | null = null;
|
||||
for (let start = text.indexOf("{"); start >= 0; start = text.indexOf("{", start + 1)) {
|
||||
for (let start = text.indexOf("{"); start >= 0;) {
|
||||
const candidate = jsonObjectAt(text, start);
|
||||
if (candidate !== null && (candidate.ok === true || candidate.ok === false)) last = candidate;
|
||||
if (candidate === null) {
|
||||
start = text.indexOf("{", start + 1);
|
||||
continue;
|
||||
}
|
||||
if (candidate.value.ok === true || candidate.value.ok === false) last = candidate.value;
|
||||
start = text.indexOf("{", candidate.end + 1);
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
function jsonObjectAt(text: string, start: number): Record<string, unknown> | null {
|
||||
function jsonObjectAt(text: string, start: number): { value: Record<string, unknown>; end: number } | null {
|
||||
let depth = 0;
|
||||
let inString = false;
|
||||
let escaped = false;
|
||||
@@ -1009,7 +1017,10 @@ function jsonObjectAt(text: string, start: number): Record<string, unknown> | nu
|
||||
}
|
||||
if (char === "\"") { inString = true; continue; }
|
||||
if (char === "{") { depth += 1; continue; }
|
||||
if (char === "}" && --depth === 0) return parseJsonObject(text.slice(start, index + 1));
|
||||
if (char === "}" && --depth === 0) {
|
||||
const value = parseJsonObject(text.slice(start, index + 1));
|
||||
return value === null ? null : { value, end: index };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user