Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cephfs: Add support to create RWX PVC from ROX PVC #4094

Merged
merged 3 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions e2e/cephfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,167 @@ var _ = Describe(cephfsType, func() {
}
})

By("create RWX clone from ROX PVC", func() {
pvc, err := loadPVC(pvcPath)
if err != nil {
framework.Failf("failed to load PVC: %v", err)
}
pvc.Namespace = f.UniqueName
err = createPVCAndvalidatePV(f.ClientSet, pvc, deployTimeout)
if err != nil {
framework.Failf("failed to create PVC: %v", err)
}

_, pv, err := getPVCAndPV(f.ClientSet, pvc.Name, pvc.Namespace)
if err != nil {
framework.Failf("failed to get PV object for %s: %v", pvc.Name, err)
}

app, err := loadApp(appPath)
if err != nil {
framework.Failf("failed to load application: %v", err)
}
app.Namespace = f.UniqueName
app.Spec.Volumes[0].PersistentVolumeClaim.ClaimName = pvc.Name
appLabels := map[string]string{
appKey: appLabel,
}
app.Labels = appLabels
optApp := metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", appKey, appLabels[appKey]),
}
err = writeDataInPod(app, &optApp, f)
if err != nil {
framework.Failf("failed to write data: %v", err)
}

appTestFilePath := app.Spec.Containers[0].VolumeMounts[0].MountPath + "/test"

err = appendToFileInContainer(f, app, appTestFilePath, "hello", &optApp)
if err != nil {
framework.Failf("failed to append data: %v", err)
}

parentFileSum, err := calculateSHA512sum(f, app, appTestFilePath, &optApp)
if err != nil {
framework.Failf("failed to get SHA512 sum for file: %v", err)
}

snap := getSnapshot(snapshotPath)
snap.Namespace = f.UniqueName
snap.Spec.Source.PersistentVolumeClaimName = &pvc.Name
err = createSnapshot(&snap, deployTimeout)
if err != nil {
framework.Failf("failed to create snapshot: %v", err)
}
validateCephFSSnapshotCount(f, 1, subvolumegroup, pv)

pvcClone, err := loadPVC(pvcClonePath)
if err != nil {
framework.Failf("failed to load PVC: %v", err)
}
// Snapshot-backed volumes support read-only access modes only.
pvcClone.Spec.DataSource.Name = snap.Name
pvcClone.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany}

pvcClone.Namespace = f.UniqueName
err = createPVCAndvalidatePV(c, pvcClone, deployTimeout)
if err != nil {
framework.Failf("failed to create PVC: %v", err)
}

validateSubvolumeCount(f, 1, fileSystemName, subvolumegroup)

// create RWX clone from ROX PVC
pvcRWXClone, err := loadPVC(pvcSmartClonePath)
if err != nil {
framework.Failf("failed to load PVC: %v", err)
}
pvcRWXClone.Spec.DataSource.Name = pvcClone.Name
pvcRWXClone.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteMany}
pvcRWXClone.Namespace = f.UniqueName

appClone, err := loadApp(appPath)
if err != nil {
framework.Failf("failed to load application: %v", err)
}
appCloneLabels := map[string]string{
appKey: appCloneLabel,
}
appClone.Name = f.UniqueName + "-app"
appClone.Namespace = f.UniqueName
appClone.Labels = appCloneLabels
appClone.Spec.Volumes[0].PersistentVolumeClaim.ClaimName = pvcRWXClone.Name
optAppClone := metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", appKey, appCloneLabels[appKey]),
}

err = createPVCAndApp("", f, pvcRWXClone, appClone, deployTimeout)
if err != nil {
framework.Failf("failed to create PVC and app: %v", err)
}
// 2 subvolumes should be created 1 for parent PVC and 1 for
// RWX clone PVC.
validateSubvolumeCount(f, 2, fileSystemName, subvolumegroup)

appCloneTestFilePath := appClone.Spec.Containers[0].VolumeMounts[0].MountPath + "/test"

cloneFileSum, err := calculateSHA512sum(f, appClone, appCloneTestFilePath, &optAppClone)
if err != nil {
framework.Failf("failed to get SHA512 sum for file: %v", err)
}

