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 & { 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): Record { 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 { 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 --confirm`, valuesPrinted: false, }; }