Skip to content

fix(csi): recover NodeStageVolume when staging was interrupted before the stash was written - #384

Open
boddumanohar wants to merge 2 commits into
mainfrom
fix/csi-nodestagevolume-orphaned-mount
Open

boddumanohar wants to merge 2 commits into
mainfrom
fix/csi-nodestagevolume-orphaned-mount

Conversation

@boddumanohar

@boddumanohar boddumanohar commented Jul 30, 2026

Copy link
Copy Markdown
Member

Bug

If NodeStageVolume fails after formatting/mounting a volume but before writing its context stash (e.g. a crash/restart mid-call, or tune2fs failing on an unrecognised ext4 feature flag), the mount exists but the stash never gets written. On retry, the driver sees the mount, assumes "already staged", and returns success without ever staging or restaging — the volume is stuck broken. Downstream, NodeUnstageVolume then also permanently fails on the same volume (volume context JSON file not found), since it hard-requires that stash to disconnect the NVMe-oF connection — blocking the pod from ever being rescheduled.

Fix

  • NodeStageVolume: detect the missing stash. If a mount exists but has no stash, force-unmount and fall through to a fresh stage, which rebuilds the VolumeContext and writes the stash.
  • NodeUnstageVolume: if the stash is missing, don't block forever. There's no NQN/model left to safely identify and disconnect the NVMe-oF connection, so proceed with the unstage anyway (leaving a possible connection leak for manual/guardian cleanup) rather than permanently pinning the volume.

Testing

  • go build, go vet, golangci-lint, go test all pass
  • Added a regression test for the NodeUnstageVolume fix
  • Reproduced the NodeStageVolume bug on a live 4-node k3s cluster (deleted the stash file under a healthy mount) and confirmed the fix re-stages instead of silently returning success

🤖 Generated with Claude Code

… the stash was written

If stageVolume() fails after FormatAndMount but before StashVolumeContext
(e.g. tune2fs aborting on an unrecognised ext4 feature flag), the mount
exists but no VolumeContext stash is ever written. On the next
NodeStageVolume retry, isStaged() reports true and IsDead() reports false
(the backing device is often still connected at this point), so the driver
took the "already staged" short-circuit and returned success forever
without ever staging or stashing the volume. restageVolume() also couldn't
recover this case on its own since it requires a stash to already exist.

Detect the missing stash directly: force-unmount and fall through to a
fresh stage, which rebuilds the VolumeContext from the request and writes
the stash on success.

Rebased onto main's csi-driver restructure (#497): the fix now lives in
internal/csi/node/stage.go instead of pkg/spdk/nodeserver.go, using
lookupVolumeContext and ns.mounter.ForceUnmount/IsDead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@boddumanohar
boddumanohar force-pushed the fix/csi-nodestagevolume-orphaned-mount branch from 1e52d68 to 1f95011 Compare September 11, 2026 13:49
… no stash

A volume whose stash was never written (NodeStageVolume interrupted between
FormatAndMount and StashVolumeContext, see the sibling fix in NodeStageVolume)
had no way to ever be unstaged: NodeUnstageVolume hard-required the stash to
rebuild an initiator and disconnect the NVMe-oF connection, so every retry hit
the exact same "volume context JSON file not found" error, forever. That pins
the volume's VolumeAttachment and permanently blocks its pod from being
rescheduled to another node.

There is no NQN or model left to safely identify and tear down the volume's
NVMe-oF connection without the stash, so guessing at it risks disconnecting
the wrong thing on a shared subsystem. Instead, proceed with the unstage and
leave a potential connection leak for manual or guardian-driven cleanup:
blocking a volume forever is worse than a leak that can be found and cleared
later.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant