Skip to content

Commit 060ee23

Browse files
committed
Delete individual volume snapshots as part of the group snapshot delete API and prevent users from deleting individual volume snapshots if it is part of an existing group snapshot
Also revert commit bb29899.
1 parent 5ca300e commit 060ee23

File tree

13 files changed

+129
-80
lines changed

13 files changed

+129
-80
lines changed

client/hack/update-crd.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ then
2828
TMP_DIR=$(mktemp -d);
2929
cd $TMP_DIR;
3030
go mod init tmp;
31-
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.11.3;
31+
go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.12.0;
3232
rm -rf $TMP_DIR;
3333
CONTROLLER_GEN=$(which controller-gen)
3434
fi

cmd/csi-snapshotter/main.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,13 @@ var (
7979
kubeAPIQPS = flag.Float64("kube-api-qps", 5, "QPS to use while communicating with the kubernetes apiserver. Defaults to 5.0.")
8080
kubeAPIBurst = flag.Int("kube-api-burst", 10, "Burst to use while communicating with the kubernetes apiserver. Defaults to 10.")
8181

82-
metricsAddress = flag.String("metrics-address", "", "(deprecated) The TCP network address where the prometheus metrics endpoint will listen (example: `:8080`). The default is empty string, which means metrics endpoint is disabled. Only one of `--metrics-address` and `--http-endpoint` can be set.")
83-
httpEndpoint = flag.String("http-endpoint", "", "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string, which means the server is disabled. Only one of `--metrics-address` and `--http-endpoint` can be set.")
84-
metricsPath = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
85-
retryIntervalStart = flag.Duration("retry-interval-start", time.Second, "Initial retry interval of failed volume snapshot creation or deletion. It doubles with each failure, up to retry-interval-max. Default is 1 second.")
86-
retryIntervalMax = flag.Duration("retry-interval-max", 5*time.Minute, "Maximum retry interval of failed volume snapshot creation or deletion. Default is 5 minutes.")
87-
enableNodeDeployment = flag.Bool("node-deployment", false, "Enables deploying the sidecar controller together with a CSI driver on nodes to manage snapshots for node-local volumes.")
88-
// TODO(xing-yang): Enable enableVolumeGroupSnapshots when the feature is fully implemented
89-
// enableVolumeGroupSnapshots = flag.Bool("enable-volume-group-snapshots", false, "Enables the volume group snapshot feature, allowing the user to create snapshots of groups of volumes.")
82+
metricsAddress = flag.String("metrics-address", "", "(deprecated) The TCP network address where the prometheus metrics endpoint will listen (example: `:8080`). The default is empty string, which means metrics endpoint is disabled. Only one of `--metrics-address` and `--http-endpoint` can be set.")
83+
httpEndpoint = flag.String("http-endpoint", "", "The TCP network address where the HTTP server for diagnostics, including metrics and leader election health check, will listen (example: `:8080`). The default is empty string, which means the server is disabled. Only one of `--metrics-address` and `--http-endpoint` can be set.")
84+
metricsPath = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
85+
retryIntervalStart = flag.Duration("retry-interval-start", time.Second, "Initial retry interval of failed volume snapshot creation or deletion. It doubles with each failure, up to retry-interval-max. Default is 1 second.")
86+
retryIntervalMax = flag.Duration("retry-interval-max", 5*time.Minute, "Maximum retry interval of failed volume snapshot creation or deletion. Default is 5 minutes.")
87+
enableNodeDeployment = flag.Bool("node-deployment", false, "Enables deploying the sidecar controller together with a CSI driver on nodes to manage snapshots for node-local volumes.")
88+
enableVolumeGroupSnapshots = flag.Bool("enable-volume-group-snapshots", false, "Enables the volume group snapshot feature, allowing the user to create a snapshot of a group of volumes.")
9089

9190
groupSnapshotNamePrefix = flag.String("groupsnapshot-name-prefix", "groupsnapshot", "Prefix to apply to the name of a created group snapshot")
9291
groupSnapshotNameUUIDLength = flag.Int("groupsnapshot-name-uuid-length", -1, "Length in characters for the generated uuid of a created group snapshot. Defaults behavior is to NOT truncate.")
@@ -228,9 +227,6 @@ func main() {
228227

229228
snapShotter := snapshotter.NewSnapshotter(csiConn)
230229
var groupSnapshotter group_snapshotter.GroupSnapshotter
231-
// TODO(xing-yang): Remove the following line when the enableVolumeGroupSnapshots feature is enabled
232-
bEnable := false
233-
enableVolumeGroupSnapshots := &bEnable
234230
if *enableVolumeGroupSnapshots {
235231
supportsCreateVolumeGroupSnapshot, err := supportsGroupControllerCreateVolumeGroupSnapshot(ctx, csiConn)
236232
if err != nil {

cmd/snapshot-controller/main.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ var (
7272
retryIntervalMax = flag.Duration("retry-interval-max", 5*time.Minute, "Maximum retry interval of failed volume snapshot creation or deletion. Default is 5 minutes.")
7373
enableDistributedSnapshotting = flag.Bool("enable-distributed-snapshotting", false, "Enables each node to handle snapshotting for the local volumes created on that node")
7474
preventVolumeModeConversion = flag.Bool("prevent-volume-mode-conversion", false, "Prevents an unauthorised user from modifying the volume mode when creating a PVC from an existing VolumeSnapshot.")
75-
// TODO(xing-yang): Enable enableVolumeGroupSnapshots when the feature is fully implemented
76-
// enableVolumeGroupSnapshots = flag.Bool("enable-volume-group-snapshots", false, "Enables the volume group snapshot feature, allowing the user to create snapshots of groups of volumes.")
75+
enableVolumeGroupSnapshots = flag.Bool("enable-volume-group-snapshots", false, "Enables the volume group snapshot feature, allowing the user to create a snapshot of a group of volumes.")
7776

7877
retryCRDIntervalMax = flag.Duration("retry-crd-interval-max", 5*time.Second, "Maximum retry interval to wait for CRDs to appear. The default is 5 seconds.")
7978
)
@@ -208,9 +207,6 @@ func main() {
208207

209208
klog.V(2).Infof("Start NewCSISnapshotController with kubeconfig [%s] resyncPeriod [%+v]", *kubeconfig, *resyncPeriod)
210209

211-
// TODO(xing-yang): Remove the following lines when the enableVolumeGroupSnapshots feature is enabled
212-
bEnable := false
213-
enableVolumeGroupSnapshots := &bEnable
214210
ctrl := controller.NewCSISnapshotCommonController(
215211
snapClient,
216212
kubeClient,

pkg/common-controller/groupsnapshot_controller_helper.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,16 +1103,7 @@ func (ctrl *csiSnapshotCommonController) processGroupSnapshotWithDeletionTimesta
11031103
groupSnapshotContent = nil
11041104
}
11051105

1106-
klog.V(5).Infof("processGroupSnapshotWithDeletionTimestamp[%s]: delete group snapshot content and remove finalizer from group snapshot if needed", utils.GroupSnapshotKey(groupSnapshot))
1107-
1108-
return ctrl.checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent(groupSnapshot, groupSnapshotContent, deleteGroupSnapshotContent)
1109-
}
1110-
1111-
// checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent deletes
1112-
// the group snapshot content and removes group snapshot finalizers if needed
1113-
func (ctrl *csiSnapshotCommonController) checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent(groupSnapshot *crdv1alpha1.VolumeGroupSnapshot, groupSnapshotContent *crdv1alpha1.VolumeGroupSnapshotContent, deleteGroupSnapshotContent bool) error {
1114-
klog.V(5).Infof("checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent VolumeGroupSnapshot[%s]: %s", utils.GroupSnapshotKey(groupSnapshot), utils.GetGroupSnapshotStatusForLogging(groupSnapshot))
1115-
1106+
klog.V(5).Infof("processGroupSnapshotWithDeletionTimestamp[%s]: check if group snapshot is a candidate for deletion", utils.GroupSnapshotKey(groupSnapshot))
11161107
if !utils.IsGroupSnapshotDeletionCandidate(groupSnapshot) {
11171108
return nil
11181109
}
@@ -1135,33 +1126,47 @@ func (ctrl *csiSnapshotCommonController) checkandRemoveGroupSnapshotFinalizersAn
11351126

11361127
}
11371128

1138-
// regardless of the deletion policy, set the VolumeSnapshotBeingDeleted on
1139-
// content object, this is to allow snapshotter sidecar controller to conduct
1140-
// a delete operation whenever the content has deletion timestamp set.
1129+
// regardless of the deletion policy, set VolumeGroupSnapshotBeingDeleted on
1130+
// group snapshot content object, this is to allow snapshotter sidecar controller
1131+
// to conduct a delete operation whenever the group snapshot content has deletion
1132+
// timestamp set.
11411133
if groupSnapshotContent != nil {
1142-
klog.V(5).Infof("checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent[%s]: Set VolumeGroupSnapshotBeingDeleted annotation on the group snapshot content [%s]", utils.GroupSnapshotKey(groupSnapshot), groupSnapshotContent.Name)
1134+
klog.V(5).Infof("processGroupSnapshotWithDeletionTimestamp[%s]: Set VolumeGroupSnapshotBeingDeleted annotation on the group snapshot content [%s]", utils.GroupSnapshotKey(groupSnapshot), groupSnapshotContent.Name)
11431135
updatedGroupSnapshotContent, err := ctrl.setAnnVolumeGroupSnapshotBeingDeleted(groupSnapshotContent)
11441136
if err != nil {
1145-
klog.V(4).Infof("checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent[%s]: failed to set VolumeGroupSnapshotBeingDeleted annotation on the group snapshot content [%s]", utils.GroupSnapshotKey(groupSnapshot), groupSnapshotContent.Name)
1137+
klog.V(4).Infof("processGroupSnapshotWithDeletionTimestamp[%s]: failed to set VolumeGroupSnapshotBeingDeleted annotation on the group snapshot content [%s]: %v", utils.GroupSnapshotKey(groupSnapshot), groupSnapshotContent.Name, err)
11461138
return err
11471139
}
11481140
groupSnapshotContent = updatedGroupSnapshotContent
11491141
}
11501142

11511143
// VolumeGroupSnapshot should be deleted. Check and remove finalizers
11521144
// If group snapshot content exists and has a deletion policy of Delete, set
1153-
// DeletionTimeStamp on the content;
1145+
// DeletionTimeStamp on the group snapshot content;
11541146
// VolumeGroupSnapshotContent won't be deleted immediately due to the VolumeGroupSnapshotContentFinalizer
11551147
if groupSnapshotContent != nil && deleteGroupSnapshotContent {
1156-
klog.V(5).Infof("checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent: set DeletionTimeStamp on group snapshot content [%s].", groupSnapshotContent.Name)
1148+
klog.V(5).Infof("processGroupSnapshotWithDeletionTimestamp[%s]: set DeletionTimeStamp on group snapshot content [%s].", utils.GroupSnapshotKey(groupSnapshot), groupSnapshotContent.Name)
11571149
err := ctrl.clientset.GroupsnapshotV1alpha1().VolumeGroupSnapshotContents().Delete(context.TODO(), groupSnapshotContent.Name, metav1.DeleteOptions{})
11581150
if err != nil {
11591151
ctrl.eventRecorder.Event(groupSnapshot, v1.EventTypeWarning, "GroupSnapshotContentObjectDeleteError", "Failed to delete group snapshot content API object")
11601152
return fmt.Errorf("failed to delete VolumeGroupSnapshotContent %s from API server: %q", groupSnapshotContent.Name, err)
11611153
}
11621154
}
11631155

1164-
klog.V(5).Infof("checkandRemoveGroupSnapshotFinalizersAndCheckandDeleteGroupSnapshotContent: Remove Finalizer for VolumeGroupSnapshot[%s]", utils.GroupSnapshotKey(groupSnapshot))
1156+
klog.V(5).Infof("processGroupSnapshotWithDeletionTimestamp[%s]: Delete individual snapshots that are part of the group snapshot", utils.GroupSnapshotKey(groupSnapshot))
1157+
1158+
// Delete the individual snapshots part of the group snapshot
1159+
for _, snapshot := range groupSnapshot.Status.VolumeSnapshotRefList {
1160+
err := ctrl.clientset.SnapshotV1().VolumeSnapshots(snapshot.Namespace).Delete(context.TODO(), snapshot.Name, metav1.DeleteOptions{})
1161+
if err != nil {
1162+
msg := fmt.Sprintf("failed to delete snapshot API object %s/%s part of group snapshot %s: %v", snapshot.Namespace, snapshot.Name, utils.GroupSnapshotKey(groupSnapshot), err)
1163+
klog.Error(msg)
1164+
ctrl.eventRecorder.Event(groupSnapshot, v1.EventTypeWarning, "SnapshotDeleteError", msg)
1165+
return fmt.Errorf(msg)
1166+
}
1167+
}
1168+
1169+
klog.V(5).Infof("processGroupSnapshotWithDeletionTimestamp[%s] : Remove Finalizer for VolumeGroupSnapshot", utils.GroupSnapshotKey(groupSnapshot))
11651170
// remove VolumeSnapshotBoundFinalizer on the VolumeGroupSnapshot object:
11661171
// a. If there is no group snapshot content found, remove the finalizer.
11671172
// b. If the group snapshot content is being deleted, i.e., with deleteGroupSnapshotContent == true,

pkg/common-controller/snapshot_controller.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,21 @@ func (ctrl *csiSnapshotCommonController) processSnapshotWithDeletionTimestamp(sn
286286
content = nil
287287
}
288288

289+
// Block deletion if this snapshot belongs to a group snapshot.
290+
if snapshot.Status != nil && snapshot.Status.VolumeGroupSnapshotName != nil {
291+
groupSnapshot, err := ctrl.groupSnapshotLister.VolumeGroupSnapshots(snapshot.Namespace).Get(*snapshot.Status.VolumeGroupSnapshotName)
292+
if err == nil {
293+
msg := fmt.Sprintf("deletion of the individual volume snapshot %s is not allowed as it belongs to group snapshot %s. Deleting the group snapshot will trigger the deletion of all the individual volume snapshots that are part of the group.", utils.SnapshotKey(snapshot), utils.GroupSnapshotKey(groupSnapshot))
294+
klog.Error(msg)
295+
ctrl.eventRecorder.Event(snapshot, v1.EventTypeWarning, "SnapshotDeleteError", msg)
296+
return fmt.Errorf(msg)
297+
}
298+
if !apierrs.IsNotFound(err) {
299+
klog.Errorf("failed to delete snapshot %s: %v", utils.SnapshotKey(snapshot), err)
300+
return err
301+
}
302+
}
303+
289304
klog.V(5).Infof("processSnapshotWithDeletionTimestamp[%s]: delete snapshot content and remove finalizer from snapshot if needed", utils.SnapshotKey(snapshot))
290305

291306
return ctrl.checkandRemoveSnapshotFinalizersAndCheckandDeleteContent(snapshot, content, deleteContent)
@@ -1139,6 +1154,27 @@ func (ctrl *csiSnapshotCommonController) updateSnapshotStatus(snapshot *crdv1.Vo
11391154
volumeSnapshotErr = content.Status.Error.DeepCopy()
11401155
}
11411156

1157+
var groupSnapshotName string
1158+
if content.Status != nil && content.Status.VolumeGroupSnapshotHandle != nil {
1159+
// If this snapshot belongs to a group snapshot, find the group snapshot
1160+
// name from the group snapshot content
1161+
groupSnapshotContentList, err := ctrl.groupSnapshotContentLister.List(labels.Everything())
1162+
if err != nil {
1163+
return nil, err
1164+
}
1165+
found := false
1166+
for _, groupSnapshotContent := range groupSnapshotContentList {
1167+
if groupSnapshotContent.Status != nil && groupSnapshotContent.Status.VolumeGroupSnapshotHandle != nil && *groupSnapshotContent.Status.VolumeGroupSnapshotHandle == *content.Status.VolumeGroupSnapshotHandle {
1168+
groupSnapshotName = groupSnapshotContent.Spec.VolumeGroupSnapshotRef.Name
1169+
found = true
1170+
break
1171+
}
1172+
}
1173+
if !found {
1174+
return nil, fmt.Errorf("updateSnapshotStatus: cannot find the group snapshot for VolumeSnapshot [%s], will not update snapshot status", utils.SnapshotKey(snapshot))
1175+
}
1176+
}
1177+
11421178
klog.V(5).Infof("updateSnapshotStatus: updating VolumeSnapshot [%+v] based on VolumeSnapshotContentStatus [%+v]", snapshot, content.Status)
11431179

11441180
snapshotObj, err := ctrl.clientset.SnapshotV1().VolumeSnapshots(snapshot.Namespace).Get(context.TODO(), snapshot.Name, metav1.GetOptions{})
@@ -1162,6 +1198,9 @@ func (ctrl *csiSnapshotCommonController) updateSnapshotStatus(snapshot *crdv1.Vo
11621198
if volumeSnapshotErr != nil {
11631199
newStatus.Error = volumeSnapshotErr
11641200
}
1201+
if groupSnapshotName != "" {
1202+
newStatus.VolumeGroupSnapshotName = &groupSnapshotName
1203+
}
11651204
updated = true
11661205
} else {
11671206
newStatus = snapshotObj.Status.DeepCopy()
@@ -1188,6 +1227,10 @@ func (ctrl *csiSnapshotCommonController) updateSnapshotStatus(snapshot *crdv1.Vo
11881227
newStatus.Error = volumeSnapshotErr
11891228
updated = true
11901229
}
1230+
if newStatus.VolumeGroupSnapshotName == nil && groupSnapshotName != "" {
1231+
newStatus.VolumeGroupSnapshotName = &groupSnapshotName
1232+
updated = true
1233+
}
11911234
}
11921235

11931236
if updated {

pkg/sidecar-controller/content_create_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestSyncContent(t *testing.T) {
4949
readyToUse: true,
5050
},
5151
},
52-
expectedListCalls: []listCall{{"sid1-1", map[string]string{}, true, time.Now(), 1, nil}},
52+
expectedListCalls: []listCall{{"sid1-1", map[string]string{}, true, time.Now(), 1, nil, ""}},
5353
expectSuccess: true,
5454
errors: noerrors,
5555
test: testSyncContent,
@@ -78,7 +78,7 @@ func TestSyncContent(t *testing.T) {
7878
size: defaultSize,
7979
},
8080
},
81-
expectedListCalls: []listCall{{"sid1-2", map[string]string{}, true, time.Now(), 1, nil}},
81+
expectedListCalls: []listCall{{"sid1-2", map[string]string{}, true, time.Now(), 1, nil, ""}},
8282
expectSuccess: true,
8383
errors: noerrors,
8484
test: testSyncContent,
@@ -194,7 +194,7 @@ func TestSyncContent(t *testing.T) {
194194
readyToUse: true,
195195
},
196196
},
197-
expectedListCalls: []listCall{{"sid1-6", map[string]string{}, true, time.Now(), 1, nil}},
197+
expectedListCalls: []listCall{{"sid1-6", map[string]string{}, true, time.Now(), 1, nil, ""}},
198198
errors: noerrors,
199199
test: testSyncContent,
200200
},

pkg/sidecar-controller/csi_handler.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import (
3434
type Handler interface {
3535
CreateSnapshot(content *crdv1.VolumeSnapshotContent, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, time.Time, int64, bool, error)
3636
DeleteSnapshot(content *crdv1.VolumeSnapshotContent, snapshotterCredentials map[string]string) error
37-
GetSnapshotStatus(content *crdv1.VolumeSnapshotContent, snapshotterListCredentials map[string]string) (bool, time.Time, int64, error)
37+
GetSnapshotStatus(content *crdv1.VolumeSnapshotContent, snapshotterListCredentials map[string]string) (bool, time.Time, int64, string, error)
3838
CreateGroupSnapshot(content *crdv1alpha1.VolumeGroupSnapshotContent, volumeIDs []string, parameters map[string]string, snapshotterCredentials map[string]string) (string, string, []*csi.Snapshot, time.Time, bool, error)
3939
GetGroupSnapshotStatus(content *crdv1alpha1.VolumeGroupSnapshotContent, snapshotterListCredentials map[string]string) (bool, time.Time, error)
4040
DeleteGroupSnapshot(content *crdv1alpha1.VolumeGroupSnapshotContent, SnapshotID []string, snapshotterCredentials map[string]string) error
@@ -113,7 +113,7 @@ func (handler *csiHandler) DeleteSnapshot(content *crdv1.VolumeSnapshotContent,
113113
return nil
114114
}
115115

116-
func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotContent, snapshotterListCredentials map[string]string) (bool, time.Time, int64, error) {
116+
func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotContent, snapshotterListCredentials map[string]string) (bool, time.Time, int64, string, error) {
117117
ctx, cancel := context.WithTimeout(context.Background(), handler.timeout)
118118
defer cancel()
119119

@@ -124,15 +124,15 @@ func (handler *csiHandler) GetSnapshotStatus(content *crdv1.VolumeSnapshotConten
124124
} else if content.Spec.Source.SnapshotHandle != nil {
125125
snapshotHandle = *content.Spec.Source.SnapshotHandle
126126
} else {
127-
return false, time.Time{}, 0, fmt.Errorf("failed to list snapshot for content %s: snapshotHandle is missing", content.Name)
127+
return false, time.Time{}, 0, "", fmt.Errorf("failed to list snapshot for content %s: snapshotHandle is missing", content.Name)
128128
}
129129

130-
csiSnapshotStatus, timestamp, size, err := handler.snapshotter.GetSnapshotStatus(ctx, snapshotHandle, snapshotterListCredentials)
130+
csiSnapshotStatus, timestamp, size, groupSnapshotID, err := handler.snapshotter.GetSnapshotStatus(ctx, snapshotHandle, snapshotterListCredentials)
131131
if err != nil {
132-
return false, time.Time{}, 0, fmt.Errorf("failed to list snapshot for content %s: %q", content.Name, err)
132+
return false, time.Time{}, 0, "", fmt.Errorf("failed to list snapshot for content %s: %q", content.Name, err)
133133
}
134134

135-
return csiSnapshotStatus, timestamp, size, nil
135+
return csiSnapshotStatus, timestamp, size, groupSnapshotID, nil
136136
}
137137

138138
func makeSnapshotName(prefix, snapshotUID string, snapshotNameUUIDLength int) (string, error) {

0 commit comments

Comments
 (0)