@@ -23,7 +23,7 @@ import (
23
23
24
24
crdv1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1beta1"
25
25
"github.com/kubernetes-csi/external-snapshotter/pkg/utils"
26
- "k8s.io/api/core/v1"
26
+ v1 "k8s.io/api/core/v1"
27
27
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
28
"k8s.io/klog"
29
29
"k8s.io/kubernetes/pkg/util/goroutinemap"
@@ -34,20 +34,20 @@ import (
34
34
// Design:
35
35
//
36
36
// This is the sidecar controller that is responsible for creating and deleting a
37
- // snapshot on the storage infrastructure through a csi volume driver. It watches
38
- // the VolumeSnapshotContent object which is either created/deleted by the
37
+ // snapshot on the storage system through a csi volume driver. It watches
38
+ // VolumeSnapshotContent objects which have been either created/deleted by the
39
39
// common snapshot controller in the case of dynamic provisioning or by the admin
40
40
// in the case of pre-provisioned snapshots.
41
41
42
- // The snapshot creation through csi volume driver should return a snapshot after
43
- // it is created successfully (however, the snapshot might not be ready to use yet if
44
- // there is an uploading phase). The creationTime will be updated accordingly
42
+ // The snapshot creation through csi driver should return a snapshot after
43
+ // it is created successfully(however, the snapshot might not be ready to use yet
44
+ // if there is an uploading phase). The creationTime will be updated accordingly
45
45
// on the status of VolumeSnapshotContent.
46
46
// After that, the sidecar controller will keep checking the snapshot status
47
47
// through csi snapshot calls. When the snapshot is ready to use, the sidecar
48
48
// controller set the status "ReadyToUse" to true on the VolumeSnapshotContent object
49
- // to indicate the snapshot is ready to use. If the creation failed for any reason,
50
- // the Error status is set accordingly.
49
+ // to indicate the snapshot is ready to be used to restore a volume.
50
+ // If the creation failed for any reason, the Error status is set accordingly.
51
51
52
52
const controllerUpdateFailMsg = "snapshot controller failed to update"
53
53
@@ -57,73 +57,50 @@ func (ctrl *csiSnapshotSideCarController) syncContent(content *crdv1.VolumeSnaps
57
57
58
58
var err error
59
59
if ctrl .shouldDelete (content ) {
60
- switch content .Spec .DeletionPolicy {
61
- case crdv1 .VolumeSnapshotContentRetain :
62
- klog .V (4 ).Infof ("VolumeSnapshotContent[%s]: policy is Retain. Keep physical snapshot and remove content finalizer" , content .Name )
63
- // It is a deletion candidate if DeletionTimestamp is not nil and
64
- // VolumeSnapshotContentFinalizer is set.
65
- if utils .IsContentDeletionCandidate (content ) {
66
- // Volume snapshot content is a deletion candidate.
67
- // Remove the content finalizer.
68
- klog .V (5 ).Infof ("syncContent: Content [%s] is a deletion candidate. Remove finalizer." , content .Name )
69
- return ctrl .removeContentFinalizer (content )
70
- }
71
-
72
- case crdv1 .VolumeSnapshotContentDelete :
73
- klog .V (4 ).Infof ("VolumeSnapshotContent[%s]: policy is Delete. Delete physical snapshot" , content .Name )
74
- err = ctrl .deleteCSISnapshot (content )
75
- if err != nil {
76
- return err
77
- }
78
- klog .V (5 ).Infof ("syncContent: check if we should remove Finalizer for VolumeSnapshotContent[%s]" , content .Name )
79
- // It is a deletion candidate if DeletionTimestamp is not nil and
80
- // VolumeSnapshotContentFinalizer is set.
81
- if utils .IsContentDeletionCandidate (content ) {
82
- // Volume snapshot content is a deletion candidate.
83
- // Remove the content finalizer.
84
- klog .V (5 ).Infof ("syncContent: Content [%s] is a deletion candidate. Remove finalizer." , content .Name )
85
- return ctrl .removeContentFinalizer (content )
86
- }
87
-
88
- default :
89
- // Unknown VolumeSnapshotDeletionPolicy
90
- ctrl .eventRecorder .Event (content , v1 .EventTypeWarning , "SnapshotUnknownDeletionPolicy" , "Volume Snapshot Content has unrecognized deletion policy" )
91
- }
92
60
klog .V (4 ).Infof ("VolumeSnapshotContent[%s]: the policy is %s" , content .Name , content .Spec .DeletionPolicy )
61
+ if content .Spec .DeletionPolicy == crdv1 .VolumeSnapshotContentDelete &&
62
+ content .Status != nil && content .Status .SnapshotHandle != nil {
63
+ // issue a CSI deletion call if the snapshot has not been deleted yet from
64
+ // underlying storage system. Note that the deletion snapshot operation will
65
+ // update content SnapshotHandle to nil upon a successful deletion. At this
66
+ // point, the finalizer on content should NOT be removed to avoid leaking.
67
+ return ctrl .deleteCSISnapshot (content )
68
+ } else {
69
+ // otherwise, either the snapshot has been deleted from the underlying
70
+ // storage system, or the deletion policy is Retain, remove the finalizer
71
+ // if there is one so that API server could delete the object if there is
72
+ // no other finalizer.
73
+ return ctrl .removeContentFinalizer (content )
74
+ }
93
75
} else {
94
- var err error
95
- klog .V (5 ).Infof ("syncContent: Call CreateSnapshot for content %s" , content .Name )
96
76
if content .Spec .Source .VolumeHandle != nil && content .Status == nil {
77
+ klog .V (5 ).Infof ("syncContent: Call CreateSnapshot for content %s" , content .Name )
97
78
if err = ctrl .createSnapshot (content ); err != nil {
98
79
ctrl .updateContentErrorStatusWithEvent (content , v1 .EventTypeWarning , "SnapshotCreationFailed" , fmt .Sprintf ("Failed to create snapshot with error %v" , err ))
99
80
return err
100
81
}
101
82
} else {
102
-
103
83
if err = ctrl .checkandUpdateContentStatus (content ); err != nil {
104
84
ctrl .updateContentErrorStatusWithEvent (content , v1 .EventTypeWarning , "SnapshotContentStatusUpdateFailed" , fmt .Sprintf ("Failed to update snapshot content status with error %v" , err ))
105
85
return err
106
86
}
107
87
}
108
-
109
- return nil
110
88
}
111
-
112
89
return nil
113
90
}
114
91
115
92
// deleteCSISnapshot starts delete action.
116
93
func (ctrl * csiSnapshotSideCarController ) deleteCSISnapshot (content * crdv1.VolumeSnapshotContent ) error {
117
94
operationName := fmt .Sprintf ("delete-%s" , content .Name )
118
- klog .V (5 ).Infof ("Snapshotter is about to delete volume snapshot content and the operation named %s" , operationName )
95
+ klog .V (5 ).Infof ("schedule to delete snapshot, operation named %s" , operationName )
119
96
ctrl .scheduleOperation (operationName , func () error {
120
97
return ctrl .deleteCSISnapshotOperation (content )
121
98
})
122
99
return nil
123
100
}
124
101
125
- // scheduleOperation starts given asynchronous operation on given volume . It
126
- // makes sure the operation is already not running.
102
+ // scheduleOperation starts given asynchronous operation on given snapshot . It
103
+ // makes sure there is no running operation with the same operationName
127
104
func (ctrl * csiSnapshotSideCarController ) scheduleOperation (operationName string , operation func () error ) {
128
105
klog .V (5 ).Infof ("scheduleOperation[%s]" , operationName )
129
106
@@ -199,17 +176,17 @@ func (ctrl *csiSnapshotSideCarController) updateContentErrorStatusWithEvent(cont
199
176
if content .Status != nil && content .Status .Error != nil && * content .Status .Error .Message == message {
200
177
klog .V (4 ).Infof ("updateContentStatusWithEvent[%s]: the same error %v is already set" , content .Name , content .Status .Error )
201
178
return nil
202
- } else if content .Status == nil {
203
- content .Status = & crdv1.VolumeSnapshotContentStatus {}
204
179
}
205
180
contentClone := content .DeepCopy ()
206
- statusError := & crdv1.VolumeSnapshotError {
181
+ if contentClone .Status == nil {
182
+ contentClone .Status = & crdv1.VolumeSnapshotContentStatus {}
183
+ }
184
+ contentClone .Status .Error = & crdv1.VolumeSnapshotError {
207
185
Time : & metav1.Time {
208
186
Time : time .Now (),
209
187
},
210
188
Message : & message ,
211
189
}
212
- contentClone .Status .Error = statusError
213
190
ready := false
214
191
contentClone .Status .ReadyToUse = & ready
215
192
newContent , err := ctrl .clientset .SnapshotV1beta1 ().VolumeSnapshotContents ().UpdateStatus (contentClone )
@@ -233,7 +210,7 @@ func (ctrl *csiSnapshotSideCarController) updateContentErrorStatusWithEvent(cont
233
210
234
211
func (ctrl * csiSnapshotSideCarController ) getCSISnapshotInput (content * crdv1.VolumeSnapshotContent ) (* crdv1.VolumeSnapshotClass , map [string ]string , error ) {
235
212
className := content .Spec .VolumeSnapshotClassName
236
- klog .V (5 ).Infof ("getCSISnapshotInput for content [%s]: VolumeSnapshotClassName [%s] " , content .Name , * className )
213
+ klog .V (5 ).Infof ("getCSISnapshotInput for content [%s]" , content .Name )
237
214
var class * crdv1.VolumeSnapshotClass
238
215
var err error
239
216
if className != nil {
@@ -358,6 +335,7 @@ func (ctrl *csiSnapshotSideCarController) deleteCSISnapshotOperation(content *cr
358
335
359
336
_ , snapshotterCredentials , err := ctrl .getCSISnapshotInput (content )
360
337
if err != nil {
338
+ ctrl .eventRecorder .Event (content , v1 .EventTypeWarning , "SnapshotDeleteError" , "Failed to get snapshot class or credentials" )
361
339
return fmt .Errorf ("failed to get input parameters to delete snapshot for content %s: %q" , content .Name , err )
362
340
}
363
341
@@ -366,10 +344,42 @@ func (ctrl *csiSnapshotSideCarController) deleteCSISnapshotOperation(content *cr
366
344
ctrl .eventRecorder .Event (content , v1 .EventTypeWarning , "SnapshotDeleteError" , "Failed to delete snapshot" )
367
345
return fmt .Errorf ("failed to delete snapshot %#v, err: %v" , content .Name , err )
368
346
}
369
-
347
+ // the snapshot has been deleted from the underlying storage system, update
348
+ // content status to remove snapshot handle etc.
349
+ newContent , err := ctrl .clearVolumeContentStatus (content .Name )
350
+ if err != nil {
351
+ ctrl .eventRecorder .Event (content , v1 .EventTypeWarning , "SnapshotDeleteError" , "Failed to clear content status" )
352
+ return err
353
+ }
354
+ // update local cache
355
+ ctrl .updateContentInCacheStore (newContent )
370
356
return nil
371
357
}
372
358
359
+ // clearVolumeContentStatus resets all fields to nil related to a snapshot in
360
+ // content.Status. On success, the latest version of the content object will be
361
+ // returned.
362
+ func (ctrl * csiSnapshotSideCarController ) clearVolumeContentStatus (
363
+ contentName string ) (* crdv1.VolumeSnapshotContent , error ) {
364
+ klog .V (5 ).Infof ("cleanVolumeSnapshotStatus content [%s]" , contentName )
365
+ // get the latest version from API server
366
+ content , err := ctrl .clientset .SnapshotV1beta1 ().VolumeSnapshotContents ().Get (contentName , metav1.GetOptions {})
367
+ if err != nil {
368
+ return nil , fmt .Errorf ("error get snapshot content %s from api server: %v" , contentName , err )
369
+ }
370
+ if content .Status != nil {
371
+ content .Status .SnapshotHandle = nil
372
+ content .Status .ReadyToUse = nil
373
+ content .Status .CreationTime = nil
374
+ content .Status .RestoreSize = nil
375
+ }
376
+ newContent , err := ctrl .clientset .SnapshotV1beta1 ().VolumeSnapshotContents ().UpdateStatus (content )
377
+ if err != nil {
378
+ return nil , newControllerUpdateError (contentName , err .Error ())
379
+ }
380
+ return newContent , nil
381
+ }
382
+
373
383
func (ctrl * csiSnapshotSideCarController ) updateSnapshotContentStatus (
374
384
content * crdv1.VolumeSnapshotContent ,
375
385
snapshotHandle string ,
@@ -497,8 +507,13 @@ func (ctrl *csiSnapshotSideCarController) GetCredentialsFromAnnotation(content *
497
507
return snapshotterCredentials , nil
498
508
}
499
509
500
- // removeContentFinalizer removes a Finalizer for VolumeSnapshotContent.
510
+ // removeContentFinalizer removes the VolumeSnapshotContentFinalizer from a
511
+ // content if there exists one.
501
512
func (ctrl csiSnapshotSideCarController ) removeContentFinalizer (content * crdv1.VolumeSnapshotContent ) error {
513
+ if ! slice .ContainsString (content .ObjectMeta .Finalizers , utils .VolumeSnapshotContentFinalizer , nil ) {
514
+ // the finalizer does not exit, return directly
515
+ return nil
516
+ }
502
517
contentClone := content .DeepCopy ()
503
518
contentClone .ObjectMeta .Finalizers = slice .RemoveString (contentClone .ObjectMeta .Finalizers , utils .VolumeSnapshotContentFinalizer , nil )
504
519
@@ -507,12 +522,11 @@ func (ctrl csiSnapshotSideCarController) removeContentFinalizer(content *crdv1.V
507
522
return newControllerUpdateError (content .Name , err .Error ())
508
523
}
509
524
525
+ klog .V (5 ).Infof ("Removed protection finalizer from volume snapshot content %s" , content .Name )
510
526
_ , err = ctrl .storeContentUpdate (contentClone )
511
527
if err != nil {
512
528
klog .Errorf ("failed to update content store %v" , err )
513
529
}
514
-
515
- klog .V (5 ).Infof ("Removed protection finalizer from volume snapshot content %s" , content .Name )
516
530
return nil
517
531
}
518
532
@@ -524,7 +538,7 @@ func (ctrl *csiSnapshotSideCarController) shouldDelete(content *crdv1.VolumeSnap
524
538
if content .ObjectMeta .DeletionTimestamp == nil {
525
539
return false
526
540
}
527
- // 1) shouldDelete returns true if content is not bound
541
+ // 1) shouldDelete returns true if a content is not bound
528
542
// (VolumeSnapshotRef.UID == "") for pre-provisioned snapshot
529
543
if content .Spec .Source .SnapshotHandle != nil && content .Spec .VolumeSnapshotRef .UID == "" {
530
544
return true
0 commit comments