feat: 支持 runner tool credential 装配
This commit is contained in:
+55
-4
@@ -33,6 +33,14 @@ interface CredentialProjection {
|
||||
projectionMountPath: string;
|
||||
}
|
||||
|
||||
interface ToolCredentialProjection {
|
||||
tool: string;
|
||||
purpose: string | null;
|
||||
secretRef: SecretRef;
|
||||
envName: string;
|
||||
secretKey: string;
|
||||
}
|
||||
|
||||
export function renderRunnerJobDryRun(options: RunnerJobRenderOptions): JsonRecord {
|
||||
const render = renderRunnerJobManifest({ ...options, dryRun: true });
|
||||
const manifest = redactTransientEnvInManifest(render.manifest, options.transientEnv ?? []);
|
||||
@@ -56,6 +64,7 @@ export function renderRunnerJobDryRun(options: RunnerJobRenderOptions): JsonReco
|
||||
sourceCommit: render.sourceCommit,
|
||||
},
|
||||
secretRefs: render.secretRefs.map((item) => ({ profile: item.profile, name: item.secretRef.name, namespace: item.secretRef.namespace ?? render.namespace, keys: item.secretRef.keys ?? [], mountPath: item.runtimeMountPath, projectionPath: item.projectionMountPath, writableCopy: true, valuesPrinted: false })),
|
||||
toolCredentials: summarizeToolCredentials(render.toolCredentials, render.namespace),
|
||||
transientEnv: summarizeTransientEnv(options.transientEnv ?? []),
|
||||
retention: {
|
||||
ttlSecondsAfterFinished: render.ttlSecondsAfterFinished,
|
||||
@@ -69,7 +78,7 @@ export function renderRunnerJobDryRun(options: RunnerJobRenderOptions): JsonReco
|
||||
};
|
||||
}
|
||||
|
||||
export function renderRunnerJobManifest(options: RunnerJobRenderOptions): { manifest: JsonRecord; namespace: string; jobName: string; runnerId: string; attemptId: string; sourceCommit: string; serviceAccountName: string; secretRefs: CredentialProjection[]; warnings: string[]; ttlSecondsAfterFinished: number } {
|
||||
export function renderRunnerJobManifest(options: RunnerJobRenderOptions): { manifest: JsonRecord; namespace: string; jobName: string; runnerId: string; attemptId: string; sourceCommit: string; serviceAccountName: string; secretRefs: CredentialProjection[]; toolCredentials: ToolCredentialProjection[]; warnings: string[]; ttlSecondsAfterFinished: number } {
|
||||
const namespace = options.namespace ?? "agentrun-v01";
|
||||
const attemptId = options.attemptId ?? `attempt_${Date.now().toString(36)}`;
|
||||
const runnerId = options.runnerId ?? `runner_${shortHash(`${options.run.id}:${attemptId}:${options.commandId}`)}`;
|
||||
@@ -78,9 +87,10 @@ export function renderRunnerJobManifest(options: RunnerJobRenderOptions): { mani
|
||||
const ttlSecondsAfterFinished = options.ttlSecondsAfterFinished ?? 86_400;
|
||||
const jobName = `agentrun-v01-runner-${shortDnsHash(options.run.id, attemptId)}`;
|
||||
const secretRefs = credentialProjections(options.run, namespace);
|
||||
const toolCredentials = toolCredentialProjections(options.run, namespace);
|
||||
const warnings: string[] = [];
|
||||
if (secretRefs.length === 0) warnings.push("run executionPolicy.secretScope 未声明 provider SecretRef;runner 将按 secret-unavailable 上报,而不会降级直连外部凭据");
|
||||
const env = runnerEnv(options, { namespace, jobName, runnerId, attemptId, sourceCommit, secretRefs });
|
||||
const env = runnerEnv(options, { namespace, jobName, runnerId, attemptId, sourceCommit, secretRefs, toolCredentials });
|
||||
const manifest: JsonRecord = {
|
||||
apiVersion: "batch/v1",
|
||||
kind: "Job",
|
||||
@@ -136,10 +146,10 @@ export function renderRunnerJobManifest(options: RunnerJobRenderOptions): { mani
|
||||
},
|
||||
},
|
||||
};
|
||||
return { manifest, namespace, jobName, runnerId, attemptId, sourceCommit, serviceAccountName, secretRefs, warnings, ttlSecondsAfterFinished };
|
||||
return { manifest, namespace, jobName, runnerId, attemptId, sourceCommit, serviceAccountName, secretRefs, toolCredentials, warnings, ttlSecondsAfterFinished };
|
||||
}
|
||||
|
||||
function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string; jobName: string; runnerId: string; attemptId: string; sourceCommit: string; secretRefs: CredentialProjection[] }): JsonRecord[] {
|
||||
function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string; jobName: string; runnerId: string; attemptId: string; sourceCommit: string; secretRefs: CredentialProjection[]; toolCredentials: ToolCredentialProjection[] }): JsonRecord[] {
|
||||
const selectedSecret = context.secretRefs.find((item) => item.profile === options.run.backendProfile);
|
||||
const codexHome = selectedSecret?.runtimeMountPath ?? defaultRuntimeHome(options.run.backendProfile);
|
||||
return [
|
||||
@@ -161,10 +171,23 @@ function runnerEnv(options: RunnerJobRenderOptions, context: { namespace: string
|
||||
{ name: "HOME", value: "/home/agentrun" },
|
||||
{ name: "CODEX_HOME", value: codexHome },
|
||||
...(selectedSecret ? [{ name: "AGENTRUN_CODEX_SECRET_HOME", value: selectedSecret.projectionMountPath }] : []),
|
||||
...toolCredentialEnvVars(context.toolCredentials),
|
||||
...transientEnvVars(options.transientEnv ?? []),
|
||||
];
|
||||
}
|
||||
|
||||
function toolCredentialEnvVars(items: ToolCredentialProjection[]): JsonRecord[] {
|
||||
return items.map((item) => ({
|
||||
name: item.envName,
|
||||
valueFrom: {
|
||||
secretKeyRef: {
|
||||
name: item.secretRef.name,
|
||||
key: item.secretKey,
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
function transientEnvVars(items: RunnerTransientEnv[]): JsonRecord[] {
|
||||
return items.map((item) => ({ name: item.name, value: item.value }));
|
||||
}
|
||||
@@ -177,6 +200,22 @@ function summarizeTransientEnv(items: RunnerTransientEnv[]): JsonRecord {
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeToolCredentials(items: ToolCredentialProjection[], namespace: string): JsonRecord {
|
||||
return {
|
||||
count: items.length,
|
||||
items: items.map((item) => ({
|
||||
tool: item.tool,
|
||||
purpose: item.purpose,
|
||||
name: item.secretRef.name,
|
||||
namespace: item.secretRef.namespace ?? namespace,
|
||||
keys: item.secretRef.keys ?? [],
|
||||
projection: { kind: "env", envName: item.envName, secretKey: item.secretKey },
|
||||
valuesPrinted: false,
|
||||
})),
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
function redactTransientEnvInManifest(manifest: JsonRecord, items: RunnerTransientEnv[]): JsonRecord {
|
||||
if (items.length === 0) return manifest;
|
||||
const names = new Set(items.map((item) => item.name));
|
||||
@@ -206,6 +245,18 @@ function credentialProjections(run: RunRecord, namespace: string): CredentialPro
|
||||
}));
|
||||
}
|
||||
|
||||
function toolCredentialProjections(run: RunRecord, namespace: string): ToolCredentialProjection[] {
|
||||
const policy: ExecutionPolicy = run.executionPolicy;
|
||||
const credentials = policy.secretScope.toolCredentials ?? [];
|
||||
return credentials.map((item) => ({
|
||||
tool: item.tool,
|
||||
purpose: item.purpose ?? null,
|
||||
secretRef: item.secretRef.namespace ? item.secretRef : { ...item.secretRef, namespace },
|
||||
envName: item.projection.envName,
|
||||
secretKey: item.projection.secretKey ?? item.secretRef.keys?.[0] ?? item.projection.envName,
|
||||
}));
|
||||
}
|
||||
|
||||
function secretVolume(item: CredentialProjection): JsonRecord {
|
||||
const secret: JsonRecord = {
|
||||
secretName: item.secretRef.name,
|
||||
|
||||
Reference in New Issue
Block a user