Skip to content

Commit 75cae8c

Browse files
committed
capacity: fix handling of topology changes with immediate binding SCs
A copy-and-pasted "return" instead of "continue" caused processing to stop after encountering a storage class with immediate binding, which had the effect that further CSIStorageCapacity objects for other storage classes never got created. This only happened when topology changed at runtime. When topology was already known when the controller started, all expected objects got created. (cherry picked from commit 9352d21)
1 parent c47eb1d commit 75cae8c

File tree

2 files changed

+194
-1
lines changed

2 files changed

+194
-1
lines changed

pkg/capacity/capacity.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ func (c *Controller) onTopologyChanges(added []*topology.Segment, removed []*top
299299
continue
300300
}
301301
if !c.immediateBinding && sc.VolumeBindingMode != nil && *sc.VolumeBindingMode == storagev1.VolumeBindingImmediate {
302-
return
302+
continue
303303
}
304304
for _, segment := range added {
305305
c.addWorkItem(segment, sc)

pkg/capacity/capacity_test.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func TestController(t *testing.T) {
9595
expectedCapacities []testCapacity
9696
modify func(ctx context.Context, clientSet *fakeclientset.Clientset, expected []testCapacity) (modifiedExpected []testCapacity, err error)
9797
capacityChange func(ctx context.Context, storage *mockCapacity, expected []testCapacity) (modifiedExpected []testCapacity)
98+
topologyChange func(ctx context.Context, topology *mockTopology, expected []testCapacity) (modifiedExpected []testCapacity)
9899
}{
99100
"empty": {
100101
expectedCapacities: []testCapacity{},
@@ -764,6 +765,162 @@ func TestController(t *testing.T) {
764765
return expected
765766
},
766767
},
768+
"add storage topology segment": {
769+
topology: mockTopology{},
770+
storage: mockCapacity{
771+
capacity: map[string]interface{}{
772+
// This matches layer0.
773+
"foo": "1Gi",
774+
},
775+
},
776+
initialSCs: []testSC{
777+
// We intentionally create a SC with immediate binding first.
778+
// It needs to be skipped while still processing the other one.
779+
// Ordering of the objects is not guaranteed, but in practice
780+
// the informer seems to be "first in, first out", which is what
781+
// we need.
782+
{
783+
name: "immediate-sc",
784+
driverName: driverName,
785+
immediateBinding: true,
786+
},
787+
{
788+
name: "late-sc",
789+
driverName: driverName,
790+
},
791+
},
792+
expectedCapacities: nil,
793+
topologyChange: func(ctx context.Context, topo *mockTopology, expected []testCapacity) []testCapacity {
794+
topo.modify([]*topology.Segment{&layer0} /* added */, nil /* removed */)
795+
return append(expected, testCapacity{
796+
uid: "CSISC-UID-1",
797+
resourceVersion: csiscRev + "0",
798+
segment: layer0,
799+
storageClassName: "late-sc",
800+
quantity: "1Gi",
801+
})
802+
},
803+
},
804+
"add storage topology segment, immediate binding": {
805+
immediateBinding: true,
806+
topology: mockTopology{},
807+
storage: mockCapacity{
808+
capacity: map[string]interface{}{
809+
// This matches layer0.
810+
"foo": "1Gi",
811+
},
812+
},
813+
initialSCs: []testSC{
814+
{
815+
name: "immediate-sc",
816+
driverName: driverName,
817+
immediateBinding: true,
818+
},
819+
{
820+
name: "late-sc",
821+
driverName: driverName,
822+
},
823+
},
824+
expectedCapacities: nil,
825+
topologyChange: func(ctx context.Context, topo *mockTopology, expected []testCapacity) []testCapacity {
826+
topo.modify([]*topology.Segment{&layer0} /* added */, nil /* removed */)
827+
// We don't check the UID here because we don't want to fail when
828+
// ordering of storage classes isn't such that the "immediate-sc" is seen first.
829+
return append(expected, testCapacity{
830+
resourceVersion: csiscRev + "0",
831+
segment: layer0,
832+
storageClassName: "immediate-sc",
833+
quantity: "1Gi",
834+
},
835+
testCapacity{
836+
resourceVersion: csiscRev + "0",
837+
segment: layer0,
838+
storageClassName: "late-sc",
839+
quantity: "1Gi",
840+
},
841+
)
842+
},
843+
},
844+
"remove storage topology segment": {
845+
topology: mockTopology{
846+
segments: []*topology.Segment{&layer0},
847+
},
848+
storage: mockCapacity{
849+
capacity: map[string]interface{}{
850+
// This matches layer0.
851+
"foo": "1Gi",
852+
},
853+
},
854+
initialSCs: []testSC{
855+
{
856+
name: "immediate-sc",
857+
driverName: driverName,
858+
immediateBinding: true,
859+
},
860+
{
861+
name: "late-sc",
862+
driverName: driverName,
863+
},
864+
},
865+
expectedCapacities: []testCapacity{
866+
{
867+
uid: "CSISC-UID-1",
868+
resourceVersion: csiscRev + "0",
869+
segment: layer0,
870+
storageClassName: "late-sc",
871+
quantity: "1Gi",
872+
},
873+
},
874+
topologyChange: func(ctx context.Context, topo *mockTopology, expected []testCapacity) []testCapacity {
875+
topo.modify(nil /* added */, topo.segments[:] /* removed */)
876+
return nil
877+
},
878+
},
879+
"add and remove storage topology segment": {
880+
topology: mockTopology{
881+
segments: []*topology.Segment{&layer0},
882+
},
883+
storage: mockCapacity{
884+
capacity: map[string]interface{}{
885+
// This matches layer0.
886+
"foo": "1Gi",
887+
"bar": "2Gi",
888+
},
889+
},
890+
initialSCs: []testSC{
891+
{
892+
name: "immediate-sc",
893+
driverName: driverName,
894+
immediateBinding: true,
895+
},
896+
{
897+
name: "late-sc",
898+
driverName: driverName,
899+
},
900+
},
901+
expectedCapacities: []testCapacity{
902+
{
903+
uid: "CSISC-UID-1",
904+
resourceVersion: csiscRev + "0",
905+
segment: layer0,
906+
storageClassName: "late-sc",
907+
quantity: "1Gi",
908+
},
909+
},
910+
topologyChange: func(ctx context.Context, topo *mockTopology, expected []testCapacity) []testCapacity {
911+
topo.modify([]*topology.Segment{&layer0other}, /* added */
912+
topo.segments[:] /* removed */)
913+
return []testCapacity{
914+
{
915+
uid: "CSISC-UID-2",
916+
resourceVersion: csiscRev + "0",
917+
segment: layer0other,
918+
storageClassName: "late-sc",
919+
quantity: "2Gi",
920+
},
921+
}
922+
},
923+
},
767924
}
768925

769926
for name, tc := range testcases {
@@ -820,6 +977,13 @@ func TestController(t *testing.T) {
820977
t.Fatalf("modified capacity: %v", err)
821978
}
822979
}
980+
if tc.topologyChange != nil {
981+
klog.Info("modifying topology")
982+
expectedCapacities = tc.topologyChange(ctx, &tc.topology, expectedCapacities)
983+
if err := validateCapacitiesEventually(ctx, c, clientSet, expectedCapacities); err != nil {
984+
t.Fatalf("modified capacity: %v", err)
985+
}
986+
}
823987
})
824988
}
825989
}
@@ -1100,6 +1264,35 @@ func (mt *mockTopology) HasSynced() bool {
11001264
return true
11011265
}
11021266

1267+
func (mt *mockTopology) modify(add, remove []*topology.Segment) {
1268+
var added, removed []*topology.Segment
1269+
for _, segment := range add {
1270+
if mt.segmentIndex(segment) == -1 {
1271+
added = append(added, segment)
1272+
mt.segments = append(mt.segments, segment)
1273+
}
1274+
}
1275+
for _, segment := range remove {
1276+
index := mt.segmentIndex(segment)
1277+
if index != -1 {
1278+
removed = append(removed, segment)
1279+
mt.segments = append(mt.segments[0:index], mt.segments[index+1:]...)
1280+
}
1281+
}
1282+
for _, cb := range mt.callbacks {
1283+
cb(added, removed)
1284+
}
1285+
}
1286+
1287+
func (mt *mockTopology) segmentIndex(segment *topology.Segment) int {
1288+
for i, otherSegment := range mt.segments {
1289+
if otherSegment == segment {
1290+
return i
1291+
}
1292+
}
1293+
return -1
1294+
}
1295+
11031296
type testCapacity struct {
11041297
uid types.UID
11051298
resourceVersion string

0 commit comments

Comments
 (0)