@@ -24,6 +24,7 @@ import (
24
24
25
25
vsv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
26
26
corev1 "k8s.io/api/core/v1"
27
+ storagev1 "k8s.io/api/storage/v1"
27
28
k8serrors "k8s.io/apimachinery/pkg/api/errors"
28
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
30
"k8s.io/apimachinery/pkg/types"
@@ -103,6 +104,21 @@ func (s CreatePVCFromVDSnapshotStep) Take(ctx context.Context, vd *v1alpha2.Virt
103
104
return & reconcile.Result {}, nil
104
105
}
105
106
107
+ if err := s .validateStorageClassCompatibility (ctx , vd , vdSnapshot , vs ); err != nil {
108
+ vd .Status .Phase = v1alpha2 .DiskFailed
109
+ s .cb .
110
+ Status (metav1 .ConditionFalse ).
111
+ Reason (vdcondition .ProvisioningFailed ).
112
+ Message (err .Error ())
113
+ s .recorder .Event (
114
+ vd ,
115
+ corev1 .EventTypeWarning ,
116
+ v1alpha2 .ReasonDataSourceSyncFailed ,
117
+ err .Error (),
118
+ )
119
+ return & reconcile.Result {}, nil
120
+ }
121
+
106
122
pvc := s .buildPVC (vd , vs )
107
123
108
124
err = s .client .Create (ctx , pvc )
@@ -158,10 +174,16 @@ func (s CreatePVCFromVDSnapshotStep) AddOriginalMetadata(vd *v1alpha2.VirtualDis
158
174
}
159
175
160
176
func (s CreatePVCFromVDSnapshotStep ) buildPVC (vd * v1alpha2.VirtualDisk , vs * vsv1.VolumeSnapshot ) * corev1.PersistentVolumeClaim {
161
- storageClassName := vs .Annotations [annotations .AnnStorageClassName ]
162
- if storageClassName == "" {
163
- storageClassName = vs .Annotations [annotations .AnnStorageClassNameDeprecated ]
177
+ var storageClassName string
178
+ if vd .Spec .PersistentVolumeClaim .StorageClass != nil && * vd .Spec .PersistentVolumeClaim .StorageClass != "" {
179
+ storageClassName = * vd .Spec .PersistentVolumeClaim .StorageClass
180
+ } else {
181
+ storageClassName = vs .Annotations [annotations .AnnStorageClassName ]
182
+ if storageClassName == "" {
183
+ storageClassName = vs .Annotations [annotations .AnnStorageClassNameDeprecated ]
184
+ }
164
185
}
186
+
165
187
volumeMode := vs .Annotations [annotations .AnnVolumeMode ]
166
188
if volumeMode == "" {
167
189
volumeMode = vs .Annotations [annotations .AnnVolumeModeDeprecated ]
@@ -219,3 +241,51 @@ func (s CreatePVCFromVDSnapshotStep) buildPVC(vd *v1alpha2.VirtualDisk, vs *vsv1
219
241
Spec : spec ,
220
242
}
221
243
}
244
+
245
+ func (s CreatePVCFromVDSnapshotStep ) validateStorageClassCompatibility (ctx context.Context , vd * v1alpha2.VirtualDisk , vdSnapshot * v1alpha2.VirtualDiskSnapshot , vs * vsv1.VolumeSnapshot ) error {
246
+ if vd .Spec .PersistentVolumeClaim .StorageClass == nil || * vd .Spec .PersistentVolumeClaim .StorageClass == "" {
247
+ return nil
248
+ }
249
+
250
+ targetSCName := * vd .Spec .PersistentVolumeClaim .StorageClass
251
+
252
+ var targetSC storagev1.StorageClass
253
+ err := s .client .Get (ctx , types.NamespacedName {Name : targetSCName }, & targetSC )
254
+ if err != nil {
255
+ return fmt .Errorf ("cannot fetch target storage class %q: %w" , targetSCName , err )
256
+ }
257
+
258
+ if vs .Spec .Source .PersistentVolumeClaimName == nil || * vs .Spec .Source .PersistentVolumeClaimName == "" {
259
+ // Can't determine original PVC, skip validation
260
+ return nil
261
+ }
262
+
263
+ pvcName := * vs .Spec .Source .PersistentVolumeClaimName
264
+
265
+ var originalPVC corev1.PersistentVolumeClaim
266
+ err = s .client .Get (ctx , types.NamespacedName {Name : pvcName , Namespace : vdSnapshot .Namespace }, & originalPVC )
267
+ if err != nil {
268
+ return fmt .Errorf ("cannot fetch original PVC %q: %w" , pvcName , err )
269
+ }
270
+
271
+ originalProvisioner := originalPVC .Annotations [annotations .AnnStorageProvisioner ]
272
+ if originalProvisioner == "" {
273
+ originalProvisioner = originalPVC .Annotations [annotations .AnnStorageProvisionerDeprecated ]
274
+ }
275
+
276
+ if originalProvisioner == "" {
277
+ // Can't determine original provisioner, skip validation
278
+ return nil
279
+ }
280
+
281
+ if targetSC .Provisioner != originalProvisioner {
282
+ return fmt .Errorf (
283
+ "cannot restore snapshot to storage class %q: incompatible storage providers. " +
284
+ "Original snapshot was created by %q, target storage class uses %q. " +
285
+ "Cross-provider snapshot restore is not supported" ,
286
+ targetSCName , originalProvisioner , targetSC .Provisioner ,
287
+ )
288
+ }
289
+
290
+ return nil
291
+ }
0 commit comments