diff --git a/go.mod b/go.mod index eefc8d8df..0961ef676 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ require ( github.com/vmware-tanzu/image-registry-operator-api v0.0.0-20250624211456-dfc90459c658 github.com/vmware-tanzu/net-operator-api v0.0.0-20250826165015-90a4bb21727b github.com/vmware-tanzu/nsx-operator/pkg/apis v0.0.0-20250813103855-288a237381b5 - github.com/vmware/govmomi v0.53.0-alpha.0.0.20250911174024-a3b63a98bfb9 + github.com/vmware/govmomi v0.53.0-alpha.0.0.20250923175025-e586c7832696 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/net v0.42.0 // indirect // * https://github.com/vmware-tanzu/vm-operator/security/dependabot/24 diff --git a/go.sum b/go.sum index 0c7dd135d..47ca82c97 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/vmware-tanzu/net-operator-api v0.0.0-20250826165015-90a4bb21727b h1:4 github.com/vmware-tanzu/net-operator-api v0.0.0-20250826165015-90a4bb21727b/go.mod h1:w6QJGm3crIA16ZIz1FVQXD2NVeJhOgGXxW05RbVTSTo= github.com/vmware-tanzu/nsx-operator/pkg/apis v0.0.0-20250813103855-288a237381b5 h1:OUPe+BjC/XWqHRWYHDCtTa/lZpEz6U8YmZcZi1Rp5BU= github.com/vmware-tanzu/nsx-operator/pkg/apis v0.0.0-20250813103855-288a237381b5/go.mod h1:Q4JzNkNMvjo7pXtlB5/R3oME4Nhah7fAObWgghVmtxk= -github.com/vmware/govmomi v0.53.0-alpha.0.0.20250911174024-a3b63a98bfb9 h1:iUUFRkImk2noxtlV+GvGEZLMdr44qkVaW2PfrKV8jpc= -github.com/vmware/govmomi v0.53.0-alpha.0.0.20250911174024-a3b63a98bfb9/go.mod h1:MKEZBs5aGMM+J33dt2rWXP7ayDyCMKi4hO4DkH694pw= +github.com/vmware/govmomi v0.53.0-alpha.0.0.20250923175025-e586c7832696 h1:ik1abQdgfaDKsNBndP3e30JLPCkVJ3MRe4F4+5i4zvU= +github.com/vmware/govmomi v0.53.0-alpha.0.0.20250923175025-e586c7832696/go.mod h1:MKEZBs5aGMM+J33dt2rWXP7ayDyCMKi4hO4DkH694pw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/pkg/providers/vsphere/vmlifecycle/create_fastdeploy.go b/pkg/providers/vsphere/vmlifecycle/create_fastdeploy.go index db7946d72..cc0ccd0e9 100644 --- a/pkg/providers/vsphere/vmlifecycle/create_fastdeploy.go +++ b/pkg/providers/vsphere/vmlifecycle/create_fastdeploy.go @@ -66,7 +66,7 @@ func fastDeploy( } logger.Info("Got destination file paths", "dstFilePaths", dstFilePaths) - // Collect the disks and remove the storage profile from them. + // Collect the disks. var ( disks []*vimtypes.VirtualDisk diskSpecs []*vimtypes.VirtualDeviceConfigSpec @@ -248,7 +248,8 @@ func fastDeploy( diskSpecs, dstDiskFormat, dstDiskPaths, - srcDiskPaths) + srcDiskPaths, + createArgs.IsEncryptedStorageProfile) } func fastDeployLinked( @@ -330,25 +331,12 @@ func fastDeployDirect( diskSpecs []*vimtypes.VirtualDeviceConfigSpec, diskFormat vimtypes.DatastoreSectorFormat, dstDiskPaths, - srcDiskPaths []string) (*vimtypes.ManagedObjectReference, error) { + srcDiskPaths []string, + isEncryptedStorageProfile bool) (*vimtypes.ManagedObjectReference, error) { logger := pkglog.FromContextOrDefault(ctx).WithName("fastDeployDirect") - // Copy each disk into the VM directory. - if err := fastDeployDirectCopyDisks( - ctx, - logger, - datacenter, - configSpec, - srcDiskPaths, - dstDiskPaths, - diskFormat); err != nil { - - return nil, err - } - - _, isVMEncrypted := configSpec.Crypto.(*vimtypes.CryptoSpecEncrypt) - + diskCopySpecs := make([]vimtypes.FileBackedVirtualDiskSpec, len(dstDiskPaths)) for i := range diskSpecs { ds := diskSpecs[i] @@ -356,19 +344,52 @@ func fastDeployDirect( // exists. ds.FileOperation = "" - if isVMEncrypted { - // If the VM is to be encrypted, then the disks need to be updated - // so they are not marked as encrypted upon VM creation. This is - // because it is not possible to change the encryption state of VM - // disks when they are being attached. Instead the disks must be - // encrypted after they are attached to the VM. - ds.Profile = nil - if ds.Backing != nil { - ds.Backing.Crypto = nil + profile := ds.Profile + if len(profile) == 0 { + profile = configSpec.VmProfile + } + + diskCopySpecs[i] = vimtypes.FileBackedVirtualDiskSpec{ + VirtualDiskSpec: vimtypes.VirtualDiskSpec{ + AdapterType: string(vimtypes.VirtualDiskAdapterTypeLsiLogic), + DiskType: string(vimtypes.VirtualDiskTypeThin), + }, + SectorFormat: string(diskFormat), + Profile: profile, + } + + // Copy the disks using the same crypto key as the VM if the storage + // class is encrypted. + if isEncryptedStorageProfile && configSpec.Crypto != nil { + if ds.Backing == nil { + ds.Backing = &vimtypes.VirtualDeviceConfigSpecBackingSpec{} + } + crypto := ds.Backing.Crypto + if crypto == nil { + crypto = configSpec.Crypto } + diskCopySpecs[i].Crypto = crypto + + // Please note, the disk is added to the VM with its crypto spec + // set to nil since it is an existing disk. The disk is already + // encrypted as part of the copy operation. + ds.Profile = profile + ds.Backing.Crypto = nil } } + // Copy each disk into the VM directory. + if err := fastDeployDirectCopyDisks( + ctx, + logger, + datacenter, + srcDiskPaths, + dstDiskPaths, + diskCopySpecs); err != nil { + + return nil, err + } + return fastDeployCreateVM(ctx, logger, folder, pool, host, configSpec) } @@ -410,35 +431,27 @@ func fastDeployDirectCopyDisks( ctx context.Context, logger logr.Logger, datacenter *object.Datacenter, - configSpec vimtypes.VirtualMachineConfigSpec, srcDiskPaths, dstDiskPaths []string, - diskFormat vimtypes.DatastoreSectorFormat) error { + dstDiskSpecs []vimtypes.FileBackedVirtualDiskSpec) error { var ( wg sync.WaitGroup copyDiskTasks = make([]*object.Task, len(srcDiskPaths)) copyDiskErrs = make(chan error, len(srcDiskPaths)) - copyDiskSpec = vimtypes.FileBackedVirtualDiskSpec{ - VirtualDiskSpec: vimtypes.VirtualDiskSpec{ - AdapterType: string(vimtypes.VirtualDiskAdapterTypeLsiLogic), - DiskType: string(vimtypes.VirtualDiskTypeThin), - }, - SectorFormat: string(diskFormat), - Profile: configSpec.VmProfile, - } - diskManager = object.NewVirtualDiskManager(datacenter.Client()) + diskManager = object.NewVirtualDiskManager(datacenter.Client()) ) for i := range srcDiskPaths { s := srcDiskPaths[i] d := dstDiskPaths[i] + c := dstDiskSpecs[i] logger.Info( "Copying disk", "dstDiskPath", d, "srcDiskPath", s, - "copyDiskSpec", copyDiskSpec) + "dstDiskSpec", c) t, err := diskManager.CopyVirtualDisk( ctx, @@ -446,7 +459,7 @@ func fastDeployDirectCopyDisks( datacenter, d, datacenter, - ©DiskSpec, + &c, false) if err != nil { logger.Error(err, "failed to copy disk, cancelling other tasks") diff --git a/pkg/providers/vsphere/vmprovider_vm_test.go b/pkg/providers/vsphere/vmprovider_vm_test.go index 3832659ad..8d9b2b79c 100644 --- a/pkg/providers/vsphere/vmprovider_vm_test.go +++ b/pkg/providers/vsphere/vmprovider_vm_test.go @@ -1880,6 +1880,95 @@ func vmTests() { v3, _ := ec.GetString(pkgconst.VMProvKeepDisksExtraConfigKey) Expect(v3).To(Equal(path.Base(ctx.ContentLibraryItemDiskPath))) }) + + When("direct mode is used", func() { + JustBeforeEach(func() { + if vm.Annotations == nil { + vm.Annotations = map[string]string{} + } + vm.Annotations[pkgconst.FastDeployAnnotationKey] = pkgconst.FastDeployModeDirect + }) + It("should succeed", func() { + vcVM, err := createOrUpdateAndGetVcVM(ctx, vmProvider, vm) + Expect(err).ToNot(HaveOccurred()) + + var moVM mo.VirtualMachine + Expect(vcVM.Properties( + ctx, + vcVM.Reference(), + []string{"config.extraConfig"}, + &moVM)).To(Succeed()) + ec := object.OptionValueList(moVM.Config.ExtraConfig) + v1, _ := ec.GetString("hello") + Expect(v1).To(Equal("world")) + v2, _ := ec.GetString("fu") + Expect(v2).To(Equal("bar")) + _, ok := ec.GetString(pkgconst.VMProvKeepDisksExtraConfigKey) + Expect(ok).To(BeFalse()) + }) + + When("disks are encrypted", func() { + BeforeEach(func() { + pkgcfg.SetContext(parentCtx, func(config *pkgcfg.Config) { + config.Features.BringYourOwnEncryptionKey = true + }) + parentCtx = vmconfig.WithContext(parentCtx) + parentCtx = vmconfig.Register(parentCtx, crypto.New()) + }) + JustBeforeEach(func() { + m := vimcrypto.NewManagerKmip(ctx.VCClient.Client) + Expect(m.MarkDefault(ctx, ctx.NativeKeyProviderID)).To(Succeed()) + + var storageClass storagev1.StorageClass + Expect(ctx.Client.Get( + ctx, + client.ObjectKey{Name: ctx.EncryptedStorageClassName}, + &storageClass)).To(Succeed()) + Expect(kubeutil.MarkEncryptedStorageClass( + ctx, + ctx.Client, + storageClass, + true)).To(Succeed()) + + vm.Spec.StorageClass = ctx.EncryptedStorageClassName + }) + It("should succeed", func() { + vcVM, err := createOrUpdateAndGetVcVM(ctx, vmProvider, vm) + Expect(err).ToNot(HaveOccurred()) + + var moVM mo.VirtualMachine + Expect(vcVM.Properties( + ctx, + vcVM.Reference(), + []string{"config"}, + &moVM)).To(Succeed()) + ec := object.OptionValueList(moVM.Config.ExtraConfig) + v1, _ := ec.GetString("hello") + Expect(v1).To(Equal("world")) + v2, _ := ec.GetString("fu") + Expect(v2).To(Equal("bar")) + _, ok := ec.GetString(pkgconst.VMProvKeepDisksExtraConfigKey) + Expect(ok).To(BeFalse()) + + for _, bd := range moVM.Config.Hardware.Device { + if d, ok := bd.(*vimtypes.VirtualDisk); ok { + var keyID *vimtypes.CryptoKeyId + switch tBack := d.Backing.(type) { + case *vimtypes.VirtualDiskFlatVer2BackingInfo: + keyID = tBack.KeyId + case *vimtypes.VirtualDiskSeSparseBackingInfo: + keyID = tBack.KeyId + case *vimtypes.VirtualDiskSparseVer2BackingInfo: + keyID = tBack.KeyId + } + Expect(keyID).ToNot(BeNil()) + Expect(keyID.ProviderId).ToNot(BeNil()) + Expect(keyID.ProviderId.Id).To(Equal(ctx.NativeKeyProviderID)) + } + } + }) + }) + }) }) })