Repository navigation
fix(kubevirt)!: evict non-migratable VMs instead of blocking the drain - #4403
Aleksei Sviridkin (lexfrei) merged 1 commit into
Conversation
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: cozystack/cozystack/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 8 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughThe KubeVirt CR template now sets ChangesKubeVirt eviction strategy
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to API evictions can now proceed for VMs that cannot migrate. Those VMs may also restart during later KubeVirt updates through the configured Evict method; this PR does not itself change the operator version, and no additional merge-blocking issue is established. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
IvanHunters
left a comment
There was a problem hiding this comment.
Verdict
NOT LGTM
The flip also switches on the workload updater's Evict fallback. The release that ships it also bumps the launcher, so the platform upgrade itself will restart every non-migratable VM, and the release note does not say so.
Findings
- [MAJOR]
packages/system/kubevirt/templates/kubevirt-cr.yaml:126, the upgrade that ships this restarts every non-migratable VM, 10 per minute, with no drain
Claim mismatches
[PARTIAL] "Running VMs get this behaviour on upgrade without a restart". The strategy applies without a restart, but the updater restarts non-migratable VMs during that same upgrade (see the finding above).
[PARTIAL] "A VM that sets its own evictionStrategy keeps it". vm-instance renders no evictionStrategy and exposes no value for it, so a tenant who would rather have the drain wait than lose a VM with a node-bound disk has no way to opt out.
Caveats
- Nothing was run on a live cluster. The admitter, updater and migratability behaviour was read from KubeVirt v1.9.0 source. A drain with an RWO VM and a GPU VM through
cozystack-pr-testis still owed. - CPU hotplug under
vmRolloutStrategy: LiveUpdate:handleCPUChangeRequesthas no migratability gate (memory has one,vm.go:3474). A socket increase on a non-migratable VM therefore setsVCPUChange, which the updater now turns into an eviction. A CPU edit then restarts the VM within about a minute. This was reasoned from source, not run. - "comes back according to its
runStrategy" holds for theAlwaysdefault, butvm-instancealso offersRerunOnFailureandManual. If the guest honours the ACPI shutdown within the 30s grace period, the VMI endsSucceededand neither strategy starts it again, so such a VM stays off after an eviction. This was read from source, not run; it may be worth one sentence in the release note. cluster-autoscaler-hetzner/-azurescale-down is no longer refused for nodes that host non-migratable VMs. The launcher pod has nosafe-to-evictannotation in v1.9.0, so what stops a node-local-disk VM from being evicted off a node that is then deleted is CA'sskip-nodes-with-local-storagedefault. I did not verify that here.- Verified: the admitter lines cited in the body, that the PDB controller only deletes, and GPU migratability.
kubernetes-nodespinsExternal, andvm-instanceis the only other VM producer.update_idempotency_test.shPASS under GNU make 4.4.1/sed 4.9 with the trailing comment in place.helm unittest32/32. Reverting the value turns the new test red.
Recommended follow-ups
- Run
cozystack-pr-testfrom v1.6.3 with an RWO VM and a GPU VM running, and watchSuccessfulEvictevents from the workload updater during the upgrade.
| {{- toYaml . | nindent 6 }} | ||
| {{- end }} | ||
| evictionStrategy: LiveMigrate | ||
| evictionStrategy: LiveMigrateIfPossible # a VMI that cannot live-migrate is evicted instead of blocking the node drain |
There was a problem hiding this comment.
[MAJOR] the upgrade that ships this restarts every non-migratable VM, 10 per minute, with no drain
The same CR keeps workloadUpdateMethods: [LiveMigrate, Evict]. In KubeVirt v1.9.0 the workload updater evicts every running VMI whose launcher image is outdated and that is not migratable (workload-updater.go:421-425). Until now that eviction got a 429. After this change it goes through. The v1.9.0 bump (3e370e7) is not in any release tag yet (v1.6.3 ships v1.8.4), so the next platform upgrade makes every VMI outdated in the same step that flips the strategy. Every VM on an RWO disk or with a passthrough GPU/hostDevice is then restarted, 10 per minute, while the upgrade converges, and again on every later KubeVirt bump. The release note says running VMs get the new behaviour without a restart. Either say in the release note that this upgrade restarts non-migratable VMs, or drop Evict from workloadUpdateMethods, or ship the flip in a release that does not also bump the launcher.
dd169cd to
6918234
Compare
7f69afe to
4a7bc86
Compare
The cluster-wide evictionStrategy was LiveMigrate. For a VMI that is not live-migratable (a ReadWriteOnce disk, a passthrough GPU, a host device), virt-api's eviction webhook answers every eviction with 429, so a node drain retries forever and never completes. LiveMigrateIfPossible keeps live migration for every VMI that can migrate and lets the eviction through for the rest. The VM is shut down and started again according to its runStrategy. A VM whose disk is bound to the drained node stays down until the node returns. KubeVirt writes the cluster strategy into a VMI's spec when the VMI is created, so a VMI started before the upgrade keeps LiveMigrate and still blocks the drain until it is restarted once. A drain and a launcher update after a KubeVirt upgrade go through the same eviction, so the workload updater's Evict fallback shuts down non-migratable VMs started under the new strategy on every later KubeVirt upgrade, where it used to get the same 429. Evict stays anyway: for kubernetes-nodes workers, which use evictionStrategy External, that eviction marks the VMI for evacuation, CAPK drains the tenant node and deletes the VMI, and the VM starts it again on the new launcher. The test pins the method list so the fallback is not dropped to avoid the shutdowns. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
4a7bc86 to
25d4a8c
Compare
|
IvanHunters Your finding changed the decision on |
IvanHunters
left a comment
There was a problem hiding this comment.
Verdict
LGTM with non-blocking notes
My earlier finding about the upgrade restarting non-migratable VMs is withdrawn, you were right: the VMI mutator stamps the cluster strategy into the VMI spec at create (defaults.go:208-211, same in v1.8.4 and v1.9.0) and LiveMigrate has been in the CR since 0.30.1, so running VMIs keep it until their next restart. The corrected body and release note match the source. What is left is wording in the new comments and one test suggestion, none of it blocking.
Findings
- [MINOR]
packages/system/kubevirt/templates/kubevirt-cr.yaml:126, the comments state the new behaviour without its upgrade precondition
- [MINOR]
packages/system/kubevirt/templates/kubevirt-cr.yaml:134, the worker comment describes a restart, but with the shipped CAPK and MHC the Machine gets replaced
- [MINOR]
packages/system/kubevirt/tests/kubevirt_cr_test.yaml:41, the new cases pin the CR values, not the eviction behaviour
Claim mismatches
[PARTIAL] "holding the drain forever". True for a bare kubectl drain, but both Talos paths cap it at 5 minutes (DrainTimeout in pkg/kubernetes/kubernetes.go and DefaultDrainTimeout in talosctl's nodedrain, both 5 minutes in v1.13.0), so today a Talos upgrade over such a VM stops waiting after 5m rather than hanging.
[PARTIAL] The CAPK link (machine.go#L414-L454, v0.1.10) is the unpatched upstream; the migratable/non-migratable split cozystack runs comes from cozystack's patches/0001-backport-eviction-live-migration.diff.
[PARTIAL] The body mentions only the VMCannotBeEvicted alert going quiet. Under LiveMigrateIfPossible the kubevirt_vmi_non_evictable metric and virt-handler's "EvictionStrategy is set but vmi is not migratable" Warning event also stop for these VMs, so nothing tells a tenant their VM will restart on the next drain (VMIMigratableOnEviction returns false for them, migrations.go:124-136). Read from source, not run.
Caveats
Not run live by you or me: a drain with a non-migratable VM, the updater restart on a later KubeVirt bump, and the CPU-socket hotplug path. helm unittest passes 33/33 on this head.
| {{- toYaml . | nindent 6 }} | ||
| {{- end }} | ||
| evictionStrategy: LiveMigrate | ||
| evictionStrategy: LiveMigrateIfPossible # a VMI that cannot live-migrate is evicted instead of blocking the node drain |
There was a problem hiding this comment.
[MINOR] the comments state the new behaviour without its upgrade precondition
The inline comment says a non-migratable VMI "is evicted instead of blocking the node drain", and the block at 131-135 says Evict "shuts down VMs that cannot migrate". On an upgraded cluster neither holds for a VMI created before the upgrade: its spec keeps LiveMigrate, the admitter reads that first (migrations.go:116-122), and drains still get 429. Someone debugging a stuck drain reads the opposite. Something like "for VMIs created after this change; restart older ones once" would fix it.
| # Evict shuts down VMs that cannot migrate on a KubeVirt upgrade, but it is | ||
| # also how such a kubernetes-nodes worker (evictionStrategy External) gets | ||
| # the new launcher without a rollout: the eviction marks the VMI for | ||
| # evacuation, CAPK drains the tenant node and deletes the VMI, and the VM |
There was a problem hiding this comment.
[MINOR] the worker comment describes a restart, but with the shipped CAPK and MHC the Machine gets replaced
The CAPK build cozystack ships (patches/0001-backport-eviction-live-migration.diff) has IsTerminal() return true for a VMI that is not migratable and carries EvacuationNodeName (pkg/kubevirt/machine.go:124-131), and the controller turns that into FailureReason on the KubevirtMachine. Its own comment says this is so "the MachineHealthCheck and MachineSet controllers will gracefully take the VM down", and kubernetes-nodes puts an MHC on every group (nodegroup.yaml:573, maxUnhealthy: 50%). So in a group of two or more the worker is remediated into a new Machine, not "the VM starts it again". Pre-existing behaviour, and the conclusion (Evict is still needed) holds, but the comment and the PR body point the reader at the wrong mechanism.
| - notExists: | ||
| path: spec.configuration.migrations | ||
|
|
||
| - it: sets the cluster evictionStrategy to LiveMigrateIfPossible |
There was a problem hiding this comment.
[MINOR] the new cases pin the CR values, not the eviction behaviour
Both go red when the value or Evict is reverted, which is good, but a KubeVirt bump that changes how the mutator or admitter treats LiveMigrateIfPossible stays green. A cheap follow-up: in the vminstance chainsaw suite, give the VM an RWO filesystem PVC, assert the VMI's .spec.evictionStrategy, and POST a dry-run Eviction to the launcher pod. I have not run that step.
<!-- Thank you for making a contribution! Here are some tips for you: - Use Conventional Commits for the PR title: `type(scope): description` - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore - Scopes are not an exhaustive list — pick the most specific scope for the change and extend the list when a genuinely new area appears. Examples: - System components: dashboard, platform, operator, cilium, kube-ovn, linstor, fluxcd, cluster-api - Managed apps: postgres, mariadb, redis, kafka, clickhouse, virtual-machine, kubernetes - Development and maintenance: api, hack, tests, ci, docs, maintenance - Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or add a `BREAKING CHANGE:` footer - If it's a work in progress, consider creating this PR as a draft. - Don't hesistate to ask for opinion and review in the community chats, even if it's still a draft. - Add the label `kind/backport` if it's a bugfix that needs to be backported to a previous version. --> ## What this PR does This fixes two comments in the KubeVirt CR that #4403 got wrong. It follows up on the [review of #4403](#4403 (review)), and I checked both findings against the source first. Only comments change, the rendered CR is the same. The comments didn't say that the new eviction behaviour applies only to VMIs created after the switch. The VMI mutator copies the cluster `evictionStrategy` into the VMI spec at creation (KubeVirt v1.9.0 `pkg/defaults/defaults.go:208-211`), and the eviction admitter reads the spec first. So a non-migratable VMI created before the switch still has `LiveMigrate` and keeps blocking the drain. The workload updater can't evict it either, and it stays on the old launcher until its VM restarts. The worker comment described the wrong mechanism. When a VMI can't migrate and has `evacuationNodeName` set, CAPK's `IsTerminal()` returns true and the controller sets `FailureReason` on the KubevirtMachine. This is upstream CAPK code at the pinned commit, the cozystack patch doesn't touch it. Every kubernetes-nodes group has a MachineHealthCheck, and CAPI v1.10.1 treats `FailureReason` as unhealthy, so the Machine gets replaced. If `maxUnhealthy` holds remediation back (at the default `50%`, a group of one), the VM starts a new VMI in place. CAPK never clears `FailureReason` though, so the Machine stays failed until remediation is allowed. The comment stays on the `evictionStrategy:` line, because `make update` inserts blocks right before it. `helm unittest` passes 33/33 and `update_idempotency_test.sh` passes. `make update` leaves the file byte-identical. The third finding (tests pin values, not behaviour) is tracked in #4422. ### Screenshots <!-- REQUIRED for UI changes: attach screenshots or screen recordings demonstrating the visual impact of your changes. PRs with UI changes without screenshots will not be merged. --> No UI change. ### Downstream repositories <!-- Cozystack is upstream for repositories that are NOT kept in sync with it automatically, and nothing in CI compares the two sides, so a change here can break them silently. Which change forces what, and which file to touch, is the trigger map in docs/agents/contributing.md under "Downstream Repositories". Walk it before you tick anything. IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM TO FILL IN: 1. Walk the trigger map against the actual diff of this PR, file by file. Do not decide from the PR title or from your memory of what you changed. 2. For every repository you tick, open the follow-up PR there and link it on the same line. A ticked box with no link claims work that does not exist, which is worse than an empty box. Search that repository for an open PR or issue covering it first, and link that instead of filing a duplicate. 3. When the follow-up is out of scope, or needs a decision that is not yours to make, open an issue in that repository instead, link that, and say so in the PR body. Do not push a speculative PR into another repository just to fill a line here. 4. Do not tick "No downstream repository is affected" to make the checklist go away. If you are unsure, leave every box empty and say so in the PR body, so a human decides. --> - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: - [ ] [cozystack/community](https://github.com/cozystack/community) - follow-up: ### Release note <!-- Write a release note: - Explain what has changed internally and for users. - Start with the same `type(scope):` prefix as in the PR title - Follow the guidelines at https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md. --> ```release-note docs(kubevirt): the KubeVirt CR comments now say that a VMI created before the evictionStrategy change keeps LiveMigrate until its VM restarts, and that the MachineHealthCheck replaces a non-migratable kubernetes-nodes worker when the workload updater evicts it. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Clarified that existing VM instances retain the eviction strategy they were created with, and may need to be restarted before they can migrate during a drain. * Expanded guidance on how eviction of non-migratable workers affects machine health and replacement, including when remediation is delayed by the `maxUnhealthy` limit. * No rendered configuration values changed. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What this PR does Part of #4422. The e2e suite now asks KubeVirt whether it would let a VM launcher pod be evicted, without evicting it. Until now nothing in e2e evicted a launcher pod, so the drain behaviour set up in #4403 was known only from reading KubeVirt. Once the `vminstance` VM is running, the suite sends a dry-run Eviction (`dryRun=All`) for its virt-launcher pod and checks the answer. Nothing gets deleted. A dry run still goes through the KubeVirt eviction webhook, which is registered with `sideEffects: NoneOnDryRun` in v1.9.0. With `LiveMigrateIfPossible` its answer follows the VMI's `LiveMigratable` condition. That condition depends on storage, so the test reads it instead of assuming it: - `LiveMigratable=True`: the eviction must be refused with the webhook's `Eviction triggered evacuation of VMI "<ns>/<name>"` message (HTTP 429). A drain retries on that while the VM migrates away. - `LiveMigratable=False`: the eviction must be allowed. The old `LiveMigrate` value refused this case with "is not live-migratable". KubeVirt v1.9.0 creates no PodDisruptionBudget for a VMI, so no PDB can refuse it either. - Anything else, including a missing condition, fails the test. The allowed answer alone proves little, because the webhook also lets an eviction through under `None`. So the test also asserts `evictionStrategy: LiveMigrateIfPossible` in the VMI spec, where virt-api copies the cluster value at creation. After the dry run it checks that the launcher pod is the same object and not terminating, and that the VMI has no `evacuationNodeName`. On the PR lane only the `False` branch runs. Post-install prep pins `StorageProfile/local` to RWO because DRBD is off there, and an RWO disk makes the VMI non-migratable. Nightly and tag runs use `replicated`, where I expect the `True` branch, but I have not seen this suite run on that lane yet. A real drain and a workload update after a KubeVirt bump are still open in #4422. I ran the script against a stub `kubectl`. Both expected answers pass, and it fails on every wrong answer I tried, such as a refusal for a non-migratable VMI, a PDB-style 429, a missing condition, two launcher pods, or a replaced pod after the dry run. `chainsaw lint` (v0.2.15) and shellcheck pass. ### Screenshots Not a UI change. ### Downstream repositories - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: - [ ] [cozystack/community](https://github.com/cozystack/community) - follow-up: The diff touches only `hack/e2e-chainsaw/vminstance/` and the suite README, and nothing in the trigger map covers either. ### Release note ```release-note test(vminstance): the e2e suite now dry-run evicts the VM launcher pod and checks the KubeVirt eviction decision against the VMI's LiveMigratable condition. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Added end-to-end coverage for dry-run eviction of a running virtual machine’s launcher pod. The test checks the VM’s live-migration policy and `LiveMigratable` condition, expecting eviction to be refused when the VM is live-migratable and accepted otherwise. * Verified that the dry run leaves the launcher pod unchanged and does not record an evacuation node on the VM. * **Documentation** * Documented the test’s behavior and the local `jq` requirement for running it. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
What this PR does
A node drain now completes when a VM on that node cannot live-migrate. The VM is shut down and started again instead of holding the drain forever. VMs that can migrate still migrate. The change is the cluster-wide
evictionStrategyin the KubeVirt CR, fromLiveMigratetoLiveMigrateIfPossible.The drain is held by virt-api's eviction webhook, not by a PodDisruptionBudget. With
LiveMigrateand a VMI that is not live-migratable, the webhook denies every eviction with HTTP 429 (pod-eviction-admitter.go#L147-L150).kubectl drainretries a 429 until--timeout, which is infinite by default. KubeVirt v1.9.0 does not create PDBs for VMIs at all; its disruption-budget controller only deletes old ones (disruptionbudget.go#L353-L406). WithLiveMigrateIfPossiblethe same webhook lets the eviction through for such a VMI (L152-L155). The VM then comes back according to itsrunStrategy.RWO disks are not the only case. A VM with a passthrough GPU is not migratable either, unless the
VGPULiveMigrationgate is on and the GPU is a mediated device (vm.go#L1310-L1319). The gate is off in Cozystack, so anyvm-instancewithgpusset blocked the drain of its node until now.In KubeVirt a drain and a launcher update go through the same eviction. After a KubeVirt upgrade the workload updater live-migrates outdated VMIs that can migrate and evicts the rest, because
workloadUpdateMethodslistsEvictas the fallback (workload-updater.go#L421-L424). The eviction call (L614-L620) hits the same webhook as a drain. So avm-instanceVM that cannot migrate either blocks both, which is what happens today, or is restarted by both, which is what happens after this PR once the VM runs under the new strategy.This has a cost. A non-migratable VM now restarts on a drain. That covers VMs on RWO storage and VMs with GPU or host device passthrough. The webhook does not look at who asked for the eviction, so this holds for any eviction through the API, not only a drain. A VM whose disk is bound to the drained node, for example on a LINSTOR class without DRBD, stays down until the node is back.
The upgrade itself restarts nothing, and existing VMs switch only after their next restart. KubeVirt writes the cluster strategy into the VMI spec when the VMI is created (defaults.go#L208-L211), and the webhook reads that field first (migrations.go#L116-L122). A VMI that was running before the upgrade keeps
LiveMigrate, so a drain still waits on it and the updater's eviction still gets 429. After one restart the VMI carriesLiveMigrateIfPossible. From then on every KubeVirt bump shuts it down through the updater, in batches of 10 every minute, thebatchEvictionSizeandbatchEvictionIntervalset in the CR (kubevirt-cr.yaml#L139-L140). Reading the source, a CPU socket increase on such a VM undervmRolloutStrategy: LiveUpdategoes the same way, since theVCPUChangecondition sends it to the updater (L320-L325). I have not run that one.I kept
Evicteven so, becausekubernetes-nodesworkers depend on it. They setevictionStrategy: External, and for that strategy the webhook marks the VMI for evacuation instead of evicting it (pod-eviction-admitter.go#L156-L157). CAPK sees the mark on a VMI that cannot migrate, drains the tenant node and deletes the VMI (machine.go#L414-L454). The worker VM hasrunStrategy: Always, so KubeVirt starts it again on the new launcher. That is how a GPU worker or any other non-migratable worker gets the new launcher. WithoutEvictthose workers would stay on the old launcher for good. Worker behaviour does not change with this PR, and the new test pins the method list so the fallback is not dropped later.vm-instancerenders noevictionStrategyand has no value for it, so a tenant has no way to opt out of either restart. KubeVirt would honour a per-VM value over the cluster one. I filed #4419 to expose it.I picked this over the options in the issue because KubeVirt already decides migratability per VMI at eviction time. The platform CR does not need to know anything about storage, and a chart shipping a VM does not have to remember an override. Deriving the strategy from the disk access mode in a chart would also miss the GPU case.
To get the new drain behaviour for a non-migratable VM that is already running, restart it once after the upgrade. Until then a drain of its node still blocks, as it does today.
The comment in the CR is on the same line as the key on purpose.
make updateinserts blocks right before theevictionStrategy:line, so a comment above the key would get separated from it.A VM with
runStrategy: RerunOnFailure,ManualorOncemay stay off after an eviction. If the guest shuts down cleanly on the ACPI request within its grace period, the VMI endsSucceeded.RerunOnFailurerestarts only a failed VMI, andManualandOncerestart nothing on their own (vm.go#L1039-L1149).Oncestays off even after an unclean stop. Only the defaultAlwaysbrings the VM back in every case.What I did not verify. I have not drained a node with such a VM on a live cluster, and I have not watched the updater restart them during an upgrade. Everything above comes from the KubeVirt v1.9.0 and CAPK v0.1.10 source. I did not check how
talosctl upgradehandles a drain that keeps getting 429. I also did not check how the cluster autoscaler treats a node with a non-migratable VM now that its launcher pod can be evicted. The pod has nosafe-to-evictannotation, so for a VM on node-local storage that likely comes down to the autoscaler'sskip-nodes-with-local-storagesetting. Finally, I did not check whether the upstreamVMCannotBeEvictedalert is deployed in Cozystack. For non-migratable VMs it stops firing after this change, since they are evictable now.Closes #4360
Screenshots
No UI change.
Downstream repositories
Release note