Skip to content
Open
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
241 changes: 241 additions & 0 deletions e2e/rbd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2094,6 +2094,247 @@ var _ = Describe("RBD", func() {
}
})

By("create PVC with layering,deep-flatten image-features and bind it to an app", func() {
err := deleteResource(rbdExamplePath + "storageclass.yaml")
if err != nil {
logAndFail("failed to delete storageclass: %v", err)
}
err = createRBDStorageClass(
f.ClientSet,
f,
defaultSCName,
nil,
map[string]string{
"imageFeatures": "layering,deep-flatten",
},
deletePolicy)
if err != nil {
logAndFail("failed to create storageclass: %v", err)
}
// set up PVC
pvc, err := loadPVC(pvcPath)
if err != nil {
logAndFail("failed to load PVC: %v", err)
}
pvc.Namespace = f.UniqueName
err = createPVCAndvalidatePV(f.ClientSet, pvc, deployTimeout)
if err != nil {
logAndFail("failed to create PVC: %v", err)
}
// validate created backend rbd images
validateRBDImageCount(f, 1, defaultRBDPool)
validateOmapCount(f, 1, rbdType, defaultRBDPool, volumesType)

if kernel.CheckKernelSupport(kernelRelease, deepFlattenSupport) {
app, aErr := loadApp(appPath)
if aErr != nil {
logAndFail("failed to load application: %v", aErr)
}
app.Namespace = f.UniqueName
err = createApp(f.ClientSet, app, deployTimeout)
if err != nil {
logAndFail("failed to create application: %v", err)
}
// delete pod as we should not create snapshot for in-use pvc
err = deletePod(app.Name, app.Namespace, f.ClientSet, deployTimeout)
if err != nil {
logAndFail("failed to delete application: %v", err)
}

}
// clean up after ourselves
err = deletePVCAndValidatePV(f.ClientSet, pvc, deployTimeout)
if err != nil {
logAndFail("failed to delete PVC: %v", err)
}
// validate created backend rbd images
validateRBDImageCount(f, 0, defaultRBDPool)
validateOmapCount(f, 0, rbdType, defaultRBDPool, volumesType)
})

By("verify temp clone and snapshot backing images inherit StorageClass image features", func() {
if !kernel.CheckKernelSupport(kernelRelease, fastDiffSupport) {
Skip("kernel does not support fast-diff, skipping image features inheritance test")
}

imageFeatures := "layering,exclusive-lock,object-map,fast-diff,deep-flatten"
expectedFeatures := []string{
"layering", "exclusive-lock", "object-map", "fast-diff", "deep-flatten",
}

err := deleteResource(rbdExamplePath + "storageclass.yaml")
if err != nil {
logAndFail("failed to delete storageclass: %v", err)
}
err = createRBDStorageClass(
f.ClientSet,
f,
defaultSCName,
nil,
map[string]string{
"imageFeatures": imageFeatures,
},
deletePolicy)
if err != nil {
logAndFail("failed to create storageclass: %v", err)
}
defer func() {
err = deleteResource(rbdExamplePath + "storageclass.yaml")
if err != nil {
logAndFail("failed to delete storageclass: %v", err)
}
err = createRBDStorageClass(f.ClientSet, f, defaultSCName, nil, nil, deletePolicy)
if err != nil {
logAndFail("failed to create storageclass: %v", err)
}
}()

// create source PVC
pvc, err := loadPVC(pvcPath)
if err != nil {
logAndFail("failed to load PVC: %v", err)
}
pvc.Namespace = f.UniqueName
err = createPVCAndvalidatePV(f.ClientSet, pvc, deployTimeout)
if err != nil {
logAndFail("failed to create PVC: %v", err)
}

// create a snapshot and verify the backing image (csi-snap-*)
// inherits StorageClass image features
err = createRBDSnapshotClass(f)
if err != nil {
logAndFail("failed to create snapshotclass: %v", err)
}
defer func() {
err = deleteRBDSnapshotClass()
if err != nil {
logAndFail("failed to delete snapshotclass: %v", err)
}
}()

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

snapImageName, err := getSnapName(snap.Namespace, snap.Name)
if err != nil {
logAndFail("failed to get snapshot image name: %v", err)
}
err = validateImageFeatures(f, snapImageName, defaultRBDPool, expectedFeatures)
if err != nil {
logAndFail("snapshot backing image %s features validation failed: %v",
snapImageName, err)
}

err = deleteSnapshot(&snap, deployTimeout)
if err != nil {
logAndFail("failed to delete snapshot: %v", err)
}

// create a PVC-PVC clone and verify the temp clone image
// (csi-vol-*-temp) inherits StorageClass image features
pvcSmartClone, err := loadPVC(pvcSmartClonePath)
if err != nil {
logAndFail("failed to load PVC: %v", err)
}
pvcSmartClone.Namespace = f.UniqueName
pvcSmartClone.Spec.DataSource.Name = pvc.Name
err = createPVCAndvalidatePV(f.ClientSet, pvcSmartClone, deployTimeout)
if err != nil {
logAndFail("failed to create cloned PVC: %v", err)
}

// the temp clone image name is the clone image name + "-temp"
imageData, err := getImageInfoFromPVC(pvcSmartClone.Namespace, pvcSmartClone.Name, f)
if err != nil {
logAndFail("failed to get image info from source PVC: %v", err)
}
tempCloneImageName := imageData.imageName + "-temp"
err = validateImageFeatures(f, tempCloneImageName, defaultRBDPool, expectedFeatures)
if err != nil {
logAndFail("temp clone image %s features validation failed: %v",
tempCloneImageName, err)
}

// clean up clone and source
err = deletePVCAndValidatePV(f.ClientSet, pvcSmartClone, deployTimeout)
if err != nil {
logAndFail("failed to delete cloned PVC: %v", err)
}
err = deletePVCAndValidatePV(f.ClientSet, pvc, deployTimeout)
if err != nil {
logAndFail("failed to delete source PVC: %v", err)
}

validateRBDImageCount(f, 0, defaultRBDPool)
validateOmapCount(f, 0, rbdType, defaultRBDPool, volumesType)
})

