@@ -35,6 +35,7 @@ import (
3535 ref "k8s.io/client-go/tools/reference"
3636 "k8s.io/kubernetes/pkg/util/goroutinemap"
3737 "k8s.io/kubernetes/pkg/util/goroutinemap/exponentialbackoff"
38+ "k8s.io/kubernetes/pkg/util/slice"
3839)
3940
4041// ==================================================================
@@ -77,6 +78,8 @@ import (
7778// In the future version, a retry policy will be added.
7879
7980const pvcKind = "PersistentVolumeClaim"
81+ const snapshotKind = "VolumeSnapshot"
82+ const snapshotAPIGroup = crdv1 .GroupName // "snapshot.storage.k8s.io"
8083const controllerUpdateFailMsg = "snapshot controller failed to update"
8184
8285const IsDefaultSnapshotClassAnnotation = "snapshot.storage.kubernetes.io/is-default-class"
@@ -85,6 +88,26 @@ const IsDefaultSnapshotClassAnnotation = "snapshot.storage.kubernetes.io/is-defa
8588func (ctrl * csiSnapshotController ) syncContent (content * crdv1.VolumeSnapshotContent ) error {
8689 glog .V (5 ).Infof ("synchronizing VolumeSnapshotContent[%s]" , content .Name )
8790
91+ if isContentDeletionCandidate (content ) {
92+ // Volume snapshot content should be deleted. Check if it's used
93+ // and remove finalizer if it's not.
94+ // Check if snapshot content is still bound to a snapshot.
95+ isUsed := ctrl .isSnapshotContentBeingUsed (content )
96+ if ! isUsed {
97+ glog .V (5 ).Infof ("syncContent: Remove Finalizer for VolumeSnapshotContent[%s]" , content .Name )
98+ return ctrl .removeContentFinalizer (content )
99+ }
100+ }
101+
102+ if needToAddContentFinalizer (content ) {
103+ // Content is not being deleted -> it should have the finalizer. The
104+ // finalizer should be added by admission plugin, this is just to add
105+ // the finalizer to old volume snapshot contents that were created before
106+ // the admission plugin was enabled.
107+ glog .V (5 ).Infof ("syncContent: Add Finalizer for VolumeSnapshotContent[%s]" , content .Name )
108+ return ctrl .addContentFinalizer (content )
109+ }
110+
88111 // VolumeSnapshotContent is not bound to any VolumeSnapshot, in this case we just return err
89112 if content .Spec .VolumeSnapshotRef == nil {
90113 // content is not bound
@@ -139,6 +162,26 @@ func (ctrl *csiSnapshotController) syncContent(content *crdv1.VolumeSnapshotCont
139162func (ctrl * csiSnapshotController ) syncSnapshot (snapshot * crdv1.VolumeSnapshot ) error {
140163 glog .V (5 ).Infof ("synchonizing VolumeSnapshot[%s]: %s" , snapshotKey (snapshot ), getSnapshotStatusForLogging (snapshot ))
141164
165+ if isSnapshotDeletionCandidate (snapshot ) {
166+ // Volume snapshot should be deleted. Check if it's used
167+ // and remove finalizer if it's not.
168+ // Check if a volume is being created from snapshot.
169+ isUsed := ctrl .isVolumeBeingCreatedFromSnapshot (snapshot )
170+ if ! isUsed {
171+ glog .V (5 ).Infof ("syncSnapshot: Remove Finalizer for VolumeSnapshot[%s]" , snapshot .Name )
172+ return ctrl .removeSnapshotFinalizer (snapshot )
173+ }
174+ }
175+
176+ if needToAddSnapshotFinalizer (snapshot ) {
177+ // Snapshot is not being deleted -> it should have the finalizer. The
178+ // finalizer should be added by admission plugin, this is just to add
179+ // the finalizer to old volume snapshots that were created before
180+ // the admission plugin was enabled.
181+ glog .V (5 ).Infof ("syncSnapshot: Add Finalizer for VolumeSnapshot[%s]" , snapshot .Name )
182+ return ctrl .addSnapshotFinalizer (snapshot )
183+ }
184+
142185 if ! snapshot .Status .Ready {
143186 return ctrl .syncUnreadySnapshot (snapshot )
144187 } else {
@@ -395,6 +438,48 @@ func IsSnapshotBound(snapshot *crdv1.VolumeSnapshot, content *crdv1.VolumeSnapsh
395438 return false
396439}
397440
441+ // isSnapshotConentBeingUsed checks if snapshot content is bound to snapshot.
442+ func (ctrl * csiSnapshotController ) isSnapshotContentBeingUsed (content * crdv1.VolumeSnapshotContent ) bool {
443+ if content .Spec .VolumeSnapshotRef != nil {
444+ snapshotObj , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshots (content .Spec .VolumeSnapshotRef .Namespace ).Get (content .Spec .VolumeSnapshotRef .Name , metav1.GetOptions {})
445+ if err != nil {
446+ glog .Infof ("isSnapshotContentBeingUsed: Cannot get snapshot %s from api server: [%v]. VolumeSnapshot object may be deleted already." , content .Spec .VolumeSnapshotRef .Name , err )
447+ return false
448+ }
449+
450+ // Check if the snapshot content is bound to the snapshot
451+ if IsSnapshotBound (snapshotObj , content ) && snapshotObj .Spec .SnapshotContentName == content .Name {
452+ glog .Infof ("isSnapshotContentBeingUsed: VolumeSnapshot %s is bound to volumeSnapshotContent [%s]" , snapshotObj .Name , content .Name )
453+ return true
454+ }
455+ }
456+
457+ glog .V (5 ).Infof ("isSnapshotContentBeingUsed: Snapshot content %s is not being used" , content .Name )
458+ return false
459+ }
460+
461+ // isVolumeBeingCreatedFromSnapshot checks if an volume is being created from the snapshot.
462+ func (ctrl * csiSnapshotController ) isVolumeBeingCreatedFromSnapshot (snapshot * crdv1.VolumeSnapshot ) bool {
463+ pvcList , err := ctrl .client .CoreV1 ().PersistentVolumeClaims (snapshot .Namespace ).List (metav1.ListOptions {})
464+ if err != nil {
465+ 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 )
466+ return false
467+ }
468+ for _ , pvc := range pvcList .Items {
469+ if pvc .Spec .DataSource != nil && len (pvc .Spec .DataSource .Name ) > 0 && pvc .Spec .DataSource .Name == snapshot .Name {
470+ if pvc .Spec .DataSource .Kind == snapshotKind && * (pvc .Spec .DataSource .APIGroup ) == snapshotAPIGroup {
471+ if pvc .Status .Phase == v1 .ClaimPending {
472+ // A volume is being created from the snapshot
473+ glog .Infof ("isVolumeBeingCreatedFromSnapshot: volume %s is being created from snapshot %s" , pvc .Name , pvc .Spec .DataSource .Name )
474+ return true
475+ }
476+ }
477+ }
478+ }
479+ glog .V (5 ).Infof ("isVolumeBeingCreatedFromSnapshot: no volume is being created from snapshot %s" , snapshot .Name )
480+ return false
481+ }
482+
398483// The function checks whether the volumeSnapshotRef in snapshot content matches the given snapshot. If match, it binds the content with the snapshot
399484func (ctrl * csiSnapshotController ) checkandBindSnapshotContent (snapshot * crdv1.VolumeSnapshot , content * crdv1.VolumeSnapshotContent ) error {
400485 if content .Spec .VolumeSnapshotRef == nil || content .Spec .VolumeSnapshotRef .Name != snapshot .Name {
@@ -864,3 +949,78 @@ func isControllerUpdateFailError(err *storage.VolumeError) bool {
864949 }
865950 return false
866951}
952+
953+ // addContentFinalizer adds a Finalizer for VolumeSnapshotContent.
954+ func (ctrl * csiSnapshotController ) addContentFinalizer (content * crdv1.VolumeSnapshotContent ) error {
955+ contentClone := content .DeepCopy ()
956+ contentClone .ObjectMeta .Finalizers = append (contentClone .ObjectMeta .Finalizers , VolumeSnapshotContentFinalizer )
957+
958+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshotContents ().Update (contentClone )
959+ if err != nil {
960+ return newControllerUpdateError (content .Name , err .Error ())
961+ }
962+
963+ _ , err = ctrl .storeContentUpdate (contentClone )
964+ if err != nil {
965+ glog .Errorf ("failed to update content store %v" , err )
966+ }
967+
968+ glog .V (5 ).Infof ("Added protection finalizer to volume snapshot content %s" , content .Name )
969+ return nil
970+ }
971+
972+ // removeContentFinalizer removes a Finalizer for VolumeSnapshotContent.
973+ func (ctrl * csiSnapshotController ) removeContentFinalizer (content * crdv1.VolumeSnapshotContent ) error {
974+ contentClone := content .DeepCopy ()
975+ contentClone .ObjectMeta .Finalizers = slice .RemoveString (contentClone .ObjectMeta .Finalizers , VolumeSnapshotContentFinalizer , nil )
976+
977+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshotContents ().Update (contentClone )
978+ if err != nil {
979+ return newControllerUpdateError (content .Name , err .Error ())
980+ }
981+
982+ _ , err = ctrl .storeContentUpdate (contentClone )
983+ if err != nil {
984+ glog .Errorf ("failed to update content store %v" , err )
985+ }
986+
987+ glog .V (5 ).Infof ("Removed protection finalizer from volume snapshot content %s" , content .Name )
988+ return nil
989+ }
990+
991+ // addSnapshotFinalizer adds a Finalizer for VolumeSnapshot.
992+ func (ctrl * csiSnapshotController ) addSnapshotFinalizer (snapshot * crdv1.VolumeSnapshot ) error {
993+ snapshotClone := snapshot .DeepCopy ()
994+ snapshotClone .ObjectMeta .Finalizers = append (snapshotClone .ObjectMeta .Finalizers , VolumeSnapshotFinalizer )
995+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshots (snapshotClone .Namespace ).Update (snapshotClone )
996+ if err != nil {
997+ return newControllerUpdateError (snapshot .Name , err .Error ())
998+ }
999+
1000+ _ , err = ctrl .storeSnapshotUpdate (snapshotClone )
1001+ if err != nil {
1002+ glog .Errorf ("failed to update snapshot store %v" , err )
1003+ }
1004+
1005+ glog .V (5 ).Infof ("Added protection finalizer to volume snapshot %s" , snapshot .Name )
1006+ return nil
1007+ }
1008+
1009+ // removeContentFinalizer removes a Finalizer for VolumeSnapshot.
1010+ func (ctrl * csiSnapshotController ) removeSnapshotFinalizer (snapshot * crdv1.VolumeSnapshot ) error {
1011+ snapshotClone := snapshot .DeepCopy ()
1012+ snapshotClone .ObjectMeta .Finalizers = slice .RemoveString (snapshotClone .ObjectMeta .Finalizers , VolumeSnapshotFinalizer , nil )
1013+
1014+ _ , err := ctrl .clientset .VolumesnapshotV1alpha1 ().VolumeSnapshots (snapshotClone .Namespace ).Update (snapshotClone )
1015+ if err != nil {
1016+ return newControllerUpdateError (snapshot .Name , err .Error ())
1017+ }
1018+
1019+ _ , err = ctrl .storeSnapshotUpdate (snapshotClone )
1020+ if err != nil {
1021+ glog .Errorf ("failed to update snapshot store %v" , err )
1022+ }
1023+
1024+ glog .V (5 ).Infof ("Removed protection finalizer from volume snapshot %s" , snapshot .Name )
1025+ return nil
1026+ }
0 commit comments