结果:已合并(2026-10-22 20:19, kubevirt/containerized-data-importer#4320). 事实上,这些内容取自 GitHub 的 PR、评审、评论、时间线、检查记录,以及 CDI 源码(PR 的基准提交
9cd4fbdb)、Go 标准库文档和贡献记录的初稿。 时间均为韩国时间。
目录
一句话总结
| PR | kubevirt/containerized-data-importer#4320 Log the storage class name, not its pointer, in filesystem overhead |
| 修改内容 | pkg/controller/common/util.go 的两行日志。新增 2 行,删除 2 行 |
| 反应 | 维护者在 16 小时后 /approve·/lgtm。在说明了一个无关的 e2e 失败后,维护者进行了 override |
| 结果 | PR 公开 10-20 00:49 → 批准 17:05 → override 10-22 16:42 → 合并 20:19,历时约 2 天 19 小时 30 分钟 |
1. 问题所在:读代码时发现的两行
这次的问题并非在生产环境中发现的。这是在撰写本博客的 “阅读 KubeVirt 源代码”系列时,追踪 CDI 大小计算逻辑的过程中,被记录在贡献记录的“代码阅读发现的贡献候选”列表中的第五条。该日志位于 第 8 篇中讨论的、为每个存储类选择文件系统预留空间值的函数内部。
GetFilesystemOverheadForStorageClass 如果找不到存储类,会查找默认类,如果还没有则使用全局预留空间。在这两个分支中会记录详情级别为 3(-v=3)的日志。PR 正文中记录的输出如下:
Storage class0x7035abda0020not found, trying default storage class
存储类名称的位置显示的是内存地址,并且前后没有空格。修改后打印出的效果如下:
Storage class "local-path" not found, trying default storage class
虽然 PR 正文中提到已经复现了此输出,但贡献记录中并未保留该复现的原始输出。上面两行摘自 PR 正文中的示例。并且未确认 TT Lab 集群中的 CDI 是否正在以详情级别 3 运行(未确认)。
2. 原因:源码中的哪一行
行号基于 PR 的基准提交 9cd4fbdb 。
pkg/controller/common/util.go第 650 行:klog.V(3).Info("Storage class", storageClassName, "not found, trying default storage class")- 同文件的第 664 行:
klog.V(3).Info("Storage class", storageClassName, "not found, continuing with global overhead")
storageClassName 是 *string 。这似乎是因为在 Kubernetes API 中经常使用指针来表示“可以为空的字符串”而导致的失误。两点叠加导致了上述输出。
klog.Info使用fmt.Sprint的方式连接参数。Go 的fmt.Sprint文档写道:“仅当两个操作数都不是字符串时,它们之间才会插入空格。”在"Storage class"与指针之间、指针与"not found…"之间,由于其中一方是字符串,因此不会插入空格。%v如果用指针打印,输出的将是其指向的地址而不是值。因此诸如0x7035abda0020这样的值会出现在名称的位置上。
Info 将 Infof 替换为
k8s.io/utils/ptr 并提供格式化字符串,空格问题就会消失。指针需要解引用,但由于它可能为 nil,因此使用了该文件已经引入的 ptr.Deref(storageClassName, "") 的
。
targetStorageClass, err := GetStorageClassByNameWithK8sFallback(ctx, client, storageClassName)
if err != nil || targetStorageClass == nil {
- klog.V(3).Info("Storage class", storageClassName, "not found, trying default storage class")
+ klog.V(3).Infof("Storage class %q not found, trying default storage class", ptr.Deref(storageClassName, ""))
...
if targetStorageClass == nil {
- klog.V(3).Info("Storage class", storageClassName, "not found, continuing with global overhead")
+ klog.V(3).Infof("Storage class %q not found, continuing with global overhead", ptr.Deref(storageClassName, ""))
%q 3. 修改内容:diff 要点 "" 选择 go vet ./pkg/controller/common/ 是为了确保即使名称为空字符串时,也能显示为
go test ./pkg/controller/common/ 。同一函数中“没有默认存储类”的日志由于只有一个字符串参数,没有问题,因此保留了原样。PR 正文中说明了这只是一个修改日志的变更,并且已经过验证。 -mod=vendor干净无误,
的 43 个测试全部通过(golang:1.27.2 容器, Assisted-by: Claude )。未单独开启 Issue,仅说明是“读代码时发现的”。人工智能的使用体现在提交尾注
和 DCO 签名中,以及正文末尾的段落中:“在 Claude 的协助下发现并修复,并经过了审查和上述日志输出的复现”。此提交在 GitHub 上也显示为未签名提交。似乎漏掉了原则 3 的 SSH 签名。
| 4. 反应:16 小时获批,随后遇到一个 e2e 测试失败 | 时间 |
|---|---|
| 10-20 00:49 | 日 release-note-none·size/XS PR 公开。prow 添加了 |
| 10-20 17:05 | 标签,指定了两名评审员 /approve·/lgtm 维护者在评审中给出 lgtm·approved 和一句感谢。 |
| 10-21 22:23 | 标签 /retest-required 我们输入了 |
| 10-22 01:05 | ,但机器人回复“不受信任的作者无法直接运行测试” |
| 10-22 16:42 | 记录失败测试的内容并请求重新测试的评论 /override pull-containerized-data-importer-e2e-ceph-wffc |
| 10-22 20:19 | 维护者 |
tide 进行合并 批准很迅速。因为只有两行,且将修改前后的日志并排展示,所以似乎没有什么需要额外询问的。这位维护者与第 7 篇 和第 6 篇
的评审员是同一个人。 e2e-ceph-wffc 批准后阻碍合并的是 e2e 测试集
DataImportCron … with snapshot sources [test_id:10033] 。从名字来看,这似乎是围绕 ceph 存储和延迟绑定(WaitForFirstConsumer)运行的测试集。失败的只有一个,是名为
的测试。546 个测试中有 545 个通过。仅修改了两行日志的变更,看起来不可能破坏快照源的 DataImportCron 测试。 /retest-required 因此我先输入了重新测试命令 /ok-to-test ,但 prow 称这是外部作者的命令而拒绝执行。在此仓库中,不受信任的作者无法使用
来启动或重新运行测试。几个小时后,我写下了失败测试的名称和 545/546 的数字,用一句话解释了该变更与该测试无关,然后请求某人帮忙触发重新测试。维护者没有重新测试,而是直接 override 了该检查,四小时后便合并了。
- 「
Info5. 收获:开源贡献技巧」fmt.Sprint这样用逗号连接值的日志需要多看两眼。Infof的空格规则导致混用字符串和非字符串时空格会消失。如果再加上指针,打印出来的就会是地址而不是值。%q与 - 「以及能够容忍 nil 的解引用是最简解法。阅读代码也是贡献的源泉。
- 「即使在生产中没有遇到不便,为了理清架构而逐行阅读代码时也会发现这类问题。将发现的内容记入记录的候选列表中,在大型 PR(第 7 篇)等待评审的间隙,单独将其作为小型 PR 提交。外部贡献者在无法重新测试时应进行说明。
- 「如果没有权限直接重新运行测试,你能做的就是提供足够的信息让维护者做出判断。在一条评论中包含失败的测试名称、通过数量以及该变更与该测试无关的原因。维护者看到后便选择了 override。」
/retest-required当命令被拦截时,请仔细阅读机器人的回复。
输入 虽然是白跑一趟,但机器人的回复准确指出了外部作者在此仓库中无法进行的操作。这与第 6 篇中看到的 KubeVirt 本体遵循相同的规则。
小 T 的碎碎念。
本应用本该写着“找不到某某仓库”,却写成了仓库名称而不是“7035号抽屉”,而且连空格都没有。把写着名字的纸条放进抽屉里,结果只抄了抽屉编号。拿出来把名字读出来写上之后,很快就得到了“赞”。不过隔壁班的一张试卷批错了只能等待,我也没法要求重新批改。于是我写道“错的是这道题,跟我的笔记没关系”,老师就把那张试卷递过来了。
- 链接 所有资料均查阅于 2026-10-29。 PR:
b2e1e1e8) - kubevirt/containerized-data-importer#4320
pkg/controller/common/util.go(合并提交, 修改的源码(基准提交): - Go 문서:
fmt.Sprint,k8s.io/utils/ptr.Deref - 第 650 行 第 664 行