if parentFileSum != cloneFileSum {
framework.Failf(
"SHA512 sums of files in parent and ROX should not differ. parentFileSum: %s cloneFileSum: %s",
parentFileSum,
cloneFileSum)
}

// Now try to write to the PVC as its a RWX PVC
err = appendToFileInContainer(f, app, appCloneTestFilePath, "testing", &optApp)
if err != nil {
framework.Failf("failed to append data: %v", err)
}

// Deleting snapshot before deleting pvcClone should succeed. It will be
// deleted once all volumes that are backed by this snapshot are gone.
err = deleteSnapshot(&snap, deployTimeout)
if err != nil {
framework.Failf("failed to delete snapshot: %v", err)
}

// delete parent pvc and app
err = deletePVCAndApp("", f, pvc, app)
if err != nil {
framework.Failf("failed to delete PVC or application: %v", err)
}

// delete ROX clone PVC
err = deletePVCAndValidatePV(c, pvcClone, deployTimeout)
if err != nil {
framework.Failf("failed to delete PVC or application: %v", err)
}
// delete RWX clone PVC and app
err = deletePVCAndApp("", f, pvcRWXClone, appClone)
if err != nil {
framework.Failf("failed to delete PVC or application: %v", err)
}

validateSubvolumeCount(f, 0, fileSystemName, subvolumegroup)
validateOmapCount(f, 0, cephfsType, metadataPool, volumesType)

err = deleteResource(cephFSExamplePath + "storageclass.yaml")
if err != nil {
framework.Failf("failed to delete CephFS storageclass: %v", err)
}

err = createCephfsStorageClass(f.ClientSet, f, false, nil)
if err != nil {
framework.Failf("failed to create CephFS storageclass: %v", err)
}
})

if testCephFSFscrypt {
kmsToTest := map[string]kmsConfig{
"secrets-metadata-test": secretsMetadataKMS,
Expand Down
24 changes: 21 additions & 3 deletions internal/cephfs/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ func checkValidCreateVolumeRequest(
sID *store.SnapshotIdentifier,
req *csi.CreateVolumeRequest,
) error {
volCaps := req.GetVolumeCapabilities()
switch {
case pvID != nil:
if vol.Size < parentVol.Size {
Expand All @@ -224,12 +225,12 @@ func checkValidCreateVolumeRequest(
vol.Size)
}

if vol.BackingSnapshot {
return errors.New("cloning snapshot-backed volumes is currently not supported")
if parentVol.BackingSnapshot && store.IsVolumeCreateRO(volCaps) {
return errors.New("creating read-only clone from a snapshot-backed volume is not supported")
}

case sID != nil:
if vol.BackingSnapshot {
volCaps := req.GetVolumeCapabilities()
isRO := store.IsVolumeCreateRO(volCaps)
if !isRO {
return errors.New("backingSnapshot may be used only with read-only access modes")
Expand Down Expand Up @@ -298,6 +299,23 @@ func (cs *ControllerServer) CreateVolume(
return nil, status.Error(codes.InvalidArgument, err.Error())
}

// As we are trying to create RWX volume from backing snapshot, we need to
// retrieve the snapshot details from the backing snapshot and create a
// subvolume clone from the snapshot.
if parentVol != nil && parentVol.BackingSnapshot && !store.IsVolumeCreateRO(req.VolumeCapabilities) {
// unset pvID as we dont have real subvolume for the parent volumeID as its a backing snapshot
pvID = nil
parentVol, _, sID, err = store.NewSnapshotOptionsFromID(ctx, parentVol.BackingSnapshotID, cr,
req.GetSecrets(), cs.ClusterName, cs.SetMetadata)
if err != nil {
if errors.Is(err, cerrors.ErrSnapNotFound) {
return nil, status.Error(codes.NotFound, err.Error())
}

return nil, status.Error(codes.Internal, err.Error())
}
}

vID, err := store.CheckVolExists(ctx, volOptions, parentVol, pvID, sID, cr, cs.ClusterName, cs.SetMetadata)
if err != nil {
if cerrors.IsCloneRetryError(err) {
Expand Down