Skip to content

Commit c68695b

Browse files
authored
Merge pull request #952 from RaunakShah/deletegroupsnapshot-part-2
DeleteVolumeGroupSnapshot API - part 2
2 parents 5ca300e + 060ee23 commit c68695b

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)