Skip to content

Commit 86b7ce1

Browse files
committed
Centralize VolumeGroupSnapshot members resource creation in the snapshot-controller
Previously, the csi-snapshotter was responsible for creating individual VolumeSnapshots and VolumeSnapshotContents for each member when dynamically provisioning a VolumeGroupSnapshot. This commit relocates this logic to the snapshot-controller, bringing all resource creation processes under a single authority.
1 parent 8d54afd commit 86b7ce1

File tree

5 files changed

+198
-129
lines changed

5 files changed

+198
-129
lines changed

deploy/kubernetes/csi-snapshotter/rbac-csi-snapshotter.yaml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,9 @@ rules:
3434
- apiGroups: ["snapshot.storage.k8s.io"]
3535
resources: ["volumesnapshotclasses"]
3636
verbs: ["get", "list", "watch"]
37-
- apiGroups: ["snapshot.storage.k8s.io"]
38-
resources: ["volumesnapshots"]
39-
verbs: ["get", "list", "watch", "update", "patch", "create"]
4037
- apiGroups: ["snapshot.storage.k8s.io"]
4138
resources: ["volumesnapshotcontents"]
42-
verbs: ["get", "list", "watch", "update", "patch", "create"]
39+
verbs: ["get", "list", "watch", "update", "patch"]
4340
- apiGroups: ["snapshot.storage.k8s.io"]
4441
resources: ["volumesnapshotcontents/status"]
4542
verbs: ["update", "patch"]

deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ rules:
3838
verbs: ["patch"]
3939
- apiGroups: ["snapshot.storage.k8s.io"]
4040
resources: ["volumesnapshots"]
41-
verbs: ["get", "list", "watch", "update", "patch", "delete"]
41+
verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
4242
- apiGroups: ["snapshot.storage.k8s.io"]
4343
resources: ["volumesnapshots/status"]
4444
verbs: ["update", "patch"]

pkg/common-controller/groupsnapshot_controller_helper.go

Lines changed: 188 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package common_controller
1818

1919
import (
2020
"context"
21+
"crypto/sha256"
2122
"fmt"
2223
"time"
2324

@@ -459,16 +460,24 @@ func (ctrl *csiSnapshotCommonController) syncUnreadyGroupSnapshot(groupSnapshot
459460
return fmt.Errorf("VolumeGroupSnapshotHandle should not be set in the group snapshot content for dynamic provisioning for group snapshot %s", uniqueGroupSnapshotName)
460461
}
461462

462-
newGroupSnapshot, err := ctrl.bindandUpdateVolumeGroupSnapshot(contentObj, groupSnapshot)
463+
// TODO(leonardoce): introduce a current context in this function
464+
newGroupSnapshotContentObj, err := ctrl.createSnapshotsForGroupSnapshotContent(context.TODO(), contentObj, groupSnapshot)
465+
if err != nil {
466+
klog.V(4).Infof("createSnapshotsForGroupSnapshotContent[%s]: failed to create snapshots and snapshotcontents for group snapshot %v: %v",
467+
contentObj.Name, groupSnapshot.Name, err.Error())
468+
return err
469+
}
470+
471+
updatedGroupSnapshot, err := ctrl.bindandUpdateVolumeGroupSnapshot(newGroupSnapshotContentObj, groupSnapshot)
463472
if err != nil {
464473
klog.V(4).Infof("bindandUpdateVolumeGroupSnapshot[%s]: failed to bind group snapshot content [%s] to group snapshot %v", uniqueGroupSnapshotName, contentObj.Name, err)
465474
return err
466475
}
467-
klog.V(5).Infof("bindandUpdateVolumeGroupSnapshot %v", newGroupSnapshot)
476+
klog.V(5).Infof("bindandUpdateVolumeGroupSnapshot %v", updatedGroupSnapshot)
468477
return nil
469478
}
470479

