@@ -21,6 +21,7 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"reflect"
24
+ sysruntime "runtime"
24
25
"strconv"
25
26
"strings"
26
27
"sync"
@@ -53,6 +54,7 @@ import (
53
54
"k8s.io/client-go/tools/cache"
54
55
"k8s.io/client-go/tools/record"
55
56
"k8s.io/klog"
57
+ "k8s.io/kubernetes/pkg/util/slice"
56
58
)
57
59
58
60
// This is a unit test framework for snapshot controller.
@@ -110,7 +112,8 @@ type controllerTest struct {
110
112
// List of expected CSI list snapshot calls
111
113
expectedListCalls []listCall
112
114
// Function to call as the test.
113
- test testCall
115
+ test testCall
116
+ expectSuccess bool
114
117
}
115
118
116
119
type testCall func (ctrl * csiSnapshotController , reactor * snapshotReactor , test controllerTest ) error
@@ -177,6 +180,11 @@ func withContentFinalizer(content *crdv1.VolumeSnapshotContent) *crdv1.VolumeSna
177
180
return content
178
181
}
179
182
183
+ func withPVCFinalizer (pvc * v1.PersistentVolumeClaim ) * v1.PersistentVolumeClaim {
184
+ pvc .ObjectMeta .Finalizers = append (pvc .ObjectMeta .Finalizers , PVCFinalizer )
185
+ return pvc
186
+ }
187
+
180
188
// React is a callback called by fake kubeClient from the controller.
181
189
// In other words, every snapshot/content change performed by the controller ends
182
190
// here.
@@ -331,6 +339,32 @@ func (r *snapshotReactor) React(action core.Action) (handled bool, ret runtime.O
331
339
klog .V (4 ).Infof ("GetClaim: claim %s not found" , name )
332
340
return true , nil , fmt .Errorf ("cannot find claim %s" , name )
333
341
342
+ case action .Matches ("update" , "persistentvolumeclaims" ):
343
+ obj := action .(core.UpdateAction ).GetObject ()
344
+ claim := obj .(* v1.PersistentVolumeClaim )
345
+
346
+ // Check and bump object version
347
+ storedClaim , found := r .claims [claim .Name ]
348
+ if found {
349
+ storedVer , _ := strconv .Atoi (storedClaim .ResourceVersion )
350
+ requestedVer , _ := strconv .Atoi (claim .ResourceVersion )
351
+ if storedVer != requestedVer {
352
+ return true , obj , errVersionConflict
353
+ }
354
+ // Don't modify the existing object
355
+ claim = claim .DeepCopy ()
356
+ claim .ResourceVersion = strconv .Itoa (storedVer + 1 )
357
+ } else {
358
+ return true , nil , fmt .Errorf ("cannot update claim %s: claim not found" , claim .Name )
359
+ }
360
+
361
+ // Store the updated object to appropriate places.
362
+ r .claims [claim .Name ] = claim
363
+ r .changedObjects = append (r .changedObjects , claim )
364
+ r .changedSinceLastSync ++
365
+ klog .V (4 ).Infof ("saved updated claim %s" , claim .Name )
366
+ return true , claim , nil
367
+
334
368
case action .Matches ("get" , "storageclasses" ):
335
369
name := action .(core.GetAction ).GetName ()
336
370
storageClass , found := r .storageClasses [name ]
@@ -550,6 +584,9 @@ func (r *snapshotReactor) syncAll() {
550
584
for _ , v := range r .contents {
551
585
r .changedObjects = append (r .changedObjects , v )
552
586
}
587
+ for _ , pvc := range r .claims {
588
+ r .changedObjects = append (r .changedObjects , pvc )
589
+ }
553
590
r .changedSinceLastSync = 0
554
591
}
555
592
@@ -699,6 +736,7 @@ func newSnapshotReactor(kubeClient *kubefake.Clientset, client *fake.Clientset,
699
736
client .AddReactor ("delete" , "volumesnapshotcontents" , reactor .React )
700
737
client .AddReactor ("delete" , "volumesnapshots" , reactor .React )
701
738
kubeClient .AddReactor ("get" , "persistentvolumeclaims" , reactor .React )
739
+ kubeClient .AddReactor ("update" , "persistentvolumeclaims" , reactor .React )
702
740
kubeClient .AddReactor ("get" , "persistentvolumes" , reactor .React )
703
741
kubeClient .AddReactor ("get" , "storageclasses" , reactor .React )
704
742
kubeClient .AddReactor ("get" , "secrets" , reactor .React )
@@ -746,6 +784,7 @@ func newTestController(kubeClient kubernetes.Interface, clientset clientset.Inte
746
784
ctrl .contentListerSynced = alwaysReady
747
785
ctrl .snapshotListerSynced = alwaysReady
748
786
ctrl .classListerSynced = alwaysReady
787
+ ctrl .pvcListerSynced = alwaysReady
749
788
750
789
return ctrl , nil
751
790
}
@@ -845,7 +884,7 @@ func newSnapshotArray(name, className, boundToContent, snapshotUID, claimName st
845
884
}
846
885
847
886
// newClaim returns a new claim with given attributes
848
- func newClaim (name , claimUID , capacity , boundToVolume string , phase v1.PersistentVolumeClaimPhase , class * string ) * v1.PersistentVolumeClaim {
887
+ func newClaim (name , claimUID , capacity , boundToVolume string , phase v1.PersistentVolumeClaimPhase , class * string , bFinalizer bool ) * v1.PersistentVolumeClaim {
849
888
claim := v1.PersistentVolumeClaim {
850
889
ObjectMeta : metav1.ObjectMeta {
851
890
Name : name ,
@@ -877,14 +916,25 @@ func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.Persisten
877
916
claim .Status .Capacity = claim .Spec .Resources .Requests
878
917
}
879
918
919
+ if bFinalizer {
920
+ return withPVCFinalizer (& claim )
921
+ }
880
922
return & claim
881
923
}
882
924
883
925
// newClaimArray returns array with a single claim that would be returned by
884
926
// newClaim() with the same parameters.
885
927
func newClaimArray (name , claimUID , capacity , boundToVolume string , phase v1.PersistentVolumeClaimPhase , class * string ) []* v1.PersistentVolumeClaim {
886
928
return []* v1.PersistentVolumeClaim {
887
- newClaim (name , claimUID , capacity , boundToVolume , phase , class ),
929
+ newClaim (name , claimUID , capacity , boundToVolume , phase , class , false ),
930
+ }
931
+ }
932
+
933
+ // newClaimArrayFinalizer returns array with a single claim that would be returned by
934
+ // newClaim() with the same parameters plus finalizer.
935
+ func newClaimArrayFinalizer (name , claimUID , capacity , boundToVolume string , phase v1.PersistentVolumeClaimPhase , class * string ) []* v1.PersistentVolumeClaim {
936
+ return []* v1.PersistentVolumeClaim {
937
+ newClaim (name , claimUID , capacity , boundToVolume , phase , class , true ),
888
938
}
889
939
}
890
940
@@ -961,6 +1011,14 @@ func testSyncContent(ctrl *csiSnapshotController, reactor *snapshotReactor, test
961
1011
return ctrl .syncContent (test .initialContents [0 ])
962
1012
}
963
1013
1014
+ func testAddPVCFinalizer (ctrl * csiSnapshotController , reactor * snapshotReactor , test controllerTest ) error {
1015
+ return ctrl .ensureSnapshotSourceFinalizer (test .initialSnapshots [0 ])
1016
+ }
1017
+
1018
+ func testRemovePVCFinalizer (ctrl * csiSnapshotController , reactor * snapshotReactor , test controllerTest ) error {
1019
+ return ctrl .checkandRemoveSnapshotSourceFinalizer (test .initialSnapshots [0 ])
1020
+ }
1021
+
964
1022
var (
965
1023
classEmpty string
966
1024
classGold = "gold"
@@ -1097,6 +1155,113 @@ func runSyncTests(t *testing.T, tests []controllerTest, snapshotClasses []*crdv1
1097
1155
}
1098
1156
}
1099
1157
1158
+ // This tests ensureSnapshotSourceFinalizer and checkandRemoveSnapshotSourceFinalizer
1159
+ func runPVCFinalizerTests (t * testing.T , tests []controllerTest , snapshotClasses []* crdv1.VolumeSnapshotClass ) {
1160
+ snapshotscheme .AddToScheme (scheme .Scheme )
1161
+ for _ , test := range tests {
1162
+ klog .V (4 ).Infof ("starting test %q" , test .name )
1163
+
1164
+ // Initialize the controller
1165
+ kubeClient := & kubefake.Clientset {}
1166
+ client := & fake.Clientset {}
1167
+
1168
+ ctrl , err := newTestController (kubeClient , client , nil , t , test )
1169
+ if err != nil {
1170
+ t .Fatalf ("Test %q construct persistent content failed: %v" , test .name , err )
1171
+ }
1172
+
1173
+ reactor := newSnapshotReactor (kubeClient , client , ctrl , nil , nil , test .errors )
1174
+ for _ , snapshot := range test .initialSnapshots {
1175
+ ctrl .snapshotStore .Add (snapshot )
1176
+ reactor .snapshots [snapshot .Name ] = snapshot
1177
+ }
1178
+ for _ , content := range test .initialContents {
1179
+ if ctrl .isDriverMatch (test .initialContents [0 ]) {
1180
+ ctrl .contentStore .Add (content )
1181
+ reactor .contents [content .Name ] = content
1182
+ }
1183
+ }
1184
+
1185
+ pvcIndexer := cache .NewIndexer (cache .MetaNamespaceKeyFunc , cache.Indexers {})
1186
+ for _ , claim := range test .initialClaims {
1187
+ reactor .claims [claim .Name ] = claim
1188
+ pvcIndexer .Add (claim )
1189
+ }
1190
+ ctrl .pvcLister = corelisters .NewPersistentVolumeClaimLister (pvcIndexer )
1191
+
1192
+ for _ , volume := range test .initialVolumes {
1193
+ reactor .volumes [volume .Name ] = volume
1194
+ }
1195
+ for _ , storageClass := range test .initialStorageClasses {
1196
+ reactor .storageClasses [storageClass .Name ] = storageClass
1197
+ }
1198
+ for _ , secret := range test .initialSecrets {
1199
+ reactor .secrets [secret .Name ] = secret
1200
+ }
1201
+
1202
+ // Inject classes into controller via a custom lister.
1203
+ indexer := cache .NewIndexer (cache .MetaNamespaceKeyFunc , cache.Indexers {})
1204
+ for _ , class := range snapshotClasses {
1205
+ indexer .Add (class )
1206
+ }
1207
+ ctrl .classLister = storagelisters .NewVolumeSnapshotClassLister (indexer )
1208
+
1209
+ // Run the tested functions
1210
+ err = test .test (ctrl , reactor , test )
1211
+ if err != nil {
1212
+ t .Errorf ("Test %q failed: %v" , test .name , err )
1213
+ }
1214
+
1215
+ // Verify PVCFinalizer tests results
1216
+ evaluatePVCFinalizerTests (ctrl , reactor , test , t )
1217
+ }
1218
+ }
1219
+
1220
+ // Evaluate PVCFinalizer tests results
1221
+ func evaluatePVCFinalizerTests (ctrl * csiSnapshotController , reactor * snapshotReactor , test controllerTest , t * testing.T ) {
1222
+ // Evaluate results
1223
+ bHasPVCFinalizer := false
1224
+ name := sysruntime .FuncForPC (reflect .ValueOf (test .test ).Pointer ()).Name ()
1225
+ index := strings .LastIndex (name , "." )
1226
+ if index == - 1 {
1227
+ t .Errorf ("Test %q: failed to test finalizer - invalid test call name [%s]" , test .name , name )
1228
+ return
1229
+ }
1230
+ names := []rune (name )
1231
+ funcName := string (names [index + 1 : len (name )])
1232
+ klog .V (4 ).Infof ("test %q: PVCFinalizer test func name: [%s]" , test .name , funcName )
1233
+
1234
+ if funcName == "testAddPVCFinalizer" {
1235
+ for _ , pvc := range reactor .claims {
1236
+ if test .initialClaims [0 ].Name == pvc .Name {
1237
+ if ! slice .ContainsString (test .initialClaims [0 ].ObjectMeta .Finalizers , PVCFinalizer , nil ) && slice .ContainsString (pvc .ObjectMeta .Finalizers , PVCFinalizer , nil ) {
1238
+ klog .V (4 ).Infof ("test %q succeeded. PVCFinalizer is added to PVC %s" , test .name , pvc .Name )
1239
+ bHasPVCFinalizer = true
1240
+ }
1241
+ break
1242
+ }
1243
+ }
1244
+ if test .expectSuccess && ! bHasPVCFinalizer {
1245
+ t .Errorf ("Test %q: failed to add finalizer to PVC %s" , test .name , test .initialClaims [0 ].Name )
1246
+ }
1247
+ }
1248
+ bHasPVCFinalizer = true
1249
+ if funcName == "testRemovePVCFinalizer" {
1250
+ for _ , pvc := range reactor .claims {
1251
+ if test .initialClaims [0 ].Name == pvc .Name {
1252
+ if slice .ContainsString (test .initialClaims [0 ].ObjectMeta .Finalizers , PVCFinalizer , nil ) && ! slice .ContainsString (pvc .ObjectMeta .Finalizers , PVCFinalizer , nil ) {
1253
+ klog .V (4 ).Infof ("test %q succeeded. PVCFinalizer is removed from PVC %s" , test .name , pvc .Name )
1254
+ bHasPVCFinalizer = false
1255
+ }
1256
+ break
1257
+ }
1258
+ }
1259
+ if test .expectSuccess && bHasPVCFinalizer {
1260
+ t .Errorf ("Test %q: failed to remove finalizer from PVC %s" , test .name , test .initialClaims [0 ].Name )
1261
+ }
1262
+ }
1263
+ }
1264
+
1100
1265
func getSize (size int64 ) * resource.Quantity {
1101
1266
return resource .NewQuantity (size , resource .BinarySI )
1102
1267
}
0 commit comments