Skip to content

Fail fast with clear error when data mover pod is unschedulable - #10276

Open
kaovilai wants to merge 3 commits into
velero-io:mainfrom
kaovilai:datamover-unschedulable-early-error
Open

Fail fast with clear error when data mover pod is unschedulable#10276
kaovilai wants to merge 3 commits into
velero-io:mainfrom
kaovilai:datamover-unschedulable-early-error

Conversation

@kaovilai

Copy link
Copy Markdown
Member

Summary

Fixes #9735: when a data-mover exposer pod (CSI snapshot backup/restore, or pod-volume backup/restore) is unschedulable because its required node affinity (typically driven by node-agent loadAffinity) can't be satisfied by any node, Velero currently waits out the full preparing/operation timeout (10+ minutes by default) before failing with a generic "timeout on preparing data upload" message and no indication of the actual scheduling problem.

A prior fix (#9697) intentionally removed a blanket Unschedulable-phase check from IsPodUnrecoverable, because Unschedulable isn't always permanent — insufficient CPU/memory can resolve via cluster autoscaling, untolerated taints can be removed, another pod can complete and free resources. This PR does not reintroduce that generic check. Instead it adds a narrower, targeted one:

  • kube.IsPodUnschedulableDueToNodeAffinity checks whether the pod's PodScheduled condition is False and whether its required node affinity is satisfiable by any node currently in the cluster. Node affinity is purely label-based (unlike resource requests), so if zero nodes' labels satisfy it right now, waiting cannot change that — it's a permanent scheduling mismatch, most commonly a stale/incorrect loadAffinity config.
  • Combined with the existing check via kube.IsPodUnrecoverableOrUnschedulable, wired into all three exposer PeekExposed implementations (CSI snapshot, generic restore, pod-volume) and all four pod-watch controller handlers (DataUpload, DataDownload, PodVolumeBackup, PodVolumeRestore) — all four VGDP paths share the same loadAffinity/ToSystemAffinity mechanism, so the bug and the fix are both shared across them.
  • Node-selector-term matching (In/NotIn/Exists/DoesNotExist) is hand-rolled rather than pulling in k8s.io/component-helpers as a new dependency; unknown/unimplemented operators (Gt/Lt) default to "might be satisfiable" to avoid false positives.

Test plan

  • New unit tests in pkg/util/kube/pod_test.go: TestIsPodUnschedulableDueToNodeAffinity (condition-true no-op, no-affinity no-op, zero-node-match → true, some-node-matches → false) and TestIsPodUnrecoverableOrUnschedulable (fall-through, precedence/short-circuit, healthy-pod no-op).
  • go build ./..., gofmt/goimports clean, go test ./pkg/util/kube/... ./pkg/exposer/... ./pkg/controller/... passing (envtest-only TestAPIs skipped locally — missing kubebuilder binary in this sandbox, unrelated to this change).

Fixes #9735

Note

Responses generated with Claude

A prior fix (velero-io#9697) removed a blanket "Unschedulable" phase check from
IsPodUnrecoverable because Unschedulable isn't always permanent (e.g.
insufficient CPU/memory can resolve via autoscaling, untolerated
taints can be removed). That left node-affinity misconfigurations
(most commonly a node-agent loadAffinity referencing labels no node
actually has) to run out the full preparing/operation timeout before
failing with a generic "timeout on preparing data upload" message.

Adds a narrower, targeted check: IsPodUnschedulableDueToNodeAffinity
verifies whether the pod's required node affinity can be satisfied by
ANY node currently in the cluster. Unlike generic scheduling failures,
node affinity is purely label-based - if zero nodes' labels satisfy it
right now, no amount of waiting on existing nodes changes that, so
it's safe to report immediately instead of waiting out the timeout.
Wired into all three exposer PeekExposed implementations and all four
pod-watch controller handlers (DataUpload, DataDownload,
PodVolumeBackup, PodVolumeRestore) via a combined
IsPodUnrecoverableOrUnschedulable helper, since all four VGDP paths
share the same loadAffinity mechanism.

Fixes velero-io#9735

> [!Note]
> Responses generated with Claude

Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
@netlify

netlify Bot commented Aug 14, 2026

Copy link
Copy Markdown

Deploy Preview for velero ready!

Name Link
🔨 Latest commit 4fc24dc
🔍 Latest deploy log https://app.netlify.com/projects/velero/deploys/6a8460f884b61700082ab7cc
😎 Deploy Preview https://deploy-preview-10276--velero.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

A one-time node-list snapshot could race with a node that's mid-join
(e.g. a cluster-autoscaler-provisioned node with matching labels that
hasn't registered yet) or an admin mid-relabel, producing a false
permanent verdict - the same trap velero-io#9697 hit with a blanket
Unschedulable check. Gate on the PodScheduled condition's own
LastTransitionTime: only report once it's held False for at least
unschedulableNodeAffinityGracePeriod (2m), which still fails much
faster than the default ~10 minute preparing timeout while riding out
ordinary node-join latency.

Also documents (rather than implements) why MatchFields and Gt/Lt
node-selector operators are out of scope: the only producer of node
affinity for these pods is Velero's own ToSystemAffinity, which builds
requirements exclusively from a metav1.LabelSelector (an enum with no
Gt/Lt) plus CSI topology requirements (always equality-based) -
neither MatchFields nor Gt/Lt can occur in practice for this caller.

Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

@kaovilai
kaovilai marked this pull request as ready for review August 18, 2026 03:17
@kaovilai
kaovilai requested a review from a team as a code owner August 18, 2026 03:17
Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Velero should add a clear error when data mover pod is unschedulable due to affinity/topology mismatch

1 participant