471-
// If we reach here, it is a dynamically provisioned group snapshot, and the VolumeGroupSnapshotContent object is not yet created.
480+
// If reach here, it is a dynamically provisioned group snapshot, and the VolumeGroupSnapshotContent object is not yet created.
472481
var groupSnapshotContent *crdv1alpha1.VolumeGroupSnapshotContent
473482
if groupSnapshotContent, err = ctrl.createGroupSnapshotContent(groupSnapshot); err != nil {
474483
ctrl.updateGroupSnapshotErrorStatusWithEvent(groupSnapshot, true, v1.EventTypeWarning, "GroupSnapshotContentCreationFailed", fmt.Sprintf("failed to create group snapshot content with error %v", err))
@@ -485,6 +494,182 @@ func (ctrl *csiSnapshotCommonController) syncUnreadyGroupSnapshot(groupSnapshot
485494
return nil
486495
}
487496

497+
func (ctrl *csiSnapshotCommonController) createSnapshotsForGroupSnapshotContent(
498+
ctx context.Context,
499+
groupSnapshotContent *crdv1alpha1.VolumeGroupSnapshotContent,
500+
groupSnapshot *crdv1alpha1.VolumeGroupSnapshot,
501+
) (*crdv1alpha1.VolumeGroupSnapshotContent, error) {
502+
// No status is present, or no volume snapshot was provisioned.
503+
// Let's wait for the snapshotter sidecar to fill it.
504+
if groupSnapshotContent.Status == nil || len(groupSnapshotContent.Status.VolumeSnapshotHandlePairList) == 0 {
505+
return groupSnapshotContent, nil
506+
}
507+
508+
// The contents of the volume group snapshot class are needed to set the
509+
// metadata containing the secrets to recover the snapshots
510+
if groupSnapshot.Spec.VolumeGroupSnapshotClassName == nil {
511+
return groupSnapshotContent, fmt.Errorf(
512+
"createSnapshotsForGroupSnapshotContent: internal error: cannot find reference to volume group snapshot class")
513+
}
514+
515+
groupSnapshotClass, err := ctrl.groupSnapshotClassLister.Get(*groupSnapshot.Spec.VolumeGroupSnapshotClassName)
516+
if err != nil {
517+
return groupSnapshotContent, fmt.Errorf(
518+
"createSnapshotsForGroupSnapshotContent: failed to get volume snapshot class %s: %q",
519+
*groupSnapshot.Spec.VolumeGroupSnapshotClassName, err)
520+
}
521+
522+
groupSnapshotSecret, err := utils.GetGroupSnapshotSecretReference(
523+
utils.GroupSnapshotterSecretParams,
524+
groupSnapshotClass.Parameters,
525+
groupSnapshotContent.GetObjectMeta().GetName(), nil)
526+
if err != nil {
527+
return groupSnapshotContent, fmt.Errorf(
528+
"createSnapshotsForGroupSnapshotContent: failed to get secret reference for group snapshot content %s: %v",
529+
groupSnapshotContent.Name, err)
530+
}
531+
532+
// TODO(leonardoce): this API server call is very expensive. We need to introduce a
533+
// PV lister on the controller and an indexer on spec.csi.driver + "^" + spec.csi.volumeHandle
534+
// to be used for fast lookups
535+
pvs, err := ctrl.client.CoreV1().PersistentVolumes().List(context.TODO(), metav1.ListOptions{})
536+
if err != nil {
537+
return groupSnapshotContent, fmt.Errorf("createSnapshotsForGroupSnapshotContent: error get PersistentVolumes list from API server: %v", err)
538+
}
539+
540+
// Phase 1: create the VolumeSnapshotContent and VolumeSnapshot objects
541+
klog.V(4).Infof(
542+
"createSnapshotsForGroupSnapshotContent[%s]: creating volumesnapshots and volumesnapshotcontent for group snapshot content",
543+
groupSnapshotContent.Name)
544+
545+
groupSnapshotContent.Status.PVVolumeSnapshotContentList = make(
546+
[]crdv1alpha1.PVVolumeSnapshotContentPair,
547+
len(groupSnapshotContent.Status.VolumeSnapshotHandlePairList),
548+
)
549+
for i, snapshot := range groupSnapshotContent.Status.VolumeSnapshotHandlePairList {
550+
snapshotHandle := snapshot.SnapshotHandle
551+
volumeHandle := snapshot.VolumeHandle
552+
553+
pv := utils.GetPersistentVolumeFromHandle(pvs, groupSnapshotContent.Spec.Driver, volumeHandle)
554+
if pv == nil {
555+
klog.Errorf(
556+
"updateGroupSnapshotContentStatus: unable to find PV for volumeHandle:[%s] and CSI driver:[%s]",
557+
volumeHandle,
558+
groupSnapshotContent.Spec.Driver)
559+
}
560+
561+
volumeSnapshotContentName := getSnapshotContentNameForVolumeGroupSnapshotContent(
562+
string(groupSnapshotContent.UID), volumeHandle)
563+
564+
volumeSnapshotName := getSnapshotNameForVolumeGroupSnapshotContent(
565+
string(groupSnapshotContent.UID), volumeHandle)
566+
567+
volumeSnapshotNamespace := groupSnapshotContent.Spec.VolumeGroupSnapshotRef.Namespace
568+
569+
volumeSnapshotContent := &crdv1.VolumeSnapshotContent{
570+
ObjectMeta: metav1.ObjectMeta{
571+
Name: volumeSnapshotContentName,
572+
},
573+
Spec: crdv1.VolumeSnapshotContentSpec{
574+
VolumeSnapshotRef: v1.ObjectReference{
575+
Kind: "VolumeSnapshot",
576+
Name: volumeSnapshotName,
577+
Namespace: volumeSnapshotNamespace,
578+
},
579+
DeletionPolicy: groupSnapshotContent.Spec.DeletionPolicy,
580+
Driver: groupSnapshotContent.Spec.Driver,
581+
Source: crdv1.VolumeSnapshotContentSource{
582+
SnapshotHandle: &snapshotHandle,
583+
},
584+
},
585+
// The status will be set by VolumeSnapshotContent reconciler
586+
// in the CSI snapshotter sidecar.
587+
}
588+
589+
if pv != nil {
590+
volumeSnapshotContent.Spec.SourceVolumeMode = pv.Spec.VolumeMode
591+
}
592+
593+
if groupSnapshotSecret != nil {
594+
klog.V(5).Infof("createSnapshotsForGroupSnapshotContent: set annotation [%s] on volume snapshot content [%s].", utils.AnnDeletionSecretRefName, volumeSnapshotContent.Name)
595+
metav1.SetMetaDataAnnotation(&volumeSnapshotContent.ObjectMeta, utils.AnnDeletionSecretRefName, groupSnapshotSecret.Name)
596+
597+
klog.V(5).Infof("createSnapshotsForGroupSnapshotContent: set annotation [%s] on volume snapshot content [%s].", utils.AnnDeletionSecretRefNamespace, volumeSnapshotContent.Name)
598+
metav1.SetMetaDataAnnotation(&volumeSnapshotContent.ObjectMeta, utils.AnnDeletionSecretRefNamespace, groupSnapshotSecret.Namespace)
599+
}
600+
601+
label := make(map[string]string)
602+
label[utils.VolumeGroupSnapshotNameLabel] = groupSnapshotContent.Spec.VolumeGroupSnapshotRef.Name
603+
volumeSnapshot := &crdv1.VolumeSnapshot{
604+
ObjectMeta: metav1.ObjectMeta{
605+
Name: volumeSnapshotName,
606+
Namespace: volumeSnapshotNamespace,
607+
Labels: label,
608+
Finalizers: []string{utils.VolumeSnapshotInGroupFinalizer},
609+
},
610+
Spec: crdv1.VolumeSnapshotSpec{
611+
Source: crdv1.VolumeSnapshotSource{
612+
VolumeSnapshotContentName: &volumeSnapshotContentName,
613+
},
614+
},
615+
// The status will be set by VolumeSnapshot reconciler
616+
}
617+
618+
_, err := ctrl.clientset.SnapshotV1().VolumeSnapshotContents().Create(ctx, volumeSnapshotContent, metav1.CreateOptions{})
619+
if err != nil && !apierrs.IsAlreadyExists(err) {
620+
return groupSnapshotContent, fmt.Errorf(
621+
"createSnapshotsForGroupSnapshotContent: creating volumesnapshotcontent %w", err)
622+
}
623+
624+
_, err = ctrl.clientset.SnapshotV1().VolumeSnapshots(volumeSnapshotNamespace).Create(ctx, volumeSnapshot, metav1.CreateOptions{})
625+
if err != nil && !apierrs.IsAlreadyExists(err) {
626+
return groupSnapshotContent, fmt.Errorf(
627+
"createSnapshotsForGroupSnapshotContent: creating volumesnapshot %w", err)
628+
}
629+
630+
groupSnapshotContent.Status.PVVolumeSnapshotContentList[i] = crdv1alpha1.PVVolumeSnapshotContentPair{
631+
VolumeSnapshotContentRef: v1.LocalObjectReference{
632+
Name: volumeSnapshotContentName,
633+
},
634+
}
635+
if pv != nil {
636+
groupSnapshotContent.Status.PVVolumeSnapshotContentList[i].PersistentVolumeRef = v1.LocalObjectReference{
637+
Name: pv.Name,
638+
}
639+
}
640+
}
641+
642+
// Phase 2: set the backlinks
643+
var patches []utils.PatchOp
644+
patches = append(patches, utils.PatchOp{
645+
Op: "replace",
646+
Path: "/status/pvVolumeSnapshotContentList",
647+
Value: groupSnapshotContent.Status.PVVolumeSnapshotContentList,
648+
})
649+
650+
newGroupSnapshotObj, err := utils.PatchVolumeGroupSnapshotContent(
651+
groupSnapshotContent,
652+
patches,
653+
ctrl.clientset,
654+
"status")
655+
if err != nil {
656+
return nil, newControllerUpdateError(utils.GroupSnapshotKey(groupSnapshot), err.Error())
657+
}
658+
659+
return newGroupSnapshotObj, nil
660+
}
661+
662+
// getSnapshotNameForVolumeGroupSnapshotContent returns a unique snapshot name for a VolumeGroupSnapshotContent.
663+
func getSnapshotNameForVolumeGroupSnapshotContent(groupSnapshotContentUUID, volumeHandle string) string {
664+
return fmt.Sprintf("snapshot-%x", sha256.Sum256([]byte(groupSnapshotContentUUID+volumeHandle)))
665+
}
666+
667+
// getSnapshotContentNameForVolumeGroupSnapshotContent returns a unique content name for the
668+
// passed in VolumeGroupSnapshotContent.
669+
func getSnapshotContentNameForVolumeGroupSnapshotContent(groupSnapshotContentUUID, volumeHandle string) string {
670+
return fmt.Sprintf("snapcontent-%x", sha256.Sum256([]byte(groupSnapshotContentUUID+volumeHandle)))
671+
}
672+
488673
// getPreprovisionedGroupSnapshotContentFromStore tries to find a pre-provisioned
489674
// volume group snapshot content object from group snapshot content cache store
490675
// for the passed in VolumeGroupSnapshot.

0 commit comments

Comments
 (0)