chore: sync hwlab node control-plane state

Capture D601 v03 control-plane YAML/CLI changes, update CI/CD skill notes, and document the hwlab.pikapython.com admin password reset boundary with follow-up issue #319.
This commit is contained in:
Codex
2026-06-13 02:47:22 +00:00
parent 0c4a04a9dc
commit c5c1ff7f58
9 changed files with 59 additions and 822 deletions
+4 -1
View File
@@ -86,11 +86,14 @@ bun scripts/cli.ts hwlab nodes control-plane infra apply --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra apply --node D601 --lane v03 --confirm
bun scripts/cli.ts hwlab nodes control-plane infra tools-image status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra tools-image build --node D601 --lane v03 --confirm
bun scripts/cli.ts hwlab nodes control-plane infra runtime-image status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra runtime-image preload --node D601 --lane v03 --confirm
bun scripts/cli.ts hwlab nodes control-plane infra runtime-image logs --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra argo status --node D601 --lane v03
bun scripts/cli.ts hwlab nodes control-plane infra argo apply --node D601 --lane v03 --confirm
```
`config/hwlab-node-control-plane.yaml` 渲染 D601 HWLAB v03 的节点本地 CI/CD、git-mirror、Tekton 和 Argo 前置对象。confirmed apply 只做 control-plane bootstrap,不触发 runtime rollout,不创建 PK01 DB,也不修改 Caddy/FRP。node-local registry 镜像只能作为 tools image 输出 artifact;输入 base image 必须是 YAML 中声明的公开 registry 来源,缺失 output image 时通过 `status.next.blockers` 暴露。D601 Argo CD 安装也必须由 YAML 声明:官方 manifest URL、版本、镜像 rewrite/preload、CRD、期望 workload 和 AppProject/Application 都来自 YAML,不能使用手工 kubectl/argo CLI 作为正式安装路径。
`config/hwlab-node-control-plane.yaml` 渲染 D601 HWLAB v03 的节点本地 CI/CD、git-mirror、Tekton、runtime dependency image preload 和 Argo 前置对象。confirmed apply 只做 control-plane bootstrap,不触发 runtime rollout,不创建 PK01 DB,也不修改 Caddy/FRP。node-local registry 镜像只能作为 tools image 或 runtime dependency 的输出 artifact;输入 base/pull image 必须是 YAML 中声明的公开 registry 来源,缺失 output image 时通过 `status.next.blockers``runtime-image status` 暴露。D601 Argo CD 安装也必须由 YAML 声明:官方 manifest URL、版本、镜像 rewrite/preload、CRD、期望 workload 和 AppProject/Application 都来自 YAML,不能使用手工 kubectl/argo CLI 作为正式安装路径。
---
+10 -31
View File
@@ -56,34 +56,26 @@ targets:
repository: pikasTech/HWLAB
branch: v0.3
gitops:
branch: v0.3-gitops
branch: v0.3-d601-gitops
path: deploy/gitops/node/d601/runtime-v03
gitMirror:
namespace: devops-infra
serviceReadName: git-mirror-http
serviceWriteName: git-mirror-write
cachePvcName: hwlab-git-mirror-cache
cacheHostPath: /var/lib/rancher/k3s/storage/hwlab-d601-v03-git-mirror-cache
cachePvcStorage: 20Gi
servicePort: 8080
deploymentReplicas: 1
deploymentReplicas: 0
secretName: git-mirror-github-ssh
syncConfigMapName: git-mirror-sync-script
syncJobPrefix: git-mirror-hwlab-d601-v03-sync-manual
flushJobPrefix: git-mirror-hwlab-d601-v03-flush-manual
readUrl: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
writeUrl: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
storage:
localPath:
namespace: kube-system
configMapName: local-path-config
provisionerDeployment: local-path-provisioner
helperImage: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
imagePullPolicy: IfNotPresent
readUrl: http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git
writeUrl: http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git
tekton:
pipelineName: hwlab-v03-ci-image-publish
serviceAccountName: hwlab-v03-tekton-runner
pipelineRunPrefix: hwlab-v03-ci-poll
pipelineName: hwlab-d601-v03-ci-image-publish
serviceAccountName: hwlab-d601-v03-tekton-runner
pipelineRunPrefix: hwlab-d601-v03-ci-poll
toolsImage:
output: 127.0.0.1:5000/hwlab/hwlab-ci-node-tools:node22-alpine-bun-v1
sourceKind: dockerfile
@@ -132,22 +124,9 @@ targets:
buildMode: node-local
argo:
namespace: argocd
projectName: hwlab-v03
applicationName: hwlab-node-v03
applicationFile: application-v03.yaml
repoURL: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
obsoleteApplications:
- hwlab-d601-v03
resourceTracking:
manageEndpointBridge: true
repositoryCredential:
enabled: true
repoURL: git@github.com:pikasTech/HWLAB.git
secretName: hwlab-node-v03-repository
sourceSecret:
namespace: hwlab-ci
name: hwlab-git-ssh
key: ssh-privatekey
projectName: hwlab-d601
applicationName: hwlab-d601-v03
applicationFile: application-d601-v03.yaml
install:
enabled: true
sourceKind: url
+1 -73
View File
@@ -8,7 +8,6 @@ metadata:
relatedIssues:
- 280
- 281
- 1119
- 297
- 300
@@ -87,7 +86,7 @@ postgres:
purpose: admin-and-secret-sync
- id: D601-public
cidr: 36.49.29.73/32
purpose: platform-infra-and-hwlab-v03-app
purpose: platform-infra-standby-app
- id: G14-public
cidr: 202.98.17.68/32
purpose: platform-infra-runtime
@@ -121,11 +120,6 @@ postgres:
user: sub2api
address: 10.0.8.0/22
method: scram-sha-256
- type: hostssl
database: hwlab_d601_v03
user: hwlab_d601_v03_app
address: 10.0.8.0/22
method: scram-sha-256
- type: hostssl
database: sub2api
user: sub2api
@@ -146,11 +140,6 @@ postgres:
user: sub2api
address: 36.49.29.73/32
method: scram-sha-256
- type: hostssl
database: hwlab_d601_v03
user: hwlab_d601_v03_app
address: 36.49.29.73/32
method: scram-sha-256
- type: hostssl
database: langbot
user: langbot
@@ -230,20 +219,6 @@ secrets:
SUB2API_DB_NAME: sub2api
randomHex:
SUB2API_DB_PASSWORD: 32
- name: hwlab-d601-v03-db-credentials
sourceRef: platform-db/hwlab-d601-v03-db.env
type: env
requiredKeys:
- HWLAB_D601_V03_DB_USER
- HWLAB_D601_V03_DB_PASSWORD
- HWLAB_D601_V03_DB_NAME
createIfMissing:
enabled: true
values:
HWLAB_D601_V03_DB_USER: hwlab_d601_v03_app
HWLAB_D601_V03_DB_NAME: hwlab_d601_v03
randomHex:
HWLAB_D601_V03_DB_PASSWORD: 32
- name: langbot-db-credentials
sourceRef: platform-db/langbot-db.env
type: env
@@ -284,15 +259,6 @@ objects:
createdb: false
createrole: false
superuser: false
- name: hwlab_d601_v03_app
passwordRef:
sourceRef: platform-db/hwlab-d601-v03-db.env
key: HWLAB_D601_V03_DB_PASSWORD
login: true
attributes:
createdb: false
createrole: false
superuser: false
- name: langbot
passwordRef:
sourceRef: platform-db/langbot-db.env
@@ -317,11 +283,6 @@ objects:
encoding: UTF8
locale: C.UTF-8
extensions: []
- name: hwlab_d601_v03
owner: hwlab_d601_v03_app
encoding: UTF8
locale: C.UTF-8
extensions: []
- name: langbot
owner: langbot
encoding: UTF8
@@ -350,36 +311,6 @@ exports:
- scope: platform-infra
secret: sub2api-secrets
key: DATABASE_URL
- name: hwlab-d601-v03-cloud-api-database-url
sourceSecretRef: platform-db/hwlab-d601-v03-db.env
render:
envKey: DATABASE_URL
format: postgresql://$(HWLAB_D601_V03_DB_USER):$(HWLAB_D601_V03_DB_PASSWORD)@$(PGHOST):5432/$(HWLAB_D601_V03_DB_NAME)?sslmode=require&uselibpqcompat=true
variables:
PGHOST: 82.156.23.220
writeToSecretSource:
sourceRef: hwlab/d601-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-d601-v03-openfga-datastore-uri
sourceSecretRef: platform-db/hwlab-d601-v03-db.env
render:
envKey: DATASTORE_URI
format: postgresql://$(HWLAB_D601_V03_DB_USER):$(HWLAB_D601_V03_DB_PASSWORD)@$(PGHOST):5432/$(HWLAB_D601_V03_DB_NAME)?sslmode=require
variables:
PGHOST: 82.156.23.220
writeToSecretSource:
sourceRef: hwlab/d601-v03-openfga-db.env
key: DATASTORE_URI
mode: update-or-insert
consumers:
- scope: hwlab-v03
secret: hwlab-v03-openfga
key: datastore-uri
- name: langbot-database-url
sourceSecretRef: platform-db/langbot-db.env
render:
@@ -440,9 +371,6 @@ observability:
- kind: psql-app-role
database: sub2api
user: sub2api
- kind: psql-app-role
database: hwlab_d601_v03
user: hwlab_d601_v03_app
- kind: psql-app-role
database: langbot
user: langbot
+2 -2
View File
@@ -20,12 +20,12 @@ CI/CD、GitOps、rollout、artifact 发布、PR 合并后的 runtime lane 滚动
`hwlab nodes secret status|ensure --node G14 --lane v03 --name hwlab-v03-code-agent-provider` 是 v03 Code Agent / MoonBridge provider SecretRef 的受控 bootstrap 入口;`ensure` 只从集群内既有 `hwlab-v02/hwlab-v02-code-agent-provider` 复制 `openai-api-key``opencode-api-key` 到 lane-local Secret,输出仅披露 source/target Secret 名、key presence、decoded byte count、mutation 和后续命令,禁止打印 base64、解码值、完整 API key 或可复用凭据。OpenFGA 和 master admin API key 继续使用同一命名空间下的 `hwlab nodes secret ... --name hwlab-v03-openfga|hwlab-v03-master-server-admin-api-key`
`hwlab.pikapython.com` / D601 v03 的 bootstrap admin password 是 HWLAB runtime Secret 生命周期的一部分,必须收敛到 UniDesk YAML 与受控 `hwlab nodes secret ...` CLI;明文只能存在于 Git 忽略、owner-only 的 `.state/secrets/...` 来源文件,CLI、issue、日志和 trace 只能输出 presence、byte count、fingerprint、mutation 与后续命令。当前声明式重设能力缺口由 [GitHub issue #319](https://github.com/pikasTech/unidesk/issues/319) 追踪;不要把人工生成 hash、手工写 k8s Secret 或原生 `kubectl rollout` 沉淀为长期入口。
`hwlab nodes control-plane infra plan|status|apply --node D601 --lane v03` 是 D601 HWLAB v03 节点本地 CI/CD 与 git-mirror 前置控制面的 YAML 驱动入口,配置真相源是 `config/hwlab-node-control-plane.yaml``plan` 只读展示 YAML target 和将渲染的 control-plane 对象;`status` 只读观察 D601 Tekton、CI namespace、git-mirror、Argo、node-local registry 和 tools image readiness`apply --dry-run` 只输出 manifest 摘要;`apply --confirm` 只收敛 D601 control-plane bootstrap 对象,不触发 HWLAB runtime rollout,不创建 PK01 DB,也不修改 Caddy/FRP。tools image 的 node-local registry 地址只能作为输出 artifact;输入 base image 必须由 YAML 声明为公开 registry 来源,缺少 output image 时应在 `status.next.blockers` 中体现,而不是把现有 node-local image 当成输入基础镜像。
`hwlab nodes control-plane infra tools-image status|build|logs --node D601 --lane v03` 是 D601 tools image 的受控入口。Dockerfile 必须由 `config/hwlab-node-control-plane.yaml``tekton.toolsImage.dockerfileInline` 声明,输入镜像必须列在 `publicBaseImages`,构建参数和网络模式也来自 YAMLconfirmed build 只在 D601 后台异步构建并推送到 node-local registry,返回 status/logs 轮询命令。`hwlab nodes control-plane infra argo status|apply|logs --node D601 --lane v03` 是 D601 Argo CD 的声明式安装入口。Argo 版本、官方 manifest URL、镜像 rewrite/preload、field manager、imagePullPolicy、CRD 列表、期望 Deployment/StatefulSet 以及生成的 AppProject/Application 都必须来自同一个 YAML`argo apply --confirm` 只执行可重复 server-side apply 和后台轮询,不把原生 `kubectl apply`、手工 Argo CLI 或临时 manifest 作为正式安装路径。
`hwlab nodes control-plane runtime-migration --node <node-id> --lane vNN [--dry-run|--allow-live-db-read --dry-run|--confirm]` 是 node-scoped runtime lane 的受控 schema migration 入口。它只通过目标 runtime namespace 当前 `deployment/hwlab-cloud-api -c hwlab-cloud-api` 内 repo-owned `cmd/hwlab-cloud-api/migrate.ts` 执行,输出 report path、source commit 和有界 stdout/stderr 摘要;不读取或打印 Secret 值、不手写 `psql`、不把 pod 内临时命令沉淀成正式流程。D601 v03 这类由 UniDesk YAML 声明的外置 PK01 PostgreSQL 切换,DB/Secret/bridge 仍以 UniDesk YAML 和 `platform-db postgres ...``hwlab nodes control-plane apply|trigger-current` 为 source truthruntime migration 只负责在已发布 runtime 上补齐应用 schema。
## Command Model
- `help` 输出命令索引,适合作为交互式入口。
File diff suppressed because it is too large Load Diff
+1 -9
View File
@@ -418,8 +418,6 @@ function summarizeRuntimeLaneTriggerJobProgress(job: JobRecord, stdoutTail: stri
const stageStatus = stringField(lastEvent.status);
const sourceCommit = stringField(lastEvent.sourceCommit) ?? firstMatch(stdoutTail, /"sourceCommit"\s*:\s*"([0-9a-f]{40})"/iu);
const pipelineRun = stringField(lastEvent.pipelineRun) ?? firstMatch(stdoutTail, /"pipelineRun"\s*:\s*"([^"]+)"/u);
const node = stringField(lastEvent.node) ?? commandOption(job.command, "--node") ?? "G14";
const lane = stringField(lastEvent.lane) ?? commandOption(job.command, "--lane") ?? "v03";
const pipelineCreated = /pipelinerun\.tekton\.dev\/[^ \n]+ created/u.test(stdoutTail)
? true
: stage === "create-pipelinerun" && stageStatus === "failed"
@@ -467,19 +465,13 @@ function summarizeRuntimeLaneTriggerJobProgress(job: JobRecord, stdoutTail: stri
slow ? "visibility-warning" : null,
].filter(Boolean).join(" "),
nextCommand: pipelineRun
? `bun scripts/cli.ts hwlab nodes control-plane status --node ${node} --lane ${lane} --pipeline-run ${pipelineRun}`
? `bun scripts/cli.ts hwlab nodes control-plane status --node ${stringField(lastEvent.node) ?? "G14"} --lane ${stringField(lastEvent.lane) ?? "v03"} --pipeline-run ${pipelineRun}`
: job.status === "running"
? `bun scripts/cli.ts job status ${job.id} --tail-bytes 12000`
: null,
};
}
function commandOption(command: readonly string[], name: string): string | null {
const index = command.indexOf(name);
const value = index >= 0 ? command[index + 1] : undefined;
return typeof value === "string" && value.trim().length > 0 && !value.startsWith("--") ? value.trim() : null;
}
function genericJobProgress(job: JobRecord, stderrTailOverride?: string): JobProgressSummary {
const nowMs = Date.now();
const stderrTail = stderrTailOverride ?? tailFile(job.stderrFile, 96_000);
+1 -6
View File
@@ -878,7 +878,7 @@ function connectionStringExport(item: Record<string, unknown>, path: string): Co
consumers: arrayOfRecords(item.consumers, `${path}.consumers`).map((consumer, index) => ({
scope: stringField(consumer, "scope", `${path}.consumers[${index}]`),
secret: stringField(consumer, "secret", `${path}.consumers[${index}]`),
key: kubernetesSecretKey(stringField(consumer, "key", `${path}.consumers[${index}]`), `${path}.consumers[${index}].key`),
key: envKey(stringField(consumer, "key", `${path}.consumers[${index}]`), `${path}.consumers[${index}].key`),
})),
};
}
@@ -909,11 +909,6 @@ function envKey(value: string, path: string): string {
return value;
}
function kubernetesSecretKey(value: string, path: string): string {
if (!/^[A-Za-z0-9._-]+$/u.test(value)) throw new Error(`${path} must be a Kubernetes Secret key`);
return value;
}
function configSummary(pg: PostgresHostConfig): Record<string, unknown> {
return {
path: pg.configPath,
+4 -5
View File
@@ -1,11 +1,10 @@
#!/bin/sh
set -eu
self_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
self_repo=$(CDPATH= cd -- "$self_dir/.." && pwd)
repo=${UNIDESK_TRAN_REPO_ROOT:-$self_repo}
if [ ! -f "$repo/scripts/cli.ts" ] && [ -f /root/unidesk/scripts/cli.ts ]; then
repo=/root/unidesk
repo=${UNIDESK_TRAN_REPO_ROOT:-/root/unidesk}
if [ ! -f "$repo/scripts/cli.ts" ]; then
self_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
repo=$(CDPATH= cd -- "$self_dir/.." && pwd)
fi
tran_timeout_seconds() {
+4 -5
View File
@@ -1,11 +1,10 @@
#!/bin/sh
set -eu
self_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
self_repo=$(CDPATH= cd -- "$self_dir/.." && pwd)
repo=${UNIDESK_TRANS_REPO_ROOT:-$self_repo}
if [ ! -f "$repo/scripts/cli.ts" ] && [ -f /root/unidesk/scripts/cli.ts ]; then
repo=/root/unidesk
repo=${UNIDESK_TRANS_REPO_ROOT:-/root/unidesk}
if [ ! -f "$repo/scripts/cli.ts" ]; then
self_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
repo=$(CDPATH= cd -- "$self_dir/.." && pwd)
fi
exec bun "$repo/scripts/cli.ts" ssh "$@"