fix(device): self-diagnosing errors for malformed ObjectId args (IM-3248) - #27
Merged
Conversation
IM-3248: local validation of positional args used as ObjectIds in API paths, with self-diagnosing errors (offending value, expected format, copy-pasteable lookup command).
IM-3248: the gateway (Traefik fork) returns a generic '404 page not found' text/plain body when a request path doesn't match any route shape (e.g. a malformed ObjectId segment) — indistinguishable from a real resource-not-found response, which is JSON. Detect that exact body and rewrite it into a message that names the request path and explains it's a routing mismatch, not a missing resource. Acts as a fallback for commands that don't yet validate their id arguments locally.
IM-3248: needed by subordinate-resource commands (e.g. group layerfs/ project bulk delete) that validate a CSV id list scoped by an already-validated parent id rather than the offending value itself.
IM-3248: incloud-cli only surfaced the gateway's generic '404 page not found' when a non-ObjectId value (e.g. a serial number or typo) was passed where a device/client/asset/group id was expected — indistinguishable from a real resource-not-found response. Wire the new cmdutil ObjectId validators onto every device-package command that uses a positional argument directly as an id in its API path, so the mistake is caught locally with a message naming the offending value, the expected format, and a copy-pasteable lookup command. Left unvalidated (no reliable copy-pasteable lookup command exists, or the argument isn't an id): device exec cancel (diagnosis id), device config task get / device import-status (job id), device uplink get/perf (bare uplink id, no parent to scope a lookup by), device config schema get (json key, not an id), device exec cli's command argument, and device import's file path. These fall back to the gateway 404 message from the api package fix.
IM-3248: existing tests used placeholder ids like 'c1', 'device123', 'abc123' for positional args that are now validated as ObjectIds.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题现象
用非 ObjectId 的值当设备 ID(或 client/asset/group id 等)查询时,incloud-cli 只输出:
这条错误没有任何上下文,人和 AI 工具都无法判断到底是「资源不存在」还是「参数本身就不对」。
为什么定位在 CLI,而不是网关或后端
实测对比过三种请求:
GET /api/v1/devices/17572(非法 ID)→ 404,content-type: text/plain,body 是404 page not found,没有x-trace-id/x-upstream-addr。这是网关(Traefik fork)默认路由的http.NotFoundHandler()吐出来的:网关按 ObjectId 形状匹配路由,非法 ID 拼出的路径匹配不到任何路由规则,根本没进到后端服务。GET /api/v1/devices/507f1f77bcf86cd799439011(合法但不存在)→ 404,content-type: application/json,body 是{"error":"resource_not_found","status":404,"message":"..."}。这条已经是正确的资源级 404。两种 404 长得像但含义完全不同,且非法 ID 的请求根本没有触达 nezha-device-manager。所以网关和后端都不需要改,问题在 CLI:它应该在本地就能判断「17572」不是合法 ID,不必等一次网络往返才收到一条没有信息量的错误。
改了什么
internal/cmdutil/objectid.go(新增):本地校验 24 位十六进制 ObjectId 的 cobraArgs校验器族(单个位置参数 / CSV 列表 / 变长参数全部校验),错误信息包含原始值、期望格式样例、以及可直接复制执行的查找命令(如incloud device list -q 17572)。资源名和查找命令都是参数化的,不同资源(device / client / asset / device group / 及有父子关系的子资源如 project、layerfs、config snapshot)各自传入合适的提示命令。挂载校验器:给
internal/cmd/device/下所有把位置参数直接拼进 URL 当 ID 用的命令挂上对应校验器(device id / client id / asset id / device group id,含 CSV 及变长参数场景,含按父资源 ID 二次校验子资源 ID 的场景,如device group project get <group-id> <project-id>)。跳过的位置参数(逐个核实过,均因缺乏可复制执行的查找命令,或参数本身不是 ID):
device exec cancel(diagnosis id)—— 没有对应的 list 命令可用于查找device config task get/device import-status(job id)—— 同上device uplink get/device uplink perf(uplink id)—— 命令本身是裸单参数,没有父级 ID 可用来构造有意义的查找命令device config schema get(json key,如dns)—— 不是 IDdevice exec cli的第二个参数(要执行的 shell 命令文本)—— 不是 IDdevice import <file>(本地文件路径)—— 不是 ID这些命令仍然会命中下面的网关兜底。
internal/api/rest.go兜底:execute()(及Download())在响应为 404 且 body 去掉首尾空白后正好是404 page not found时,把错误文本换成:这条覆盖所有没挂本地校验器的命令,以及 ID 拼接路径本身写错的情况。
*HTTPError的类型和StatusCode语义保持不变,errors.As调用方(如edge_cli_config.go)不受影响。补充单元测试:
internal/cmdutil/objectid_test.go(校验器行为)、internal/api/rest_integration_test.go(网关 404 兜底),并把现有测试里用作 ID 的占位符(c1、device123、abc123等)替换成合法形状的 ObjectId。验证
make fmt && make lint && make build && make test全部通过(make lint报的 2 条nolintlint是importcmd.go/log_diagnostic.go里已存在于 main 分支的问题,与本次改动无关,已核实)。在 dev 环境用构建出的二进制实测三种场景:
1. 非法 ID
2. 合法但不存在的 ID
3. 正常 ID
关联 IM-3248。