From 55a9bab82de1a606d0fc250a5a3bca120c71b0fe Mon Sep 17 00:00:00 2001 From: pikastech Date: Tue, 21 Jul 2026 02:50:31 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A3=B0=E6=98=8E=20HWPOD=20host=20Pos?= =?UTF-8?q?tgreSQL=20=E6=8C=81=E4=B9=85=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/hwlab-node-lanes.yaml | 6 ++- config/platform-db/postgres-nc01.yaml | 48 ++++++++++++++++++++ config/secrets-distribution.yaml | 9 ++++ scripts/src/hwlab-native-development.test.ts | 9 +++- scripts/src/hwlab-native-development.ts | 28 +++++++++++- scripts/src/hwlab-node-lanes.ts | 24 +++++++++- 6 files changed, 118 insertions(+), 6 deletions(-) diff --git a/config/hwlab-node-lanes.yaml b/config/hwlab-node-lanes.yaml index 6d3e9f26..556d0bbd 100644 --- a/config/hwlab-node-lanes.yaml +++ b/config/hwlab-node-lanes.yaml @@ -217,7 +217,11 @@ lanes: sourceRef: hwlab/nc01-v03-admin.env sourceKey: HWLAB_API_KEY specRegistry: - runtimeDir: .state/hwpod-native/specs/runtime + database: + sourceRef: hwlab/nc01-v03-cloud-api-db.env + sourceKey: DATABASE_URL + schema: hwpod + table: runtime_specs builtInConfigRefs: - config/hwlab-hwpod-preinstalls/constart-71freq-c.yaml#hwpodPreinstall.specDocument publicExposure: diff --git a/config/platform-db/postgres-nc01.yaml b/config/platform-db/postgres-nc01.yaml index cd033dad..a772c70b 100644 --- a/config/platform-db/postgres-nc01.yaml +++ b/config/platform-db/postgres-nc01.yaml @@ -161,6 +161,11 @@ postgres: user: tasktree_production address: 10.42.0.0/16 method: scram-sha-256 + - type: hostssl + database: hwlab_nc01_v03 + user: hwlab_nc01_v03_app + address: 10.42.0.0/16 + method: scram-sha-256 - type: hostssl database: hwlab_nc01_production user: hwlab_nc01_production_app @@ -299,6 +304,20 @@ secrets: TASKTREE_PRODUCTION_DB_NAME: tasktree_production randomHex: TASKTREE_PRODUCTION_DB_PASSWORD: 32 + - name: hwlab-nc01-v03-db-credentials + sourceRef: platform-db/hwlab-nc01-v03-db.env + type: env + requiredKeys: + - HWLAB_NC01_V03_DB_USER + - HWLAB_NC01_V03_DB_PASSWORD + - HWLAB_NC01_V03_DB_NAME + createIfMissing: + enabled: true + values: + HWLAB_NC01_V03_DB_USER: hwlab_nc01_v03_app + HWLAB_NC01_V03_DB_NAME: hwlab_nc01_v03 + randomHex: + HWLAB_NC01_V03_DB_PASSWORD: 32 - name: hwlab-nc01-production-db-credentials sourceRef: platform-db/hwlab-nc01-production-db.env type: env @@ -397,6 +416,15 @@ objects: createdb: false createrole: false superuser: false + - name: hwlab_nc01_v03_app + passwordRef: + sourceRef: platform-db/hwlab-nc01-v03-db.env + key: HWLAB_NC01_V03_DB_PASSWORD + login: true + attributes: + createdb: false + createrole: false + superuser: false - name: hwlab_nc01_production_app passwordRef: sourceRef: platform-db/hwlab-nc01-production-db.env @@ -457,6 +485,11 @@ objects: encoding: UTF8 locale: C.UTF-8 extensions: [] + - name: hwlab_nc01_v03 + owner: hwlab_nc01_v03_app + encoding: UTF8 + locale: C.UTF-8 + extensions: [] - name: hwlab_nc01_production owner: hwlab_nc01_production_app encoding: UTF8 @@ -592,6 +625,21 @@ exports: - scope: hwlab-production secret: hwlab-tasktree-db key: DATABASE_URL + - name: hwlab-nc01-v03-cloud-api-database-url + sourceSecretRef: platform-db/hwlab-nc01-v03-db.env + render: + envKey: DATABASE_URL + format: postgresql://$(HWLAB_NC01_V03_DB_USER):$(HWLAB_NC01_V03_DB_PASSWORD)@$(PGHOST):5432/$(HWLAB_NC01_V03_DB_NAME)?sslmode=require + variables: + PGHOST: 10.42.0.1 + writeToSecretSource: + sourceRef: hwlab/nc01-v03-cloud-api-db.env + key: DATABASE_URL + mode: update-or-insert + consumers: + - scope: hwlab-v03 + secret: hwlab-cloud-api-v03-db + key: DATABASE_URL - name: hwlab-nc01-production-cloud-api-database-url sourceSecretRef: platform-db/hwlab-nc01-production-db.env render: diff --git a/config/secrets-distribution.yaml b/config/secrets-distribution.yaml index 15580b6b..09615f19 100644 --- a/config/secrets-distribution.yaml +++ b/config/secrets-distribution.yaml @@ -519,6 +519,15 @@ kubernetesSecrets: - sourceRef: hwlab/web-probe-monitor-db.env sourceKey: DATABASE_URL targetKey: DATABASE_URL + - name: hwlab-nc01-v03-cloud-api-db + targetId: hwlab-nc01-v03 + secretName: hwlab-cloud-api-v03-db + type: Opaque + namespace: hwlab-v03 + data: + - sourceRef: hwlab/nc01-v03-cloud-api-db.env + sourceKey: DATABASE_URL + targetKey: database-url - name: hwlab-tasktree-db targetId: hwlab-nc01-v03 secretName: hwlab-tasktree-db diff --git a/scripts/src/hwlab-native-development.test.ts b/scripts/src/hwlab-native-development.test.ts index 160f0aa7..2972db42 100644 --- a/scripts/src/hwlab-native-development.test.ts +++ b/scripts/src/hwlab-native-development.test.ts @@ -56,10 +56,15 @@ test("L1 Workbench exposure uses YAML HTTPS origin and separate bind/probe addre expect(hwlabNativeDevelopmentHelp()).toMatchObject({ ok: true, command: "hwlab nodes native-development workbench" }); }); -test("L1 HWPOD spec registry resolves runtime state and frozen built-ins from owning YAML", () => { +test("L1 HWPOD spec registry resolves PostgreSQL authority and frozen built-ins from owning YAML", () => { const native = hwlabRuntimeLaneSpecForNode("v03", "NC01").nativeDevelopment?.hwpod; expect(native).toBeDefined(); - expect(native!.specRegistry.runtimeDir).toBe(".state/hwpod-native/specs/runtime"); + expect(native!.specRegistry.database).toEqual({ + sourceRef: "hwlab/nc01-v03-cloud-api-db.env", + sourceKey: "DATABASE_URL", + schema: "hwpod", + table: "runtime_specs", + }); expect(native!.specRegistry.builtInConfigRefs).toHaveLength(1); const resolved = resolveConfigRef(native!.specRegistry.builtInConfigRefs[0], "HWPOD built-in spec"); expect(resolved.value).toMatchObject({ kind: "Hwpod", metadata: { name: "constart-71freq-c" } }); diff --git a/scripts/src/hwlab-native-development.ts b/scripts/src/hwlab-native-development.ts index 7b7b3d1a..c76264cd 100644 --- a/scripts/src/hwlab-native-development.ts +++ b/scripts/src/hwlab-native-development.ts @@ -59,6 +59,25 @@ export function runHwlabNativeDevelopmentCommand(args: string[]): Record 0) throw new Error(`native-development workbench status does not accept positional arguments: ${remaining.join(" ")}`); @@ -268,6 +287,11 @@ function hwlabHwpodNativeDevelopmentEnvironment(native: NonNullable { const resolved = resolveConfigRef(configRef, "nativeDevelopment.hwpod.specRegistry.builtInConfigRefs"); return { configRef, sha256: resolved.sha256, document: resolved.value }; @@ -284,7 +308,9 @@ function hwlabHwpodNativeDevelopmentEnvironment(native: NonNullable, key: string, path: string): s return value; } +function postgresIdentifierField(obj: Record, key: string, path: string): string { + const value = stringField(obj, key, path); + if (!/^[a-z_][a-z0-9_]*$/u.test(value)) throw new Error(`${path}.${key} must be a lowercase PostgreSQL identifier`); + return value; +} + function enumStringField(obj: Record, key: string, path: string, allowed: readonly T[]): T { const value = stringField(obj, key, path); if (!allowed.includes(value as T)) throw new Error(`${path}.${key} must be one of ${allowed.join(", ")}`); @@ -1624,6 +1638,7 @@ function nativeHwpodConfig(value: unknown, path: string): NonNullable