@@ -35,6 +35,7 @@ import (
35
35
ref "k8s.io/client-go/tools/reference"
36
36
"k8s.io/kubernetes/pkg/util/goroutinemap"
37
37
"k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
38
+ "k8s.io/kubernetes/pkg/util/slice"
38
39
)
39
40
40
41
// ==================================================================
@@ -78,6 +79,9 @@ import (
78
79
79
80
const pvcKind = "PersistentVolumeClaim"
80
81
const apiGroup = ""
82
+ const snapshotKind = "VolumeSnapshot"
83
+ const snapshotAPIGroup = crdv1 .GroupName
84
+
81
85
const controllerUpdateFailMsg = "snapshot controller failed to update"
82
86
83
87
const IsDefaultSnapshotClassAnnotation = "snapshot.storage.kubernetes.io/is-default-class"
@@ -86,6 +90,26 @@ const IsDefaultSnapshotClassAnnotation = "snapshot.storage.kubernetes.io/is-defa
86
90
func (ctrl * csiSnapshotController ) syncContent (content * crdv1.VolumeSnapshotContent ) error {
87
91
glog .V (5 ).Infof ("synchronizing VolumeSnapshotContent[%s]" , content .Name )
88
92
93
+ if isContentDeletionCandidate (content ) {
94
+ // Volume snapshot content should be deleted. Check if it's used
95
+ // and remove finalizer if it's not.
96
+ // Check if snapshot content is still bound to a snapshot.
97
+ isUsed := ctrl .isSnapshotContentBeingUsed (content )
98
+ if ! isUsed {
99
+ glog .V (5 ).Infof ("syncContent: Remove Finalizer for VolumeSnapshotContent[%s]" , content .Name )
100
+ return ctrl .removeContentFinalizer (content )
101
+ }
102
+ }
103
+
104
+ if needToAddContentFinalizer (content ) {
105
+ // Content is not being deleted -> it should have the finalizer. The
106
+ // finalizer should be added by admission plugin, this is just to add
107
+ // the finalizer to old volume snapshot contents that were created before
108
+ // the admission plugin was enabled.
109
+ glog .V (5 ).Infof ("syncContent: Add Finalizer for VolumeSnapshotContent[%s]" , content .Name )
110
+ return ctrl .addContentFinalizer (content )
111
+ }
112
+
89
113
// VolumeSnapshotContent is not bound to any VolumeSnapshot, in this case we just return err
90
114
if content .Spec .VolumeSnapshotRef == nil {
91
115
// content is not bound
@@ -140,6 +164,26 @@ func (ctrl *csiSnapshotController) syncContent(content *crdv1.VolumeSnapshotCont
140
164
func (ctrl * csiSnapshotController ) syncSnapshot (snapshot * crdv1.VolumeSnapshot ) error {
141
165
glog .V (5 ).Infof ("synchonizing VolumeSnapshot[%s]: %s" , snapshotKey (snapshot ), getSnapshotStatusForLogging (snapshot ))
142
166
167
+ if isSnapshotDeletionCandidate (snapshot ) {
168
+ // Volume snapshot should be deleted. Check if it's used
169
+ // and remove finalizer if it's not.
170
+ // Check if a volume is being created from snapshot.
171
+ isUsed := ctrl .isVolumeBeingCreatedFromSnapshot (snapshot )
172
+ if ! isUsed {
173
+ glog .V (5 ).Infof ("syncSnapshot: Remove Finalizer for VolumeSnapshot[%s]" , snapshot .Name )
174
+ return ctrl .removeSnapshotFinalizer (snapshot )
175
+ }
176
+ }
177
+
178
+ if needToAddSnapshotFinalizer (snapshot ) {
179
+ // Snapshot is not being deleted -> it should have the finalizer. The
180
+ // finalizer should be added by admission plugin, this is just to add
181
+ // the finalizer to old volume snapshots that were created before
182
+ // the admission plugin was enabled.
183
+ glog .V (5 ).Infof ("syncSnapshot: Add Finalizer for VolumeSnapshot[%s]" , snapshot .Name )
184
+ return ctrl .addSnapshotFinalizer (snapshot )
185
+ }
186
+
143
187
if ! snapshot .Status .Ready {
144
188
return ctrl .syncUnreadySnapshot (snapshot )
145
189
} else {
@@ -396,6 +440,48 @@ func IsSnapshotBound(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapsh
396
440
return false
397
441
}
398
442
443
+ // isSnapshotConentBeingUsed checks if snapshot content is bound to snapshot.
444
+ func (ctrl * csiSnapshotController ) isSnapshotContentBeingUsed (content * crdv1.VolumeSnapshotContent ) bool {
445
+ if content .Spec .VolumeSnapshotRef != nil {
446
+ snapshotObj , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshots (content .Spec .VolumeSnapshotRef .Namespace ).Get (content .Spec .VolumeSnapshotRef .Name , metav1.GetOptions {})
447
+ if err != nil {
448
+ glog .Infof ("isSnapshotContentBeingUsed: Cannot get snapshot %s from api server: [%v]. VolumeSnapshot object may be deleted already." , content .Spec .VolumeSnapshotRef .Name , err )
449
+ return false
450
+ }
451
+
452
+ // Check if the snapshot content is bound to the snapshot
453
+ if IsSnapshotBound (snapshotObj , content ) && snapshotObj .Spec .SnapshotContentName == content .Name {
454
+ glog .Infof ("isSnapshotContentBeingUsed: VolumeSnapshot %s is bound to volumeSnapshotContent [%s]" , snapshotObj .Name , content .Name )
455
+ return true
456
+ }
457
+ }
458
+
459
+ glog .V (5 ).Infof ("isSnapshotContentBeingUsed: Snapshot content %s is not being used" , content .Name )
460
+ return false
461
+ }
462
+
463
+ // isVolumeBeingCreatedFromSnapshot checks if an volume is being created from the snapshot.
464
+ func (ctrl * csiSnapshotController ) isVolumeBeingCreatedFromSnapshot (snapshot * crdv1.VolumeSnapshot ) bool {
465
+ pvcList , err := ctrl .client .CoreV1 ().PersistentVolumeClaims (snapshot .Namespace ).List (metav1.ListOptions {})
466
+ if err != nil {
467
+ glog .Errorf ("Failed to retrieve PVCs from the API server to check if volume snapshot %s is being used by a volume: %q" , snapshot .Name , err )
468
+ return false
469
+ }
470
+ for _ , pvc := range pvcList .Items {
471
+ if pvc .Spec .DataSource != nil && len (pvc .Spec .DataSource .Name ) > 0 && pvc .Spec .DataSource .Name == snapshot .Name {
472
+ if pvc .Spec .DataSource .Kind == snapshotKind && * (pvc .Spec .DataSource .APIGroup ) == snapshotAPIGroup {
473
+ if pvc .Status .Phase == v1 .ClaimPending {
474
+ // A volume is being created from the snapshot
475
+ glog .Infof ("isVolumeBeingCreatedFromSnapshot: volume %s is being created from snapshot %s" , pvc .Name , pvc .Spec .DataSource .Name )
476
+ return true
477
+ }
478
+ }
479
+ }
480
+ }
481
+ glog .V (5 ).Infof ("isVolumeBeingCreatedFromSnapshot: no volume is being created from snapshot %s" , snapshot .Name )
482
+ return false
483
+ }
484
+
399
485
// The function checks whether the volumeSnapshotRef in snapshot content matches the given snapshot. If match, it binds the content with the snapshot
400
486
func (ctrl * csiSnapshotController ) checkandBindSnapshotContent (snapshot * crdv1.VolumeSnapshot , content * crdv1.VolumeSnapshotContent ) error {
401
487
if content .Spec .VolumeSnapshotRef == nil || content .Spec .VolumeSnapshotRef .Name != snapshot .Name {
@@ -871,3 +957,78 @@ func isControllerUpdateFailError(err *storage.VolumeError) bool {
871
957
}
872
958
return false
873
959
}
960
+
961
+ // addContentFinalizer adds a Finalizer for VolumeSnapshotContent.
962
+ func (ctrl * csiSnapshotController ) addContentFinalizer (content * crdv1.VolumeSnapshotContent ) error {
963
+ contentClone := content .DeepCopy ()
964
+ contentClone .ObjectMeta .Finalizers = append (contentClone .ObjectMeta .Finalizers , VolumeSnapshotContentFinalizer )
965
+
966
+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshotContents ().Update (contentClone )
967
+ if err != nil {
968
+ return newControllerUpdateError (content .Name , err .Error ())
969
+ }
970
+
971
+ _ , err = ctrl .storeContentUpdate (contentClone )
972
+ if err != nil {
973
+ glog .Errorf ("failed to update content store %v" , err )
974
+ }
975
+
976
+ glog .V (5 ).Infof ("Added protection finalizer to volume snapshot content %s" , content .Name )
977
+ return nil
978
+ }
979
+
980
+ // removeContentFinalizer removes a Finalizer for VolumeSnapshotContent.
981
+ func (ctrl * csiSnapshotController ) removeContentFinalizer (content * crdv1.VolumeSnapshotContent ) error {
982
+ contentClone := content .DeepCopy ()
983
+ contentClone .ObjectMeta .Finalizers = slice .RemoveString (contentClone .ObjectMeta .Finalizers , VolumeSnapshotContentFinalizer , nil )
984
+
985
+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshotContents ().Update (contentClone )
986
+ if err != nil {
987
+ return newControllerUpdateError (content .Name , err .Error ())
988
+ }
989
+
990
+ _ , err = ctrl .storeContentUpdate (contentClone )
991
+ if err != nil {
992
+ glog .Errorf ("failed to update content store %v" , err )
993
+ }
994
+
995
+ glog .V (5 ).Infof ("Removed protection finalizer from volume snapshot content %s" , content .Name )
996
+ return nil
997
+ }
998
+
999
+ // addSnapshotFinalizer adds a Finalizer for VolumeSnapshot.
1000
+ func (ctrl * csiSnapshotController ) addSnapshotFinalizer (snapshot * crdv1.VolumeSnapshot ) error {
1001
+ snapshotClone := snapshot .DeepCopy ()
1002
+ snapshotClone .ObjectMeta .Finalizers = append (snapshotClone .ObjectMeta .Finalizers , VolumeSnapshotFinalizer )
1003
+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshots (snapshotClone .Namespace ).Update (snapshotClone )
1004
+ if err != nil {
1005
+ return newControllerUpdateError (snapshot .Name , err .Error ())
1006
+ }
1007
+
1008
+ _ , err = ctrl .storeSnapshotUpdate (snapshotClone )
1009
+ if err != nil {
1010
+ glog .Errorf ("failed to update snapshot store %v" , err )
1011
+ }
1012
+
1013
+ glog .V (5 ).Infof ("Added protection finalizer to volume snapshot %s" , snapshot .Name )
1014
+ return nil
1015
+ }
1016
+
1017
+ // removeContentFinalizer removes a Finalizer for VolumeSnapshot.
1018
+ func (ctrl * csiSnapshotController ) removeSnapshotFinalizer (snapshot * crdv1.VolumeSnapshot ) error {
1019
+ snapshotClone := snapshot .DeepCopy ()
1020
+ snapshotClone .ObjectMeta .Finalizers = slice .RemoveString (snapshotClone .ObjectMeta .Finalizers , VolumeSnapshotFinalizer , nil )
1021
+
1022
+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshots (snapshotClone .Namespace ).Update (snapshotClone )
1023
+ if err != nil {
1024
+ return newControllerUpdateError (snapshot .Name , err .Error ())
1025
+ }
1026
+
1027
+ _ , err = ctrl .storeSnapshotUpdate (snapshotClone )
1028
+ if err != nil {
1029
+ glog .Errorf ("failed to update snapshot store %v" , err )
1030
+ }
1031
+
1032
+ glog .V (5 ).Infof ("Removed protection finalizer from volume snapshot %s" , snapshot .Name )
1033
+ return nil
1034
+ }
0 commit comments