Skip to content

Commit 9fe0e17

Browse files
committed
Support any number of node names in affinity.
When we process our PVs, We shouldn't care about the amount of nodes. If none of them exists we can just clear it out and if any one of them does exist, we should skip it.
1 parent ad20e13 commit 9fe0e17

File tree

4 files changed

+55
-120
lines changed

4 files changed

+55
-120
lines changed

pkg/common/common.go

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import (
4444
"k8s.io/client-go/rest"
4545
"k8s.io/client-go/tools/clientcmd"
4646
"k8s.io/client-go/tools/record"
47-
volumeUtil "k8s.io/kubernetes/pkg/volume/util"
4847
"k8s.io/utils/mount"
4948
)
5049

@@ -495,44 +494,25 @@ func GetVolumeMode(volUtil util.VolumeUtil, fullPath string) (v1.PersistentVolum
495494
return "", fmt.Errorf("Block device check for %q failed: %s", fullPath, errblk)
496495
}
497496

498-
// NodeExists checks to see if a Node exists in the Indexer of a NodeLister.
499-
// It tries to get the node and if it fails, it uses the well known label
500-
// `kubernetes.io/hostname` to find the Node.
501-
func NodeExists(nodeLister corelisters.NodeLister, nodeName string) (bool, error) {
502-
_, err := nodeLister.Get(nodeName)
503-
if errors.IsNotFound(err) {
497+
// AnyNodeExists checks to see if a Node exists in the Indexer of a NodeLister.
498+
// If this fails, it uses the well known label `kubernetes.io/hostname` to find the Node.
499+
// It aborts early if an unexpected error occurs and it's uncertain if a node would exist or not.
500+
func AnyNodeExists(nodeLister corelisters.NodeLister, nodeNames []string) bool {
501+
for _, nodeName := range nodeNames {
502+
_, err := nodeLister.Get(nodeName)
503+
if err == nil || !errors.IsNotFound(err) {
504+
return true
505+
}
504506
req, err := labels.NewRequirement(NodeLabelKey, selection.Equals, []string{nodeName})
505507
if err != nil {
506-
return false, err
508+
return true
507509
}
508510
nodes, err := nodeLister.List(labels.NewSelector().Add(*req))
509-
if err != nil {
510-
return false, err
511+
if err != nil || len(nodes) > 0 {
512+
return true
511513
}
512-
return len(nodes) > 0, nil
513514
}
514-
return err == nil, err
515-
}
516-
517-
// NodeAttachedToLocalPV gets the name of the Node that a local PV has a NodeAffinity to.
518-
// It assumes that there should be only one matching Node for a local PV and that
519-
// the local PV follows the form:
520-
//
521-
// nodeAffinity:
522-
// required:
523-
// nodeSelectorTerms:
524-
// - matchExpressions:
525-
// - key: kubernetes.io/hostname
526-
// operator: In
527-
// values:
528-
// - <node1>
529-
func NodeAttachedToLocalPV(pv *v1.PersistentVolume) (string, bool) {
530-
nodeNames := volumeUtil.GetLocalPersistentVolumeNodeNames(pv)
531-
// We assume that there should only be one matching node.
532-
if nodeNames == nil || len(nodeNames) != 1 {
533-
return "", false
534-
}
535-
return nodeNames[0], true
515+
return false
536516
}
537517

538518
// IsLocalPVWithStorageClass checks that a PV is a local PV that belongs to any of the passed in StorageClasses.

pkg/common/common_test.go

Lines changed: 24 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ func TestGetVolumeMode(t *testing.T) {
477477
}
478478
}
479479

480-
func TestNodeExists(t *testing.T) {
480+
func TestAnyNodeExists(t *testing.T) {
481481
nodeName := "test-node"
482482
nodeWithName := &v1.Node{
483483
ObjectMeta: metav1.ObjectMeta{
@@ -495,21 +495,39 @@ func TestNodeExists(t *testing.T) {
495495
tests := []struct {
496496
nodeAdded *v1.Node
497497
// Required.
498-
nodeQueried *v1.Node
498+
nodeQueried []string
499499
expectedResult bool
500500
}{
501501
{
502502
nodeAdded: nodeWithName,
503-
nodeQueried: nodeWithName,
503+
nodeQueried: []string{nodeName},
504504
expectedResult: true,
505505
},
506506
{
507507
nodeAdded: nodeWithLabel,
508-
nodeQueried: nodeWithName,
508+
nodeQueried: []string{nodeName},
509509
expectedResult: true,
510510
},
511511
{
512-
nodeQueried: nodeWithName,
512+
nodeQueried: []string{nodeName},
513+
expectedResult: false,
514+
},
515+
{
516+
nodeAdded: nodeWithName,
517+
nodeQueried: []string{"other-node", nodeName},
518+
expectedResult: true,
519+
},
520+
{
521+
nodeAdded: nodeWithLabel,
522+
nodeQueried: []string{"other-node", nodeName},
523+
expectedResult: true,
524+
},
525+
{
526+
nodeQueried: []string{},
527+
expectedResult: false,
528+
},
529+
{
530+
nodeQueried: nil,
513531
expectedResult: false,
514532
},
515533
}
@@ -523,62 +541,13 @@ func TestNodeExists(t *testing.T) {
523541
nodeInformer.Informer().GetStore().Add(test.nodeAdded)
524542
}
525543

526-
exists, err := NodeExists(nodeInformer.Lister(), test.nodeQueried.Name)
527-
if err != nil {
528-
t.Errorf("Got unexpected error: %s", err.Error())
529-
}
544+
exists := AnyNodeExists(nodeInformer.Lister(), test.nodeQueried)
530545
if exists != test.expectedResult {
531546
t.Errorf("expected result: %t, actual: %t", test.expectedResult, exists)
532547
}
533548
}
534549
}
535550

536-
func TestNodeAttachedToLocalPV(t *testing.T) {
537-
nodeName := "testNodeName"
538-
539-
tests := []struct {
540-
name string
541-
pv *v1.PersistentVolume
542-
expectedNodeName string
543-
expectedStatus bool
544-
}{
545-
{
546-
name: "NodeAffinity will all necessary fields",
547-
pv: withNodeAffinity(pv(), []string{nodeName}, NodeLabelKey),
548-
expectedNodeName: nodeName,
549-
expectedStatus: true,
550-
},
551-
{
552-
name: "empty nodeNames array",
553-
pv: withNodeAffinity(pv(), []string{}, NodeLabelKey),
554-
expectedNodeName: "",
555-
expectedStatus: false,
556-
},
557-
{
558-
name: "multiple nodeNames",
559-
pv: withNodeAffinity(pv(), []string{nodeName, "newNode"}, NodeLabelKey),
560-
expectedNodeName: "",
561-
expectedStatus: false,
562-
},
563-
{
564-
name: "wrong node label key",
565-
pv: withNodeAffinity(pv(), []string{nodeName}, "wrongLabel"),
566-
expectedNodeName: "",
567-
expectedStatus: false,
568-
},
569-
}
570-
571-
for _, test := range tests {
572-
nodeName, ok := NodeAttachedToLocalPV(test.pv)
573-
if ok != test.expectedStatus {
574-
t.Errorf("test: %s, status: %t, expectedStaus: %t", test.name, ok, test.expectedStatus)
575-
}
576-
if nodeName != test.expectedNodeName {
577-
t.Errorf("test: %s, nodeName: %s, expectedNodeName: %s", test.name, nodeName, test.expectedNodeName)
578-
}
579-
}
580-
}
581-
582551
func TestIsLocalPVWithStorageClass(t *testing.T) {
583552
tests := []struct {
584553
name string

pkg/node-cleanup/controller/controller.go

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"k8s.io/client-go/tools/record"
3737
"k8s.io/client-go/util/workqueue"
3838
"k8s.io/klog/v2"
39+
volumeUtil "k8s.io/kubernetes/pkg/volume/util"
3940

4041
"sigs.k8s.io/sig-storage-local-static-provisioner/pkg/common"
4142
cleanupmetrics "sigs.k8s.io/sig-storage-local-static-provisioner/pkg/metrics/node-cleanup"
@@ -196,18 +197,15 @@ func (c *CleanupController) syncHandler(ctx context.Context, pvName string) erro
196197
return err
197198
}
198199

199-
nodeName, ok := common.NodeAttachedToLocalPV(pv)
200-
if !ok {
200+
nodeNames := volumeUtil.GetLocalPersistentVolumeNodeNames(pv)
201+
if nodeNames == nil {
201202
// For whatever reason the PV isn't formatted properly so we will
202203
// never be able to get its corresponding Node, so ignore.
203204
klog.Errorf("error getting node attached to pv: %s", pv)
204205
return nil
205206
}
206207

207-
nodeExists, err := common.NodeExists(c.nodeLister, nodeName)
208-
if err != nil {
209-
return err
210-
}
208+
nodeExists := common.AnyNodeExists(c.nodeLister, nodeNames)
211209
// Check that the node the PV/PVC reference is still deleted
212210
if nodeExists {
213211
return nil
@@ -242,7 +240,7 @@ func (c *CleanupController) syncHandler(ctx context.Context, pvName string) erro
242240
}
243241

244242
cleanupmetrics.PersistentVolumeClaimDeleteTotal.Inc()
245-
klog.Infof("Deleted PVC %q that pointed to Node %q", pvClaimRef.Name, nodeName)
243+
klog.Infof("Deleted PVC %q that pointed to non-existent Nodes %q", pvClaimRef.Name, nodeNames)
246244
return nil
247245
}
248246

@@ -264,18 +262,13 @@ func (c *CleanupController) startCleanupTimersIfNeeded() {
264262
continue
265263
}
266264

267-
nodeName, ok := common.NodeAttachedToLocalPV(pv)
268-
if !ok {
265+
nodeNames := volumeUtil.GetLocalPersistentVolumeNodeNames(pv)
266+
if nodeNames == nil {
269267
klog.Errorf("error getting node attached to pv: %s", pv)
270268
continue
271269
}
272270

273-
shouldEnqueue, err := c.shouldEnqueueEntry(pv, nodeName)
274-
if err != nil {
275-
klog.Errorf("error determining whether to enqueue entry with pv %q: %v", pv.Name, err)
276-
continue
277-
}
278-
271+
shouldEnqueue := c.shouldEnqueueEntry(pv, nodeNames)
279272
if shouldEnqueue {
280273
klog.Infof("Starting timer for resource deletion, resource:%s, timer duration: %s", pv.Spec.ClaimRef, c.pvcDeletionDelay.String())
281274
c.eventRecorder.Event(pv.Spec.ClaimRef, v1.EventTypeWarning, "ReferencedNodeDeleted", fmt.Sprintf("PVC is tied to a deleted Node. PVC will be cleaned up in %s if the Node doesn't come back", c.pvcDeletionDelay.String()))
@@ -288,13 +281,12 @@ func (c *CleanupController) startCleanupTimersIfNeeded() {
288281
// shouldEnqueuePV checks if a PV should be enqueued to the entryQueue.
289282
// The PV must be a local PV, have a StorageClass present in the list of storageClassNames, have a NodeAffinity
290283
// to a deleted Node, and have a PVC bound to it (otherwise there's nothing to clean up).
291-
func (c *CleanupController) shouldEnqueueEntry(pv *v1.PersistentVolume, nodeName string) (bool, error) {
284+
func (c *CleanupController) shouldEnqueueEntry(pv *v1.PersistentVolume, nodeNames []string) bool {
292285
if pv.Spec.ClaimRef == nil {
293-
return false, nil
286+
return false
294287
}
295288

296-
exists, err := common.NodeExists(c.nodeLister, nodeName)
297-
return !exists && err == nil, err
289+
return !common.AnyNodeExists(c.nodeLister, nodeNames)
298290
}
299291

300292
// deletePVC deletes the PVC with the given name and namespace

pkg/node-cleanup/deleter/deleter.go

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package deleter
1818

1919
import (
2020
"context"
21-
"fmt"
2221
"time"
2322

2423
v1 "k8s.io/api/core/v1"
@@ -28,6 +27,7 @@ import (
2827
"k8s.io/client-go/kubernetes"
2928
corelisters "k8s.io/client-go/listers/core/v1"
3029
"k8s.io/klog/v2"
30+
volumeUtil "k8s.io/kubernetes/pkg/volume/util"
3131

3232
"sigs.k8s.io/sig-storage-local-static-provisioner/pkg/common"
3333
cleanupmetrics "sigs.k8s.io/sig-storage-local-static-provisioner/pkg/metrics/node-cleanup"
@@ -82,12 +82,7 @@ func (d *Deleter) DeletePVs(ctx context.Context) {
8282
continue
8383
}
8484

85-
referencesDeletedNode, err := d.referencesNonExistentNode(pv)
86-
if err != nil {
87-
klog.Errorf("error determining if pv %q references deleted node: %v", pv.Name, err)
88-
continue
89-
}
90-
if !referencesDeletedNode {
85+
if !d.referencesNonExistentNode(pv) {
9186
// PV's node is up so PV is not stale
9287
continue
9388
}
@@ -124,14 +119,13 @@ func (d *Deleter) DeletePVs(ctx context.Context) {
124119
// operator: In
125120
// values:
126121
// - <node1>
127-
func (d *Deleter) referencesNonExistentNode(localPV *v1.PersistentVolume) (bool, error) {
128-
nodeName, ok := common.NodeAttachedToLocalPV(localPV)
129-
if !ok {
130-
return false, fmt.Errorf("Error retrieving node")
122+
func (d *Deleter) referencesNonExistentNode(localPV *v1.PersistentVolume) bool {
123+
nodeNames := volumeUtil.GetLocalPersistentVolumeNodeNames(localPV)
124+
if nodeNames == nil {
125+
return false
131126
}
132127

133-
exists, err := common.NodeExists(d.nodeLister, nodeName)
134-
return !exists && err == nil, err
128+
return !common.AnyNodeExists(d.nodeLister, nodeNames)
135129
}
136130

137131
func (d *Deleter) deletePV(ctx context.Context, pvName string) error {

0 commit comments

Comments
 (0)