fix: 校验 Code Agent Codex home 挂载契约
This commit is contained in:
@@ -98,6 +98,12 @@ export function inspectCodeAgentProviderManifestRefs({ deployEnv = {}, workloadE
|
||||
if (deployEnv?.CODEX_HOME !== contract.codexHome.path || workloadInspection.codexHome.present !== true) {
|
||||
missingCodexHomeContract.push(`CODEX_HOME must be ${contract.codexHome.path}`);
|
||||
}
|
||||
if (workloadInspection.codexHome.writableHomeMountPresent !== true) {
|
||||
missingCodexHomeContract.push(`${contract.codexHome.path} must be a writable volumeMount`);
|
||||
}
|
||||
if (workloadInspection.codexHome.emptyDirPresent !== true) {
|
||||
missingCodexHomeContract.push(`${contract.codexHome.path} must be backed by emptyDir`);
|
||||
}
|
||||
if (workloadInspection.codexHome.configMapPresent !== true) {
|
||||
missingCodexHomeContract.push(`${contract.codexHome.configMapName}/${contract.codexHome.configKey}`);
|
||||
}
|
||||
@@ -424,6 +430,13 @@ function inspectCodexHomeWorkloadMounts(workloadEnv = {}) {
|
||||
const volumeMounts = Array.isArray(workloadEnv.__volumeMounts) ? workloadEnv.__volumeMounts : [];
|
||||
const volumes = Array.isArray(workloadEnv.__volumes) ? workloadEnv.__volumes : [];
|
||||
const homeMount = volumeMounts.find((entry) => entry?.name === "hwlab-code-agent-codex-home" && entry?.mountPath === contract.codexHome.path);
|
||||
const writableHomeMount = homeMount && homeMount.readOnly !== true;
|
||||
const homeVolume = volumes.find((entry) =>
|
||||
entry?.name === "hwlab-code-agent-codex-home" &&
|
||||
entry?.emptyDir &&
|
||||
typeof entry.emptyDir === "object" &&
|
||||
!Array.isArray(entry.emptyDir)
|
||||
);
|
||||
const configMount = volumeMounts.find((entry) =>
|
||||
entry?.name === "hwlab-code-agent-codex-config" &&
|
||||
entry?.mountPath === `${contract.codexHome.path}/${contract.codexHome.configKey}` &&
|
||||
@@ -448,8 +461,9 @@ function inspectCodexHomeWorkloadMounts(workloadEnv = {}) {
|
||||
);
|
||||
return {
|
||||
path: contract.codexHome.path,
|
||||
present: Boolean(homeMount),
|
||||
writableHomeMountPresent: Boolean(homeMount),
|
||||
present: Boolean(writableHomeMount && homeVolume),
|
||||
writableHomeMountPresent: Boolean(writableHomeMount),
|
||||
emptyDirPresent: Boolean(homeVolume),
|
||||
configMapPresent: Boolean(configMount && configVolume),
|
||||
authSecretPresent: Boolean(authMount && authVolume),
|
||||
configMapName: contract.codexHome.configMapName,
|
||||
|
||||
@@ -39,6 +39,8 @@ async function makeFixture({
|
||||
workloadEnvMirrors = true,
|
||||
workloadSkillsCommitId = null,
|
||||
workloadCodeAgentProviderEnv = true,
|
||||
workloadCodexHomeVolumeMounts = true,
|
||||
workloadCodexHomeVolumes = true,
|
||||
workloadDbEnv = true,
|
||||
workloadDbSslMode = "disable"
|
||||
} = {}) {
|
||||
@@ -184,7 +186,7 @@ async function makeFixture({
|
||||
...workloadProviderEnv
|
||||
]
|
||||
: [],
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && workloadCodexHomeVolumeMounts
|
||||
? {
|
||||
volumeMounts: [
|
||||
{ name: "hwlab-code-agent-codex-home", mountPath: "/codex-home" },
|
||||
@@ -195,7 +197,7 @@ async function makeFixture({
|
||||
: {})
|
||||
}
|
||||
],
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && workloadCodexHomeVolumes
|
||||
? {
|
||||
volumes: [
|
||||
{ name: "hwlab-code-agent-codex-home", emptyDir: {} },
|
||||
@@ -248,11 +250,48 @@ test("passes when cloud-api preserves provider env and DEV egress contract", asy
|
||||
assert.equal(plan.codeAgentProvider.secretRef.present, true);
|
||||
assert.equal(plan.codeAgentProvider.egress.deployMatchesDevProxy, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.present, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.writableHomeMountPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.emptyDirPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.configMapPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.authSecretPresent, true);
|
||||
assert.deepEqual(plan.diagnostics, []);
|
||||
});
|
||||
|
||||
test("blocks when cloud-api CODEX_HOME mount is missing its writable emptyDir volume", async () => {
|
||||
const repoRoot = await makeFixture({ workloadCodexHomeVolumes: false });
|
||||
const plan = await buildDesiredStatePlan({ repoRoot });
|
||||
const diagnostic = plan.diagnostics.find((entry) => entry.code === "code_agent_provider_contract_mismatch");
|
||||
|
||||
assert.equal(plan.status, "blocked");
|
||||
assert.equal(plan.codeAgentProvider.ready, false);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.present, false);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.writableHomeMountPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.emptyDirPresent, false);
|
||||
assert.ok(diagnostic);
|
||||
assert.ok(diagnostic.actual.missingCodexHomeContract.includes("/codex-home must be backed by emptyDir"));
|
||||
});
|
||||
|
||||
test("blocks when cloud-api CODEX_HOME mount is read-only", async () => {
|
||||
const repoRoot = await makeFixture();
|
||||
const workloadsPath = path.join(repoRoot, "deploy/k8s/base/workloads.yaml");
|
||||
const workloads = JSON.parse(await readFile(workloadsPath, "utf8"));
|
||||
workloads.items[0].spec.template.spec.containers[0].volumeMounts.find((entry) =>
|
||||
entry.name === "hwlab-code-agent-codex-home"
|
||||
).readOnly = true;
|
||||
await writeFile(workloadsPath, `${JSON.stringify(workloads, null, 2)}\n`);
|
||||
|
||||
const plan = await buildDesiredStatePlan({ repoRoot });
|
||||
const diagnostic = plan.diagnostics.find((entry) => entry.code === "code_agent_provider_contract_mismatch");
|
||||
|
||||
assert.equal(plan.status, "blocked");
|
||||
assert.equal(plan.codeAgentProvider.ready, false);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.present, false);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.writableHomeMountPresent, false);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.emptyDirPresent, true);
|
||||
assert.ok(diagnostic);
|
||||
assert.ok(diagnostic.actual.missingCodexHomeContract.includes("/codex-home must be a writable volumeMount"));
|
||||
});
|
||||
|
||||
test("blocks when cloud-api DB SSL mode drifts back to require", async () => {
|
||||
const repoRoot = await makeFixture({
|
||||
deployDbSslMode: "require",
|
||||
|
||||
@@ -304,7 +304,9 @@ function inspectCodeAgentProviderDesiredState(ctx, deployService, workloadRecord
|
||||
deployMismatches: inspection.deployMismatches.map((entry) => entry.name),
|
||||
workloadMismatches: inspection.workloadMismatches.map((entry) => entry.name),
|
||||
missingSecretRefs: inspection.missingSecretRefs,
|
||||
missingEgressContract: inspection.missingEgressContract
|
||||
missingEgressContract: inspection.missingEgressContract,
|
||||
missingCodexHomeContract: inspection.missingCodexHomeContract,
|
||||
missingNoProxyContract: inspection.missingNoProxyContract
|
||||
},
|
||||
secretMaterialRead: false,
|
||||
valuesRedacted: true
|
||||
|
||||
Reference in New Issue
Block a user