Skip to content

Commit 5c6de36

Browse files
authored
Merge pull request #477 from pohly/release-2.0-storage-capacity
capacity: fix handling of topology changes with immediate binding SCs
2 parents c47eb1d + 75cae8c commit 5c6de36

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)