Skip to content

Commit 9595e5a

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

File tree

7 files changed

+166
-36
lines changed

7 files changed

+166
-36
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

+35-17
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"
@@ -361,20 +362,37 @@ func (ctrl *csiSnapshotController) storeContentUpdate(content interface{}) (bool
361362

362363
// createSnapshot starts new asynchronous operation to create snapshot
363364
func (ctrl *csiSnapshotController) createSnapshot(snapshot *crdv1.VolumeSnapshot) error {
364-
klog.V(5).Infof("createSnapshot[%s]: started", snapshotKey(snapshot))
365-
opName := fmt.Sprintf("create-%s[%s]", snapshotKey(snapshot), string(snapshot.UID))
365+
key := snapshotKey(snapshot)
366+
klog.V(5).Infof("createSnapshot[%s]: started", key)
367+
opName := fmt.Sprintf("create-%s[%s]", key, string(snapshot.UID))
366368
ctrl.scheduleOperation(opName, func() error {
367-
snapshotObj, err := ctrl.createSnapshotOperation(snapshot)
369+
snapshotObj, state, err := ctrl.createSnapshotOperation(snapshot)
368370
if err != nil {
369371
ctrl.updateSnapshotErrorStatusWithEvent(snapshot, v1.EventTypeWarning, "SnapshotCreationFailed", fmt.Sprintf("Failed to create snapshot: %v", err))
370372
klog.Errorf("createSnapshot [%s]: error occurred in createSnapshotOperation: %v", opName, err)
373+
374+
// Handle state:
375+
if state == snapshotter.SnapshottingFinished {
376+
// Snapshotting finished, remove obj from snapshotsInProgress.
377+
ctrl.snapshotsInProgress.Delete(key)
378+
} else if state == snapshotter.SnapshottingInBackground {
379+
// Snapshotting still in progress.
380+
klog.V(4).Infof("createSnapshot [%s]: Temporary error received, adding Snapshot back in queue: %v", key, err)
381+
ctrl.snapshotsInProgress.Store(key, snapshotObj)
382+
} else {
383+
// State is SnapshottingNoChange. Don't change snapshotsInProgress.
384+
}
385+
371386
return err
372387
}
388+
389+
// If no errors, update the snapshot.
373390
_, updateErr := ctrl.storeSnapshotUpdate(snapshotObj)
374391
if updateErr != nil {
375392
// We will get an "snapshot update" event soon, this is not a big error
376-
klog.V(4).Infof("createSnapshot [%s]: cannot update internal cache: %v", snapshotKey(snapshotObj), updateErr)
393+
klog.V(4).Infof("createSnapshot [%s]: cannot update internal cache: %v", key, updateErr)
377394
}
395+
378396
return nil
379397
})
380398
return nil
@@ -584,7 +602,7 @@ func (ctrl *csiSnapshotController) checkandUpdateBoundSnapshotStatusOperation(sn
584602
if err != nil {
585603
return nil, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err)
586604
}
587-
driverName, snapshotID, creationTime, size, readyToUse, err = ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials)
605+
driverName, snapshotID, creationTime, size, readyToUse, _, err = ctrl.handler.CreateSnapshot(snapshot, volume, class.Parameters, snapshotterCredentials)
588606
if err != nil {
589607
klog.Errorf("checkandUpdateBoundSnapshotStatusOperation: failed to call create snapshot to check whether the snapshot is ready to use %q", err)
590608
return nil, err
@@ -611,30 +629,30 @@ func (ctrl *csiSnapshotController) checkandUpdateBoundSnapshotStatusOperation(sn
611629
// 2. Update VolumeSnapshot status with creationtimestamp information
612630
// 3. Create the VolumeSnapshotContent object with the snapshot id information.
613631
// 4. Bind the VolumeSnapshot and VolumeSnapshotContent object
614-
func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
632+
func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, snapshotter.SnapshottingState, error) {
615633
klog.Infof("createSnapshot: Creating snapshot %s through the plugin ...", snapshotKey(snapshot))
616634

617635
if snapshot.Status.Error != nil && !isControllerUpdateFailError(snapshot.Status.Error) {
618636
klog.V(4).Infof("error is already set in snapshot, do not retry to create: %s", snapshot.Status.Error.Message)
619-
return snapshot, nil
637+
return snapshot, snapshotter.SnapshottingNoChange, nil
620638
}
621639

622640
// If PVC is not being deleted and finalizer is not added yet, a finalizer should be added.
623641
klog.V(5).Infof("createSnapshotOperation: Check if PVC is not being deleted and add Finalizer for source of snapshot [%s] if needed", snapshot.Name)
624642
err := ctrl.ensureSnapshotSourceFinalizer(snapshot)
625643
if err != nil {
626644
klog.Errorf("createSnapshotOperation failed to add finalizer for source of snapshot %s", err)
627-
return nil, err
645+
return nil, snapshotter.SnapshottingNoChange, err
628646
}
629647

630648
class, volume, contentName, snapshotterCredentials, err := ctrl.getCreateSnapshotInput(snapshot)
631649
if err != nil {
632-
return nil, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err)
650+
return nil, snapshotter.SnapshottingNoChange, fmt.Errorf("failed to get input parameters to create snapshot %s: %q", snapshot.Name, err)
633651
}
634652

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

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

653671
if err != nil {
654-
return nil, err
672+
return nil, snapshotter.SnapshottingInBackground, err
655673
}
656674
// Create VolumeSnapshotContent in the database
657675
volumeRef, err := ref.GetReference(scheme.Scheme, volume)
658676
if err != nil {
659-
return nil, err
677+
return nil, snapshotter.SnapshottingInBackground, err
660678
}
661679
snapshotRef, err := ref.GetReference(scheme.Scheme, snapshot)
662680
if err != nil {
663-
return nil, err
681+
return nil, snapshotter.SnapshottingInBackground, err
664682
}
665683

666684
if class.DeletionPolicy == nil {
@@ -713,15 +731,15 @@ func (ctrl *csiSnapshotController) createSnapshotOperation(snapshot *crdv1.Volum
713731
strerr := fmt.Sprintf("Error creating volume snapshot content object for snapshot %s: %v.", snapshotKey(snapshot), err)
714732
klog.Error(strerr)
715733
ctrl.eventRecorder.Event(newSnapshot, v1.EventTypeWarning, "CreateSnapshotContentFailed", strerr)
716-
return nil, newControllerUpdateError(snapshotKey(snapshot), err.Error())
734+
return nil, snapshotter.SnapshottingInBackground, newControllerUpdateError(snapshotKey(snapshot), err.Error())
717735
}
718736

719737
// save succeeded, bind and update status for snapshot.
720738
result, err := ctrl.bindandUpdateVolumeSnapshot(snapshotContent, newSnapshot)
721739
if err != nil {
722-
return nil, err
740+
return nil, snapshotter.SnapshottingInBackground, err
723741
}
724-
return result, nil
742+
return result, snapshotter.SnapshottingFinished, nil
725743
}
726744

727745
// Delete a snapshot

pkg/controller/snapshot_controller_base.go

+49-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

@@ -230,6 +240,28 @@ func (ctrl *csiSnapshotController) snapshotWorker() {
230240
klog.V(2).Infof("error getting snapshot %q from informer: %v", key, err)
231241
return false
232242
}
243+
244+
// Not found in informer cache, attempt to get from snapshotsInProgress.
245+
inProgressObj, found := ctrl.snapshotsInProgress.Load(key)
246+
if found {
247+
snapshot, ok := inProgressObj.(*crdv1.VolumeSnapshot)
248+
if !ok {
249+
klog.Errorf("expected vs, got %+v", inProgressObj)
250+
return false
251+
}
252+
253+
newSnapshot, err := ctrl.checkAndUpdateSnapshotClass(snapshot)
254+
if err == nil {
255+
klog.V(5).Infof("passed checkAndUpdateSnapshotClass for snapshot %q", key)
256+
ctrl.updateSnapshot(newSnapshot)
257+
}
258+
259+
ctrl.snapshotQueue.Forget(keyObj)
260+
ctrl.snapshotsInProgress.Delete(key)
261+
262+
return false
263+
}
264+
233265
// The snapshot is not in informer cache, the event must have been "delete"
234266
vsObj, found, err := ctrl.snapshotStore.GetByKey(key)
235267
if err != nil {
@@ -251,6 +283,10 @@ func (ctrl *csiSnapshotController) snapshotWorker() {
251283
if err == nil {
252284
ctrl.deleteSnapshot(newSnapshot)
253285
}
286+
287+
ctrl.snapshotQueue.Forget(keyObj)
288+
ctrl.snapshotsInProgress.Delete(key)
289+
254290
return false
255291
}
256292

@@ -393,12 +429,22 @@ func (ctrl *csiSnapshotController) updateSnapshot(snapshot *crdv1.VolumeSnapshot
393429
}
394430
err = ctrl.syncSnapshot(snapshot)
395431
if err != nil {
432+
sKey := snapshotKey(snapshot)
433+
434+
// if the snapshot has been deleted, remove from snapshots in progress
435+
if _, exists, _ := ctrl.snapshotStore.Get(sKey); !exists {
436+
ctrl.snapshotsInProgress.Delete(sKey)
437+
} else {
438+
// otherwise, add back to the snapshot queue to retry.
439+
ctrl.snapshotQueue.AddRateLimited(sKey)
440+
}
441+
396442
if errors.IsConflict(err) {
397443
// Version conflict error happens quite often and the controller
398444
// recovers from it easily.
399-
klog.V(3).Infof("could not sync claim %q: %+v", snapshotKey(snapshot), err)
445+
klog.V(3).Infof("could not sync claim %q: %+v", sKey, err)
400446
} else {
401-
klog.Errorf("could not sync volume %q: %+v", snapshotKey(snapshot), err)
447+
klog.Errorf("could not sync volume %q: %+v", sKey, err)
402448
}
403449
}
404450
}

0 commit comments

Comments
 (0)