fix(vm-instance): emit empty disk object so KubeVirt accepts disks without bus - #2643
fix(vm-instance): emit empty disk object so KubeVirt accepts disks without bus#2643myasnikovdaniil wants to merge 1 commit into
Conversation
…thout bus
When a disk entry has no bus set (the schema default), the previous
template rendered `disk:` with no value — YAML parses that as
`disk: null` and KubeVirt's virtualmachines-mutator webhook denies the
request with: "spec.template.spec.domain.devices.disks.disk in body
must be of type object: null".
Restructure the disk loop so the disk/cdrom value is always a valid
object: `disk: {}` when bus is empty, `disk: {bus: <x>}` when set.
Mirrors how `cloudinitdisk` already does it in the same template.
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a deployment failure where VMInstances with disks lacking an explicit bus configuration were rejected by the KubeVirt admission webhook. By ensuring that the disk and cdrom fields are rendered as empty objects rather than null when the bus is unset, the generated manifests now comply with the expected KubeVirt schema. Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request refactors the disk configuration logic in the VM instance template by introducing variables for device types and simplifying the conditional blocks. Feedback was provided regarding the safe access of Kubernetes annotations to prevent template rendering errors when the field is missing. Additionally, it was recommended to quote the disk bus value to ensure correct YAML parsing and string representation.
| {{- if $dv }} | ||
| {{- if and (hasKey $dv.metadata.annotations "vm-disk.cozystack.io/optical") (eq (index $dv.metadata.annotations "vm-disk.cozystack.io/optical") "true") }} | ||
| cdrom: | ||
| {{- $isOptical := and (hasKey $dv.metadata.annotations "vm-disk.cozystack.io/optical") (eq (index $dv.metadata.annotations "vm-disk.cozystack.io/optical") "true") }} |
There was a problem hiding this comment.
Using hasKey on metadata.annotations can be risky because the annotations field is optional in Kubernetes objects. If it is missing (nil), hasKey will cause a template rendering error. It is safer to use the dig function to navigate the object path with a default value.
{{- $isOptical := eq (dig "metadata" "annotations" "vm-disk.cozystack.io/optical" "" $dv) "true" }}References
- Use defensive programming to safely handle potentially missing fields in Kubernetes objects (like annotations) to avoid template rendering panics.
| {{- $deviceKey := ternary "cdrom" "disk" $isOptical }} | ||
| {{- if $disk.bus }} | ||
| {{ $deviceKey }}: | ||
| bus: {{ $disk.bus }} |
There was a problem hiding this comment.
The bus value should be quoted to ensure it is always treated as a string in the rendered YAML, especially to avoid issues with values that might be interpreted as booleans or numbers by some YAML parsers.
bus: {{ $disk.bus | quote }}References
- Helm template correctness: missing quote (link)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR refactors the KubeVirt disk device stanza generation in the VM Helm template to use computed variables for cleaner device type selection and conditional bus field inclusion. ChangesDisk device generation refactoring
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM — there is nothing left to merge: this branch's only commit already landed on main verbatim, so merging now would only add an empty merge commit and a duplicate history entry. This PR should be closed as superseded, not merged.
Business context: a VMInstance disk without bus rendered disk: with no value (disk: null), which the KubeVirt admission webhook rejects; the fix renders disk: {} / cdrom: {} instead.
Why close
The identical change shipped to main as commit 2e3e7bdb3 (authorship preserved: Myasnikov Daniil), picked up by #2602's strict-SSA fix series.
Evidence: git patch-id --stable yields the same hash (1f10c2bf...) for this branch's 0f3d2fa3a and main's 2e3e7bdb3; 2e3e7bdb3 is an ancestor of origin/main; git diff 0f3d2fa3a origin/main -- packages/apps/vm-instance/templates/vm.yaml is empty.
The fix itself is correct and has been live on main for ~4 weeks: the $isOptical + ternary refactor is semantically equivalent to the old branch, and disk: {} lets KubeVirt default the bus.
Follow-up material (against main, not this PR)
packages/apps/vm-instanceships no helm-unittest coverage at all (unlike postgres/kafka/kubernetes/mongodb/etc.). The null-vs-{}regression is exactly what a render test pins; helm-unittest can mocklookupviakubernetesProvider.objects(required here — without it the template hits thefail "Specified disk not exists in cluster"branch). Minimal set: disk withoutbus→disk == {}; disk withbus: virtio→disk.bus: virtio; DataVolume annotatedvm-disk.cozystack.io/optical: "true"→cdromkey.hasKey $dv.metadata.annotations ...(vm.yaml:70) is nil-unsafe: a DataVolume with no annotations at all fails the whole render withwrong type for value; expected map[string]interface {}. DVs created by the vm-disk chart always carry the annotation, so in-platform risk is low, but a manually createdvm-disk-*DV breaks the VM render. Nil-safe form:eq (dig "metadata" "annotations" "vm-disk.cozystack.io/optical" "" $dv) "true".
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM — there is nothing left to merge. This branch's only commit is byte-identical to current main, and the same change already shipped to main independently, so merging now would add only a no-op merge commit and a duplicate history entry. Close as superseded.
Business context: a VMInstance disk without an explicit bus rendered disk: with no value (disk: null), which the KubeVirt admission webhook rejects; the fix emits disk: {} / cdrom: {} instead so KubeVirt can default the bus.
Blockers
- The fix is already on
main; this PR is a no-op. Evidence:git diff origin/main HEAD -- packages/apps/vm-instance/templates/vm.yamlis empty (head content equals currentmainat374cac106);git patch-id --stableyields the same hash1f10c2bf…for this branch's0f3d2fa3aand main's2e3e7bdb3;git merge-base --is-ancestor 2e3e7bdb3 origin/mainsucceeds. Commit2e3e7bdb3(same author, 2026-05-13) is already contained inmain.
The fix itself is correct, which is why closing rather than reworking is the right move: disk: {} is a valid DiskTarget and lets KubeVirt default the bus, whereas disk: null fails the object-type schema check. All four shapes render valid output — disk/cdrom × bus-set/bus-unset — and the $isOptical + ternary refactor is behavior-preserving. Evidence: helm template over disks: [{name: a}, {name: b, bus: sata}] (one annotated optical) renders disk: {} for the no-bus disk and cdrom: + bus: sata for the optical one.
Non-blocking (these apply to the live main code, not introduced by this PR)
-
packages/apps/vm-instanceships no helm-unittest coverage, so the null-vs-{}regression has no pinning test. Evidence: notests/directory and no*_test.yamlexists underpackages/apps/vm-instance. A render test can mocklookupviakubernetesProvider.objects. Minimal set: disk withoutbus→disk == {}; disk withbus: virtio→disk.bus == virtio; DataVolume annotatedvm-disk.cozystack.io/optical: "true"→cdromkey emitted. -
hasKey $dv.metadata.annotations …is nil-unsafe: a DataVolume with noannotationsat all aborts the whole render withwrong type for value; expected map[string]interface {}. In-platform DataVolumes always carry the annotation, so risk is low, but a manually createdvm-disk-*DataVolume breaks the VM render. Evidence:packages/apps/vm-instance/templates/vm.yaml:70onmain. Nil-safe form:eq (dig "metadata" "annotations" "vm-disk.cozystack.io/optical" "" $dv) "true".
|
Confirmed superseded — this branch's only commit ( |
Problem
A VMInstance with disks whose
busfield is unset (the schema default —busis optional, no default value) fails to deploy. The KubeVirt admission webhook rejects the rendered VirtualMachine with:Root cause
packages/apps/vm-instance/templates/vm.yamlrenders the disk entry as:YAML parses
disk:(no value) asdisk: null. KubeVirt'sDiskTargetschema requires an object, so the mutator rejects it.The template only attached a child (
bus: …) when$disk.buswas non-empty, so the empty-bus path produced a key with no value.Reproduction
On dev10:
→
kubectl get vminstance test -n tenant-rootshowsInstallFailedwith the exact admission-webhook message above.Fix
Restructure the disk loop so the
disk/cdromvalue is always a valid object:busset →disk: { bus: <x> }busempty →disk: {}This mirrors how the same template already renders
cloudinitdisk. Both thediskandcdrombranches get the fix.Verification
helm install --dry-run=serveragainst dev10 (KubeVirt v1.6.3) withdisks: [{ name: test-ui }](no bus):disk: null— admission webhook rejects on real install.disk: {}— install succeeds;dry-run=serverreturns the full manifest, no admission error.helm install --dry-run=serverwithdisks: [{ name: test-ui, bus: sata }]:disk:\n bus: sata— install succeeds.Release note
```release-note
fix(vm-instance): emit "disk: {}" instead of "disk: null" so VMInstances with disks lacking an explicit bus type are accepted by the KubeVirt admission webhook.
```
Summary by CodeRabbit