Skip to content

Commit 82dfc90

Browse files
committed
Add state to snapshot create and configurable retry logic
Signed-off-by: Grant Griffiths <[email protected]>
1 parent 850184d commit 82dfc90

File tree

7 files changed

+140
-33
lines changed

7 files changed

+140
-33
lines changed

cmd/csi-snapshotter/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ var (
6363
csiAddress = flag.String("csi-address", "/run/csi/socket", "Address of the CSI driver socket.")
6464
createSnapshotContentRetryCount = flag.Int("create-snapshotcontent-retrycount", 5, "Number of retries when we create a snapshot content object for a snapshot.")
6565
createSnapshotContentInterval = flag.Duration("create-snapshotcontent-interval", 10*time.Second, "Interval between retries when we create a snapshot content object for a snapshot.")
66+
retryIntervalStart = flag.Duration("retry-interval-start", time.Second, "Initial retry interval of failed snapshot creation. It doubles with each failure, up to retry-interval-max.")
67+
retryIntervalMax = flag.Duration("retry-interval-max", 5*time.Minute, "Maximum retry interval of failed snapshot creation.")
6668
resyncPeriod = flag.Duration("resync-period", 60*time.Second, "Resync interval of the controller.")
6769
snapshotNamePrefix = flag.String("snapshot-name-prefix", "snapshot", "Prefix to apply to the name of a created snapshot")
6870
snapshotNameUUIDLength = flag.Int("snapshot-name-uuid-length", -1, "Length in characters for the generated uuid of a created snapshot. Defaults behavior is to NOT truncate.")
@@ -191,6 +193,8 @@ func main() {
191193
coreFactory.Core().V1().PersistentVolumeClaims(),
192194
*createSnapshotContentRetryCount,
193195
*createSnapshotContentInterval,
196+
*retryIntervalStart,
197+
*retryIntervalMax,
194198
snapShotter,
195199
*csiTimeout,
196200
*resyncPeriod,

pkg/controller/csi_handler.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030

3131
// Handler is responsible for handling VolumeSnapshot events from informer.
3232
type Handler interface {
33-
CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error)
33+
CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, snapshotter.SnapshottingState, error)
3434
DeleteSnapshot(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) error
3535
GetSnapshotStatus(content *crdv1.VolumeSnapshotContent) (bool, time.Time, int64, error)
3636
}
@@ -58,19 +58,20 @@ func NewCSIHandler(
5858
}
5959
}
6060

