From 1b026d01c4d69dab8b8b08f92cac8450d5e96307 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Tue, 9 Jun 2026 10:05:49 +0800 Subject: [PATCH] fix: use external postgres for v0.3 runtime lane --- deploy/deploy.schema.json | 17 ++++++++++ deploy/deploy.yaml | 5 +++ scripts/gitops-render.mjs | 65 ++++++++++++++++++++++++++++++++++--- scripts/src/runtime-lane.ts | 6 ++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/deploy/deploy.schema.json b/deploy/deploy.schema.json index 40ba9234..22b3273c 100644 --- a/deploy/deploy.schema.json +++ b/deploy/deploy.schema.json @@ -328,6 +328,7 @@ "type": "object", "additionalProperties": { "type": "string" } }, + "externalPostgres": { "$ref": "#/$defs/externalPostgres" }, "artifactCatalog": { "type": "string" }, "runtimePath": { "type": "string" }, "imageTagMode": { "type": "string", "enum": ["short", "full"] }, @@ -356,6 +357,22 @@ }, "additionalProperties": false }, + "externalPostgres": { + "type": "object", + "required": ["enabled", "serviceName", "endpointAddress", "port"], + "properties": { + "enabled": { "type": "boolean" }, + "serviceName": { + "type": "string", + "minLength": 1, + "maxLength": 63, + "pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$" + }, + "endpointAddress": { "type": "string", "minLength": 1 }, + "port": { "type": "integer", "minimum": 1, "maximum": 65535 } + }, + "additionalProperties": false + }, "serviceDeclaration": { "type": "object", "required": ["runtimeKind", "entrypoint", "componentPaths"], diff --git a/deploy/deploy.yaml b/deploy/deploy.yaml index bb6c1bf5..d7646642 100644 --- a/deploy/deploy.yaml +++ b/deploy/deploy.yaml @@ -249,6 +249,11 @@ lanes: artifactCatalog: deploy/artifact-catalog.v03.json runtimePath: runtime-v03 imageTagMode: full + externalPostgres: + enabled: true + serviceName: g14-platform-postgres + endpointAddress: 10.42.0.1 + port: 5432 envReuseServices: - hwlab-cloud-api - hwlab-cloud-web diff --git a/scripts/gitops-render.mjs b/scripts/gitops-render.mjs index d0f144e4..66bef5e4 100644 --- a/scripts/gitops-render.mjs +++ b/scripts/gitops-render.mjs @@ -4772,6 +4772,57 @@ function v02PostgresManifest({ profile = "v02", migrationSql, source }) { }; } +function externalPostgresConfigForLane(deploy, profile) { + if (!isRuntimeLane(profile)) return null; + const config = deploy?.lanes?.[profile]?.externalPostgres; + if (!config || config.enabled !== true) return null; + assert.ok(typeof config.serviceName === "string" && config.serviceName.length > 0, `deploy.lanes.${profile}.externalPostgres.serviceName is required`); + assert.ok(typeof config.endpointAddress === "string" && config.endpointAddress.length > 0, `deploy.lanes.${profile}.externalPostgres.endpointAddress is required`); + const port = Number(config.port ?? 5432); + assert.ok(Number.isInteger(port) && port > 0 && port <= 65535, `deploy.lanes.${profile}.externalPostgres.port must be a valid TCP port`); + return { serviceName: config.serviceName, endpointAddress: config.endpointAddress, port }; +} + +function externalPostgresManifest({ profile = "v03", config, source }) { + const namespace = namespaceNameForProfile(profile); + const labels = { + "app.kubernetes.io/name": config.serviceName, + "app.kubernetes.io/part-of": "hwlab", + "hwlab.pikastech.local/component": "platform-db-bridge", + "hwlab.pikastech.local/environment": profile, + "hwlab.pikastech.local/gitops-target": profile, + "hwlab.pikastech.local/profile": profile, + "hwlab.pikastech.local/source-commit": source.full + }; + const annotations = { "hwlab.pikastech.local/rendered-by": "scripts/gitops-render.mjs" }; + return { + apiVersion: "v1", + kind: "List", + items: [ + { + apiVersion: "v1", + kind: "Service", + metadata: { name: config.serviceName, namespace, labels, annotations }, + spec: { type: "ClusterIP", ports: [{ name: "postgres", port: config.port, targetPort: config.port, protocol: "TCP" }] } + }, + { + apiVersion: "v1", + kind: "Endpoints", + metadata: { name: config.serviceName, namespace, labels, annotations }, + subsets: [{ addresses: [{ ip: config.endpointAddress }], ports: [{ name: "postgres", port: config.port, protocol: "TCP" }] }] + }, + { + apiVersion: "discovery.k8s.io/v1", + kind: "EndpointSlice", + metadata: { name: `${config.serviceName}-host`, namespace, labels: { ...labels, "kubernetes.io/service-name": config.serviceName }, annotations }, + addressType: "IPv4", + ports: [{ name: "postgres", port: config.port, protocol: "TCP" }], + endpoints: [{ addresses: [config.endpointAddress], conditions: { ready: true } }] + } + ] + }; +} + function v02OpenFgaManifest({ profile = "v02", source }) { const namespace = namespaceNameForProfile(profile); const name = "hwlab-openfga"; @@ -4863,10 +4914,14 @@ function v02OpenFgaManifest({ profile = "v02", source }) { }; } -function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev" } = {}) { +function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev", externalPostgres = false } = {}) { const resources = ["namespace.yaml", "services.yaml", "health-contract.yaml", "workloads.yaml", "deepseek-proxy.yaml"]; if (!isRuntimeLane(profile)) resources.splice(1, 0, "code-agent-codex-config.yaml"); - if (isRuntimeLane(profile)) resources.push("postgres.yaml", "openfga.yaml", "observability.yaml"); + if (isRuntimeLane(profile)) { + if (externalPostgres) resources.push("external-postgres.yaml"); + else resources.push("postgres.yaml"); + resources.push("openfga.yaml", "observability.yaml"); + } if (includeDeviceAgent) resources.push("device-agent-71-freq.yaml"); resources.push("node-frpc.yaml"); return { @@ -4998,6 +5053,7 @@ async function plannedFiles(args) { for (const profile of profiles) { const namespace = namespaceNameForProfile(profile); const includeDeviceAgent = profile === "dev"; + const externalPostgres = externalPostgresConfigForLane(deploy, profile); const profileLabels = { ...baseLabels, "hwlab.pikastech.local/environment": runtimeLabelForProfile(profile), "hwlab.pikastech.local/profile": runtimeLabelForProfile(profile) }; const runtimeNamespace = cloneJson(namespaceTemplate); runtimeNamespace.metadata.name = namespace; @@ -5005,14 +5061,15 @@ async function plannedFiles(args) { annotate(runtimeNamespace.metadata, annotations); const runtimePath = runtimePathForProfile(profile); const endpoints = profileEndpoint(args, profile); - putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent })); + putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent, externalPostgres: Boolean(externalPostgres) })); putJson(`${runtimePath}/namespace.yaml`, runtimeNamespace); if (!isRuntimeLane(profile)) putJson(`${runtimePath}/code-agent-codex-config.yaml`, transformListNamespace(config, namespace, profileLabels, annotations)); putJson(`${runtimePath}/services.yaml`, transformServices({ services, namespace, labels: profileLabels, annotations, profile, deploy })); putJson(`${runtimePath}/health-contract.yaml`, transformHealthContract(health, namespace, profileLabels, annotations, endpoints.runtimeEndpoint, endpoints.webEndpoint, profile)); putJson(`${runtimePath}/workloads.yaml`, transformWorkloads({ workloads, deploy, catalog: artifactCatalog, source, sourceBranch: args.sourceBranch, registryPrefix: args.registryPrefix, runtimeEndpoint: endpoints.runtimeEndpoint, webEndpoint: endpoints.webEndpoint, profile, useDeployImages: args.useDeployImages, metricsSidecarSha256 })); putJson(`${runtimePath}/deepseek-proxy.yaml`, deepSeekProxyManifest({ profile, source, sourceBranch: args.sourceBranch, sourceRepo: args.sourceRepo, deploy, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages, metricsSidecarSha256 })); - if (isRuntimeLane(profile)) putJson(`${runtimePath}/postgres.yaml`, v02PostgresManifest({ profile, migrationSql, source })); + if (isRuntimeLane(profile) && externalPostgres) putJson(`${runtimePath}/external-postgres.yaml`, externalPostgresManifest({ profile, config: externalPostgres, source })); + if (isRuntimeLane(profile) && !externalPostgres) putJson(`${runtimePath}/postgres.yaml`, v02PostgresManifest({ profile, migrationSql, source })); if (isRuntimeLane(profile)) putJson(`${runtimePath}/openfga.yaml`, v02OpenFgaManifest({ profile, source })); if (isRuntimeLane(profile)) putJson(`${runtimePath}/observability.yaml`, observabilityManifest({ deploy, profile, namespace, labels: profileLabels, annotations, metricsSidecarScript })); if (includeDeviceAgent) putJson(`${runtimePath}/device-agent-71-freq.yaml`, deviceAgent71FreqManifest({ profile, source, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages })); diff --git a/scripts/src/runtime-lane.ts b/scripts/src/runtime-lane.ts index 717b66db..4d3ecf50 100644 --- a/scripts/src/runtime-lane.ts +++ b/scripts/src/runtime-lane.ts @@ -33,6 +33,12 @@ export type RuntimeLaneConfig = { runtimePath?: string; imageTagMode?: string; endpoint?: string; + externalPostgres?: { + enabled?: boolean; + serviceName?: string; + endpointAddress?: string; + port?: number; + }; publicEndpoints?: { frontend?: string; api?: string;