@@ -23,6 +23,7 @@ import (
2323
2424 vsv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
2525 corev1 "k8s.io/api/core/v1"
26+ storagev1 "k8s.io/api/storage/v1"
2627 k8serrors "k8s.io/apimachinery/pkg/api/errors"
2728 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829 "k8s.io/apimachinery/pkg/types"
@@ -103,6 +104,21 @@ func (s CreatePersistentVolumeClaimStep) Take(ctx context.Context, vi *v1alpha2.
103104 return & reconcile.Result {}, nil
104105 }
105106
107+ if err := s .validateStorageClassCompatibility (ctx , vi , vdSnapshot , vs ); err != nil {
108+ vi .Status .Phase = v1alpha2 .ImageFailed
109+ s .cb .
110+ Status (metav1 .ConditionFalse ).
111+ Reason (vicondition .ProvisioningFailed ).
112+ Message (err .Error ())
113+ s .recorder .Event (
114+ vi ,
115+ corev1 .EventTypeWarning ,
116+ v1alpha2 .ReasonDataSourceSyncFailed ,
117+ err .Error (),
118+ )
119+ return & reconcile.Result {}, nil
120+ }
121+
106122 pvc := s .buildPVC (vi , vs )
107123
108124 err = s .client .Create (ctx , pvc )
@@ -124,9 +140,14 @@ func (s CreatePersistentVolumeClaimStep) Take(ctx context.Context, vi *v1alpha2.
124140}
125141
126142func (s CreatePersistentVolumeClaimStep ) buildPVC (vi * v1alpha2.VirtualImage , vs * vsv1.VolumeSnapshot ) * corev1.PersistentVolumeClaim {
127- storageClassName := vs .Annotations [annotations .AnnStorageClassName ]
128- if storageClassName == "" {
129- storageClassName = vs .Annotations [annotations .AnnStorageClassNameDeprecated ]
143+ var storageClassName string
144+ if vi .Spec .PersistentVolumeClaim .StorageClass != nil && * vi .Spec .PersistentVolumeClaim .StorageClass != "" {
145+ storageClassName = * vi .Spec .PersistentVolumeClaim .StorageClass
146+ } else {
147+ storageClassName = vs .Annotations [annotations .AnnStorageClassName ]
148+ if storageClassName == "" {
149+ storageClassName = vs .Annotations [annotations .AnnStorageClassNameDeprecated ]
150+ }
130151 }
131152 volumeMode := vs .Annotations [annotations .AnnVolumeMode ]
132153 if volumeMode == "" {
@@ -185,3 +206,52 @@ func (s CreatePersistentVolumeClaimStep) buildPVC(vi *v1alpha2.VirtualImage, vs
185206 Spec : spec ,
186207 }
187208}
209+
210+ func (s CreatePersistentVolumeClaimStep ) validateStorageClassCompatibility (ctx context.Context , vi * v1alpha2.VirtualImage , vdSnapshot * v1alpha2.VirtualDiskSnapshot , vs * vsv1.VolumeSnapshot ) error {
211+ if vi .Spec .PersistentVolumeClaim .StorageClass == nil || * vi .Spec .PersistentVolumeClaim .StorageClass == "" {
212+ return nil
213+ }
214+
215+ targetSCName := * vi .Spec .PersistentVolumeClaim .StorageClass
216+
217+ var targetSC storagev1.StorageClass
218+ err := s .client .Get (ctx , types.NamespacedName {Name : targetSCName }, & targetSC )
219+ if err != nil {
220+ return fmt .Errorf ("cannot fetch target storage class %q: %w" , targetSCName , err )
221+ }
222+
223+ log , _ := logger .GetDataSourceContext (ctx , "objectref" )
224+ if vs .Spec .Source .PersistentVolumeClaimName == nil || * vs .Spec .Source .PersistentVolumeClaimName == "" {
225+ log .With ("volumeSnapshot.name" , vs .Name ).Debug ("Cannot determine original PVC from VolumeSnapshot, skipping storage class compatibility validation" )
226+ return nil
227+ }
228+
229+ pvcName := * vs .Spec .Source .PersistentVolumeClaimName
230+
231+ var originalPVC corev1.PersistentVolumeClaim
232+ err = s .client .Get (ctx , types.NamespacedName {Name : pvcName , Namespace : vdSnapshot .Namespace }, & originalPVC )
233+ if err != nil {
234+ return fmt .Errorf ("cannot fetch original PVC %q: %w" , pvcName , err )
235+ }
236+
237+ originalProvisioner := originalPVC .Annotations [annotations .AnnStorageProvisioner ]
238+ if originalProvisioner == "" {
239+ originalProvisioner = originalPVC .Annotations [annotations .AnnStorageProvisionerDeprecated ]
240+ }
241+
242+ if originalProvisioner == "" {
243+ log .With ("pvc.name" , pvcName ).Debug ("Cannot determine original provisioner from PVC annotations, skipping storage class compatibility validation" )
244+ return nil
245+ }
246+
247+ if targetSC .Provisioner != originalProvisioner {
248+ return fmt .Errorf (
249+ "cannot restore snapshot to storage class %q: incompatible storage providers. " +
250+ "Original snapshot was created by %q, target storage class uses %q. " +
251+ "Cross-provider snapshot restore is not supported" ,
252+ targetSCName , originalProvisioner , targetSC .Provisioner ,
253+ )
254+ }
255+
256+ return nil
257+ }
0 commit comments