mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
158 lines
7.6 KiB
Markdown
158 lines
7.6 KiB
Markdown
# ADR-0139:Kubernetes Plugin Package Active Pointer CAS
|
||
|
||
- 状态:Accepted(ConfigMap exact pointer、stage evidence verifier、
|
||
resourceVersion CAS、并发/读取窗口/响应丢失恢复与 subpath 门禁已实现;ADR-0140
|
||
已补 durable startup recovery、标准 OCI verifier、一次性 admin process、独立
|
||
image 与最小权限 Job/RBAC;真实集群专项门仍待完成)
|
||
- 日期:2026-07-24
|
||
- 关联 RFC:QL-RFC-0001 D-08、D-09、D-132、D-135 至 D-137
|
||
- 关联 ADR:ADR-0087、ADR-0129、ADR-0134、ADR-0138
|
||
|
||
## 背景
|
||
|
||
ADR-0138 已冻结完整 activation intent、fresh publish 与 recovery inspect 的区别,并
|
||
实现本地 POSIX pointer。PostgreSQL Repository 也已能耐久保存 Cluster installation
|
||
和 PackageLock,但没有跨 admin 副本可见的 active pointer publisher。
|
||
|
||
Cluster 不能复用本机文件锁;也不能把 active generation 只写数据库后假定 Kubernetes
|
||
资源已发布。另一方面,为一个 ConfigMap adapter 新增 workspace package会继续加剧
|
||
packages 过细,而 `@qinglong/cluster-admin` 已拥有受审 Kubernetes client 依赖和
|
||
短生命周期 mutation authority。
|
||
|
||
## 决策
|
||
|
||
### 1. Publisher 留在既有 cluster-admin
|
||
|
||
新增显式
|
||
`@qinglong/cluster-admin/plugin-package-kubernetes-activation` subpath,不从 root、
|
||
administration 或 cluster-control production 入口导出。不新增 workspace package、
|
||
第三方依赖、timer、watcher、cache、sidecar 或数据库 connection。
|
||
|
||
runtime-core 新增共享 `normalizePluginPackageActivationIntent`,所有 Cluster pointer
|
||
在读写前都使用 exact schema、identifier、digest 和 generation 上限;publisher 不复制
|
||
第二套 activation intent 语义。
|
||
|
||
### 2. 一个目标对应一个有界 ConfigMap
|
||
|
||
目标 identity 固定绑定 cluster identity、namespace、Project 和 Package。资源名为
|
||
`ql3p-` 加 domain-separated SHA-256 的前 208 bit;完整 256-bit target digest仍写入
|
||
label 并在读取 canonical intent 后重算复验,因此名称截断不缩窄内容校验。
|
||
|
||
ConfigMap:
|
||
|
||
- `apiVersion=v1`、`kind=ConfigMap`、mutable;
|
||
- data 只有 `active.json`,上限 32 KiB;
|
||
- pointer exact 保存 schema、cluster identity digest、activation intent 和 receipt;
|
||
- labels exact 保存 managed-by、active schema 和完整 target digest;
|
||
- annotation exact 保存 intent digest;
|
||
- 禁止 binaryData、deletion timestamp、finalizer 和 owner reference。
|
||
|
||
资源名冲突或 metadata/data/pointer 漂移统一失败关闭。208-bit 名称只用于 Kubernetes
|
||
寻址,完整 identity、lock、stage 双证据和 intent digest 才是 authority。
|
||
|
||
### 3. stage evidence 由注入端口复验
|
||
|
||
`ClusterPluginPackageStageEvidenceVerifier.verify(intent)` 必须返回并精确匹配:
|
||
|
||
- lock digest;
|
||
- stage reference;
|
||
-领域 stage receipt digest;
|
||
- adapter 外部 evidence digest;
|
||
- content digest。
|
||
|
||
publisher 在每次 inspect 以及 publication 最终复核时调用 verifier。具体实现可以是
|
||
exact OCI digest、对象存储 receipt 或后续受审 Kubernetes 资源,但本 ADR 不允许
|
||
publisher 自行下载、缓存或猜测来源。verifier 不可用返回 unavailable,事实漂移返回
|
||
conflict。
|
||
|
||
### 4. create/replace 使用 resourceVersion CAS
|
||
|
||
首装只在 ConfigMap 不存在且 previous lock 为 null 时 create。升级先读取 current
|
||
pointer,必须 exact 等于 previous active lock,再用该对象的 resourceVersion replace。
|
||
|
||
第一次 observe 与取得写入 resourceVersion 之间存在第二次 GET;第二次返回的实际
|
||
pointer必须再次复验 previous lock。否则另一个副本在窗口中发布新 generation 后,
|
||
stale writer 会拿赢家的新 resourceVersion 将其覆盖。
|
||
|
||
Kubernetes 409 只在重新 inspect 得到完全相同 intent 时作为 exact replay;其他 winner
|
||
均为 conflict。两个并发候选最多一个成功。
|
||
|
||
### 5. 响应丢失只由恢复 inspect 裁决
|
||
|
||
create/replace 返回非 409 错误时,publisher 不在 fresh 路径猜测是否已经提交,而是
|
||
返回 unavailable,让数据库 record 保持 `activating`。ADR-0138 coordinator 在恢复时
|
||
只调用 inspect:
|
||
|
||
- exact ConfigMap pointer → 提交 durable active receipt;
|
||
- pointer absent → durable activation failure;
|
||
- pointer 为其他 generation 或 tamper → durable fact conflict。
|
||
|
||
成功 API 响应后仍要重新 GET pointer 并再次复验 stage evidence;不能只信 response
|
||
body 或本地 request。
|
||
|
||
### 6. 产品入口继续分阶段开放
|
||
|
||
ADR-0140 已提供:
|
||
|
||
- 标准 OCI Distribution stage evidence verifier;OCI manifest digest 与 bundle
|
||
layer digest 分离,签名通过 lock-annotated OCI referrer取得;
|
||
- `ql3-plugin-package-recover` production admin binary;
|
||
- 独立 cluster-admin image;
|
||
- 仅有 ConfigMap `get/create/update` 的 ServiceAccount、Role/RoleBinding 和
|
||
600 秒一次性 Job manifest。
|
||
|
||
仍不提供:
|
||
|
||
- object-store stage;
|
||
- Approved Action durable consumer、Policy/Audit route;
|
||
- operator repair;
|
||
- ADR-0149 active generation source 之上的 Task/Workflow/Prompt/Tool/Trigger
|
||
语义 materializer;
|
||
- ConfigMap GC、Package byte distribution、Runtime/UI Extension。
|
||
|
||
因此 adapter 可执行不等于 Cluster 已开放插件安装。
|
||
|
||
## 拒绝的方案
|
||
|
||
- 新增 `plugin-package-kubernetes` workspace package:拒绝;一个 adapter 文件没有
|
||
独立发布或运行生命周期,显式 subpath 足以隔离。
|
||
- 只在 PostgreSQL 切 active generation:拒绝;无法证明 Kubernetes 可见事实已发布。
|
||
- 使用普通 replace 或 merge patch:拒绝;缺少 exact previous resourceVersion fence。
|
||
- 只校验第一次 GET:拒绝;第二次 GET 后可覆盖窗口中的新赢家。
|
||
- 在 publisher 内实现 OCI client:拒绝;会合并来源、缓存、stage 与 pointer authority。
|
||
- 将 ConfigMap 设为 immutable 并每 generation 新建:拒绝;会把唯一当前 authority
|
||
退化为目录/label 扫描和 GC 问题。
|
||
|
||
## 影响
|
||
|
||
- Cluster 多副本获得单一、可 inspect、resourceVersion-fenced active pointer。
|
||
- edge/standalone 不依赖 cluster-admin;共享 intent normalizer 使六种本机制品各增加
|
||
2,532 bytes,但 files 与 loaded modules 不变,最大值仍低于硬门禁。
|
||
- packages importer 保持 21;cluster-admin 继续复用现有
|
||
`@kubernetes/client-node`,dependency audit 无新增 dependency。
|
||
- Kubernetes client 只进入独立短生命周期 admin image;常驻 cluster-control
|
||
production closure 不变。
|
||
- ConfigMap 只保存低敏摘要和 identity,不保存 bundle、Secret 或可执行内容。
|
||
|
||
## 验证
|
||
|
||
当前门禁覆盖:
|
||
|
||
1. canonical ConfigMap create、inspect 和 exact replay;
|
||
2. exact previous lock replacement 与 stale writer conflict;
|
||
3. create response loss 后 inspect 收敛且不 republish;
|
||
4. 同一 resourceVersion 的两个候选 single winner;
|
||
5. 两次 GET 之间插入新 winner 时 stale writer 不覆盖;
|
||
6. stage evidence drift 在任何 API mutation 前失败;
|
||
7. ConfigMap pointer canonical/tamper fail-closed;
|
||
8. root 不导出、显式 subpath 可达;
|
||
9. runtime-core intent normalizer 与 generation 边界;
|
||
10. cluster-admin 全量 55 pass/1 条件 skip,ConfigMap response loss 已通过 durable
|
||
startup recovery 在下一轮 inspect 收敛且不再次 create;
|
||
11. 标准 OCI allowlist、manifest/layer/referrer/signature 与重启重新取证;
|
||
12. 独立 admin image 的真实非 root、只读 rootfs CLI smoke;
|
||
13. dependency/source 与 Kustomize/RBAC audit 仍为 21 importer、`findings=[]`。
|
||
|
||
真实 Kubernetes API 的 ConfigMap 专项 CAS、RBAC 正负向和多 Pod response-loss fault
|
||
仍是产品开放前的独立 Gate。
|