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

rbd: add additional space for encrypted volumes #4582

Draft
wants to merge 2 commits into
base: devel
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions PendingReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
- deploy: radosNamespaceCephFS can be configured for ceph-csi-cephfs chart in [PR](https://github.com/ceph/ceph-csi/pull/4652)
- build: update ceph release to squid in [PR](https://github.com/ceph/ceph-csi/pull/4735)
- build: CentOS Stream 9 is used as OS in the container-images [PR](https://github.com/ceph/ceph-csi/pull/4735)
- rbd: add additional space for encrypted volumes for Luks2 header in [PR](https://github.com/ceph/ceph-csi/pull/4582)

## NOTE
210 changes: 210 additions & 0 deletions e2e/rbd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import (

. "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/cloud-provider/volume/helpers"
"k8s.io/kubernetes/test/e2e/framework"
e2edebug "k8s.io/kubernetes/test/e2e/framework/debug"
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
Expand Down Expand Up @@ -1983,6 +1985,214 @@ var _ = Describe("RBD", func() {
}
})

By("create/resize/clone/restore a encrypted block pvc and verify the image size", func() {
err := deleteResource(rbdExamplePath + "storageclass.yaml")
if err != nil {
framework.Failf("failed to delete storageclass: %v", err)
}
err = createRBDStorageClass(
f.ClientSet,
f,
defaultSCName,
nil,
map[string]string{"encrypted": "true", "encryptionType": util.EncryptionTypeBlock.String()},
deletePolicy)
if err != nil {
framework.Failf("failed to create storageclass: %v", err)
}

err = createRBDSnapshotClass(f)
if err != nil {
framework.Failf("failed to create storageclass: %v", err)
}
defer func() {
err = deleteRBDSnapshotClass()
if err != nil {
framework.Failf("failed to delete VolumeSnapshotClass: %v", err)
}
}()

var (
imageSize uint64
resizeImageSize uint64
sizeInBytes int64
)

//nolint:goconst // The string "1Gi" is used multiple times in rbd.go, so it's not a const value.
pvcSize := "1Gi"
if sizeInBytes, err = helpers.RoundUpToB(resource.MustParse(pvcSize)); err != nil {
framework.Failf("failed to parse pvc size: %v", err)
}
imageSize = uint64(sizeInBytes) + util.Luks2HeaderSize
Madhu-1 marked this conversation as resolved.
Show resolved Hide resolved

pvc, err := loadPVC(rawPvcPath)
if err != nil {
framework.Failf("failed to load PVC: %v", err)
}
pvc.Namespace = f.UniqueName
pvc.Spec.Resources.Requests[v1.ResourceStorage] = resource.MustParse(pvcSize)

app, err := loadApp(rawAppPath)
if err != nil {
framework.Failf("failed to load application: %v", err)
}
labelKey := "app"
labelValue := "rbd-pod-block-encrypted"
opt := metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", labelKey, labelValue),
}

app.Labels = map[string]string{labelKey: labelValue}
app.Namespace = f.UniqueName
err = createPVCAndApp("", f, pvc, app, deployTimeout)
if err != nil {
framework.Failf("failed to create PVC and application: %v", err)
}

// validate created backend rbd images
err = validateImageSize(f, pvc, imageSize)
if err != nil {
framework.Failf("failed to validate image size: %v", err)
}
err = checkDeviceSize(app, f, &opt, pvcSize)
if err != nil {
framework.Failf("failed to validate device size: %v", err)
}

// create clone PVC and validate the image size
labelValueClonePod := "rbd-pod-block-encrypted-clone"
optClonePod := metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", labelKey, labelValueClonePod),
}

pvcClone, err := loadPVC(pvcBlockSmartClonePath)
if err != nil {
framework.Failf("failed to load PVC: %v", err)
}
pvcClone.Spec.DataSource.Name = pvc.Name
pvcClone.Spec.Resources.Requests[v1.ResourceStorage] = resource.MustParse(pvcSize)
pvcClone.Namespace = f.UniqueName

appClone, err := loadApp(appBlockSmartClonePath)
if err != nil {
framework.Failf("failed to load application: %v", err)
}
appClone.Namespace = f.UniqueName
appClone.Spec.Volumes[0].PersistentVolumeClaim.ClaimName = pvcClone.Name
appClone.Labels = map[string]string{labelKey: labelValueClonePod}

err = createPVCAndApp("", f, pvcClone, appClone, deployTimeout)
if err != nil {
framework.Failf("failed to create clone PVC and application : %v", err)
}

err = validateImageSize(f, pvcClone, imageSize)
if err != nil {
framework.Failf("failed to validate image size: %v", err)
}
err = checkDeviceSize(appClone, f, &optClonePod, pvcSize)
if err != nil {
framework.Failf("failed to validate device size: %v", err)
}

// create snapshot and restore PVC and validate the image size
labelValueRestorePod := "rbd-pod-block-encrypted-restore"
optRestorePod := metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", labelKey, labelValueRestorePod),
}
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)
}

pvcRestore, err := loadPVC(pvcBlockRestorePath)
if err != nil {
framework.Failf("failed to load PVC: %v", err)
}
pvcRestore.Spec.DataSource.Name = snap.Name
pvcRestore.Spec.VolumeMode = pvc.Spec.VolumeMode
pvcRestore.Spec.Resources.Requests[v1.ResourceStorage] = resource.MustParse(pvcSize)
pvcRestore.Namespace = f.UniqueName

appRestore, err := loadApp(appBlockRestorePath)
if err != nil {
framework.Failf("failed to load application: %v", err)
}
appRestore.Namespace = f.UniqueName
appRestore.Spec.Volumes[0].PersistentVolumeClaim.ClaimName = pvcRestore.Name
appRestore.Labels = map[string]string{labelKey: labelValueRestorePod}

err = createPVCAndApp("", f, pvcRestore, appRestore, deployTimeout)
if err != nil {
framework.Failf("failed to create clone PVC and application : %v", err)
}

err = validateImageSize(f, pvcRestore, imageSize)
if err != nil {
framework.Failf("failed to validate image size: %v", err)
}
err = checkDeviceSize(appRestore, f, &optRestorePod, pvcSize)
if err != nil {
framework.Failf("failed to validate device size: %v", err)
}

// resize PVC and validate the image size
resizePVCSize := "2Gi"
if sizeInBytes, err = helpers.RoundUpToB(resource.MustParse(resizePVCSize)); err != nil {
framework.Failf("failed to parse resize pvc size: %v", err)
}
resizeImageSize = uint64(sizeInBytes) + util.Luks2HeaderSize

err = expandPVCSize(f.ClientSet, pvc, resizePVCSize, deployTimeout)
if err != nil {
framework.Failf("failed to expand pvc size: %v", err)
}
// wait for application pod to come up after resize
err = waitForPodInRunningState(app.Name, app.Namespace, f.ClientSet, deployTimeout, noError)
if err != nil {
framework.Failf("timeout waiting for pod to be in running state: %v", err)
}

err = validateImageSize(f, pvc, resizeImageSize)
if err != nil {
framework.Failf("failed to validate image size after resize: %v", err)
}
err = checkDeviceSize(app, f, &opt, resizePVCSize)
if err != nil {
framework.Failf("failed to validate device size after resize: %v", err)
}

// delete resources
err = deletePVCAndApp("", f, pvc, app)
if err != nil {
framework.Failf("failed to delete pvc and app: %v", err)
}
iPraveenParihar marked this conversation as resolved.
Show resolved Hide resolved
err = deletePVCAndApp("", f, pvcClone, appClone)
if err != nil {
framework.Failf("failed to delete clone pvc and app: %v", err)
}
err = deletePVCAndApp("", f, pvcRestore, appRestore)
if err != nil {
framework.Failf("failed to delete clone pvc and app: %v", err)
}
err = deleteSnapshot(&snap, deployTimeout)
if err != nil {
framework.Failf("failed to delete snapshot: %v", err)
}
err = deleteResource(rbdExamplePath + "storageclass.yaml")
if err != nil {
framework.Failf("failed to delete storageclass: %v", err)
}

// validate created backend rbd images
validateRBDImageCount(f, 0, defaultRBDPool)
validateOmapCount(f, 0, rbdType, defaultRBDPool, snapsType)
})

