@@ -18,6 +18,7 @@ package common_controller
1818
1919import (
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