@@ -92,11 +92,28 @@ func (h *helper) GetVolumeConfig(
9292 annotations := processPVCAnnotations (pvc , fsType )
9393 volumeConfig := getVolumeConfig (ctx , pvc , pvName , pvcSize , annotations , sc , requisiteTopology , preferredTopology )
9494
95+ // Get clone annotations
96+ sourceSnapshotName := getAnnotation (annotations , AnnCloneFromSnapshot )
97+ sourcePVCName := getAnnotation (annotations , AnnCloneFromPVC )
98+
9599 // Check if we're cloning a PVC, and if so, do some further validation
96- if cloneSourcePVName , err := h .getCloneSourceInfo (ctx , pvc ); err != nil {
97- return nil , err
98- } else if cloneSourcePVName != "" {
99- volumeConfig .CloneSourceVolume = cloneSourcePVName
100+ if sourcePVCName != "" && sourceSnapshotName == "" {
101+ if cloneSourcePVName , err := h .getCloneSourceInfo (ctx , pvc ); err != nil {
102+ return nil , err
103+ } else if cloneSourcePVName != "" {
104+ volumeConfig .CloneSourceVolume = cloneSourcePVName
105+ }
106+ }
107+
108+ // Check if we're cloning from a snapshot, and if so, do some further validation
109+ if sourceSnapshotName != "" {
110+ cloneSourceVolume , cloneSourceSnapshot , err := h .getSnapshotCloneSourceInfo (ctx , pvc )
111+ if err != nil {
112+ return nil , err
113+ } else if cloneSourceVolume != "" && cloneSourceSnapshot != "" {
114+ volumeConfig .CloneSourceVolume = cloneSourceVolume
115+ volumeConfig .CloneSourceSnapshot = cloneSourceSnapshot
116+ }
100117 }
101118
102119 // Check if we're importing a volume and do some further validation
@@ -276,8 +293,73 @@ func (h *helper) getStorageClass(ctx context.Context, name string) (*k8sstoragev
276293 return sc , nil
277294}
278295
296+ // getSnapshotCloneSourceInfo accepts the PVC of a volume being provisioned by CSI and inspects it
297+ // for the annotations indicating a snapshot clone operation (of which CSI is unaware).
298+ // The method completes several checks on the source snapshot, and PVC if provided, and returns the
299+ // name of the source PV and snapshot as needed by Trident to clone a volume.
300+ func (h * helper ) getSnapshotCloneSourceInfo (
301+ ctx context.Context , clonePVC * v1.PersistentVolumeClaim ,
302+ ) (string , string , error ) {
303+ // Check if this is a snapshot clone operation
304+ annotations := processPVCAnnotations (clonePVC , "" )
305+ sourceSnapshotName := getAnnotation (annotations , AnnCloneFromSnapshot )
306+ if sourceSnapshotName == "" {
307+ return "" , "" , fmt .Errorf ("annotation 'cloneFromSnapshot' is empty" )
308+ }
309+
310+ // Get the VolumeSnapshot
311+ snapshot , err := h .getVolumeSnapshot (ctx , sourceSnapshotName , clonePVC .Namespace )
312+ if err != nil {
313+ return "" , "" , err
314+ }
315+
316+ // If the clone from PVC annotation is also set, ensure it matches the snapshot
317+ sourcePVCName := getAnnotation (annotations , AnnCloneFromPVC )
318+ if sourcePVCName != "" {
319+ snapSourcePVC := snapshot .Spec .Source .PersistentVolumeClaimName
320+ if snapSourcePVC == nil {
321+ return "" , "" , fmt .Errorf ("cannot verify clone source PVC for snapshot '%s', " +
322+ "PersistentVolumeClaimName is not set in the snapshot spec" , sourceSnapshotName )
323+ }
324+ if sourcePVCName != * snapSourcePVC {
325+ return "" , "" , fmt .Errorf ("clone source snapshot '%s' does not originate from the given source " +
326+ "PVC '%s'" , sourceSnapshotName , sourcePVCName )
327+ }
328+ }
329+
330+ // Ensure the VolumeSnapshot is ready to use
331+ if snapshot .Status .ReadyToUse == nil || ! * snapshot .Status .ReadyToUse {
332+ return "" , "" , fmt .Errorf ("snapshot '%s' is not ready to use" , snapshot .Name )
333+ }
334+
335+ snapshotContent , err := h .getSnapshotContentFromSnapshot (ctx , snapshot )
336+ if err != nil {
337+ Logc (ctx ).WithFields (LogFields {
338+ "sourceSnapshotName" : sourceSnapshotName ,
339+ }).Errorf ("Clone source snapshot content not found: %v" , err )
340+ return "" , "" , err
341+ }
342+
343+ // Ensure the VolumeSnapshotContent is ready to use
344+ if snapshotContent .Status .ReadyToUse == nil || ! * snapshotContent .Status .ReadyToUse {
345+ return "" , "" , fmt .Errorf ("volumeSnapshotContent '%s' is not ready to use" , snapshotContent .Name )
346+ }
347+
348+ if snapshotContent .Status .SnapshotHandle == nil {
349+ return "" , "" , fmt .Errorf ("volumeSnapshotContent '%s' does not have a snapshot handle" ,
350+ snapshotContent .Name )
351+ }
352+
353+ volumeName , snapshotName , err := storage .ParseSnapshotID (* snapshotContent .Status .SnapshotHandle )
354+ if err != nil {
355+ return "" , "" , err
356+ }
357+
358+ return volumeName , snapshotName , nil
359+ }
360+
279361// getCloneSourceInfo accepts the PVC of a volume being provisioned by CSI and inspects it
280- // for the annotations indicating a clone operation (of which CSI is unaware). If a clone is
362+ // for the annotations indicating a PVC clone operation (of which CSI is unaware). If a clone is
281363// being created, the method completes several checks on the source PVC/PV and returns the
282364// name of the source PV as needed by Trident to clone a volume as well as an optional
283365// snapshot name (also potentially unknown to CSI). Note that these legacy clone annotations
@@ -287,7 +369,7 @@ func (h *helper) getCloneSourceInfo(ctx context.Context, clonePVC *v1.Persistent
287369 annotations := processPVCAnnotations (clonePVC , "" )
288370 sourcePVCName := getAnnotation (annotations , AnnCloneFromPVC )
289371 if sourcePVCName == "" {
290- return "" , nil
372+ return "" , fmt . Errorf ( "annotation 'cloneFromPVC' is empty" )
291373 }
292374
293375 // Check that the source PVC is in the same namespace.
@@ -476,6 +558,49 @@ func (h *helper) getSnapshotContentByName(ctx context.Context, name string) (*vs
476558 return vsc , nil
477559}
478560
561+ // getVolumeSnapshot returns a VolumeSnapshot if it exists.
562+ func (h * helper ) getVolumeSnapshot (
563+ ctx context.Context , name , namespace string ,
564+ ) (* vsv1.VolumeSnapshot , error ) {
565+ fields := LogFields {"snapshotName" : name , "namespace" : namespace }
566+ Logc (ctx ).WithFields (fields ).Trace (">>>> getVolumeSnapshot" )
567+ defer Logc (ctx ).WithFields (fields ).Trace ("<<<< getVolumeSnapshot" )
568+
569+ // Get the VolumeSnapshot
570+ snapshot , err := h .snapClient .SnapshotV1 ().VolumeSnapshots (namespace ).Get (ctx , name , getOpts )
571+ if err != nil {
572+ statusErr , ok := err .(* apierrors.StatusError )
573+ if ok && statusErr .Status ().Reason == metav1 .StatusReasonNotFound {
574+ return nil , errors .NotFoundError ("snapshot %s not found; %v" , name , statusErr )
575+ }
576+ return nil , err
577+ }
578+ return snapshot , nil
579+ }
580+
581+ // getSnapshotContentBySnapshotName returns the VolumeSnapshotContent referenced by a VolumeSnapshot
582+ func (h * helper ) getSnapshotContentFromSnapshot (
583+ ctx context.Context , snapshot * vsv1.VolumeSnapshot ,
584+ ) (* vsv1.VolumeSnapshotContent , error ) {
585+ fields := LogFields {"snapshotName" : snapshot .Name }
586+ Logc (ctx ).WithFields (fields ).Trace (">>>> getSnapshotContentFromSnapshot" )
587+ defer Logc (ctx ).WithFields (fields ).Trace ("<<<< getSnapshotContentFromSnapshot" )
588+
589+ // Extract the VolumeSnapshotContent name from the VolumeSnapshot status
590+ snapshotContentName := snapshot .Status .BoundVolumeSnapshotContentName
591+ if snapshotContentName == nil || * snapshotContentName == "" {
592+ return nil , errors .NotFoundError ("boundVolumeSnapshotContentName not found for snapshot %s" , snapshot .Name )
593+ }
594+
595+ // Get the VolumeSnapshotContent
596+ vsc , err := h .getSnapshotContentByName (ctx , * snapshotContentName )
597+ if err != nil {
598+ return nil , err
599+ }
600+
601+ return vsc , nil
602+ }
603+
479604// getSnapshotInternalNameFromAnnotation gets the snapshotInternalName from an annotation on a VolumeSnapshotContent
480605// for snapshot import.
481606func (h * helper ) getSnapshotInternalNameFromAnnotation (
0 commit comments