fix: expose PikaOA runtime query failures
This commit is contained in:
@@ -80,6 +80,10 @@ if printf '%s' "$args" | grep -q ' wait '; then
|
||||
fi
|
||||
if printf '%s' "$args" | grep -Eq '(^| )get namespace '; then
|
||||
name="$(namespace_after_resource "$@")"
|
||||
if [ -f "$root/fail-runtime-query" ]; then
|
||||
printf '%s\n' 'unable to connect to the server' >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$root/namespaces/$name" ]; then
|
||||
printf 'Error from server (NotFound): namespaces "%s" not found\n' "$name" >&2
|
||||
exit 1
|
||||
@@ -92,6 +96,10 @@ if printf '%s' "$args" | grep -Eq '(^| )get namespace '; then
|
||||
exit 0
|
||||
fi
|
||||
if printf '%s' "$args" | grep -q ' get deployment,statefulset,job,service,persistentvolumeclaim,pod '; then
|
||||
if [ -f "$root/fail-runtime-resource-query" ]; then
|
||||
printf '%s\n' 'resource query failed' >&2
|
||||
exit 1
|
||||
fi
|
||||
printf '%s\n' '{"items":[]}'
|
||||
exit 0
|
||||
fi
|
||||
@@ -108,6 +116,8 @@ exit 64
|
||||
const fixturePath = join(root, "pikaoa.yaml");
|
||||
const fixture = readFileSync(rootPath("config", "fixtures", "pikaoa-test-target.yaml"), "utf8")
|
||||
.replace("validationOnly: true", "validationOnly: false")
|
||||
.replace("statusRequestTimeoutSeconds: 3", "statusRequestTimeoutSeconds: 31")
|
||||
.replace("logTailLines: 8", "logTailLines: 101")
|
||||
.replace("/tmp/unidesk-pikaoa-test-target-fixture", stateRoot);
|
||||
writeFileSync(fixturePath, fixture);
|
||||
|
||||
@@ -155,13 +165,39 @@ exit 64
|
||||
const submitted = await projection("start", "success", true);
|
||||
assert.equal(submitted.code, "start-submitted", JSON.stringify(lastCapture));
|
||||
assert.equal(submitted.mutation, true);
|
||||
assert.deepEqual((submitted.target as Record<string, unknown>).taskState, {
|
||||
rootPath: stateRoot,
|
||||
statusRequestTimeoutSeconds: 31,
|
||||
logTailLines: 101,
|
||||
});
|
||||
const running = await projection("status", "success");
|
||||
assert.equal((running.status as Record<string, unknown>).code, "start-running");
|
||||
rmSync(join(fakeRoot, "slow"));
|
||||
const succeeded = await waitFor("success", "start-succeeded");
|
||||
const succeededStatus = succeeded.status as Record<string, unknown>;
|
||||
assert.equal((succeededStatus.task as Record<string, unknown>).exitCode, 0);
|
||||
assert.ok((succeededStatus.logTail as unknown[]).length <= 8);
|
||||
assert.ok((succeededStatus.logTail as unknown[]).length <= 101);
|
||||
|
||||
writeFileSync(join(fakeRoot, "fail-runtime-query"), "1\n");
|
||||
const runtimeQueryFailed = await projection("status", "success");
|
||||
const runtimeQueryFailedStatus = runtimeQueryFailed.status as Record<string, unknown>;
|
||||
assert.equal(runtimeQueryFailed.ok, false);
|
||||
assert.equal(runtimeQueryFailedStatus.code, "runtime-query-failed");
|
||||
assert.equal(runtimeQueryFailedStatus.taskCode, "start-succeeded");
|
||||
assert.equal((runtimeQueryFailedStatus.runtime as Record<string, unknown>).code, "runtime-query-failed");
|
||||
const runtimeQueryFailedText = await runPikaoaCommand(config, args("status", "success").slice(0, -2), localCapture);
|
||||
assert.match(runtimeQueryFailedText.renderedText, /status=runtime-query-failed/);
|
||||
rmSync(join(fakeRoot, "fail-runtime-query"));
|
||||
|
||||
writeFileSync(join(fakeRoot, "fail-runtime-resource-query"), "1\n");
|
||||
const runtimeResourceQueryFailed = await projection("status", "success");
|
||||
const runtimeResourceQueryFailedStatus = runtimeResourceQueryFailed.status as Record<string, unknown>;
|
||||
assert.equal(runtimeResourceQueryFailed.ok, false);
|
||||
assert.equal(runtimeResourceQueryFailedStatus.code, "runtime-resource-query-failed");
|
||||
assert.equal(runtimeResourceQueryFailedStatus.taskCode, "start-succeeded");
|
||||
assert.equal((runtimeResourceQueryFailedStatus.runtime as Record<string, unknown>).code, "runtime-resource-query-failed");
|
||||
rmSync(join(fakeRoot, "fail-runtime-resource-query"));
|
||||
|
||||
const repeated = await projection("start", "success", true);
|
||||
assert.equal(repeated.code, "start-already-succeeded");
|
||||
assert.equal(repeated.mutation, false);
|
||||
|
||||
@@ -299,8 +299,8 @@ function parseTarget(id: string, root: Record<string, unknown>, configLabel: str
|
||||
fieldManager: kubernetesName(nonEmpty(root.fieldManager, `${path}.fieldManager`), `${path}.fieldManager`, 63),
|
||||
taskState: {
|
||||
rootPath: taskStateRoot(taskState.rootPath, `${path}.taskState.rootPath`),
|
||||
statusRequestTimeoutSeconds: positiveInteger(taskState.statusRequestTimeoutSeconds, `${path}.taskState.statusRequestTimeoutSeconds`, 30),
|
||||
logTailLines: positiveInteger(taskState.logTailLines, `${path}.taskState.logTailLines`, 100),
|
||||
statusRequestTimeoutSeconds: positiveSafeInteger(taskState.statusRequestTimeoutSeconds, `${path}.taskState.statusRequestTimeoutSeconds`),
|
||||
logTailLines: positiveSafeInteger(taskState.logTailLines, `${path}.taskState.logTailLines`),
|
||||
},
|
||||
cleanup: {
|
||||
mode: exactString(cleanup.mode, `${path}.cleanup.mode`, "delete-namespace") as "delete-namespace",
|
||||
@@ -1124,14 +1124,24 @@ else:
|
||||
code = {"queued": "start-running", "running": "start-running", "succeeded": "start-succeeded", "failed": "start-failed", "canceled": "start-canceled"}.get(state, "start-state-unknown")
|
||||
ok = state not in ("failed", "canceled", "unknown")
|
||||
exists = True
|
||||
task_code = code
|
||||
runtime = {"queried": True, "exists": runtime_code in ("runtime-present", "runtime-resource-query-failed"), "code": runtime_code}
|
||||
for key, path in (("namespace", namespace_path), ("resources", resources_path)):
|
||||
if path.exists():
|
||||
runtime_paths = []
|
||||
if runtime_code in ("runtime-present", "runtime-resource-query-failed"):
|
||||
runtime_paths.append(("namespace", namespace_path))
|
||||
if runtime_code == "runtime-present":
|
||||
runtime_paths.append(("resources", resources_path))
|
||||
for key, path in runtime_paths:
|
||||
if path.exists() and path.stat().st_size > 0:
|
||||
try:
|
||||
runtime[key] = json.loads(path.read_text())
|
||||
except Exception:
|
||||
runtime["code"] = "runtime-response-unreadable"
|
||||
payload = {"ok": ok, "mutation": False, "code": code, "jobId": job_id, "taskExists": exists, "task": task, "runnerAlive": runner_alive, "logTail": events, "runtime": runtime, "valuesPrinted": False}
|
||||
else:
|
||||
runtime["code"] = "runtime-response-unreadable"
|
||||
if runtime["code"] in ("runtime-query-failed", "runtime-resource-query-failed", "runtime-response-unreadable"):
|
||||
code, ok = runtime["code"], False
|
||||
payload = {"ok": ok, "mutation": False, "code": code, "taskCode": task_code, "jobId": job_id, "taskExists": exists, "task": task, "runnerAlive": runner_alive, "logTail": events, "runtime": runtime, "valuesPrinted": False}
|
||||
print(json.dumps(payload, separators=(",", ":")))
|
||||
PY
|
||||
`;
|
||||
@@ -1213,6 +1223,7 @@ function summarizeRemoteStatus(parsed: Record<string, unknown>, target: TestTarg
|
||||
return {
|
||||
ok: parsed.ok === true,
|
||||
code: parsed.code ?? "start-status-unknown",
|
||||
taskCode: parsed.taskCode ?? "start-status-unknown",
|
||||
jobId: parsed.jobId ?? asyncJobIdentity(target, context).id,
|
||||
taskExists: parsed.taskExists === true,
|
||||
task: optionalRecord(parsed.task, "status.task"),
|
||||
@@ -1540,6 +1551,11 @@ function positiveInteger(value: unknown, path: string, max: number): number {
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
function positiveSafeInteger(value: unknown, path: string): number {
|
||||
if (!Number.isSafeInteger(value) || Number(value) <= 0) throw new Error(`${path} 必须是正安全整数`);
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
function nonNegativeInteger(value: unknown, path: string): number {
|
||||
if (!Number.isSafeInteger(value) || Number(value) < 0) throw new Error(`${path} 必须是非负安全整数`);
|
||||
return Number(value);
|
||||
|
||||
Reference in New Issue
Block a user