61-
func (handler *csiHandler) CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error) {
61+
func (handler *csiHandler) CreateSnapshot(snapshot *crdv1.VolumeSnapshot, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, snapshotter.SnapshottingState, error) {
6262

6363
ctx, cancel := context.WithTimeout(context.Background(), handler.timeout)
6464
defer cancel()
6565

6666
snapshotName, err := makeSnapshotName(handler.snapshotNamePrefix, string(snapshot.UID), handler.snapshotNameUUIDLength)
6767
if err != nil {
68-
return "", "", time.Time{}, 0, false, err
68+
return "", "", time.Time{}, 0, false, snapshotter.SnapshottingFinished, err
6969
}
7070
newParameters, err := removePrefixedParameters(parameters)
7171
if err != nil {
72-
return "", "", time.Time{}, 0, false, fmt.Errorf("failed to remove CSI Parameters of prefixed keys: %v", err)
72+
return "", "", time.Time{}, 0, false, snapshotter.SnapshottingFinished, fmt.Errorf("failed to remove CSI Parameters of prefixed keys: %v", err)
7373
}
74+
7475
return handler.snapshotter.CreateSnapshot(ctx, snapshotName, volume, newParameters, snapshotterCredentials)
7576
}
7677

pkg/controller/framework_test.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
snapshotscheme "github.com/kubernetes-csi/external-snapshotter/pkg/client/clientset/versioned/scheme"
3636
informers "github.com/kubernetes-csi/external-snapshotter/pkg/client/informers/externalversions"
3737
storagelisters "github.com/kubernetes-csi/external-snapshotter/pkg/client/listers/volumesnapshot/v1alpha1"
38+
"github.com/kubernetes-csi/external-snapshotter/pkg/snapshotter"
3839
"k8s.io/api/core/v1"
3940
storagev1 "k8s.io/api/storage/v1"
4041
storagev1beta1 "k8s.io/api/storage/v1beta1"
@@ -772,6 +773,8 @@ func newTestController(kubeClient kubernetes.Interface, clientset clientset.Inte
772773
coreFactory.Core().V1().PersistentVolumeClaims(),
773774
3,
774775
5*time.Millisecond,
776+
5*time.Millisecond,
777+
10*time.Second,
775778
fakeSnapshot,
776779
5*time.Millisecond,
777780
60*time.Second,
@@ -1329,10 +1332,10 @@ type fakeSnapshotter struct {
13291332
t *testing.T
13301333
}
13311334

1332-
func (f *fakeSnapshotter) CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error) {
1335+
func (f *fakeSnapshotter) CreateSnapshot(ctx context.Context, snapshotName string, volume *v1.PersistentVolume, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, snapshotter.SnapshottingState, error) {
13331336
if f.createCallCounter >= len(f.createCalls) {
13341337
f.t.Errorf("Unexpected CSI Create Snapshot call: snapshotName=%s, volume=%v, index: %d, calls: %+v", snapshotName, volume.Name, f.createCallCounter, f.createCalls)
1335-
return "", "", time.Time{}, 0, false, fmt.Errorf("unexpected call")
1338+
return "", "", time.Time{}, 0, false, snapshotter.SnapshottingFinished, fmt.Errorf("unexpected call")
13361339
}
13371340
call := f.createCalls[f.createCallCounter]
13381341
f.createCallCounter++
@@ -1359,10 +1362,10 @@ func (f *fakeSnapshotter) CreateSnapshot(ctx context.Context, snapshotName strin
13591362
}
13601363

13611364
if err != nil {
1362-
return "", "", time.Time{}, 0, false, fmt.Errorf("unexpected call")
1365+
return "", "", time.Time{}, 0, false, snapshotter.SnapshottingFinished, fmt.Errorf("unexpected call")
13631366
}
13641367

1365-
return call.driverName, call.snapshotId, call.creationTime, call.size, call.readyToUse, call.err
1368+
return call.driverName, call.snapshotId, call.creationTime, call.size, call.readyToUse, snapshotter.SnapshottingFinished, call.err
13661369
}
13671370

13681371
func (f *fakeSnapshotter) DeleteSnapshot(ctx context.Context, snapshotID string, snapshotterCredentials map[string]string) error {

pkg/controller/snapshot_controller.go

+31-14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"time"
2323

2424
crdv1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1"
25+
"github.com/kubernetes-csi/external-snapshotter/pkg/snapshotter"
2526
"k8s.io/api/core/v1"
2627
storagev1 "k8s.io/api/storage/v1"
2728
storage "k8s.io/api/storage/v1beta1"
@@ -364,17 +365,33 @@ func (ctrl *csiSnapshotController) createSnapshot(snapshot *crdv1.VolumeSnapshot
364365
klog.V(5).Infof("createSnapshot[%s]: started", snapshotKey(snapshot))
365366
opName := fmt.Sprintf("create-%s[%s]", snapshotKey(snapshot), string(snapshot.UID))
366367
ctrl.scheduleOperation(opName, func() error {
367-
snapshotObj, err := ctrl.createSnapshotOperation(snapshot)
368+
snapshotObj, state, err := ctrl.createSnapshotOperation(snapshot)
368369
if err != nil {
369370
ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotCreationFailed", fmt.Sprintf("Failed to create snapshot: %v", err))
370371
klog.Errorf("createSnapshot [%s]: error occurred in createSnapshotOperation: %v", opName, err)
372+
373+
// Handle state:
374+
if state == snapshotter.SnapshottingFinished {
375+
// Snapshotting finished, remove obj from snapshotsInProgress.
376+
ctrl.snapshotsInProgress.Delete(string(snapshotObj.UID))
377+
} else if state == snapshotter.SnapshottingInBackground {
378+
// Snapshotting still in progress.
379+
klog.V(4).Infof("createSnapshot [%s]: Temporary error received, adding Snapshot back in queue: %v", snapshotKey(snapshotObj), err)
380+
ctrl.snapshotsInProgress.Store(string(snapshotObj.UID), snapshotObj)
381+
} else {
382+
// State is SnapshottingNoChange. Don't change snapshotsInProgress.
383+
}
384+
371385
return err
372386
}
387+
388+
// If no errors, update the snapshot.
373389
_, updateErr := ctrl.storeSnapshotUpdate(snapshotObj)
374390
if updateErr != nil {
375391
// We will get an "snapshot update" event soon, this is not a big error
376392
klog.V(4).Infof("createSnapshot [%s]: cannot update internal cache: %v", snapshotKey(snapshotObj), updateErr)
377393
}
394+
378395
return nil
379396
})
380397
return nil
@@ -584,7 +601,7 @@ func (ctrl *csiSnapshotController) checkandUpdateBoundSnapshotStatusOperation(sn
584601
if err != nil {
585602
return nil, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err)
586603
}
587-
driverName, snapshotID, creationTime, size, readyToUse, err = ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials)
604+
driverName, snapshotID, creationTime, size, readyToUse, _, err = ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials)
588605
if err != nil {
589606
klog.Errorf("checkandUpdateBoundSnapshotStatusOperation: failed to call create snapshot to check whether the snapshot is ready to use %q", err)
590607
return nil, err
@@ -611,30 +628,30 @@ func (ctrl *csiSnapshotController) checkandUpdateBoundSnapshotStatusOperation(sn
611628
// 2. Update VolumeSnapshot status with creationtimestamp information
612629
// 3. Create the VolumeSnapshotContent object with the snapshot id information.
613630
// 4. Bind the VolumeSnapshot and VolumeSnapshotContent object
614-
func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
631+
func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, snapshotter.SnapshottingState, error) {
615632
klog.Infof("createSnapshot: Creating snapshot %s through the plugin ...", snapshotKey(snapshot))
616633

617634
if snapshot.Status.Error != nil && !isControllerUpdateFailError(snapshot.Status.Error) {
618635
klog.V(4).Infof("error is already set in snapshot, do not retry to create: %s", snapshot.Status.Error.Message)
619-
return snapshot, nil
636+
return snapshot, snapshotter.SnapshottingInBackground, nil
620637
}
621638

622639
// If PVC is not being deleted and finalizer is not added yet, a finalizer should be added.
623640
klog.V(5).Infof("createSnapshotOperation: Check if PVC is not being deleted and add Finalizer for source of snapshot [%s] if needed", snapshot.Name)
624641
err := ctrl.ensureSnapshotSourceFinalizer(snapshot)
625642
if err != nil {
626643
klog.Errorf("createSnapshotOperation failed to add finalizer for source of snapshot %s", err)
627-
return nil, err
644+
return nil, snapshotter.SnapshottingInBackground, err
628645
}
629646

630647
class, volume, contentName, snapshotterCredentials, err := ctrl.getCreateSnapshotInput(snapshot)
631648
if err != nil {
632-
return nil, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err)
649+
return nil, snapshotter.SnapshottingInBackground, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err)
633650
}
634651

635-
driverName, snapshotID, creationTime, size, readyToUse, err := ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials)
652+
driverName, snapshotID, creationTime, size, readyToUse, state, err := ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials)
636653
if err != nil {
637-
return nil, fmt.Errorf("failed to take snapshot of the volume, %s: %q", volume.Name, err)
654+
return nil, state, fmt.Errorf("failed to take snapshot of the volume, %s: %q", volume.Name, err)
638655
}
639656

