结果:已合并(2026-10-26 19:58, kubevirt/containerized-data-importer#4319),Issue #3005 已关闭。 截至 2026-10-29,尚无包含此更改的 CDI 版本(最新版本为 2026-09-06 的 v1.66.1)。实际上,这些内容来自 GitHub 上的 PR、评审、行评论、正文修改历史、时间线、检查 记录,以及 CDI 源码(PR 的基础提交
9cd4fbdb)、local-path-provisioner 源码(标签 v0.0.32)、贡献账本的实测原始数据(StorageProfile 列表、48 小时日志汇总)和 草稿。时间均为韩国时间。
目录
一句话总结
| PR | kubevirt/containerized-data-importer#4319 Add storage capabilities for rancher.io/local-path |
| 更改内容 | storagecapabilities.go 一个文件。 rancher.io/local-path 项,以及节点本地路径 provisioner 共用的一个助手。增加 12 行,删除 4 行 |
| 反应 | 两位评审人员各一条行评论(合并、去掉两个测试),维护人员重新打开并关联旧 Issue |
| 结果 | PR 发布 10-17 23:38 → /approve 10-19 16:59 → /lgtm 10-26 15:15 → 合并 19:58,约 8 天 20 小时 20 分钟。3 个提交,通过 21 个检查 |
1. 问题所在:空白的 StorageProfile
CDI(Containerized Data Importer)是将 KubeVirt VM 磁盘转换为 PVC 的组件。如果将 DataVolume 用作 spec.storage 则无需填写访问模式或卷模式,其默认值从每个 StorageClass 创建的 StorageProfile 中获取。StorageProfile 的默认值由
CDI 通过查看 provisioner 名称来填充。
2026-10-17 使用读取命令检查 TT Lab 集群(CDI v1.66.0)时,四个 StorageProfile 中有两个为空(账本 measure/kv).
| StorageProfile | Provisioner | claimPropertySets | Recognized |
|---|---|---|---|
| local-path | rancher.io/local-path |
无 | UnrecognizedProvisioner |
| local-path-retain | rancher.io/local-path |
无 | UnrecognizedProvisioner |
| 两个 NFS 类 | nfs.csi.k8s.io |
RWX, Filesystem | RecognizedProvisioner |
这两个 local-path 类创建于 57 天前,provisioner 镜像为 v0.0.32。在 cdi-deployment 的 48 小时日志中,每个类都有
Set metric:local-path complete:false … degraded:true 这样的相同行出现了十次。正如在 PR 正文中所写,该指标是 CDIStorageProfilesIncomplete
警报监测的值。
在此状态下,如果使用 spec.storage 创建访问模式为空的 DataVolume,它将停留在 ErrClaimNotValid: no accessMode specified in StorageProfile local-path
状态,正如我在 PR 正文中所写的那样。这是在草稿中写为“用户要运行的命令和预期结果”的内容,账本中没有实际运行记录(未实测)。
账本中没有记录表明 TT Lab 的 VM 因为此错误而停止。这不是生产事故,而是在调查过程中发现的问题。
2. 原因:源码中的哪一行
CDI 行号基于 PR 的基础提交 9cd4fbdb 。
- Provisioner 名称和默认值的表格是
storagecapabilities.go第 39 行起 的CapabilitiesByProvisionerKey。hostpath-provisioner 和k8s.io/minikube-hostpath作为{{rwo, file}}存在,但rancher.io/local-path不存在。 - 如果不在表中,
storageprofile-controller.go第 206–220 行 的reconcileConditions将 Recognized 设为False,并将原因设为UnrecognizedProvisioner(消息“Provisioner is not recognized”)。 第 1 节中的日志行在同一文件的 第 462 行 打印。
要填入的值为 {{rwo, file}} 的依据是 provisioner 端的源码(local-path-provisioner v0.0.32 provisioner.go).
- 第 345 行:如果在节点路径卷上请求除 RWO 和 RWOP 之外的访问模式,
则会以
NodePath only supports ReadWriteOnce and ReadWriteOncePod (1.22+) access modes拒绝。 - 第 405 行·第 458 行:
创建的 PV 永远是
VolumeMode: &fs(Filesystem),因此不会出现 Block。 - 如果单独设置共享文件系统路径,RWX 也是可能的,但这并非默认设置,仅凭 StorageClass 无法得知。我在正文中写道,此类用户可以直接填写 StorageProfile 的
spec.claimPropertySets。由于没有快照和 CSI 克隆功能,因此克隆策略保留当前的默认值(copy)不变。
PR 正文中还提到了另一点。CDI 自己的 kind 开发环境(cluster-up/cluster/kind/manifests/local-storage-class.yaml)也使用该 provisioner。
在此更改之后,该环境的 StorageProfile 也将被填充,因此我请求告知是否有任何 e2e 测试期望它是空的。
3. 更改内容:三次 diff
第一个提交(10-17)在表中添加了一行,并在 StorageProfile 控制器测试中添加了一个针对新 provisioner 的测试以及 Recognized 表项。
这与类似的先例 #4286(Mayastor,2026-09-27 合并)形状相同。
PR 正文中的测试结果如下(Go 1.27.2, GOFLAGS=-mod=vendor,main 9cd4fbd).
| 测试 | 结果 |
|---|---|
| 新测试,无表项 | 7 个中有 2 个失败 |
| 新测试,带表项 | 通过 |
go test ./pkg/controller/ 修改后的版本 / 修改前 |
385 通过·3 失败 / 383 通过·3 失败 |
失败的三个测试在双方中都是相同的与 VDDK 相关的测试(Update PVC from POD),因此我在正文中指出这与此更改无关。
第二个提交(10-18)采纳了评审人员的建议。意见认为,通过单个目录创建卷的 provisioner 最终都是 {rwo, file} ,因此应该将它们合并。于是创建了 createLocalPathCapabilities() 助手,并让 hostpath-provisioner(CSI·遗留)、 k8s.io/minikube-hostpath,
rancher.io/local-path 使用它。hostpath-provisioner 的 overlay 变体是 RWX,因此保持不变。我把直接提供整个驱动器而非目录的 DirectPV 排除在外,并回复称如果需要可以添加。此时 go test ./pkg/controller/ 的 388 个测试全部通过(回复中记录的值)。
第三个提交(10-26)按照另一位评审人员的意见,删除了第一个提交中的两个单元测试。意见指出,新 provisioner 只是复用了已有测试的助手,并没有新的逻辑。此提交
仅运行了 gofmt·go vet ,未重新运行测试(账本记录)。合并前的 CI 测试已通过。
var CapabilitiesByProvisionerKey = map[string][]StorageCapabilities{
// hostpath-provisioner
- ProvisionerHPPCSI: {{rwo, file}},
+ ProvisionerHPPCSI: createLocalPathCapabilities(),
ProvisionerHPPOverlayCSI: {{rwx, file}},
- ProvisionerHPPLegacy: {{rwo, file}},
- "k8s.io/minikube-hostpath": {{rwo, file}},
+ ProvisionerHPPLegacy: createLocalPathCapabilities(),
+ // other node-local path provisioners
+ "k8s.io/minikube-hostpath": createLocalPathCapabilities(),
+ "rancher.io/local-path": createLocalPathCapabilities(), // local-path-provisioner, default in k3s and kind
...
+// createLocalPathCapabilities is shared by provisioners that back each volume
+// with a directory on a single node, so they only offer RWO filesystem volumes.
+func createLocalPathCapabilities() []StorageCapabilities {
+ return []StorageCapabilities{{rwo, file}}
+}
最终 diff 为一个文件,增加 12 行,删除 4 行。甚至比最初还要小。
所有三个提交都使用了 AI 的 Assisted-by: Claude 尾注和 DCO 签名,正文末尾附有段落说明:“在阅读 provisioner 源码以及编写更改和测试草稿时得到了 Claude 的
帮助,我已审查了每一行,StorageProfile 输出结果来自我自己的集群”。这符合 KubeVirt 组织的
AI 贡献政策的建议。SSH 签名仅附加在第一个提交上,
反映评审意见的第二个和第三个提交在 GitHub 上显示为未签名提交。
4.反应:合并、移除以及重新打开的 Issue
| 时间 | 天 |
|---|---|
| 10-17 23:38 | 发布 PR。prow 附加了 size/S 标签,提示“不受信任的作者不能直接运行测试”,指定了两位评审人员 |
| 10-18 20:25 | 评审人员 A 的行评论:这些本地路径 provisioner 最终都是 rwo 和 file,所以可以合并 |
| 10-18 21:03 | 合并为助手的第二个提交,21:05 回复。标签变更为 size/M 变为 |
| 10-19 16:57 | 维护人员将 2024 年 10 月自动关闭的 Issue #3005 /reopen |
| 10-19 16:58, 17:00 | 同一维护人员修改了我们 PR 正文中的“Which issue(s)”栏,
填入了 Fixes #3005 (正文修改历史) |
| 10-19 16:59 | 评审人员 A 提出了 /approve 和 /test all。17:13 coveralls 覆盖率为 50.288%(+0.08%) |
| 10-25 11:33 | 连续六天没有 /lgtm ,因此向指定的两位评审人员请求一次:“由于已有 approve 且测试通过,现在只剩 lgtm 了” |
| 10-25 17:53 | 评审人员 B 的行评论:没有新逻辑,不知道为什么需要两个单元测试 |
| 10-26 00:04 | 删除了两个测试的第三个提交,00:07 回复。标签再次变为 size/S |
| 10-26 15:15 | 评审人员 B 提出了 /lgtm |
| 10-26 19:58 | tide 执行合并,Issue #3005 随之关闭 |
这次 PR 中最值得学习的场景发生在 10-19 的两行记录中。我们在编写草稿时曾写明没有处理相同问题的 Issue 或 PR(账本草稿列表中为“搜索 0”),
并且在 PR 正文的 Issue 栏中也填入了 NONE 。然而, #3005
“StorageProfile needs to be patched for the rancher local-path provisioner”早已存在。它于 2023-11-24 打开,另一位维护人员回复的大意是:“这是未识别的 provisioner 的默认行为,请在 DV 中写入访问模式或修改 profile,或者也可以发送添加默认值的 PR”(2024-01)。此后无人发送 PR,于是机器人于 2024-10-03 将其自动关闭。我们的搜索似乎只看到了打开的项目。
维护人员并未单独指出这一点。相反,他重新打开了 Issue 并将其关联到我们的 PR 正文中,随着 PR 的合并,沉睡 2 年 11 个月之久的 Issue 就此关闭。
对于两条行评论,我都一次性接受并简短回复了。第一次回复是:“感谢评审,第二个提交中已合并为助手,DirectPV 由于提供整个驱动器而非目录已被排除”;第二次回复是:“确实如此,现有的 hostpath 测试已经覆盖了该助手,只是在重复使用,因此两个都删除了”。两位评审人员的意见分别减少了我们在第一个提交中添加的内容(测试)和更改的形状(助手),结果使得 PR 变得更小了。
5. 收获:开源贡献技巧
- 「重复搜索时也要包含已关闭的 Issue。”自动关闭(rotten)的 Issue 往往意味着“无人修复”而非“已解决”。寻找此类 Issue 并
Fixes #번호连接起来,如果在正文中引用维护者留在Issue中的答复(“可以发送PR”),审查可能会更快。 - 「值的依据可在相关项目的源码中找到。」
{rwo, file}选择 的理由看起来是 local-path-provisioner 的错误提示和创建 PV 的代码行,所以在审查中没有人直接询问该值本身。所有的提问都关于代码的形态(组合方式、测试数量)。 - 「如果审查减少了 PR 的改动,照单全收即可。添加更多测试并不总是好事。审查者的观点是,如果没有新的逻辑却增加测试,只会增加需要维护的代码量。维护者希望的范围可能会比最初提交的人所设定的范围更窄。
- 「在 approve 之后如果迟迟没有 lgtm,可以只陈述事实提醒一次。“已获得 approve 且测试已通过,只差 lgtm 了”这简短的一句话,让当天的审查重新推进了起来。
Titi 的一句话。 我们小区的仓库管理员说没听过“泥路仓库”这个名字。于是他在存放在那里的每个箱子上都贴上了“不知如何保管”的标签。我从仓库的规则手册里找到并告知他,泥路仓库一个格子只能放一个人,且只接收箱子形状的物品。仓库管理员大叔建议把四个类似的仓库串联记下来,另一位大叔说不需要两个确认盖章。接着,有人从抽屉里翻出三年前写着同样请求的便条,和我们的便条一起放进了“已解决”的箱子里。
链接
所有资料于 2026-10-29 查阅。
- PR: kubevirt/containerized-data-importer#4319 (合并提交
fd4146ac) - 已关闭的 Issue: #3005 (2023-11-24 开启,2024-10-03 自动关闭,2026-10-19 重新开启)
- 修改后的源码(基于提交):
storagecapabilities.go,storageprofile-controller.go - local-path-provisioner 源码 (v0.0.32):
provisioner.go第 345 行, 第 458 行 - 先例: containerized-data-importer#4286 Mayastor 默认值
- 同一天同一调查中产生的 PR: 第 6 篇,kubevirt/kubevirt#19499