12 KiB
Device Pod 正式接入规格
本文是 HWLAB v0.2 正式接入 device-pod 的规格说明。device-pod 是一个逻辑设备能力单元,不是 Kubernetes Pod 名称,也不是 code agent 本地 profile 文件。正式接入后,profile 定义 device-pod,因此 profile 必须由管理员和服务端权威存储管理,不能由 code agent 本地文件决定路由或资源边界。
实施跟踪见 pikasTech/HWLAB#533,原 docs/plan/v02-device-pod-spec-migration.md 和旧 device-pod MVP 计划全文已迁入该 issue 评论。
旧的 device-pod-cli 本地 profile 闭环只用于 CLI MVP 和真实硬件最小验证。进入正式多用户系统后,所有用户态设备访问必须收敛到:
device-pod-cli or cloud-web
-> cloud-api auth + device_pod_grants + lease
-> hwlab-device-pod internal REST
-> gateway transport
-> device-host-cli
-> Keil / pyOCD / UART / target
在系统中的职责划分
device-pod 是云端可授权、可租约保护、可审计的逻辑设备能力单元。hwlab-cloud-api 是用户身份、grant、lease、profile authority 和用户态 REST API 的入口;hwlab-device-pod 是内部执行服务;hwlab-gateway 只承载 transport;device-host-cli 只在硬件 host 侧执行 Keil、pyOCD、UART 和 workspace 操作。
普通用户、浏览器和 Code Agent session 不直接持有 gateway route、host workspace route、Kubernetes Service 直连能力或 profile 修改权。
设计目标
- 用最少组件把
device-pod-cli从“本地 profile + RPC/gateway 调用”迁到“1:1 REST 请求”。 cloud-api是用户身份、device grant、profile authority 和 lease 判断入口。hwlab-device-pod承接设备业务:profile 校验后的运行、job 生命周期、freshness、blocker、bounded output 和 gateway 调用。device-pod-cli只做 selector 解析、REST 请求和 JSON 输出,不再保存或上传权威 profile。- 第一阶段只部署一个
hwlab-device-podDeployment/Service,管理多个逻辑devicePodId,避免为每台设备创建独立 k8s Service/Deployment。 - 普通用户和 code agent session 不获得 Kubernetes 用户、Service 直连权限、gateway route 或 host workspace route。
逻辑模型
一个 device-pod 由四个设备能力要素组成:
device-pod
= deviceTarget
+ debugInterface
+ projectWorkspace
+ ioInterface
deviceTarget:被测设备目标,例如开发板、用户 PCB 或仪器模块。debugInterface:下载、复位、chip-id、probe 状态和调试连接能力。projectWorkspace:源码、工程、构建工具链和 artifact 边界。ioInterface:UART、DI/DO、采样、日志和其他设备 I/O 观测/控制能力。
devicePodId 是云端和用户界面的稳定身份。实际 k8s Pod 可以重建、滚动或扩容;用户和 code agent 不依赖实际 Pod name。
Profile Authority
正式接入后,profile 是管理员侧资源:
admin UI/API
-> cloud-api
-> device_pods.profile_json + profile_hash
-> hwlab-device-pod internal execution
code agent 本地文件只能作为非权威 hint/cache,最多包含:
{
"devicePodId": "device-pod-71-freq",
"profileHash": "sha256:...",
"cloudApiUrl": "..."
}
本地 hint/cache 不得包含以下字段,也不得参与授权或执行路由:
gatewaySessionIdresourceIdcapabilityIdhostWorkspaceRoothostCli- Windows workspace 路径
- probe UID、串口端口、Keil 路径等硬件路由字段
正式 profile 必须由 cloud-api 从 DB 读取;hwlab-device-pod 不接受浏览器、code agent 或 CLI 上传的 profile 作为执行依据。若 hwlab-device-pod 需要 profile snapshot,应只接受 cloud-api 内部服务凭据转发的 snapshot,或通过内部服务凭据向 cloud-api 拉取。该凭据不得挂载进 code agent session Pod。
内部架构
正式 device-pod 由 profile registry、job lifecycle、freshness/blocker、bounded output、gateway/device-host adapter 和 lease integration 组成。第一阶段只有一个 hwlab-device-pod Deployment 管理多个 devicePodId;profile authority 和 user grant 在 cloud-api/Postgres 中,device-pod 服务只接受 cloud-api 内部调用。
当前 v02 部署中的 hwlab-device-pod 微服务实现情况见 spec-v02-hwlab-device-pod-service.md。
Profile Shape
device_pods.profile_json 至少表达以下 server-side 字段:
{
"schemaVersion": 1,
"devicePodId": "device-pod-71-freq",
"target": {
"id": "target-id"
},
"projectWorkspace": {
"workspaceRoot": "F:\\Work\\Project",
"projectPath": "FirmWare/MDK-ARM/app.uvprojx",
"targetName": "app",
"hexPath": "FirmWare/MDK-ARM/app/app.hex"
},
"debugInterface": {
"type": "cmsis-dap",
"probeUid": "...",
"uv4Path": "C:\\Keil_v5\\UV4\\UV4.exe"
},
"ioInterface": {
"uart": [
{ "id": "uart/1", "port": "COM4", "baudRate": 921600 }
]
},
"route": {
"gatewaySessionId": "gws_...",
"resourceId": "res_...",
"capabilityId": "cap_...",
"hostWorkspaceRoot": "F:\\Work\\Project",
"hostCli": "node tools\\device-host-cli.mjs"
}
}
profile_json 不得保存 Git key、云 token、kubeconfig、数据库 URL 或长期 secret。profile_hash 由 cloud-api 对规范化 profile JSON 计算并在所有响应中返回;用户可见响应只能返回脱敏 profile 摘要和 hash。
数据表口径
正式规格推荐 device_pods 直接保存权威 profile 和 hash,避免额外 profile 微服务:
CREATE TABLE IF NOT EXISTS device_pods (
id TEXT PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
profile_json TEXT NOT NULL DEFAULT '{}',
profile_hash TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
历史版本中的 profile_ref、gateway_ref 或 device_pod_json 可以在迁移时折叠进 profile_json。第一阶段不新增 device_pod_profile_revisions;需要审计版本、回滚或多环境批准时再引入 profile revision 表。
device_pod_grants 仍只表达用户是否拥有完整使用权;它不保存 profile,也不拆 capability。
REST API
API 接口说明
用户态 API 只经过 cloud-api 暴露:
GET /v1/device-pods
GET /v1/device-pods/{devicePodId}/status
GET /v1/device-pods/{devicePodId}/debug-probe/chip-id
GET /v1/device-pods/{devicePodId}/io-probe/uart/1
GET /v1/device-pods/{devicePodId}/io-probe/uart/1/tail?maxBytes=12000
POST /v1/device-pods/{devicePodId}/jobs
GET /v1/device-pods/{devicePodId}/jobs/{jobId}
GET /v1/device-pods/{devicePodId}/jobs/{jobId}/output
POST /v1/device-pods/{devicePodId}/jobs/{jobId}/cancel
管理员 API 由 cloud-api 提供:
POST /v1/admin/device-pods
PUT /v1/admin/device-pods/{devicePodId}
POST /v1/admin/device-pod-grants
DELETE /v1/admin/device-pod-grants/{devicePodId}/{userId}
POST /v1/device-pods/{devicePodId}/jobs 用 intent 表达具体业务,避免把 REST surface 扩张成大量一次性 route:
{
"intent": "workspace.build",
"args": { "profile": "debug" },
"reason": "DEV smoke"
}
第一阶段 intent 集合:
workspace.lsworkspace.catworkspace.rgworkspace.apply-patchworkspace.builddebug.statusdebug.chip-iddebug.downloaddebug.resetio.portsio.uart.readio.uart.write
所有响应必须包含 devicePodId、targetId、profileHash、traceId、operationId、status、freshness、blocker 和 bounded output metadata。真实硬件响应不得把 fake、dry-run、SOURCE、LOCAL 或过期缓存标为 DEV-LIVE。
微服务职责
| 服务 | 职责 |
|---|---|
hwlab-cloud-api |
用户身份、admin/user、device grant、lease、profile authority、用户态 REST API、转发到内部 device-pod。 |
hwlab-device-pod |
多 devicePodId 运行 registry、profile runtime validation、job store、freshness、bounded output、gateway/device-host-cli adapter。 |
device-pod-cli |
把 devicePodId:surface:path operation args 1:1 转成 cloud-api REST;不保存权威 profile、不直连 gateway。 |
device-host-cli |
Windows host 侧自包含业务工具,负责 Keil、pyOCD、UART、workspace 文件操作。 |
hwlab-gateway |
只做受控 transport,不理解用户权限和 device-pod 授权。 |
hwlab-cloud-web |
展示用户可见 device pod、admin 管理 profile/grant、显示 job/status/freshness。 |
Kubernetes 口径
v0.2 第一阶段使用一个 hwlab-device-pod Deployment 和一个 ClusterIP Service:
hwlab-v02/hwlab-device-pod
replicas: 1
manages: many devicePodId
不为每个 devicePodId 创建 Deployment、Service、Ingress、Secret 或 namespace。这样更符合当前规模:运维对象少、GitOps diff 少、问题定位简单,也不会把设备数量直接放大成 k8s 资源数量。
只有在满足以下条件时,才考虑拆分为多个 hwlab-device-pod shard 或 per-device workload:
- 单个服务内 job 队列和 freshness 监控互相影响;
- 不同设备需要不同 host network、USB、Secret 或资源 request;
- 设备数量增长到单实例状态管理明显吃力;
- 强隔离需求超过应用层 grant 和内部服务凭据能覆盖的范围。
普通用户和 code agent session Pod 不应直接调用 hwlab-device-pod Service。正式路径是 code agent -> cloud-api -> hwlab-device-pod。
验收标准
device-pod-cli在正式模式下不读取.device-pod/<devicePodId>.json作为权威 profile,只向 cloud-api 提交devicePodId、intent 和 args。- 普通用户无授权时不能看到或使用任何 device pod;授权后拥有对应 device pod 的完整使用权。
- code agent 不能通过修改本地文件改变 gateway session、resource、host workspace、probe UID 或 UART port。
hwlab-device-pod不接受无内部服务凭据的 profile snapshot 或 job 请求。hwlab-device-pod一个实例可以列出并执行多个devicePodId的状态/job。- cloud-api compatibility fallback 只能返回 blocked authority payload,不得合成 fake device pod 数据或作为正式 device-pod DEV-LIVE 证据。
- 强副作用 job 必须有 reason,并在物理互斥需要时获取
device_leases。
测试规格
T1
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:普通用户无 grant 时访问 /v1/device-pods 不得看到任何 device pod;获得 grant 后能看到对应 devicePodId 和脱敏 profileHash,不能看到 gatewaySessionId、hostWorkspaceRoot 或 probe UID 等敏感路由字段。
T2
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:尝试通过本地 .device-pod/*.json 修改 gateway route 或 workspace route,正式 cloud-api/device-pod 路径必须忽略该本地文件并继续使用服务端 profile authority。
T3
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:提交一个强副作用 job,例如 download/reset,缺少 reason 或 lease 时必须被拒绝;获得 lease 后响应必须包含 devicePodId、profileHash、traceId、operationId、freshness、blocker 和 bounded output metadata。
规格的实现情况
| 规格项 | 状态 | 说明 |
|---|---|---|
| 逻辑 device-pod 模型 | 已实现为规格 | 四要素、profile shape 和 Kubernetes 口径已定义。 |
| profile server authority | 未完全实现 | 当前 v02 device-pod 服务未读取正式 DB profile。 |
| 用户 grant + lease | 未完全实现 | 与 spec-user-access 配套,仍待完整实现。 |
| REST/job API | 未完全实现 | 当前只读/fake device-pod payload 已部署,正式 job lifecycle 未完成。 |
| 禁止 fake 作为 DEV-LIVE | 已实现/持续约束 | 规格和服务 payload 要求显式标记 fake/source。 |