Skip to content

rd delete folds the name while its gates compare it byte-exact, so a mixed-case delete strands replicas and snapshots #194

Description

@kvaps

rd d PVC-X against a definition stored as pvc-x deletes the definition and leaves its replicas and snapshots behind, with live DRBD state under them.

The store folds a name when it addresses one object and compares it byte-exact when it lists several. pkg/store/k8s/crdname.go lowercases on the way to the CRD name, so ResourceDefinitions().Delete and Get resolve whatever case the operator typed. Resources().ListByDefinition and Snapshots().ListByDefinition select on spec.resourceDefinitionName and compare it as stored (pkg/store/k8s/resources.go, snapshots.go), and both spellings are legal LINSTOR input.

So on the delete path the snapshot gate lists zero rows and passes, the cascade lists zero replicas and reaps nothing, and the final Delete folds and takes the definition. What is left points at a definition that is gone, and the replica still has DRBD up on its node.

Reproduced on envtest during the review of #191: the gate saw 0 snapshots, Delete returned nil, and one replica and one snapshot survived.

This is not a regression. The merge base compares the same way and has no FoldName at all. It was carried as a review note through several rounds of #191 and is filed here instead, because the door it leaves is destructive rather than a read that answers short.

The fix FoldName's own doc comment describes is a folded field beside the display one, selected on and never rendered, which is a CRD schema change plus a migration for objects adopted from LINSTOR. Folding on the write side instead would fold what clients read back: crdToWireResource reports these spec values as the object's names, and the annotation in crdname.go exists to keep the stored spelling, because linstor-csi and runbooks compare those strings.

Worth knowing for whoever picks this up: the in-memory store keys its objects by the spelling they were written with, so a unit fixture cannot express a stored spelling that differs from the queried one. A test for this needs envtest or the fold has to reach the in-memory store first.

Activity

  1. IvanHunters commented on Sep 16, 2026

    @IvanHunters

    Filed #195 for the same boundary before seeing this one; closing mine as a duplicate. Two details from it that are not here, in case they are useful.

    A reproduction that needs no REST call. The StoragePool CRD's own CEL rule compares with lowerAscii(), so it admits a pool whose spec.nodeName is Node-Case on node node-case. StoragePools().ListByNode(ctx, "node-case") then answers empty, and node delete node-case is not refused. Same root, third kind, and it reaches the node-delete gate rather than the definition-delete one.

    Coordinates at d2c6112, for whoever picks this up:

    • pkg/store/k8s/resources.go:126 selects with the caller's spelling, :127 filters with r.Spec.ResourceDefinitionName == rdName
    • pkg/store/k8s/snapshots.go:103 and :139
    • pkg/store/k8s/resources.go:1002 stores the spec value verbatim as ResourceDefinitionName: in.Name
    • the FoldName doc block starts at pkg/store/store.go:289
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions