fix: restore codex-api via pod-local forwarder
This commit is contained in:
@@ -379,13 +379,20 @@ function validateCloudApiCodeAgentSource(ctx, env) {
|
||||
const contract = DEV_CODE_AGENT_PROVIDER_CONTRACT;
|
||||
expectEqual(ctx, env.HWLAB_CODE_AGENT_PROVIDER, contract.codeAgentProvider, "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_PROVIDER", "cloud API Code Agent provider");
|
||||
expectEqual(ctx, env.HWLAB_CODE_AGENT_MODEL, contract.model, "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_MODEL", "cloud API Code Agent model");
|
||||
expectEqual(ctx, env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE, contract.profiles.defaultProfile, "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE", "cloud API default provider profile");
|
||||
expectEqual(ctx, env[contract.profiles.deepSeekModelEnv], "deepseek-chat", `$.services.hwlab-cloud-api.env.${contract.profiles.deepSeekModelEnv}`, "cloud API DeepSeek profile model");
|
||||
expectEqual(ctx, env[contract.profiles.deepSeekBaseUrlEnv], contract.profiles.deepSeekBaseUrl, `$.services.hwlab-cloud-api.env.${contract.profiles.deepSeekBaseUrlEnv}`, "cloud API DeepSeek profile base URL");
|
||||
expectEqual(ctx, env[contract.profiles.codexApiModelEnv], contract.model, `$.services.hwlab-cloud-api.env.${contract.profiles.codexApiModelEnv}`, "cloud API codex-api profile model");
|
||||
expectEqual(ctx, env[contract.profiles.codexApiBaseUrlEnv], contract.egress.defaultBaseUrl, `$.services.hwlab-cloud-api.env.${contract.profiles.codexApiBaseUrlEnv}`, "cloud API codex-api profile base URL");
|
||||
expectEqual(ctx, env[contract.forwarder.upstreamEnv], contract.forwarder.upstreamBaseUrl, `$.services.hwlab-cloud-api.env.${contract.forwarder.upstreamEnv}`, "cloud API codex-api upstream base URL");
|
||||
expectEqual(ctx, env[contract.forwarder.portEnv], String(contract.forwarder.port), `$.services.hwlab-cloud-api.env.${contract.forwarder.portEnv}`, "cloud API codex-api forwarder port");
|
||||
expectEqual(ctx, env.HWLAB_CODE_AGENT_TIMEOUT_MS, codeAgentBackendTimeoutMs, "$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_TIMEOUT_MS", "cloud API Code Agent hard timeout budget");
|
||||
expectEqual(
|
||||
ctx,
|
||||
env.HWLAB_CODE_AGENT_OPENAI_BASE_URL,
|
||||
contract.egress.defaultBaseUrl,
|
||||
"$.services.hwlab-cloud-api.env.HWLAB_CODE_AGENT_OPENAI_BASE_URL",
|
||||
"cloud API Code Agent DEV egress/proxy base URL"
|
||||
"cloud API Code Agent pod-local Codex API forwarder base URL"
|
||||
);
|
||||
expectEqual(
|
||||
ctx,
|
||||
@@ -572,16 +579,24 @@ function validateCloudApiCodeAgentArtifacts(ctx, workloads) {
|
||||
const secretRef = contract.secretRefs[0];
|
||||
const container = mapByServiceId(listItems(workloads)).get("hwlab-cloud-api")?.spec?.template?.spec?.containers?.[0];
|
||||
const env = new Map((container?.env ?? []).map((entry) => [entry.name, entry]));
|
||||
const podContainers = mapByServiceId(listItems(workloads)).get("hwlab-cloud-api")?.spec?.template?.spec?.containers ?? [];
|
||||
const volumes = mapByServiceId(listItems(workloads)).get("hwlab-cloud-api")?.spec?.template?.spec?.volumes ?? [];
|
||||
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_PROVIDER")?.value, contract.codeAgentProvider, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_PROVIDER", "cloud API workload Code Agent provider");
|
||||
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_MODEL")?.value, contract.model, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_MODEL", "cloud API workload Code Agent model");
|
||||
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE")?.value, contract.profiles.defaultProfile, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_DEFAULT_PROVIDER_PROFILE", "cloud API workload default provider profile");
|
||||
expectEqual(ctx, env.get(contract.profiles.deepSeekModelEnv)?.value, "deepseek-chat", `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.${contract.profiles.deepSeekModelEnv}`, "cloud API workload DeepSeek profile model");
|
||||
expectEqual(ctx, env.get(contract.profiles.deepSeekBaseUrlEnv)?.value, contract.profiles.deepSeekBaseUrl, `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.${contract.profiles.deepSeekBaseUrlEnv}`, "cloud API workload DeepSeek profile base URL");
|
||||
expectEqual(ctx, env.get(contract.profiles.codexApiModelEnv)?.value, contract.model, `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.${contract.profiles.codexApiModelEnv}`, "cloud API workload codex-api profile model");
|
||||
expectEqual(ctx, env.get(contract.profiles.codexApiBaseUrlEnv)?.value, contract.egress.defaultBaseUrl, `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.${contract.profiles.codexApiBaseUrlEnv}`, "cloud API workload codex-api profile base URL");
|
||||
expectEqual(ctx, env.get(contract.forwarder.upstreamEnv)?.value, contract.forwarder.upstreamBaseUrl, `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.${contract.forwarder.upstreamEnv}`, "cloud API workload codex-api upstream base URL");
|
||||
expectEqual(ctx, env.get(contract.forwarder.portEnv)?.value, String(contract.forwarder.port), `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.${contract.forwarder.portEnv}`, "cloud API workload codex-api forwarder port");
|
||||
expectEqual(ctx, env.get("HWLAB_CODE_AGENT_TIMEOUT_MS")?.value, codeAgentBackendTimeoutMs, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_TIMEOUT_MS", "cloud API workload Code Agent hard timeout budget");
|
||||
expectEqual(
|
||||
ctx,
|
||||
env.get("HWLAB_CODE_AGENT_OPENAI_BASE_URL")?.value,
|
||||
contract.egress.defaultBaseUrl,
|
||||
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.HWLAB_CODE_AGENT_OPENAI_BASE_URL",
|
||||
"cloud API workload Code Agent DEV egress/proxy base URL"
|
||||
"cloud API workload Code Agent pod-local Codex API forwarder base URL"
|
||||
);
|
||||
expectEqual(
|
||||
ctx,
|
||||
@@ -599,6 +614,7 @@ function validateCloudApiCodeAgentArtifacts(ctx, workloads) {
|
||||
expectEqual(ctx, env.get("CODEX_HOME")?.value, "/codex-home", "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.CODEX_HOME", "cloud API workload CODEX_HOME contract");
|
||||
expectNoProxyIncludes(ctx, env.get("NO_PROXY")?.value, contract.noProxyRequired, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.NO_PROXY");
|
||||
expectNoProxyIncludes(ctx, env.get("no_proxy")?.value, contract.noProxyRequired, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.env.no_proxy");
|
||||
validateCodexApiForwarderArtifacts(ctx, podContainers, contract);
|
||||
validateCodexHomeMountArtifacts(ctx, container?.volumeMounts ?? [], volumes, contract);
|
||||
expect(
|
||||
ctx,
|
||||
@@ -622,6 +638,26 @@ function expectNoProxyIncludes(ctx, value, required, path, label = "NO_PROXY con
|
||||
}
|
||||
}
|
||||
|
||||
function validateCodexApiForwarderArtifacts(ctx, containers, contract) {
|
||||
const sidecar = (containers ?? []).find((entry) => entry.name === contract.forwarder.containerName);
|
||||
expect(ctx, Boolean(sidecar), "codex_api_forwarder_missing", "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder", "cloud API pod includes codex-api loopback forwarder sidecar");
|
||||
if (!sidecar) return;
|
||||
expect(ctx, JSON.stringify(sidecar.command) === JSON.stringify(contract.forwarder.command), "codex_api_forwarder_command_mismatch", "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder.command", "codex-api forwarder command");
|
||||
expect(
|
||||
ctx,
|
||||
(sidecar.ports ?? []).some((entry) => entry.name === contract.forwarder.portName && entry.containerPort === contract.forwarder.port),
|
||||
"codex_api_forwarder_port_missing",
|
||||
"deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder.ports",
|
||||
"codex-api forwarder exposes only a Pod-local container port"
|
||||
);
|
||||
const env = new Map((sidecar.env ?? []).map((entry) => [entry.name, entry.value]));
|
||||
expectEqual(ctx, env.get(contract.forwarder.listenHostEnv), "127.0.0.1", `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder.env.${contract.forwarder.listenHostEnv}`, "codex-api forwarder loopback listen host env");
|
||||
expectEqual(ctx, env.get(contract.forwarder.upstreamEnv), contract.forwarder.upstreamBaseUrl, `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder.env.${contract.forwarder.upstreamEnv}`, "codex-api forwarder upstream env");
|
||||
expectEqual(ctx, env.get(contract.forwarder.portEnv), String(contract.forwarder.port), `deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder.env.${contract.forwarder.portEnv}`, "codex-api forwarder port env");
|
||||
expectNoProxyIncludes(ctx, env.get("NO_PROXY"), contract.noProxyRequired, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder.env.NO_PROXY", "codex-api forwarder NO_PROXY contract");
|
||||
expectNoProxyIncludes(ctx, env.get("no_proxy"), contract.noProxyRequired, "deploy/k8s/base/workloads.yaml.hwlab-cloud-api.containers.hwlab-codex-api-forwarder.env.no_proxy", "codex-api forwarder no_proxy contract");
|
||||
}
|
||||
|
||||
function validateCodexHomeMountArtifacts(ctx, volumeMounts, volumes, contract) {
|
||||
expect(
|
||||
ctx,
|
||||
|
||||
@@ -201,7 +201,7 @@ function recordObservation(ctx, observation) {
|
||||
}
|
||||
|
||||
function expectedEnvValue(name, service, desiredCommitId) {
|
||||
if (name === "HWLAB_COMMIT_ID" || name === "HWLAB_SKILLS_COMMIT_ID") return desiredCommitId;
|
||||
if (name === "HWLAB_COMMIT_ID" || name === "HWLAB_SKILLS_COMMIT_ID") return service?.commitId ?? service?.imageTag ?? desiredCommitId;
|
||||
if (name === "HWLAB_IMAGE") return service?.image ?? null;
|
||||
if (name === "HWLAB_IMAGE_TAG") return service?.imageTag ?? null;
|
||||
if (name === "HWLAB_IMAGE_DIGEST") return service?.digest ?? "not_published";
|
||||
@@ -306,7 +306,8 @@ function inspectCodeAgentProviderDesiredState(ctx, deployService, workloadRecord
|
||||
missingSecretRefs: inspection.missingSecretRefs,
|
||||
missingEgressContract: inspection.missingEgressContract,
|
||||
missingCodexHomeContract: inspection.missingCodexHomeContract,
|
||||
missingNoProxyContract: inspection.missingNoProxyContract
|
||||
missingNoProxyContract: inspection.missingNoProxyContract,
|
||||
missingForwarderContract: inspection.missingForwarderContract
|
||||
},
|
||||
secretMaterialRead: false,
|
||||
valuesRedacted: true
|
||||
@@ -326,8 +327,8 @@ function inspectCodeAgentProviderDesiredState(ctx, deployService, workloadRecord
|
||||
serviceId: "hwlab-cloud-api",
|
||||
path: `${workloadsPath}.hwlab-cloud-api.env.HWLAB_CODE_AGENT_OPENAI_BASE_URL`,
|
||||
field: "HWLAB_CODE_AGENT_OPENAI_BASE_URL",
|
||||
kind: "egress-proxy",
|
||||
value: inspection.egress.workloadMatchesDevProxy ? "dev-egress-proxy" : "missing_or_mismatched",
|
||||
kind: "codex-api-forwarder",
|
||||
value: inspection.egress.workloadMatchesDevProxy ? "pod-local-codex-api-forwarder" : "missing_or_mismatched",
|
||||
expected: DEV_CODE_AGENT_PROVIDER_CONTRACT.egress.target
|
||||
});
|
||||
return inspection;
|
||||
@@ -443,7 +444,8 @@ function indexWorkloads(workloads) {
|
||||
env: {
|
||||
...envListToObject(container?.env),
|
||||
__volumeMounts: Array.isArray(container?.volumeMounts) ? container.volumeMounts : [],
|
||||
__volumes: Array.isArray(item?.spec?.template?.spec?.volumes) ? item.spec.template.spec.volumes : []
|
||||
__volumes: Array.isArray(item?.spec?.template?.spec?.volumes) ? item.spec.template.spec.volumes : [],
|
||||
__podContainers: Array.isArray(item?.spec?.template?.spec?.containers) ? item.spec.template.spec.containers : []
|
||||
},
|
||||
path: `${workloadsPath}.${item?.kind ?? "Workload"}.${item?.metadata?.name ?? serviceId}.containers[${containerIndex}]`
|
||||
};
|
||||
@@ -796,10 +798,12 @@ export async function buildDesiredStatePlan(options = {}) {
|
||||
|
||||
const deployImage = parseImageRef(deployService?.image);
|
||||
const catalogImage = parseImageRef(catalogService?.image);
|
||||
const serviceImageTag = catalogService?.imageTag ?? deployImage.tag;
|
||||
const serviceImageTag = catalogService?.imageTag ?? deployImage.tag ?? catalogImage.tag ?? desiredImageTag;
|
||||
const serviceCommitId = catalogService?.commitId ?? serviceImageTag;
|
||||
const expectedService = {
|
||||
image: deployService?.image ?? catalogService?.image ?? null,
|
||||
imageTag: desiredImageTag,
|
||||
imageTag: serviceImageTag,
|
||||
commitId: serviceCommitId,
|
||||
digest: catalogService?.digest ?? "not_published"
|
||||
};
|
||||
|
||||
@@ -816,8 +820,8 @@ export async function buildDesiredStatePlan(options = {}) {
|
||||
});
|
||||
if (!deployImage.valid) {
|
||||
addMismatch(ctx, "invalid_image", `${deployPathBase}.image`, deployImage.reason, "tagged image ref", deployService.image, { serviceId });
|
||||
} else if (deployImage.tag !== desiredImageTag) {
|
||||
addMismatch(ctx, "image_tag_mismatch", `${deployPathBase}.image`, `${serviceId} deploy image tag must match deploy commit tag`, desiredImageTag, deployImage.tag, { serviceId });
|
||||
} else if (deployImage.tag !== serviceImageTag) {
|
||||
addMismatch(ctx, "image_tag_mismatch", `${deployPathBase}.image`, `${serviceId} deploy image tag must match service artifact tag`, serviceImageTag, deployImage.tag, { serviceId });
|
||||
}
|
||||
service.deploy = {
|
||||
image: deployService.image,
|
||||
@@ -843,7 +847,7 @@ export async function buildDesiredStatePlan(options = {}) {
|
||||
field: "commitId",
|
||||
kind: "commit",
|
||||
value: catalogService.commitId,
|
||||
expected: desiredCommitId
|
||||
expected: serviceCommitId
|
||||
});
|
||||
recordObservation(ctx, {
|
||||
source: "catalog",
|
||||
@@ -861,16 +865,18 @@ export async function buildDesiredStatePlan(options = {}) {
|
||||
field: "imageTag",
|
||||
kind: "tag",
|
||||
value: catalogService.imageTag,
|
||||
expected: desiredImageTag
|
||||
expected: serviceImageTag
|
||||
});
|
||||
if (!commitEquivalent(catalogService.commitId, desiredCommitId)) {
|
||||
addMismatch(ctx, "commit_mismatch", `${catalogPathBase}.commitId`, `${serviceId} catalog service commitId must match deploy commitId`, desiredCommitId, catalogService.commitId, { serviceId });
|
||||
if (!commitPattern.test(String(catalogService.commitId ?? ""))) {
|
||||
addMismatch(ctx, "invalid_commit", `${catalogPathBase}.commitId`, `${serviceId} catalog service commitId must be a short or full lowercase Git SHA`, "7-40 lowercase hex", catalogService.commitId, { serviceId });
|
||||
} else if (!commitEquivalent(catalogService.commitId, serviceImageTag)) {
|
||||
addMismatch(ctx, "commit_mismatch", `${catalogPathBase}.commitId`, `${serviceId} catalog service commitId must match service artifact tag`, serviceImageTag, catalogService.commitId, { serviceId });
|
||||
}
|
||||
if (!catalogImage.valid) {
|
||||
addMismatch(ctx, "invalid_image", `${catalogPathBase}.image`, catalogImage.reason, "tagged image ref", catalogService.image, { serviceId });
|
||||
}
|
||||
if (catalogService.imageTag !== desiredImageTag) {
|
||||
addMismatch(ctx, "image_tag_mismatch", `${catalogPathBase}.imageTag`, `${serviceId} catalog imageTag must match deploy commit tag`, desiredImageTag, catalogService.imageTag, { serviceId });
|
||||
if (catalogService.imageTag !== serviceImageTag) {
|
||||
addMismatch(ctx, "image_tag_mismatch", `${catalogPathBase}.imageTag`, `${serviceId} catalog imageTag must match service artifact tag`, serviceImageTag, catalogService.imageTag, { serviceId });
|
||||
}
|
||||
if (catalogImage.valid && catalogImage.tag !== catalogService.imageTag) {
|
||||
addMismatch(ctx, "image_tag_mismatch", `${catalogPathBase}.image`, `${serviceId} catalog image tag must match catalog imageTag`, catalogService.imageTag, catalogImage.tag, { serviceId });
|
||||
@@ -901,8 +907,8 @@ export async function buildDesiredStatePlan(options = {}) {
|
||||
});
|
||||
if (!workloadImage.valid) {
|
||||
addMismatch(ctx, "invalid_image", `${workload.path}.image`, workloadImage.reason, "tagged image ref", workload.image, { serviceId });
|
||||
} else if (workloadImage.tag !== desiredImageTag) {
|
||||
addMismatch(ctx, "image_tag_mismatch", `${workload.path}.image`, `${serviceId} workload image tag must match deploy commit tag`, desiredImageTag, workloadImage.tag, { serviceId });
|
||||
} else if (workloadImage.tag !== serviceImageTag) {
|
||||
addMismatch(ctx, "image_tag_mismatch", `${workload.path}.image`, `${serviceId} workload image tag must match service artifact tag`, serviceImageTag, workloadImage.tag, { serviceId });
|
||||
}
|
||||
if (deployService?.image && workload.image !== deployService.image) {
|
||||
addMismatch(ctx, "image_mismatch", `${workload.path}.image`, `${serviceId} workload image must match deploy image`, deployService.image, workload.image, { serviceId });
|
||||
|
||||
@@ -41,6 +41,7 @@ export const DEFAULT_GITOPS_ONLY_PATHS = Object.freeze([
|
||||
const serviceSpecificPaths = Object.freeze({
|
||||
"hwlab-cloud-api": [
|
||||
"cmd/hwlab-cloud-api/",
|
||||
"cmd/hwlab-codex-api-responses-forwarder/",
|
||||
"cmd/hwlab-deepseek-responses-bridge/",
|
||||
"internal/cloud/",
|
||||
"internal/db/",
|
||||
|
||||
Reference in New Issue
Block a user