ByFileAndBlockEncryption("create a PVC and bind it to an app using rbd-nbd mounter with encryption", func(
validator encryptionValidateFunc, _ validateFunc, encType util.EncryptionType,
) {
Expand Down
20 changes: 20 additions & 0 deletions e2e/rbd_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,7 @@ type imageInfo struct {
StripeUnit int `json:"stripe_unit"`
StripeCount int `json:"stripe_count"`
ObjectSize int `json:"object_size"`
Size uint64 `json:"size"`
}

// getImageInfo queries rbd about the given image and returns its metadata, and returns
Expand Down Expand Up @@ -1126,3 +1127,22 @@ func validateStripe(f *framework.Framework,

return nil
}

// validateImageSize validates the size of the image.
func validateImageSize(f *framework.Framework, pvc *v1.PersistentVolumeClaim, imageSize uint64) error {
imageData, err := getImageInfoFromPVC(pvc.Namespace, pvc.Name, f)
if err != nil {
return err
}

imgInfo, err := getImageInfo(f, imageData.imageName, defaultRBDPool)
if err != nil {
return err
}

if imgInfo.Size != imageSize {
return fmt.Errorf("image %s size %d does not match expected %d", imgInfo.Name, imgInfo.Size, imageSize)
}

return nil
}
22 changes: 22 additions & 0 deletions internal/rbd/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,17 @@ func (cs *ControllerServer) CreateSnapshot(
return nil, status.Error(codes.Internal, err.Error())
}

err = vol.Connect(cr)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
defer vol.Destroy(ctx)

err = vol.getImageInfo()
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

csiSnap, err := vol.toSnapshot().ToCSI(ctx)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
Expand Down Expand Up @@ -1294,6 +1305,17 @@ func cloneFromSnapshot(
}
}

err = rbdSnap.Connect(cr)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
defer rbdSnap.Destroy(ctx)

err = rbdSnap.getImageInfo()
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

csiSnap, err := rbdSnap.ToCSI(ctx)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
Expand Down
8 changes: 8 additions & 0 deletions internal/rbd/encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ const (
metadataDEK = "rbd.csi.ceph.com/dek"
oldMetadataDEK = ".rbd.csi.ceph.com/dek"

// luks2 header size metadata key.
luks2HeaderSizeKey = "rbd.csi.ceph.com/luks2HeaderSize"

encryptionPassphraseSize = 20

// rbdDefaultEncryptionType is the default to use when the
Expand Down Expand Up @@ -130,6 +133,11 @@ func (ri *rbdImage) setupBlockEncryption(ctx context.Context) error {
return err
}

err = ri.SetMetadata(luks2HeaderSizeKey, strconv.FormatUint(util.Luks2HeaderSize, 10))
if err != nil {
return fmt.Errorf("failed to save %s metadata on image: %w", luks2HeaderSizeKey, err)
}

err = ri.ensureEncryptionMetadataSet(rbdImageEncryptionPrepared)
if err != nil {
log.ErrorLog(ctx, "failed to save encryption status, deleting "+
Expand Down
Loading