Skip to content

Commit

Permalink
Support BYOK for Instance Storage PVCs
Browse files Browse the repository at this point in the history
This patch adds support for creating Instance Storage PVCs
with the EncryptionClass annotation that matches the VM's
EncryptionClass.
  • Loading branch information
akutz committed Oct 17, 2024
1 parent 0bad8f4 commit f7d6752
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 0 deletions.
5 changes: 5 additions & 0 deletions controllers/virtualmachine/volume/volume_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@ func (r *Reconciler) createInstanceStoragePVC(
},
}

if c := ctx.VM.Spec.Crypto; c != nil && c.EncryptionClassName != "" {
// Assign the InstanceStorage PVC the same EncryptionClass as the VM.
pvc.Annotations[constants.EncryptionClassNameAnnotation] = c.EncryptionClassName
}

if err := controllerutil.SetControllerReference(ctx.VM, pvc, r.Client.Scheme()); err != nil {
// This is an unexpected error.
return fmt.Errorf("cannot set controller reference on PersistentVolumeClaim: %w", err)
Expand Down
38 changes: 38 additions & 0 deletions controllers/virtualmachine/volume/volume_controller_intg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ func intgTestsReconcile() {
config.Features.InstanceStorage = true
config.InstanceStorage.PVPlacementFailedTTL = 0
})
})

JustBeforeEach(func() {
vm.Spec.Volumes = append(vm.Spec.Volumes, builder.DummyInstanceStorageVirtualMachineVolumes()...)
vm.Labels = map[string]string{constants.InstanceStorageLabelKey: "true"}
Expect(ctx.Client.Create(ctx, vm)).To(Succeed())
Expand Down Expand Up @@ -305,6 +307,42 @@ func intgTestsReconcile() {
waitForVirtualMachineInstanceStorage(vmKey, false, false)
})
})

When("VM has non-empty spec.crypto.encryptionClassName", func() {
BeforeEach(func() {
if vm.Annotations == nil {
vm.Annotations = map[string]string{}
}
vm.Annotations[constants.InstanceStorageSelectedNodeAnnotationKey] = dummySelectedNode
vm.Annotations[constants.InstanceStorageSelectedNodeMOIDAnnotationKey] = dummySelectedNodeMOID

vm.Spec.Crypto = &vmopv1.VirtualMachineCryptoSpec{
EncryptionClassName: "my-encryption-class",
}
})
Specify("PVCs are created with the expected annotation", func() {
Expect(ctx.Client.Get(ctx, vmKey, vm)).To(Succeed())

volumes := instancestorage.FilterVolumes(vm)
Expect(volumes).ToNot(BeEmpty())

Eventually(func(g Gomega) {
for i := range volumes {
var obj corev1.PersistentVolumeClaim
g.Expect(ctx.Client.Get(
ctx,
client.ObjectKey{
Namespace: vm.Namespace,
Name: volumes[i].PersistentVolumeClaim.ClaimName,
},
&obj)).To(Succeed())
g.Expect(obj.Annotations).To(HaveKeyWithValue(
constants.EncryptionClassNameAnnotation,
vm.Spec.Crypto.EncryptionClassName))
}
}).Should(Succeed())
})
})
})

Context("Reconcile", func() {
Expand Down
51 changes: 51 additions & 0 deletions controllers/virtualmachine/volume/volume_controller_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,57 @@ func unitTestsReconcile() {
Expect(err.Error()).To(ContainSubstring("insufficient quota"))
expectPVCsStatus(volCtx, ctx, true, false, 0)
})

When("VM has nil spec.crypto", func() {
BeforeEach(func() {
vm.Spec.Crypto = nil
})
Specify("PVCs are created sans the expected annotation", func() {
Expect(reconciler.ReconcileNormal(volCtx)).To(Succeed())
pvcList, err := getInstanceStoragePVCs(volCtx, ctx)
Expect(err).ToNot(HaveOccurred())
Expect(pvcList).ToNot(BeEmpty())
for i := range pvcList {
Expect(pvcList[i].Annotations).ToNot(HaveKey(constants.EncryptionClassNameAnnotation))
}
})
})

When("VM has empty spec.crypto.encryptionClassName", func() {
BeforeEach(func() {
vm.Spec.Crypto = &vmopv1.VirtualMachineCryptoSpec{
EncryptionClassName: "",
}
})
Specify("PVCs are created sans the expected annotation", func() {
Expect(reconciler.ReconcileNormal(volCtx)).To(Succeed())
pvcList, err := getInstanceStoragePVCs(volCtx, ctx)
Expect(err).ToNot(HaveOccurred())
Expect(pvcList).ToNot(BeEmpty())
for i := range pvcList {
Expect(pvcList[i].Annotations).ToNot(HaveKey(constants.EncryptionClassNameAnnotation))
}
})
})

When("VM has non-empty spec.crypto.encryptionClassName", func() {
BeforeEach(func() {
vm.Spec.Crypto = &vmopv1.VirtualMachineCryptoSpec{
EncryptionClassName: "my-encryption-class",
}
})
Specify("PVCs are created with the expected annotation", func() {
Expect(reconciler.ReconcileNormal(volCtx)).To(Succeed())
pvcList, err := getInstanceStoragePVCs(volCtx, ctx)
Expect(err).ToNot(HaveOccurred())
Expect(pvcList).ToNot(BeEmpty())
for i := range pvcList {
Expect(pvcList[i].Annotations).To(HaveKeyWithValue(
constants.EncryptionClassNameAnnotation,
vm.Spec.Crypto.EncryptionClassName))
}
})
})
})

When("VM does not have BiosUUID", func() {
Expand Down
5 changes: 5 additions & 0 deletions pkg/providers/vsphere/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ const (
CloudInitGuestInfoUserdata = "guestinfo.userdata"
CloudInitGuestInfoUserdataEncoding = "guestinfo.userdata.encoding"

// EncryptionClassNameAnnotation specifies the name of an EncryptionClass
// resource. This is used by APIs that participate in BYOK but cannot modify
// their spec to do so, such as the PersistentVolumeClaim API.
EncryptionClassNameAnnotation = "encryption.vmware.com/encryption-class-name"

// InstanceStoragePVCNamePrefix prefix of auto-generated PVC names.
InstanceStoragePVCNamePrefix = "instance-pvc-"
// InstanceStorageLabelKey identifies resources related to instance storage.
Expand Down

0 comments on commit f7d6752

Please sign in to comment.