640657
klog.V(5).Infof("Created snapshot: driver %s, snapshotId %s, creationTime %v, size %d, readyToUse %t", driverName, snapshotID, creationTime, size, readyToUse)
@@ -651,16 +668,16 @@ func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.Volum
651668
}
652669

653670
if err != nil {
654-
return nil, err
671+
return nil, snapshotter.SnapshottingInBackground, err
655672
}
656673
// Create VolumeSnapshotContent in the database
657674
volumeRef, err := ref.GetReference(scheme.Scheme, volume)
658675
if err != nil {
659-
return nil, err
676+
return nil, snapshotter.SnapshottingInBackground, err
660677
}
661678
snapshotRef, err := ref.GetReference(scheme.Scheme, snapshot)
662679
if err != nil {
663-
return nil, err
680+
return nil, snapshotter.SnapshottingInBackground, err
664681
}
665682

666683
if class.DeletionPolicy == nil {
@@ -713,15 +730,15 @@ func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.Volum
713730
strerr := fmt.Sprintf("Error creating volume snapshot content object for snapshot %s: %v.", snapshotKey(snapshot), err)
714731
klog.Error(strerr)
715732
ctrl.eventRecorder.Event(newSnapshot, v1.EventTypeWarning, "CreateSnapshotContentFailed", strerr)
716-
return nil, newControllerUpdateError(snapshotKey(snapshot), err.Error())
733+
return nil, snapshotter.SnapshottingInBackground, newControllerUpdateError(snapshotKey(snapshot), err.Error())
717734
}
718735

