写操作与错误
通过 API 修改状态的程序都遵守同一套规则:先查询服务端的能力,写请求带幂等键与期望版本,按统一的格式读取错误。本页逐条介绍这些规则以及承载它们的字段。
先查询能力
调用业务方法前,先调用 ServerInfoService/GetServerInfo。响应有两个字段:
| 字段 | 含义 |
|---|---|
source_revision | 服务端构建自哪个源码版本,仅供展示 |
capabilities | 字符串列表,如 workspace.create、workspace.filesystem、workspace.desktop,完整清单与命名规则见能力名 |
- 没有 API 版本号可以比较。
- 需要某个能力的操作在能力列表里找不到它时,应该让这一个操作失败,而不是拒绝整个服务端。
kibo在每条命令前做精确的字符串匹配,缺失时退出码为14。
写操作的约定
有两个字段让写请求可以安全地重发、安全地并发:幂等键让重发的请求不会重复执行,期望版本让请求不会覆盖别人的修改。
幂等键
除了下面列出的两个请求,每个写请求都带一个客户端生成的命令 ID(幂等键):
| 服务面 | 字段位置 | 格式 |
|---|---|---|
| Workspace API | 请求中的 CommandMetadata metadata,字段 command_id | ^[A-Za-z0-9][A-Za-z0-9._/-]*$,1 到 128 字节 |
| Operator API | 请求中的 string command_id | 同上 |
不符合上表格式的命令 ID,v0.3.x 仍然接受,v0.4.0 起返回不可重试的 invalid_argument。部署方怎样查出这些调用方,见从 v0.2.x 升级到 v0.3.0。
下面这些 ID 有额外的限制:
| ID | 限制 | 不符合时 |
|---|---|---|
| 所有写请求 | 不能以 expiration/、host-memory/、guest-shutdown/ 开头,这些前缀留给 ctrl 自己发起的命令 | Workspace API 现在就返回 invalid_argument,Operator API v0.4.0 起返回 |
ApplyArtifactReclamation 的 command_id | 不能含 / | invalid_argument |
BeginArtifactPublication 里 Template、Platform 发布的 command_id | 匹配 ^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$:不能含 /,可以含 : | BeginArtifactPublication 本身成功,这次发布在写入清单时失败 |
GetWorkspaceCommand与GetVolumeCommand读取已有的命令,ID 不受这些规则限制。- Go SDK 的
kibotos.ValidateCommandID检查上表的格式和保留前缀;程序要自己调用,SDK 发送请求时不检查。kibo在发送前检查--command-id。
命令 ID 只出现在请求消息里,不是 HTTP 头。请在提交前把它保存下来。
服务端把命令 ID 与输入和已有的记录比较:
- 用相同的命令 ID 和相同的输入再次提交,返回第一次的结果,不会重复执行。失败也一样:命令失败后用同一个 ID 重发,只会回放这次失败。
- 一个命令 ID 对应一次尝试。结果未知或命令还在执行时,保持原 ID 重发;错误详情带
command_settled时,失败已确定,要重试就换新 ID。 - 命令 ID 由对象推导时(比如防止重复创建),让它带上会随尝试变化的部分,比如已失败的次数,否则失败一次后再也跑不起来。
- 用相同的命令 ID 但不同的输入提交,返回
already_exists。 CreateWorkspace与UpdateWorkspaceNetworkPolicy比较输入时不看network_policy.revision:这个值由服务端分配,请求里填什么都被忽略。
RevokeEnrollment 与 RetainLaunchRevision 没有命令 ID:它们的效果本身可以重复施加。重复撤销不改变结果,重复保留到同一时刻也一样。
命令的返回时机
命令什么时候返回,取决于命令本身:
| 命令 | 返回时机 | 结果怎么查 |
|---|---|---|
CreateWorkspace、StartWorkspace | 受理即返回,Workspace 处于 CREATING 或 STARTING,执行器继续执行 | GetWorkspaceCommand |
StopWorkspace、DeleteWorkspace、RevokeWorkspaceDataPlane | 等命令完成,最长 1 分钟 | 超时返回可重试的 UNAVAILABLE,命令继续执行;用 GetWorkspaceCommand 查 |
UpdateWorkspaceNetworkPolicy | AVAILABLE 的 Workspace 等 node 确认或报告失败,最长 1 分钟;STOPPED 的 Workspace 立即返回 | 超时或 node 不可达时返回可重试的 TIMED_OUT 或 UNAVAILABLE,Workspace 处于 UNKNOWN,直到 node 再次汇报。见修改什么时候生效 |
ExportWorkspace、ImportTransfer、RestoreWorkspace | 等命令完成,最长 1 分钟 | 超时同上;错误详情带当前的 Transfer,用它的 transfer_id 调 GetTransfer |
Volume 的创建、扩容与删除,ResizeWorkspaceWritableLayer | 在请求内完成,响应带回结果状态 | 结果未知时,Volume 命令用 GetVolumeCommand,可写层扩容用 GetWorkspaceCommand |
重要
超时或结果未知时也可以用原来的命令 ID 重发,不要换一个新的。
版本校验
修改已有对象的请求带一个期望版本。对象的当前版本与期望不一致时服务端拒绝,避免覆盖别人的修改。
Workspace API 的请求:
| 请求 | 期望版本字段 | 对应的对象版本 |
|---|---|---|
UpdateWorkspaceExpiration | expected_revision | expiration.revision |
StopWorkspace、StartWorkspace、DeleteWorkspace | expected_expiration_revision | expiration.revision。没有到期设置时也有版本号 |
UpdateWorkspaceMemory、UpdateWorkspaceCpu | expected_revision | 资源配置的版本 resources_revision,两者共用 |
UpdateWorkspaceVolumeBinding | expected_revision | Volume 绑定的版本 binding_revision,与资源版本各自独立 |
UpdateWorkspaceNetworkPolicy | expected_revision,可以不带 | network_policy.revision。为 0 时不检查;不为 0 时即使内容与当前相同,版本不符也拒绝 |
UpdateEgress、UpdateResolver | expected_revision | Egress 或 Resolver 的 revision |
Workspace、Volume、Egress、Resolver 的 labels 与 annotations 修改,如 UpdateWorkspaceLabels、UpdateVolumeAnnotations | expected_revision | 对象的 labels_revision 或 annotations_revision |
Operator API 的请求:
| 请求 | 期望版本字段 | 对应的对象版本 |
|---|---|---|
DrainNode、CancelNodeDrain、IsolateNode、RetireNode、SetNodeOvercommit | expected_registration_version | node 的 registration_version |
SetOvercommitDefaults | expected_version | 超配默认值的 version |
SetStoragePoolCapacity | expected_version | 存储池的 version |
SetPlatformDefault | expected_revision 与 expected_default_version | 当前默认版本及其指针版本 |
SetTemplateDefault | expected_revision,或 expect_no_default | 当前默认版本 |
ResizeWorkspaceWritableLayer 与 ResizeVolume 没有期望版本,只有命令 ID 和目标容量。
版本不一致时返回什么错误,取决于是哪个版本:
| 不一致的版本 | 错误 |
|---|---|
到期设置的版本:UpdateWorkspaceExpiration 的 expected_revision,或停止、启动、删除的 expected_expiration_revision | failed_precondition |
| 其他期望版本 | aborted |
到期时间在 Workspace 状态不允许修改时也返回 failed_precondition,用错误详情里的 observed_workspace 区分这两种情况,见到期时间。
修改已删除对象的 labels 或 annotations 同样返回 failed_precondition。
收到这两种错误后,重新读取对象状态再决定下一步,不要自动用新版本重放旧请求。
错误格式
错误使用 Connect 的标准错误码。错误详情中附带 kibotos.ctrl.v1.ErrorDetail,按下面这些字段决定怎样处理:
| 字段 | 用法 |
|---|---|
reason | 更细的原因,FailureReason 枚举,如 SESSION_EXPIRED、CAPABILITY_UNAVAILABLE、CONFLICT。先看它,再看标准错误码 |
retryable | 重试是否可能成功。为 false 时,先修改请求或对象;用不用新的命令 ID 看 command_settled |
command_settled | 这是命令已记录的结果:同一个命令 ID 只会回放它,重试要换新 ID。没有这个字段的可重试失败还没有结果,用同一个 ID 重发 |
accepted_bytes | 只出现在 SendInput 返回的 resource_exhausted 上:guest 在拒绝之前已经接收的字节数 |
observed_workspace、observed_volume、observed_transfer | 失败时对象的状态。用它决定下一步,例如区分版本冲突与状态不允许修改 |
stage、message 等其他字段与全部原因见 API 参考中的 ErrorDetail 与 FailureReason。
kibo 优先按 reason 决定退出码,其次按标准错误码。退出码见 kibo 退出码。