Skip to content

Commit a1b3374

Browse files
authored
Merge pull request #47 from xing-yang/delete_volume_finalizer
Add Delete Volume Finalizer
2 parents b36da39 + 603855f commit a1b3374

File tree

5 files changed

+366
-6
lines changed

5 files changed

+366
-6
lines changed

deploy/kubernetes/rbac.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ rules:
2525
verbs: ["get", "list", "watch"]
2626
- apiGroups: [""]
2727
resources: ["persistentvolumeclaims"]
28-
verbs: ["get", "list", "watch"]
28+
verbs: ["get", "list", "watch", "update"]
2929
- apiGroups: ["storage.k8s.io"]
3030
resources: ["storageclasses"]
3131
verbs: ["get", "list", "watch"]

pkg/controller/framework_test.go

Lines changed: 168 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"errors"
2222
"fmt"
2323
"reflect"
24+
sysruntime "runtime"
2425
"strconv"
2526
"strings"
2627
"sync"
@@ -53,6 +54,7 @@ import (
5354
"k8s.io/client-go/tools/cache"
5455
"k8s.io/client-go/tools/record"
5556
"k8s.io/klog"
57+
"k8s.io/kubernetes/pkg/util/slice"
5658
)
5759

5860
// This is a unit test framework for snapshot controller.
@@ -110,7 +112,8 @@ type controllerTest struct {
110112
// List of expected CSI list snapshot calls
111113
expectedListCalls []listCall
112114
// Function to call as the test.
113-
test testCall
115+
test testCall
116+
expectSuccess bool
114117
}
115118

116119
type testCall func(ctrl *csiSnapshotController, reactor *snapshotReactor, test controllerTest) error
@@ -177,6 +180,11 @@ func withContentFinalizer(content *crdv1.VolumeSnapshotContent) *crdv1.VolumeSna
177180
return content
178181
}
179182

183+
func withPVCFinalizer(pvc *v1.PersistentVolumeClaim) *v1.PersistentVolumeClaim {
184+
pvc.ObjectMeta.Finalizers = append(pvc.ObjectMeta.Finalizers, PVCFinalizer)
185+
return pvc
186+
}
187+
180188
// React is a callback called by fake kubeClient from the controller.
181189
// In other words, every snapshot/content change performed by the controller ends
182190
// here.
@@ -331,6 +339,32 @@ func (r *snapshotReactor) React(action core.Action) (handled bool, ret runtime.O
331339
klog.V(4).Infof("GetClaim: claim %s not found", name)
332340
return true, nil, fmt.Errorf("cannot find claim %s", name)
333341

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+
334368
case action.Matches("get", "storageclasses"):
335369
name := action.(core.GetAction).GetName()
336370
storageClass, found := r.storageClasses[name]
@@ -550,6 +584,9 @@ func (r *snapshotReactor) syncAll() {
550584
for _, v := range r.contents {
551585
r.changedObjects = append(r.changedObjects, v)
552586
}
587+
for _, pvc := range r.claims {
588+
r.changedObjects = append(r.changedObjects, pvc)
589+
}
553590
r.changedSinceLastSync = 0
554591
}
555592

@@ -699,6 +736,7 @@ func newSnapshotReactor(kubeClient *kubefake.Clientset, client *fake.Clientset,
699736
client.AddReactor("delete", "volumesnapshotcontents", reactor.React)
700737
client.AddReactor("delete", "volumesnapshots", reactor.React)
701738
kubeClient.AddReactor("get", "persistentvolumeclaims", reactor.React)
739+
kubeClient.AddReactor("update", "persistentvolumeclaims", reactor.React)
702740
kubeClient.AddReactor("get", "persistentvolumes", reactor.React)
703741
kubeClient.AddReactor("get", "storageclasses", reactor.React)
704742
kubeClient.AddReactor("get", "secrets", reactor.React)
@@ -746,6 +784,7 @@ func newTestController(kubeClient kubernetes.Interface, clientset clientset.Inte
746784
ctrl.contentListerSynced = alwaysReady
747785
ctrl.snapshotListerSynced = alwaysReady
748786
ctrl.classListerSynced = alwaysReady
787+
ctrl.pvcListerSynced = alwaysReady
749788

750789
return ctrl, nil
751790
}
@@ -845,7 +884,7 @@ func newSnapshotArray(name, className, boundToContent, snapshotUID, claimName st
845884
}
846885

847886
// 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 {
849888
claim := v1.PersistentVolumeClaim{
850889
ObjectMeta: metav1.ObjectMeta{
851890
Name: name,
@@ -877,14 +916,25 @@ func newClaim(name, claimUID, capacity, boundToVolume string, phase v1.Persisten
877916
claim.Status.Capacity = claim.Spec.Resources.Requests
878917
}
879918

919+
if bFinalizer {
920+
return withPVCFinalizer(&claim)
921+
}
880922
return &claim
881923
}
882924

883925
// newClaimArray returns array with a single claim that would be returned by
884926
// newClaim() with the same parameters.
885927
func newClaimArray(name, claimUID, capacity, boundToVolume string, phase v1.PersistentVolumeClaimPhase, class *string) []*v1.PersistentVolumeClaim {
886928
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),
888938
}
889939
}
890940

@@ -961,6 +1011,14 @@ func testSyncContent(ctrl *csiSnapshotController, reactor *snapshotReactor, test
9611011
return ctrl.syncContent(test.initialContents[0])
9621012
}
9631013

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+
9641022
var (
9651023
classEmpty string
9661024
classGold = "gold"
@@ -1097,6 +1155,113 @@ func runSyncTests(t *testing.T, tests []controllerTest, snapshotClasses []*crdv1
10971155
}
10981156
}
10991157

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+
11001265
func getSize(size int64) *resource.Quantity {
11011266
return resource.NewQuantity(size, resource.BinarySI)
11021267
}

0 commit comments

Comments
 (0)