By("create PVC without layering,deep-flatten image-features and bind it to an app", func() {
err := deleteResource(rbdExamplePath + "storageclass.yaml")
if err != nil {
logAndFail("failed to delete storageclass: %v", err)
}
err = createRBDStorageClass(
f.ClientSet,
f,
defaultSCName,
nil,
map[string]string{
"imageFeatures": "",
},
deletePolicy)
if err != nil {
logAndFail("failed to create storageclass: %v", err)
}
// set up PVC
pvc, err := loadPVC(pvcPath)
if err != nil {
logAndFail("failed to load PVC: %v", err)
}
pvc.Namespace = f.UniqueName
err = createPVCAndvalidatePV(f.ClientSet, pvc, deployTimeout)
if err != nil {
logAndFail("failed to create PVC: %v", err)
}
// validate created backend rbd images
validateRBDImageCount(f, 1, defaultRBDPool)
validateOmapCount(f, 1, rbdType, defaultRBDPool, volumesType)

// checking the minimal kernel version for fast-diff as its
// higher kernel version than other default image features.
if kernel.CheckKernelSupport(kernelRelease, fastDiffSupport) {
app, aErr := loadApp(appPath)
if aErr != nil {
logAndFail("failed to load application: %v", aErr)
}
app.Namespace = f.UniqueName
err = createApp(f.ClientSet, app, deployTimeout)
if err != nil {
logAndFail("failed to create application: %v", err)
}
// delete pod as we should not create snapshot for in-use pvc
err = deletePod(app.Name, app.Namespace, f.ClientSet, deployTimeout)
if err != nil {
logAndFail("failed to delete application: %v", err)
}

}
// clean up after ourselves
err = deletePVCAndValidatePV(f.ClientSet, pvc, deployTimeout)
if err != nil {
logAndFail("failed to delete PVC: %v", err)
}
// validate created backend rbd images
validateRBDImageCount(f, 0, defaultRBDPool)
validateOmapCount(f, 0, rbdType, defaultRBDPool, volumesType)
})

