結果:マージ(2026-10-22 20:19、 kubevirt/containerized-data-importer#4320). 実はGitHubのPR・レビュー・コメント・タイムライン・テスト記録、CDIソース(PRのベースコミット
9cd4fbdb)、Go標準ライブラリのドキュメント、コントリビューション台帳の下書きから持ってきました。 日時は韓国時間です。
目次
- 1行まとめ
- 1. 何が問題だったのか:コードを読んでいて見つけた2行
- 2. 原因:ソースのどの行か
- 3. 何を変えたのか:diffの要点
- 4. 反応:承認は16時間、その次はe2eが1つ
- 5. 学んだこと:オープンソース貢献のコツ
- リンク
1行まとめ
| PR | kubevirt/containerized-data-importer#4320 Log the storage class name, not its pointer, in filesystem overhead |
| 変更点 | pkg/controller/common/util.go のログ2行。2行追加、2行削除 |
| 反応 | メンテナーが16時間後に /approve·/lgtm。関係のないe2eの失敗が1つあることを説明すると、メンテナーが override |
| 結果 | PR公開 10-20 00:49 → 承認 17:05 → override 10-22 16:42 → マージ 20:19、約2日19時間30分 |
1. 何が問題だったのか:コードを読んでいて見つけた2行
今回の件は、運用で見かけたログではありません。このブログの 「KubeVirt ソースコードリーディング」シリーズを書きながらCDIのサイズ計算を 追っていて、コントリビューション台帳の「コードリーディングで見つけた貢献候補」のリストに5番目に書いておいたものです。このログは 第8回で扱った ファイルシステムの空き容量の値をストレージクラスごとに選択する関数の中にあります。
GetFilesystemOverheadForStorageClass はストレージクラスが見つからない場合、デフォルトクラスを探し、それもない場合はグローバルな空き容量を使用します。その2つの分岐で
詳細レベル3(-v=3)のログを残します。PRの本文に書いた出力は次のようでした。
Storage class0x7035abda0020not found, trying default storage class
ストレージクラス名の部分にメモリアドレスがあり、前後のスペースもありません。修正した後はこのように記録されます。
Storage class "local-path" not found, trying default storage class
PRの本文にはこの出力を再現したと書きましたが、コントリビューション台帳にはその再現の元の出力が残っていません。上記の2行はPR本文の例を転載したものです。 TT LabクラスターのCDIが詳細レベル3で稼働しているかも確認していません(未確認)。
2. 原因:ソースのどの行か
行番号はPRのベースコミット 9cd4fbdb を基準としています。
pkg/controller/common/util.go650行目: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で「空であってもよい文字列」をポインタで受け取る際によく起こるミスだと思われます。
2つの要素が重なって上記の出力になります。
klog.Infoは引数をfmt.Sprintの方式でつなぎます。Goのfmt.Sprintドキュメントには「2つのオペランドがいずれも文字列ではない場合のみ、その間にスペースを入れる」と記載されています。"Storage class"とポインタの間、ポインタと"not found…"の間は、片方が文字列であるためスペースが入りません。%vでポインタを記録すると、指し示している値ではなくアドレスが出力されます。そのため0x7035abda0020のような値が名前の部分に入ってしまいます。
Info を Infof に変更し、フォーマット文字列を指定すればスペースの問題は解消されます。ポインタは逆参照する必要がありますが、nil である可能性もあるため、このファイルがすでにインポートしていた
k8s.io/utils/ptr の ptr.Deref(storageClassName, "") を使用しました。
3. 何を変えたのか:diffの要点
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 を選んだのは、名前が空文字列のときにも "" と表示されるようにするための意図です。同じ関数にある「デフォルトのストレージクラスがない」ログは引数が文字列1つであるため問題なく、そのままにしておきました。PRの本文には、ログを変更するだけの変更であることと、確認した内容を記載しました。 go vet ./pkg/controller/common/ はクリーンであり、
go test ./pkg/controller/common/ は43個すべて合格しました(golang:1.27.2コンテナ、 -mod=vendor)。Issueは別に立てず、「コードを読んでいて見つけた」と記載しました。
AIの使用については、コミットのフッター Assisted-by: Claude とDCO署名、本文の終わりの「Claudeの助けを借りて見つけて修正し、レビューして上記のログ出力を再現した」という段落で明らかにしました。このコミットもGitHubでは署名されていないコミットに見えます。原則3のSSH署名が抜けているようです。
4. 反応:承認は16時間、その次はe2eが1つ
| 時刻 | 日 |
|---|---|
| 10-20 00:49 | PR公開。prow が release-note-none·size/XS ラベル、レビュアー2名指定 |
| 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 がマージ |
承認は早かったです。2行であり、修正前と修正後のログを並べて示したため、個別に質問することがなかったためだと思われます。このメンテナーは 第7回と 第6回のレビュアーと同じ人物です。
承認後にマージをブロックしたのは、e2eのグループである e2e-ceph-wffc でした。名前からしてCephストレージと遅延バインディング(WaitForFirstConsumer)で実行されるグループだと思われます。失敗は1件だけで、
DataImportCron … with snapshot sources [test_id:10033] というテストでした。546個中545個が合格しました。ログの文字列を2つ変更しただけの変更が、スナップショットソースのDataImportCronのテストを壊す方法はないように思われました。
そのため、まず再テストのコマンド /retest-required を入力したのですが、prow は外部作成者のコマンドであるとして受け付けませんでした。このリポジトリでは、信頼されていない作成者が /ok-to-test で
テストを開始することも、再実行することもできません。数時間後、失敗したテストの名前と 545/546 という数字を記載し、この変更がそのテストに影響しないことを1文で説明した後、誰かに再テストをかけてもらうようにお願いしました。メンテナーは再テストの代わりにそのテストを override し、4時間後にマージされました。
5. 学んだこと:オープンソース貢献のコツ
- 「
Infoにカンマで値をつなぎ合わせるログは改めて確認します。」fmt.Sprintのスペース規則のため、文字列と文字列以外の値を混ぜるとスペースが消えてしまいます。 ここにポインタまで混ざると、値の代わりにアドレスが出力されます。Infofと%q、そしてnilに耐える逆参照がもっとも短い解決策です。 - 「コードリーディングもコントリビューションの源泉です。」 運用上の不便を経験していなくても、構造を説明するためにコードを1行ずつ追っていくと、このようなものが見つかります。見つけたものを台帳の候補リストに書き留めておき、大きなPR(第7回)がレビューを受けている間に、小さく分けて別途送信しました。
- 「外部コントリビューターは再テストの代わりに説明を行います。」 テストを自分で再実行する権限がない場合、メンテナーが判断するための材料を提供することが自分にできることです。失敗したテストの名前、合格数、変更がそのテストに影響しない理由を1つのコメントにまとめました。メンテナーはそれを見て override を選択しました。
- 「コマンドがブロックされたら、ボットの返答を読みます。」
/retest-requiredを入力したのは無駄足でしたが、ボットの返答により、このリポジトリで外部作成者が実行できない作業を正確に知ることができました。第6回で見たKubeVirt本体と同じルールです。
ティーの一言。 「〇〇の倉庫が見つかりませんでした」と書くべきメモ帳に、倉庫名の代わりに「引き出し7035番」と、しかもスペースなしで書かれていました。名前が書かれたメモ用紙を引き出しに入れて、引き出しの番号だけを書き写したんですね。メモ用紙を取り出して名前を読んで書き写すように変更したら、すぐに「いいね」をもらいました。ところが、隣のクラスのテスト用紙が1枚間違っていて待たなければならず、私はもう一度採点してほしいと言うこともできませんでした。そこで「間違っている問題はこちらで、私のメモとは関係ありません」と書き添えてお渡ししたら、先生がそのテスト用紙を合格にしてくださいました。
リンク
すべての資料は2026-10-29に閲覧しました。
- PR: kubevirt/containerized-data-importer#4320 (マージコミット
b2e1e1e8) - 修正したソース(ベースコミット):
pkg/controller/common/util.go650行目, 664行目 - Go 문서:
fmt.Sprint,k8s.io/utils/ptr.Deref - ファイルシステムの空き容量計算をドキュメント化したPR: 第8回、containerized-data-importer#4318