fix: 移除 provider Secret 旧阻断

This commit is contained in:
Codex
2026-07-12 18:00:59 +02:00
parent 59b3821956
commit 1dbc2132ef
2 changed files with 12 additions and 182 deletions
+9 -83
View File
@@ -17,19 +17,13 @@ import {
} from "../agentrun-lanes";
import { refreshYamlLaneScript } from "./git-mirror";
import type { ConfirmOptions, DisclosureOptions } from "./options";
import {
AGENTRUN_PROVIDER_ACTIVATION_FENCE_ISSUE,
providerSecretActivationPreflight,
providerSecretDesiredEncodedFingerprint,
providerSecretStagedActivation,
type ProviderSecretActivationTarget,
} from "./provider-activation";
import { compactAgentRunLaneStatusTarget } from "./trigger";
import {
collectLaneSecretSources,
createYamlLaneJobScript,
inspectSecretSourceValue,
restartYamlLaneScript,
secretSyncScript,
type LaneSecretSource,
type LaneSecretSourceInspection,
yamlLaneGitopsPublishJobManifest,
@@ -107,7 +101,7 @@ export function providerProfileHelp(): string {
" bun scripts/cli.ts agentrun provider-profile validate --node NC01 --lane nc01-v02 --profile gpt.pika --wait",
"",
"Boundary:",
" confirmed apply returns an async job only after the provider Secret gate reports an exact data no-op; the worker skips the provider Secret write, publishes runtime GitOps using the current manager image, refreshes Argo and restarts the manager without rebuilding images or creating a PipelineRun.",
" confirmed apply syncs the selected YAML-declared provider Secret, publishes runtime GitOps using the current manager image, refreshes Argo and restarts the manager without rebuilding images or creating a PipelineRun.",
].join("\n");
}
@@ -129,11 +123,6 @@ async function providerProfileApplyAsync(config: UniDeskConfig, options: Provide
if (plan.ok !== true) return plan;
const { spec, profile } = resolveProviderProfile(options);
const secrets = spec.secrets.filter((secret) => secret.providerCredentialProfile === profile);
const activationTargets = providerProfileActivationTargets(inspectProviderProfileSources(spec, secrets));
const activationPreflight = await providerSecretActivationPreflight(config, spec, activationTargets, { full: options.full || options.raw });
if (activationPreflight.ok !== true) {
return providerProfileActivationBlockedResult(spec, profile, secrets, activationPreflight, "async-submit");
}
const args = [
"bun",
"scripts/cli.ts",
@@ -162,7 +151,6 @@ async function providerProfileApplyAsync(config: UniDeskConfig, options: Provide
profile,
job,
jobCreated: true,
activationPreflight,
statusCommand,
next: {
status: statusCommand,
@@ -249,14 +237,7 @@ function providerProfilePlan(options: ProviderProfileOptions): Record<string, un
providerServices: providerRuntimeServiceTargets(secrets),
valuesPrinted: false,
},
applySteps: ["activation-preflight", "provider-secret-no-op-skip", "gitops-publish-current-image", "argocd-refresh-sync", "manager-rollout-restart"],
activationPreflightRequired: true,
providerSecretMutationPolicy: {
mode: "fail-closed-until-manager-activation-fence",
exactNoOpWriteSkipped: true,
trackingIssue: AGENTRUN_PROVIDER_ACTIVATION_FENCE_ISSUE,
valuesPrinted: false,
},
applySteps: ["secret-sync", "gitops-publish-current-image", "argocd-refresh-sync", "manager-rollout-restart"],
rebuildImages: false,
pipelineRun: false,
valuesPrinted: false,
@@ -417,24 +398,13 @@ async function providerProfileApply(config: UniDeskConfig, options: ProviderProf
const inspected = inspectProviderProfileSources(spec, secrets);
const source = providerProfileSourceSummary(inspected);
if (!source.ready) return providerProfileSourceUnavailable("apply", configPath, spec, profile, secrets, source);
providerProfileApplyProgress(spec, profile, "activation-preflight", "running");
const activationPreflight = await providerSecretActivationPreflight(config, spec, providerProfileActivationTargets(inspected), { full: options.full || options.raw });
providerProfileApplyProgress(spec, profile, "activation-preflight", activationPreflight.ok ? "succeeded" : "failed");
if (activationPreflight.ok !== true) {
return providerProfileActivationBlockedResult(spec, profile, secrets, activationPreflight, "worker-before-secret-sync");
providerProfileApplyProgress(spec, profile, "secret-sync", "running");
const secretSync = await capture(config, spec.nodeKubeRoute, ["sh", "--", secretSyncScript(spec, inspected.map(({ secret, inspection }) => ({ targetRef: secret.targetRef, value: inspection.sourceValue!.value })))]);
const secretPayload = captureJsonPayload(secretSync);
providerProfileApplyProgress(spec, profile, "secret-sync", secretSync.exitCode === 0 && secretPayload.ok !== false ? "succeeded" : "failed", { status: secretPayload.status ?? null });
if (secretSync.exitCode !== 0 || secretPayload.ok === false) {
return applyFailure(configPath, spec, profile, "secret-sync", secretPayload, secretSync);
}
const secretPayload = {
ok: true,
status: "skipped-exact-provider-secret-no-op",
secretSyncStarted: false,
providerSecretWriteSkipped: true,
skippedProviderSecretCount: inspected.length,
reason: "provider-secret-writes-require-agentrun-manager-activation-fence",
activationFenceIssue: AGENTRUN_PROVIDER_ACTIVATION_FENCE_ISSUE,
runtimeSecretValuesDecoded: false,
valuesPrinted: false,
};
providerProfileApplyProgress(spec, profile, "secret-sync", "succeeded", { status: secretPayload.status });
providerProfileApplyProgress(spec, profile, "current-image-resolve", "running");
const liveBefore = await capture(config, spec.nodeKubeRoute, ["sh", "--", currentManagerImageScript(spec)]);
const liveBeforePayload = captureJsonPayload(liveBefore);
@@ -481,7 +451,6 @@ async function providerProfileApply(config: UniDeskConfig, options: ProviderProf
target: compactAgentRunLaneStatusTarget(spec),
profile: providerProfileSummary(spec, profile, secrets),
steps: {
activationPreflight,
secretSync: secretPayload,
gitops: publish.payload,
argo: argoPayload,
@@ -510,38 +479,6 @@ async function providerProfileApply(config: UniDeskConfig, options: ProviderProf
};
}
function providerProfileActivationBlockedResult(
spec: AgentRunLaneSpec,
profile: string,
secrets: readonly AgentRunLaneSecretSpec[],
activationPreflight: Record<string, unknown>,
stage: "async-submit" | "worker-before-secret-sync",
): Record<string, unknown> {
return {
ok: false,
command: "agentrun provider-profile apply",
mode: stage === "async-submit" ? "activation-preflight-blocked-before-job" : "activation-preflight-blocked-before-secret-sync",
status: "blocked",
degradedReason: activationPreflight.reason ?? "existing-runner-provider-secret-activation-risk",
phase: "activation-preflight",
stage,
mutation: false,
blockedBeforeMutation: true,
jobCreated: stage === "worker-before-secret-sync",
jobAlreadyRunning: stage === "worker-before-secret-sync",
secretSyncStarted: false,
target: { node: spec.nodeId, lane: spec.lane, namespace: spec.runtime.namespace, valuesPrinted: false },
profile: providerProfileSummary(spec, profile, secrets),
activationPreflight,
stagedActivation: providerSecretStagedActivation(spec, [profile]),
next: {
observeRunners: `bun scripts/cli.ts agentrun control-plane cleanup-runners --node ${spec.nodeId} --lane ${spec.lane} --dry-run`,
retryAfterManagerFence: `bun scripts/cli.ts agentrun provider-profile apply --node ${spec.nodeId} --lane ${spec.lane} --profile ${profile} --confirm`,
},
valuesPrinted: false,
};
}
function providerProfileApplyProgress(
spec: AgentRunLaneSpec,
profile: string,
@@ -618,17 +555,6 @@ function inspectProviderProfileSources(spec: AgentRunLaneSpec, secrets: readonly
});
}
function providerProfileActivationTargets(items: readonly ProviderProfileInspectedSecret[]): ProviderSecretActivationTarget[] {
return items.map(({ secret, inspection }) => {
if (inspection.sourceValue === null) throw new Error(`provider profile source ${secret.id} is unavailable before activation preflight`);
return {
id: secret.id,
targetRef: secret.targetRef,
desiredEncodedFingerprint: providerSecretDesiredEncodedFingerprint(inspection.sourceValue.value),
};
});
}
function providerProfileSourceSummary(items: readonly ProviderProfileInspectedSecret[]): {
ready: boolean;
count: number;
+3 -99
View File
@@ -39,7 +39,6 @@ import { triggerCurrentYamlLaneConfirmed } from "./cleanup-scripts";
import { readAgentRunClientConfig } from "./config";
import { displayValue } from "./options";
import { pathValue } from "./render";
import { providerSecretActivationPreflight, providerSecretDesiredEncodedFingerprint, providerSecretStagedActivation } from "./provider-activation";
import { startAsyncAgentRunJob } from "./rest-bridge";
import { collectLaneSecretSources, inspectSecretSourceValue, restartYamlLaneScript, secretSyncScript } from "./secrets";
import { capture, captureJsonPayload, compactCapture, isGitSha, stringOrNull } from "./utils";
@@ -257,66 +256,7 @@ export async function secretSync(config: UniDeskConfig, options: SecretSyncOptio
};
}
const values = inspected.map(({ spec: item, inspection }) => ({ spec: item, value: inspection.sourceValue! }));
const providerSecretsByTargetRef = new Map<string, AgentRunLaneSpec["secrets"][number]>();
for (const secret of spec.secrets) {
if (secret.providerCredentialProfile === null) continue;
const targetRef = laneSecretTargetRefKey(secret.targetRef);
const existing = providerSecretsByTargetRef.get(targetRef);
if (existing !== undefined) {
throw new Error(`provider Secret targetRef ${targetRef} is declared by both ${existing.id} and ${secret.id}`);
}
providerSecretsByTargetRef.set(targetRef, secret);
}
const providerValues = values.filter(({ spec: item }) => providerSecretsByTargetRef.has(laneSecretTargetRefKey(item.targetRef)));
const providerSecrets = providerValues.map(({ spec: item }) => providerSecretsByTargetRef.get(laneSecretTargetRefKey(item.targetRef))!);
const providerActivationTargets = providerValues.map(({ spec: item, value }) => {
return {
id: item.id,
targetRef: item.targetRef,
desiredEncodedFingerprint: providerSecretDesiredEncodedFingerprint(value.value),
};
});
const activationPreflight = providerValues.length > 0
? await providerSecretActivationPreflight(config, spec, providerActivationTargets, { full: options.full || options.raw })
: null;
const syncValues = values.filter(({ spec: item }) => !providerSecretsByTargetRef.has(laneSecretTargetRefKey(item.targetRef)));
if (activationPreflight !== null && activationPreflight.ok !== true) {
const profiles = providerSecrets
.map((secret) => secret.providerCredentialProfile)
.filter((profile): profile is string => profile !== null);
return {
ok: false,
command: "agentrun control-plane secret-sync",
mode: options.dryRun || !options.confirm ? "dry-run-blocked-before-mutation" : "confirm-blocked-before-mutation",
status: "blocked",
mutation: false,
blockedBeforeMutation: true,
secretSyncStarted: false,
configPath,
target: { node: spec.nodeId, lane: spec.lane, namespace: spec.runtime.namespace, valuesPrinted: false },
degradedReason: activationPreflight.reason ?? "existing-runner-provider-secret-activation-risk",
filter: options.secretIds.length === 0 ? null : { secretIds: options.secretIds, selected: sources.map((source) => source.id) },
plan: {
secretCount: plan.length,
items: plan,
omittedCount: 0,
valuesPrinted: false,
},
activationPreflight,
execution: {
providerSecretClassification: "exact-target-ref",
providerSecretWriteBlockedCount: providerValues.length,
secretSyncStarted: false,
valuesPrinted: false,
},
stagedActivation: providerSecretStagedActivation(spec, profiles),
next: {
observeRunners: `bun scripts/cli.ts agentrun control-plane cleanup-runners --node ${spec.nodeId} --lane ${spec.lane} --dry-run`,
retryAfterManagerFence: `bun scripts/cli.ts agentrun control-plane secret-sync --node ${spec.nodeId} --lane ${spec.lane}${options.secretIds.map((id) => ` --secret-id ${id}`).join("")} --confirm`,
},
valuesPrinted: false,
};
}
const syncValues = values;
if (options.dryRun || !options.confirm) {
return {
ok: true,
@@ -326,12 +266,9 @@ export async function secretSync(config: UniDeskConfig, options: SecretSyncOptio
configPath,
target: compactAgentRunLaneStatusTarget(spec),
filter: options.secretIds.length === 0 ? null : { secretIds: options.secretIds, selected: sources.map((source) => source.id) },
plan: { secretCount: plan.length, items: plan, activationPreflight, valuesPrinted: false },
plan: { secretCount: plan.length, items: plan, valuesPrinted: false },
execution: {
providerSecretClassification: "exact-target-ref",
providerSecretWritePolicy: "skip-exact-no-op-until-manager-activation-fence",
providerSecretWriteSkippedCount: providerValues.length,
writableNonProviderSecretCount: syncValues.length,
writableSecretCount: syncValues.length,
valuesPrinted: false,
},
next: {
@@ -341,34 +278,6 @@ export async function secretSync(config: UniDeskConfig, options: SecretSyncOptio
valuesPrinted: false,
};
}
if (syncValues.length === 0) {
return {
ok: true,
command: "agentrun control-plane secret-sync",
mode: "confirmed-provider-secret-no-op-skip",
mutation: false,
secretSyncStarted: false,
providerSecretWriteSkipped: providerValues.length > 0,
configPath,
target: compactAgentRunLaneStatusTarget(spec),
filter: options.secretIds.length === 0 ? null : { secretIds: options.secretIds, selected: sources.map((source) => source.id) },
plan: { secretCount: plan.length, items: plan, valuesPrinted: false },
activationPreflight,
result: {
ok: true,
status: "skipped-exact-provider-secret-no-op",
skippedProviderSecretCount: providerValues.length,
providerSecretClassification: "exact-target-ref",
reason: "provider-secret-writes-require-agentrun-manager-activation-fence",
runtimeSecretValuesDecoded: false,
valuesPrinted: false,
},
next: {
status: `bun scripts/cli.ts agentrun control-plane status --node ${spec.nodeId} --lane ${spec.lane}`,
},
valuesPrinted: false,
};
}
const result = await capture(config, spec.nodeKubeRoute, ["sh", "--", secretSyncScript(spec, syncValues.map(({ spec: item, value }) => ({ targetRef: item.targetRef, value: value.value })))]);
const payload = captureJsonPayload(result);
return {
@@ -387,7 +296,6 @@ export async function secretSync(config: UniDeskConfig, options: SecretSyncOptio
writableNonProviderSecretCount: syncValues.length,
valuesPrinted: false,
},
activationPreflight,
result: payload,
capture: compactCapture(result, { full: result.exitCode !== 0, stdoutTailChars: 3000, stderrTailChars: 3000 }),
next: {
@@ -397,10 +305,6 @@ export async function secretSync(config: UniDeskConfig, options: SecretSyncOptio
};
}
function laneSecretTargetRefKey(targetRef: { namespace: string; name: string; key: string }): string {
return `${targetRef.namespace}/${targetRef.name}/${targetRef.key}`;
}
export async function exposeAgentRun(_config: UniDeskConfig, options: ConfirmOptions): Promise<Record<string, unknown>> {
const clientConfig = readAgentRunClientConfig();
const exposure = clientConfig.publicExposure;