Skip to content

Commit 42d5277

Browse files
authored
Cross Namespace Volume Cloning
1 parent a9f98f3 commit 42d5277

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

frontend/csi/controller_helpers/kubernetes/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const (
5555
AnnInternalSnapshotName = annPrefix + "/internalSnapshotName"
5656
AnnMirrorRelationship = annPrefix + "/mirrorRelationship"
5757
AnnVolumeShareFromPVC = annPrefix + "/shareFromPVC"
58+
AnnVolumeCloneToNS = annPrefix + "/cloneToNamespace"
59+
AnnVolumeCloneFromNS = annPrefix + "/cloneFromNamespace"
5860
AnnVolumeShareToNS = annPrefix + "/shareToNamespace"
5961
AnnReadOnlyClone = annPrefix + "/readOnlyClone"
6062
AnnLUKSEncryption = annPrefix + "/luksEncryption" // import only

frontend/csi/controller_helpers/kubernetes/helper.go

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,37 @@ func (h *helper) getSnapshotCloneSourceInfo(
306306
if sourceSnapshotName == "" {
307307
return "", "", fmt.Errorf("annotation 'cloneFromSnapshot' is empty")
308308
}
309+
namespace := clonePVC.Namespace
310+
cloneFromNamespace := getAnnotation(annotations, AnnVolumeCloneFromNS)
311+
if cloneFromNamespace != "" {
312+
// Source snapshot is in a different namespace
313+
namespace = cloneFromNamespace
314+
}
309315

310316
// Get the VolumeSnapshot
311-
snapshot, err := h.getVolumeSnapshot(ctx, sourceSnapshotName, clonePVC.Namespace)
317+
snapshot, err := h.getVolumeSnapshot(ctx, sourceSnapshotName, namespace)
312318
if err != nil {
313319
return "", "", err
314320
}
315321

322+
// If cloning from another namespace, ensure the source PVC has the required annotation
323+
if cloneFromNamespace != "" {
324+
snapSourcePVC, err := h.waitForCachedPVCByName(ctx, *snapshot.Spec.Source.PersistentVolumeClaimName, namespace, PreSyncCacheWaitPeriod)
325+
if err != nil {
326+
Logc(ctx).WithFields(LogFields{
327+
"sourcePVCName": snapSourcePVC.Name,
328+
"namespace": namespace,
329+
}).Errorf("Clone source PVC not found in local cache: %v", err)
330+
return "", "", err
331+
}
332+
sourceAnnotations := processPVCAnnotations(snapSourcePVC, "")
333+
sourceCloneToNamespaces := getAnnotation(sourceAnnotations, AnnVolumeCloneToNS)
334+
// Ensure the source PVC has been explicitly allowed to clone to the subordinate PVC namespace
335+
if !h.matchNamespaceToAnnotation(clonePVC.Namespace, sourceCloneToNamespaces) {
336+
return "", "", fmt.Errorf("cloning to namespace %s is not allowed, it is not listed in cloneToNamespace annotation", clonePVC.Namespace)
337+
}
338+
339+
}
316340
// If the clone from PVC annotation is also set, ensure it matches the snapshot
317341
sourcePVCName := getAnnotation(annotations, AnnCloneFromPVC)
318342
if sourcePVCName != "" {
@@ -371,16 +395,33 @@ func (h *helper) getCloneSourceInfo(ctx context.Context, clonePVC *v1.Persistent
371395
if sourcePVCName == "" {
372396
return "", fmt.Errorf("annotation 'cloneFromPVC' is empty")
373397
}
398+
namespace := clonePVC.Namespace
399+
cloneFromNamespace := getAnnotation(annotations, AnnVolumeCloneFromNS)
374400

375-
// Check that the source PVC is in the same namespace.
376-
// NOTE: For VolumeContentSource this check is performed by CSI
377-
sourcePVC, err := h.waitForCachedPVCByName(ctx, sourcePVCName, clonePVC.Namespace, PreSyncCacheWaitPeriod)
401+
// Determine what namespace to look for the source PVC in
402+
if cloneFromNamespace != "" {
403+
// Source PVC is in a different namespace
404+
namespace = cloneFromNamespace
405+
}
406+
407+
// Retrieve the source PVC from the cache
408+
sourcePVC, err := h.waitForCachedPVCByName(ctx, sourcePVCName, namespace, PreSyncCacheWaitPeriod)
378409
if err != nil {
379410
Logc(ctx).WithFields(LogFields{
380411
"sourcePVCName": sourcePVCName,
381-
"namespace": clonePVC.Namespace,
412+
"namespace": namespace,
382413
}).Errorf("Clone source PVC not found in local cache: %v", err)
383-
return "", fmt.Errorf("cloning from a PVC requires both PVCs be in the same namespace: %v", err)
414+
return "", fmt.Errorf("source PVC not found in namespace: %v", err)
415+
}
416+
417+
// If cloning from another namespace, ensure the source PVC has the required annotation
418+
if cloneFromNamespace != "" {
419+
sourceAnnotations := processPVCAnnotations(sourcePVC, "")
420+
sourceCloneToNamespaces := getAnnotation(sourceAnnotations, AnnVolumeCloneToNS)
421+
// Ensure the source PVC has been explicitly allowed to clone to the subordinate PVC namespace
422+
if !h.matchNamespaceToAnnotation(clonePVC.Namespace, sourceCloneToNamespaces) {
423+
return "", fmt.Errorf("cloning to namespace %s is not allowed, it is not listed in cloneToNamespace annotation", clonePVC.Namespace)
424+
}
384425
}
385426

386427
// Check that both source and clone PVCs have the same storage class
@@ -481,10 +522,10 @@ func (h *helper) validateSubordinateVolumeConfig(
481522
return nil
482523
}
483524

484-
func (h *helper) matchNamespaceToAnnotation(namespace, shareToAnnotation string) bool {
485-
shareToNamespaces := strings.Split(shareToAnnotation, ",")
486-
for _, shareToNamespace := range shareToNamespaces {
487-
if shareToNamespace == namespace || shareToNamespace == "*" {
525+
func (h *helper) matchNamespaceToAnnotation(namespace, annotation string) bool {
526+
availableToNamespaces := strings.Split(annotation, ",")
527+
for _, availableToNamespace := range availableToNamespaces {
528+
if availableToNamespace == namespace || availableToNamespace == "*" {
488529
return true
489530
}
490531
}

0 commit comments

Comments
 (0)