719736
// save succeeded, bind and update status for snapshot.
720737
result, err := ctrl.bindandUpdateVolumeSnapshot(snapshotContent, newSnapshot)
721738
if err != nil {
722-
return nil, err
739+
return nil, snapshotter.SnapshottingInBackground, err
723740
}
724-
return result, nil
741+
return result, snapshotter.SnapshottingFinished, nil
725742
}
726743

727744
// Delete a snapshot

pkg/controller/snapshot_controller_base.go

+27-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package controller
1818

1919
import (
2020
"fmt"
21+
"sync"
2122
"time"
2223

2324
crdv1 "github.com/kubernetes-csi/external-snapshotter/pkg/apis/volumesnapshot/v1alpha1"
@@ -50,6 +51,9 @@ type csiSnapshotController struct {
5051
snapshotQueue workqueue.RateLimitingInterface
5152
contentQueue workqueue.RateLimitingInterface
5253

54+
// Map UID -> *Snapshot with all snapshots in progress in the background.
55+
snapshotsInProgress sync.Map
56+
5357
snapshotLister storagelisters.VolumeSnapshotLister
5458
snapshotListerSynced cache.InformerSynced
5559
contentLister storagelisters.VolumeSnapshotContentLister
@@ -68,6 +72,8 @@ type csiSnapshotController struct {
6872

6973
createSnapshotContentRetryCount int
7074
createSnapshotContentInterval time.Duration
75+
retryIntervalStart time.Duration
76+
retryIntervalMax time.Duration
7177
resyncPeriod time.Duration
7278
}
7379

@@ -82,6 +88,8 @@ func NewCSISnapshotController(
8288
pvcInformer coreinformers.PersistentVolumeClaimInformer,
8389
createSnapshotContentRetryCount int,
8490
createSnapshotContentInterval time.Duration,
91+
retryIntervalStart time.Duration,
92+
retryIntervalMax time.Duration,
8593
snapshotter snapshotter.Snapshotter,
8694
timeout time.Duration,
8795
resyncPeriod time.Duration,
@@ -103,10 +111,12 @@ func NewCSISnapshotController(
103111
runningOperations: goroutinemap.NewGoRoutineMap(true),
104112
createSnapshotContentRetryCount: createSnapshotContentRetryCount,
105113
createSnapshotContentInterval: createSnapshotContentInterval,
114+
retryIntervalStart: retryIntervalStart,
115+
retryIntervalMax: retryIntervalMax,
106116
resyncPeriod: resyncPeriod,
107117
snapshotStore: cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc),
108118
contentStore: cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc),
109-
snapshotQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "csi-snapshotter-snapshot"),
119+
snapshotQueue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(retryIntervalStart, retryIntervalMax), "csi-snapshotter-snapshot"),
110120
contentQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "csi-snapshotter-content"),
111121
}
112122

@@ -251,6 +261,10 @@ func (ctrl *csiSnapshotController) snapshotWorker() {
251261
if err == nil {
252262
ctrl.deleteSnapshot(newSnapshot)
253263
}
264+
265+
ctrl.snapshotQueue.Forget(keyObj)
266+
ctrl.snapshotsInProgress.Delete(string(snapshot.UID))
267+
254268
return false
255269
}
256270

@@ -393,12 +407,22 @@ func (ctrl *csiSnapshotController) updateSnapshot(snapshot *crdv1.VolumeSnapshot
393407
}
394408
err = ctrl.syncSnapshot(snapshot)
395409
if err != nil {
410+
sKey := snapshotKey(snapshot)
411+
412+
// if the snapshot has been deleted, remove from snapshots in progress
413+
if _, exists, _ := ctrl.snapshotStore.Get(sKey); !exists {
414+
ctrl.snapshotsInProgress.Delete(string(snapshot.UID))
415+
} else {
416+
// otherwise, add back to the snapshot queue to retry.
417+
ctrl.snapshotQueue.AddRateLimited(sKey)
418+
}
419+
396420
if errors.IsConflict(err) {
397421
// Version conflict error happens quite often and the controller
398422
// recovers from it easily.
399-
klog.V(3).Infof("could not sync claim %q: %+v", snapshotKey(snapshot), err)
423+
klog.V(3).Infof("could not sync claim %q: %+v", sKey, err)
400424
} else {
401-
klog.Errorf("could not sync volume %q: %+v", snapshotKey(snapshot), err)
425+
klog.Errorf("could not sync volume %q: %+v", sKey, err)
402426
}
403427
}
404428
}

0 commit comments

Comments
 (0)