ByFileAndBlockEncryption("create a PVC and bind it to an app using rbd-nbd mounter with encryption", func(
validator encryptionValidateFunc, _ validateFunc, encType crypto.EncryptionType,
) {
Expand Down
44 changes: 40 additions & 4 deletions e2e/rbd_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1223,10 +1223,12 @@ func waitToRemoveImagesFromTrash(f *framework.Framework, poolName string, t int)

// imageInfo strongly typed JSON spec for image info.
type imageInfo struct {
Name string `json:"name"`
StripeUnit int `json:"stripe_unit"`
StripeCount int `json:"stripe_count"`
ObjectSize int `json:"object_size"`
Name string `json:"name"`
StripeUnit int `json:"stripe_unit"`
StripeCount int `json:"stripe_count"`
ObjectSize int `json:"object_size"`
DataPool string `json:"data_pool"`
Features []string `json:"features"`
}

// getImageInfo queries rbd about the given image and returns its metadata, and returns
Expand Down Expand Up @@ -1305,6 +1307,40 @@ func validateStripe(f *framework.Framework,
return nil
}

// validateImageFeatures checks that the given RBD image has all the expected
// image features enabled.
func validateImageFeatures(
f *framework.Framework,
imageName, pool string,
expectedFeatures []string,
) error {
var imgInfo imageInfo

imgInfoStr, err := getImageInfo(f, imageName, pool)
if err != nil {
return err
}

err = json.Unmarshal([]byte(imgInfoStr), &imgInfo)
if err != nil {
return fmt.Errorf("unmarshal failed: %w. raw buffer response: %s", err, imgInfoStr)
}

actualSet := make(map[string]bool, len(imgInfo.Features))
for _, feat := range imgInfo.Features {
actualSet[feat] = true
}

for _, want := range expectedFeatures {
if !actualSet[want] {
return fmt.Errorf("image %s missing expected feature %q, got features: %v",
imageName, want, imgInfo.Features)
}
}

return nil
}

func validateQOS(f *framework.Framework,
pvc *v1.PersistentVolumeClaim,
wants map[string]string,
Expand Down
6 changes: 4 additions & 2 deletions internal/rbd/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,11 @@ func (rv *rbdVolume) checkCloneImage(ctx context.Context, parentVol *rbdVolume)
func (rv *rbdVolume) generateTempClone() *rbdVolume {
tempClone := rbdVolume{}
tempClone.conn = rv.conn.Copy()
// The temp clone image need to have deep flatten feature
// Use the parent volume's image features (from StorageClass) and ensure
// that layering and deep-flatten are always enabled, as these are
// required for the flatten operation on the temporary clone.
f := []string{librbd.FeatureNameLayering, librbd.FeatureNameDeepFlatten}
tempClone.ImageFeatureSet = librbd.FeatureSetFromNames(f)
tempClone.ImageFeatureSet = rv.ImageFeatureSet | librbd.FeatureSetFromNames(f)
tempClone.ClusterID = rv.ClusterID
tempClone.Monitors = rv.Monitors
tempClone.Pool = rv.Pool
Expand Down
5 changes: 3 additions & 2 deletions internal/rbd/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1391,9 +1391,10 @@ func (cs *ControllerServer) doSnapshotClone(
// generate cloned volume details from snapshot
cloneRbd := rbdSnap.toVolume()
defer cloneRbd.Destroy(ctx)
// add image feature for cloneRbd
// Use the parent volume's image features and ensure that layering and
// deep-flatten are always enabled for the snapshot backing image.
f := []string{librbd.FeatureNameLayering, librbd.FeatureNameDeepFlatten}
cloneRbd.ImageFeatureSet = librbd.FeatureSetFromNames(f)
cloneRbd.ImageFeatureSet = parentVol.ImageFeatureSet | librbd.FeatureSetFromNames(f)

err := cloneRbd.Connect(cr)
if err != nil {
Expand Down
5 changes: 3 additions & 2 deletions internal/rbd/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,10 @@ func (rv *rbdVolume) NewSnapshotByID(
return nil, err
}

// set the features for the clone image.
// Use the parent volume's image features and ensure that layering and
// deep-flatten are always enabled for the snapshot backing image.
f := []string{librbd.FeatureNameLayering, librbd.FeatureNameDeepFlatten}
rv.ImageFeatureSet = librbd.FeatureSetFromNames(f)
rv.ImageFeatureSet |= librbd.FeatureSetFromNames(f)

options, err := rv.constructImageOptions(ctx)
if err != nil {
Expand Down
Loading