182 lines
8.7 KiB
TypeScript
182 lines
8.7 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
|
|
import type { AgentRunLaneSecretSpec, AgentRunLaneSpec } from "../agentrun-lanes";
|
|
import type { UniDeskConfig } from "../config";
|
|
import { rootPath } from "../config";
|
|
import { sha256Fingerprint } from "../platform-infra-ops-library";
|
|
|
|
import { capture, captureJsonPayload, compactCapture, record, shQuote } from "./utils";
|
|
|
|
export const AGENTRUN_MANAGER_RETENTION_ISSUE = "https://github.com/pikasTech/agentrun/issues/280";
|
|
export const AGENTRUN_MANAGER_RETENTION_PULL_REQUEST = "https://github.com/pikasTech/agentrun/pull/281";
|
|
export const AGENTRUN_MANAGER_RECONCILER_ISSUE = "https://github.com/pikasTech/agentrun/issues/282";
|
|
export const AGENTRUN_PROVIDER_ACTIVATION_FENCE_ISSUE = "https://github.com/pikasTech/agentrun/issues/284";
|
|
|
|
export interface ProviderSecretActivationTarget {
|
|
readonly id: string;
|
|
readonly targetRef: AgentRunLaneSecretSpec["targetRef"];
|
|
readonly desiredEncodedFingerprint: string;
|
|
}
|
|
|
|
export function providerSecretDesiredEncodedFingerprint(value: string): string {
|
|
return sha256Fingerprint(Buffer.from(value, "utf8").toString("base64"));
|
|
}
|
|
|
|
export function providerSecretActivationPreflightScript(spec: AgentRunLaneSpec, targets: readonly ProviderSecretActivationTarget[]): string {
|
|
const script = readFileSync(rootPath("scripts/src/agentrun/provider-secret-activation-preflight.mjs"), "utf8");
|
|
return [
|
|
"set -eu",
|
|
`namespace=${shQuote(spec.runtime.namespace)}`,
|
|
`targets_json_b64=${shQuote(Buffer.from(JSON.stringify(targets), "utf8").toString("base64"))}`,
|
|
`match_labels_json_b64=${shQuote(Buffer.from(JSON.stringify(spec.deployment.runner.retention.selectors.matchLabels), "utf8").toString("base64"))}`,
|
|
`job_name_prefixes_json_b64=${shQuote(Buffer.from(JSON.stringify(spec.deployment.runner.retention.selectors.jobNamePrefixes), "utf8").toString("base64"))}`,
|
|
"tmp_dir=$(mktemp -d)",
|
|
"trap 'rm -rf \"$tmp_dir\"' EXIT",
|
|
"cat > \"$tmp_dir/provider-secret-activation-preflight.mjs\" <<'NODE'",
|
|
script,
|
|
"NODE",
|
|
"env NAMESPACE=\"$namespace\" TARGETS_JSON_B64=\"$targets_json_b64\" MATCH_LABELS_JSON_B64=\"$match_labels_json_b64\" JOB_NAME_PREFIXES_JSON_B64=\"$job_name_prefixes_json_b64\" node \"$tmp_dir/provider-secret-activation-preflight.mjs\"",
|
|
].join("\n");
|
|
}
|
|
|
|
export async function providerSecretActivationPreflight(
|
|
config: UniDeskConfig,
|
|
spec: AgentRunLaneSpec,
|
|
targets: readonly ProviderSecretActivationTarget[],
|
|
options: { readonly full?: boolean } = {},
|
|
): Promise<Record<string, unknown> & { ok: boolean }> {
|
|
const result = await capture(config, spec.nodeKubeRoute, ["sh", "--", providerSecretActivationPreflightScript(spec, targets)]);
|
|
const payload = captureJsonPayload(result);
|
|
const ok = result.exitCode === 0
|
|
&& payload.ok === true
|
|
&& payload.targetSecretObservationComplete === true
|
|
&& payload.providerSecretDataNoOp === true
|
|
&& Number(payload.providerSecretMutationCount ?? -1) === 0;
|
|
const visiblePayload = options.full === true ? payload : compactProviderSecretActivationPayload(payload);
|
|
return {
|
|
...visiblePayload,
|
|
ok,
|
|
status: ok ? "ready" : "blocked",
|
|
reason: ok ? null : payload.reason ?? (result.exitCode === 0 ? "provider-secret-activation-preflight-blocked" : "provider-secret-activation-preflight-failed"),
|
|
capture: compactCapture(result, {
|
|
full: options.full === true || result.exitCode !== 0,
|
|
stdoutTailChars: 2400,
|
|
stderrTailChars: 2400,
|
|
}),
|
|
managerAuthority: {
|
|
owner: "agentrun-manager-provider-activation-fence",
|
|
requiredBeforeActivation: record(payload).requiresManagerFence === true,
|
|
atomicFenceAvailable: false,
|
|
mutationAuthorized: false,
|
|
noOpOnly: true,
|
|
trackingIssue: AGENTRUN_PROVIDER_ACTIVATION_FENCE_ISSUE,
|
|
retentionDependencies: {
|
|
issue: AGENTRUN_MANAGER_RETENTION_ISSUE,
|
|
pullRequest: AGENTRUN_MANAGER_RETENTION_PULL_REQUEST,
|
|
stalePendingReconcilerIssue: AGENTRUN_MANAGER_RECONCILER_ISSUE,
|
|
sufficientForProviderActivation: false,
|
|
valuesPrinted: false,
|
|
},
|
|
valuesPrinted: false,
|
|
},
|
|
mutation: false,
|
|
runtimeSecretValuesDecoded: false,
|
|
valuesPrinted: false,
|
|
};
|
|
}
|
|
|
|
function compactProviderSecretActivationPayload(payload: Record<string, unknown>): Record<string, unknown> {
|
|
const targetSecrets = Array.isArray(payload.targetSecrets) ? payload.targetSecrets.map(record) : [];
|
|
const risks = Array.isArray(payload.activationRisks) ? payload.activationRisks.map(record) : [];
|
|
const visibleRisks = risks.slice(0, 1).map((risk) => ({
|
|
pod: risk.pod ?? null,
|
|
jobName: risk.jobName ?? null,
|
|
phase: risk.phase ?? null,
|
|
createdAt: risk.createdAt ?? null,
|
|
runId: risk.runId ?? null,
|
|
commandId: risk.commandId ?? null,
|
|
runnerJobId: risk.runnerJobId ?? null,
|
|
runnerId: risk.runnerId ?? null,
|
|
waitingReasons: Array.isArray(risk.waitingReasons) ? risk.waitingReasons.slice(0, 4) : [],
|
|
dependencies: Array.isArray(risk.dependencies)
|
|
? risk.dependencies.map(record).slice(0, 2).map((dependency) => ({
|
|
source: dependency.source ?? null,
|
|
name: dependency.name ?? null,
|
|
affectedDesiredKeys: Array.isArray(dependency.affectedDesiredKeys) ? dependency.affectedDesiredKeys.slice(0, 8) : [],
|
|
mutationKinds: Array.isArray(dependency.mutationKinds) ? dependency.mutationKinds.slice(0, 4) : [],
|
|
reason: dependency.reason ?? null,
|
|
valuesPrinted: false,
|
|
}))
|
|
: [],
|
|
classification: risk.classification ?? null,
|
|
valuesPrinted: false,
|
|
}));
|
|
return {
|
|
ok: payload.ok === true,
|
|
status: payload.status ?? null,
|
|
reason: payload.reason ?? null,
|
|
observedAt: payload.observedAt ?? null,
|
|
target: payload.target ?? null,
|
|
targetSecretCount: payload.targetSecretCount ?? targetSecrets.length,
|
|
targetSecrets: targetSecrets.map((target) => ({
|
|
namespace: target.namespace ?? null,
|
|
name: target.name ?? null,
|
|
desiredKeys: Array.isArray(target.desiredKeys) ? target.desiredKeys.slice(0, 8) : [],
|
|
queryOk: target.queryOk === true,
|
|
present: typeof target.present === "boolean" ? target.present : null,
|
|
mutationCount: target.mutationCount ?? null,
|
|
noOp: target.noOp === true,
|
|
mutations: Array.isArray(target.mutations)
|
|
? target.mutations.map(record).slice(0, 8).map((mutation) => ({
|
|
id: mutation.id ?? null,
|
|
key: mutation.key ?? null,
|
|
desiredFingerprintSuffix: typeof mutation.desiredEncodedFingerprint === "string" ? mutation.desiredEncodedFingerprint.slice(-12) : null,
|
|
runtimeFingerprintSuffix: typeof mutation.runtimeEncodedFingerprint === "string" ? mutation.runtimeEncodedFingerprint.slice(-12) : null,
|
|
changeKind: mutation.changeKind ?? null,
|
|
valuesPrinted: false,
|
|
}))
|
|
: [],
|
|
valuesPrinted: false,
|
|
})),
|
|
podObservation: payload.podObservation ?? null,
|
|
targetSecretObservationComplete: payload.targetSecretObservationComplete === true,
|
|
runnerObservationComplete: payload.runnerObservationComplete === true,
|
|
allObservationsComplete: payload.allObservationsComplete === true,
|
|
providerSecretMutationCount: payload.providerSecretMutationCount ?? null,
|
|
providerSecretDataNoOp: payload.providerSecretDataNoOp === true,
|
|
providerSecretMutationAuthorized: payload.providerSecretMutationAuthorized === true,
|
|
activationAuthority: payload.activationAuthority ?? null,
|
|
activationRiskCount: payload.activationRiskCount ?? risks.length,
|
|
activationRisks: visibleRisks,
|
|
activationRiskOmittedCount: Math.max(0, Number(payload.activationRiskCount ?? risks.length) - visibleRisks.length),
|
|
requiresManagerFence: payload.requiresManagerFence === true,
|
|
queryComplete: payload.queryComplete === true,
|
|
mutation: false,
|
|
runtimeSecretValuesDecoded: false,
|
|
valuesPrinted: false,
|
|
};
|
|
}
|
|
|
|
export function providerSecretStagedActivation(
|
|
spec: AgentRunLaneSpec,
|
|
profiles: readonly string[],
|
|
): Record<string, unknown> {
|
|
return {
|
|
status: "blocked-before-secret-activation",
|
|
targetProfiles: [...new Set(profiles)],
|
|
secretMutationStarted: false,
|
|
requiredSequence: [
|
|
"agentrun-manager-reconcile-terminalize-and-fence-affected-runners",
|
|
"agentrun-manager-acquire-provider-activation-admission-fence-and-secret-cas",
|
|
"repeat-provider-secret-activation-preflight",
|
|
"sync-yaml-declared-provider-secret",
|
|
],
|
|
managerTrackingIssue: AGENTRUN_MANAGER_RETENTION_ISSUE,
|
|
managerTrackingPullRequest: AGENTRUN_MANAGER_RETENTION_PULL_REQUEST,
|
|
managerReconcilerIssue: AGENTRUN_MANAGER_RECONCILER_ISSUE,
|
|
providerActivationFenceIssue: AGENTRUN_PROVIDER_ACTIVATION_FENCE_ISSUE,
|
|
retry: `bun scripts/cli.ts agentrun provider-profile apply --node ${spec.nodeId} --lane ${spec.lane} --profile <profile> --confirm`,
|
|
valuesPrinted: false,
|
|
};
|
|
}
|