fix: persist code agent workspace
This commit is contained in:
@@ -42,6 +42,11 @@ async function makeFixture({
|
||||
workloadCodeAgentProviderEnv = true,
|
||||
workloadCodexHomeVolumeMounts = true,
|
||||
workloadCodexHomeVolumes = true,
|
||||
workloadWorkspaceVolumeMount = true,
|
||||
workloadWorkspaceVolume = true,
|
||||
workloadWorkspaceInitContainer = true,
|
||||
workloadWorkspaceRecreateStrategy = true,
|
||||
workloadWorkspacePvc = true,
|
||||
workloadDbEnv = true,
|
||||
workloadDbSslMode = "disable"
|
||||
} = {}) {
|
||||
@@ -65,6 +70,8 @@ async function makeFixture({
|
||||
HWLAB_CODE_AGENT_CODEX_API_UPSTREAM_BASE_URL: "https://hyueapi.com",
|
||||
HWLAB_CODE_AGENT_CODEX_API_FORWARDER_PORT: "49280",
|
||||
HWLAB_CODE_AGENT_OPENAI_BASE_URL: providerBaseUrl,
|
||||
HWLAB_CODE_AGENT_WORKSPACE: "/workspace/hwlab",
|
||||
HWLAB_CODE_AGENT_CODEX_WORKSPACE: "/workspace/hwlab",
|
||||
CODEX_HOME: "/codex-home",
|
||||
NO_PROXY: providerNoProxy,
|
||||
no_proxy: providerNoProxy,
|
||||
@@ -84,6 +91,8 @@ async function makeFixture({
|
||||
{ name: "HWLAB_CODE_AGENT_CODEX_API_UPSTREAM_BASE_URL", value: "https://hyueapi.com" },
|
||||
{ name: "HWLAB_CODE_AGENT_CODEX_API_FORWARDER_PORT", value: "49280" },
|
||||
{ name: "HWLAB_CODE_AGENT_OPENAI_BASE_URL", value: providerBaseUrl },
|
||||
{ name: "HWLAB_CODE_AGENT_WORKSPACE", value: "/workspace/hwlab" },
|
||||
{ name: "HWLAB_CODE_AGENT_CODEX_WORKSPACE", value: "/workspace/hwlab" },
|
||||
{ name: "CODEX_HOME", value: "/codex-home" },
|
||||
{ name: "NO_PROXY", value: providerNoProxy },
|
||||
{ name: "no_proxy", value: providerNoProxy },
|
||||
@@ -175,6 +184,29 @@ async function makeFixture({
|
||||
apiVersion: "v1",
|
||||
kind: "List",
|
||||
items: [
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && workloadWorkspacePvc
|
||||
? [
|
||||
{
|
||||
apiVersion: "v1",
|
||||
kind: "PersistentVolumeClaim",
|
||||
metadata: {
|
||||
name: "hwlab-code-agent-workspace",
|
||||
namespace: "hwlab-dev",
|
||||
labels: {
|
||||
"hwlab.pikastech.local/service-id": "hwlab-code-agent-workspace"
|
||||
}
|
||||
},
|
||||
spec: {
|
||||
accessModes: ["ReadWriteOnce"],
|
||||
resources: {
|
||||
requests: {
|
||||
storage: "8Gi"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
apiVersion: "apps/v1",
|
||||
kind: "Deployment",
|
||||
@@ -186,8 +218,33 @@ async function makeFixture({
|
||||
}
|
||||
},
|
||||
spec: {
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && workloadWorkspaceRecreateStrategy
|
||||
? {
|
||||
strategy: {
|
||||
type: "Recreate"
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
template: {
|
||||
spec: {
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && workloadWorkspaceInitContainer
|
||||
? {
|
||||
initContainers: [
|
||||
{
|
||||
name: "hwlab-code-agent-workspace-init",
|
||||
image: workloadImage,
|
||||
command: [
|
||||
"sh",
|
||||
"-ceu",
|
||||
"target=/workspace/hwlab\nmarker=\"$target/.hwlab-workspace-initialized\"\nmkdir -p \"$target\"\nif [ ! -e \"$marker\" ]; then\n cp -a /app/. \"$target\"/\n printf '%s\\n' copied-from-image > \"$marker\"\nfi\n"
|
||||
],
|
||||
volumeMounts: [
|
||||
{ name: "hwlab-code-agent-workspace", mountPath: "/workspace" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
: {}),
|
||||
containers: [
|
||||
{
|
||||
name: serviceId,
|
||||
@@ -202,12 +259,19 @@ async function makeFixture({
|
||||
...workloadProviderEnv
|
||||
]
|
||||
: [],
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && workloadCodexHomeVolumeMounts
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && (workloadWorkspaceVolumeMount || workloadCodexHomeVolumeMounts)
|
||||
? {
|
||||
volumeMounts: [
|
||||
{ name: "hwlab-code-agent-codex-home", mountPath: "/codex-home" },
|
||||
{ name: "hwlab-code-agent-codex-config", mountPath: "/codex-home/config.toml", subPath: "config.toml", readOnly: true },
|
||||
{ name: "hwlab-code-agent-codex-auth", mountPath: "/codex-home/auth.json", subPath: "auth.json", readOnly: true }
|
||||
...(workloadWorkspaceVolumeMount
|
||||
? [{ name: "hwlab-code-agent-workspace", mountPath: "/workspace" }]
|
||||
: []),
|
||||
...(workloadCodexHomeVolumeMounts
|
||||
? [
|
||||
{ name: "hwlab-code-agent-codex-home", mountPath: "/codex-home" },
|
||||
{ name: "hwlab-code-agent-codex-config", mountPath: "/codex-home/config.toml", subPath: "config.toml", readOnly: true },
|
||||
{ name: "hwlab-code-agent-codex-auth", mountPath: "/codex-home/auth.json", subPath: "auth.json", readOnly: true }
|
||||
]
|
||||
: [])
|
||||
]
|
||||
}
|
||||
: {})
|
||||
@@ -230,12 +294,19 @@ async function makeFixture({
|
||||
]
|
||||
: [])
|
||||
],
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && workloadCodexHomeVolumes
|
||||
...(serviceId === "hwlab-cloud-api" && workloadCodeAgentProviderEnv && (workloadWorkspaceVolume || workloadCodexHomeVolumes)
|
||||
? {
|
||||
volumes: [
|
||||
{ name: "hwlab-code-agent-codex-home", emptyDir: {} },
|
||||
{ name: "hwlab-code-agent-codex-config", configMap: { name: "hwlab-code-agent-codex-config", items: [{ key: "config.toml", path: "config.toml" }] } },
|
||||
{ name: "hwlab-code-agent-codex-auth", secret: { secretName: "hwlab-code-agent-codex-auth", items: [{ key: "auth.json", path: "auth.json" }] } }
|
||||
...(workloadWorkspaceVolume
|
||||
? [{ name: "hwlab-code-agent-workspace", persistentVolumeClaim: { claimName: "hwlab-code-agent-workspace" } }]
|
||||
: []),
|
||||
...(workloadCodexHomeVolumes
|
||||
? [
|
||||
{ name: "hwlab-code-agent-codex-home", emptyDir: {} },
|
||||
{ name: "hwlab-code-agent-codex-config", configMap: { name: "hwlab-code-agent-codex-config", items: [{ key: "config.toml", path: "config.toml" }] } },
|
||||
{ name: "hwlab-code-agent-codex-auth", secret: { secretName: "hwlab-code-agent-codex-auth", items: [{ key: "auth.json", path: "auth.json" }] } }
|
||||
]
|
||||
: [])
|
||||
]
|
||||
}
|
||||
: {})
|
||||
@@ -307,6 +378,11 @@ test("passes when cloud-api preserves provider env and DEV egress contract", asy
|
||||
assert.equal(plan.codeAgentProvider.codexHome.emptyDirPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.configMapPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.codexHome.authSecretPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.workspace.present, true);
|
||||
assert.equal(plan.codeAgentProvider.workspace.writableMountPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.workspace.persistentVolumeClaimPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.workspace.initContainerPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.workspace.recreateStrategy, true);
|
||||
assert.deepEqual(plan.diagnostics, []);
|
||||
});
|
||||
|
||||
@@ -324,11 +400,39 @@ test("blocks when cloud-api CODEX_HOME mount is missing its writable emptyDir vo
|
||||
assert.ok(diagnostic.actual.missingCodexHomeContract.includes("/codex-home must be backed by emptyDir"));
|
||||
});
|
||||
|
||||
test("blocks when cloud-api Code Agent workspace is not backed by PVC", async () => {
|
||||
const repoRoot = await makeFixture({ workloadWorkspaceVolume: 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.workspace.present, false);
|
||||
assert.equal(plan.codeAgentProvider.workspace.writableMountPresent, true);
|
||||
assert.equal(plan.codeAgentProvider.workspace.persistentVolumeClaimPresent, false);
|
||||
assert.ok(diagnostic);
|
||||
assert.ok(diagnostic.actual.missingWorkspaceContract.includes("hwlab-code-agent-workspace must use PVC hwlab-code-agent-workspace"));
|
||||
});
|
||||
|
||||
test("blocks when cloud-api Code Agent workspace rollout strategy is not Recreate", async () => {
|
||||
const repoRoot = await makeFixture({ workloadWorkspaceRecreateStrategy: 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.workspace.present, false);
|
||||
assert.equal(plan.codeAgentProvider.workspace.recreateStrategy, false);
|
||||
assert.ok(diagnostic);
|
||||
assert.ok(diagnostic.actual.missingWorkspaceContract.includes("hwlab-cloud-api rollout strategy must be Recreate"));
|
||||
});
|
||||
|
||||
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) =>
|
||||
const deployment = workloads.items.find((entry) => entry.kind === "Deployment");
|
||||
deployment.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`);
|
||||
@@ -391,7 +495,8 @@ test("blocks when cloud-api workload points Code Agent directly at public OpenAI
|
||||
const repoRoot = await makeFixture();
|
||||
const workloadsPath = path.join(repoRoot, "deploy/k8s/base/workloads.yaml");
|
||||
const workloads = JSON.parse(await readFile(workloadsPath, "utf8"));
|
||||
const env = workloads.items[0].spec.template.spec.containers[0].env;
|
||||
const deployment = workloads.items.find((entry) => entry.kind === "Deployment");
|
||||
const env = deployment.spec.template.spec.containers[0].env;
|
||||
env.find((entry) => entry.name === "HWLAB_CODE_AGENT_OPENAI_BASE_URL").value = "https://api.openai.com/v1/responses";
|
||||
await writeFile(workloadsPath, `${JSON.stringify(workloads, null, 2)}\n`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user