Skip to content

Commit

Permalink
add ability to override destination pvc access mode based on annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
diamonwiggins committed Aug 31, 2023
1 parent e1fdcaf commit d2e584a
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 13 deletions.
47 changes: 39 additions & 8 deletions pkg/migrate/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ import (
)

const (
baseAnnotation = "kurl.sh/pvcmigrate"
scaleAnnotation = baseAnnotation + "-scale"
kindAnnotation = baseAnnotation + "-kind"
sourceNsAnnotation = baseAnnotation + "-sourcens"
sourcePVCAnnotation = baseAnnotation + "-sourcepvc"
desiredReclaimAnnotation = baseAnnotation + "-reclaim"
baseAnnotation = "kurl.sh/pvcmigrate"
scaleAnnotation = baseAnnotation + "-scale"
kindAnnotation = baseAnnotation + "-kind"
sourceNsAnnotation = baseAnnotation + "-sourcens"
sourcePVCAnnotation = baseAnnotation + "-sourcepvc"
desiredReclaimAnnotation = baseAnnotation + "-reclaim"
DesiredAccessModeAnnotation = baseAnnotation + "-desiredaccessmode"
)

// IsDefaultStorageClassAnnotation - this is also exported by https://github.com/kubernetes/kubernetes/blob/v1.21.3/pkg/apis/storage/v1/util/helpers.go#L25
Expand Down Expand Up @@ -511,6 +512,12 @@ func getPVCs(ctx context.Context, w *log.Logger, clientset k8sclient.Interface,
}
}

// Set destination access mode based on annotations
destAccessModes, err := GetDestAccessModes(*nsPvc.claim)
if err != nil {
return nil, nil, fmt.Errorf("failed to get destination access mode for PVC %s in %s: %w", nsPvc.claim.Name, ns, err)
}

// if it doesn't already exist, create it
newPVC, err := clientset.CoreV1().PersistentVolumeClaims(ns).Create(ctx, &corev1.PersistentVolumeClaim{
TypeMeta: nsPvc.claim.TypeMeta,
Expand All @@ -529,7 +536,7 @@ func getPVCs(ctx context.Context, w *log.Logger, clientset k8sclient.Interface,
corev1.ResourceStorage: desiredPvStorage,
},
},
AccessModes: nsPvc.claim.Spec.AccessModes,
AccessModes: destAccessModes,
},
}, metav1.CreateOptions{})
if err != nil {
Expand Down Expand Up @@ -1031,6 +1038,12 @@ func swapPVs(ctx context.Context, w *log.Logger, clientset k8sclient.Interface,
return fmt.Errorf("failed to remove claimrefs from PV %s: %w", migratedPVC.Spec.VolumeName, err)
}

// Set destination access mode based on annotations
destAccessModes, err := GetDestAccessModes(*originalPVC)
if err != nil {
return fmt.Errorf("failed to get destination access mode for PVC %s in %s: %w", originalPVC.Name, ns, err)
}

// create new PVC with the old name/annotations/settings, and the new PV
w.Printf("Creating new PVC %s with migrated-to PV %s\n", originalPVC.Name, migratedPVC.Spec.VolumeName)
newPVC := corev1.PersistentVolumeClaim{
Expand All @@ -1044,7 +1057,7 @@ func swapPVs(ctx context.Context, w *log.Logger, clientset k8sclient.Interface,
Labels: originalPVC.Labels, // copy labels, don't copy annotations
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: originalPVC.Spec.AccessModes,
AccessModes: destAccessModes,
Resources: originalPVC.Spec.Resources,
VolumeMode: originalPVC.Spec.VolumeMode,

Expand Down Expand Up @@ -1160,3 +1173,21 @@ func readLineWithTimeout(reader LineReader, timeout time.Duration) ([]byte, erro
return message.line, message.err
}
}

func GetDestAccessModes(srcPVC corev1.PersistentVolumeClaim) ([]corev1.PersistentVolumeAccessMode, error) {
// default to the source PVCs access mode if DesiredAccessModeAnnotation is not set
destAccessMode := srcPVC.Spec.AccessModes
if accessMode := srcPVC.Annotations[DesiredAccessModeAnnotation]; accessMode != "" {
switch accessMode {
case "ReadWriteOnce":
destAccessMode = []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce}
case "ReadOnlyMany":
destAccessMode = []corev1.PersistentVolumeAccessMode{corev1.ReadOnlyMany}
case "ReadWriteMany":
destAccessMode = []corev1.PersistentVolumeAccessMode{corev1.ReadWriteMany}
default:
return nil, fmt.Errorf("invalid access mode '%s' used in annotation '%s' for PVC '%s' in namespace '%s'", accessMode, DesiredAccessModeAnnotation, srcPVC.Name, srcPVC.Namespace)
}
}
return destAccessMode, nil
}
16 changes: 12 additions & 4 deletions pkg/preflight/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,22 +183,27 @@ func buildTmpPVCConsumerPod(pvcName, namespace, image string) *corev1.Pod {
}

// buildTmpPVC creates a temporary PVC requesting for 1Mi of storage for a provided storage class name.
func buildTmpPVC(pvc corev1.PersistentVolumeClaim, sc string) *corev1.PersistentVolumeClaim {
func buildTmpPVC(pvc corev1.PersistentVolumeClaim, sc string) (*corev1.PersistentVolumeClaim, error) {
destAccessModes, err := migrate.GetDestAccessModes(pvc)
if err != nil {
return nil, fmt.Errorf("failed to get destination access mode for PVC %s in %s: %w", pvc.Name, pvc.Namespace, err)
}

return &corev1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: k8sutil.NewPrefixedName(pvcNamePrefix, pvc.Name),
Namespace: pvc.Namespace,
},
Spec: corev1.PersistentVolumeClaimSpec{
StorageClassName: &sc,
AccessModes: pvc.Spec.AccessModes,
AccessModes: destAccessModes,
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: resource.MustParse("1Mi"),
},
},
},
}
}, nil
}

// checkVolumeAccessModes checks if the access modes of a pv are supported by the
Expand All @@ -207,7 +212,10 @@ func checkVolumeAccessModes(ctx context.Context, l *log.Logger, client k8sclient
var err error

// create temp pvc for storage class
tmpPVCSpec := buildTmpPVC(pvc, dstSC)
tmpPVCSpec, err := buildTmpPVC(pvc, dstSC)
if err != nil {
return nil, fmt.Errorf("failed to create temporary pvc spec for %s: %w", pvc.Name, err)
}
tmpPVC, err := client.CoreV1().PersistentVolumeClaims(tmpPVCSpec.Namespace).Create(
ctx, tmpPVCSpec, metav1.CreateOptions{})
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion pkg/preflight/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,10 @@ func Test_buildTmpPVC(t *testing.T) {
} {
t.Run(tt.name, func(t *testing.T) {
req := require.New(t)
pvc := buildTmpPVC(*tt.input, tt.dstStorageClass)
pvc, err := buildTmpPVC(*tt.input, tt.dstStorageClass)
if err != nil {
req.NoError(err)
}
req.Equal(tt.expectedPVC, pvc)
})
}
Expand Down

0 comments on commit d2e584a

Please sign in to comment.