From ab7cb21ec29c2893b682b784039c8706737e88ee Mon Sep 17 00:00:00 2001 From: Bryan Venteicher Date: Fri, 23 Jan 2026 16:00:51 -0600 Subject: [PATCH] Add Disabled field to VM bootstrap Add Disabled field to the VirtualMachineBootstrapSpec to allow skipping bootstrap customization. When true, the VM will not be bootstrapped even if a provider (CloudInit, LinuxPrep, Sysprep, VAppConfig) is specified. This is useful for either disabling the implicit LinuxPrep customization we do on Linux images for historical reasons, or deferring bootstrap when the guest is already configured. --- .../virtualmachine_conversion_test.go | 30 ++++++++++ .../virtualmachine_conversion_test.go | 30 ++++++++++ .../virtualmachine_conversion_test.go | 52 ++++++++--------- .../virtualmachine_conversion_test.go | 11 ++++ api/v1alpha1/virtualmachine_conversion.go | 11 ++-- api/v1alpha2/virtualmachine_conversion.go | 18 ++++++ api/v1alpha2/zz_generated.conversion.go | 16 ++---- api/v1alpha3/virtualmachine_conversion.go | 18 ++++++ api/v1alpha3/zz_generated.conversion.go | 16 ++---- api/v1alpha4/virtualmachine_conversion.go | 18 ++++++ api/v1alpha4/zz_generated.conversion.go | 16 ++---- .../virtualmachine_bootstrap_types.go | 10 ++++ ....vmware.com_virtualmachinereplicasets.yaml | 9 +++ ...vmoperator.vmware.com_virtualmachines.yaml | 9 +++ .../vsphere/vmlifecycle/bootstrap.go | 7 ++- .../vsphere/vmlifecycle/bootstrap_test.go | 56 +++++++++++++++++++ 16 files changed, 264 insertions(+), 63 deletions(-) diff --git a/api/test/v1alpha1/virtualmachine_conversion_test.go b/api/test/v1alpha1/virtualmachine_conversion_test.go index 44566bcd3..312c073c6 100644 --- a/api/test/v1alpha1/virtualmachine_conversion_test.go +++ b/api/test/v1alpha1/virtualmachine_conversion_test.go @@ -730,6 +730,36 @@ func TestVirtualMachineConversion(t *testing.T) { hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{}) }) + t.Run("VirtualMachine hub-spoke-hub with bootstrap disabled", func(t *testing.T) { + g := NewWithT(t) + hub := vmopv1.VirtualMachine{ + Spec: vmopv1.VirtualMachineSpec{ + Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{ + Disabled: true, + }, + }, + } + hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{}) + }) + + t.Run("VirtualMachine hub-spoke-hub with CloudInit and bootstrap disabled ", func(t *testing.T) { + g := NewWithT(t) + hub := vmopv1.VirtualMachine{ + Spec: vmopv1.VirtualMachineSpec{ + Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{ + CloudInit: &vmopv1.VirtualMachineBootstrapCloudInitSpec{ + RawCloudConfig: &vmopv1common.SecretKeySelector{ + Name: "my-secret", + Key: "user-data", + }, + }, + Disabled: true, + }, + }, + } + hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{}) + }) + t.Run("VirtualMachine hub-spoke Status", func(t *testing.T) { g := NewWithT(t) diff --git a/api/test/v1alpha2/virtualmachine_conversion_test.go b/api/test/v1alpha2/virtualmachine_conversion_test.go index 6d3f84fa4..a3d8f01d0 100644 --- a/api/test/v1alpha2/virtualmachine_conversion_test.go +++ b/api/test/v1alpha2/virtualmachine_conversion_test.go @@ -679,6 +679,36 @@ func TestVirtualMachineConversion(t *testing.T) { hubSpokeHub(g, &hub2, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{}) }) + t.Run("VirtualMachine hub-spoke-hub with bootstrap disabled", func(t *testing.T) { + g := NewWithT(t) + hub := vmopv1.VirtualMachine{ + Spec: vmopv1.VirtualMachineSpec{ + Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{ + Disabled: true, + }, + }, + } + hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{}) + }) + + t.Run("VirtualMachine hub-spoke-hub with CloudInit and bootstrap disabled ", func(t *testing.T) { + g := NewWithT(t) + hub := vmopv1.VirtualMachine{ + Spec: vmopv1.VirtualMachineSpec{ + Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{ + CloudInit: &vmopv1.VirtualMachineBootstrapCloudInitSpec{ + RawCloudConfig: &vmopv1common.SecretKeySelector{ + Name: "my-secret", + Key: "user-data", + }, + }, + Disabled: true, + }, + }, + } + hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{}) + }) + t.Run("VirtualMachine and spec.network.domainName", func(t *testing.T) { const ( diff --git a/api/test/v1alpha3/virtualmachine_conversion_test.go b/api/test/v1alpha3/virtualmachine_conversion_test.go index 80fec573b..1405a42fd 100644 --- a/api/test/v1alpha3/virtualmachine_conversion_test.go +++ b/api/test/v1alpha3/virtualmachine_conversion_test.go @@ -120,6 +120,7 @@ func TestVirtualMachineConversion(t *testing.T) { }, RawProperties: "my-secret", }, + Disabled: true, }, Network: &vmopv1.VirtualMachineNetworkSpec{ DomainName: "my-domain.com", @@ -382,22 +383,24 @@ func TestVirtualMachineConversion(t *testing.T) { }, }, { - name: "spec.bootstrap.cloudInit.waitOnNetwork4", + name: "spec.bootstrap.disabled", hub: &vmopv1.VirtualMachine{ Spec: vmopv1.VirtualMachineSpec{ Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{ - CloudInit: &vmopv1.VirtualMachineBootstrapCloudInitSpec{ - WaitOnNetwork4: ptrOf(true), - }, + Disabled: true, }, }, }, }, { - name: "spec.groupName", + name: "spec.bootstrap.cloudInit.waitOnNetwork4", hub: &vmopv1.VirtualMachine{ Spec: vmopv1.VirtualMachineSpec{ - GroupName: "my-group", + Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{ + CloudInit: &vmopv1.VirtualMachineBootstrapCloudInitSpec{ + WaitOnNetwork4: ptrOf(true), + }, + }, }, }, }, @@ -410,28 +413,21 @@ func TestVirtualMachineConversion(t *testing.T) { WaitOnNetwork6: ptrOf(true), }, }, - PromoteDisksMode: vmopv1.VirtualMachinePromoteDisksModeOffline, - BootOptions: &vmopv1.VirtualMachineBootOptions{ - Firmware: vmopv1.VirtualMachineBootOptionsFirmwareTypeEFI, - BootDelay: &metav1.Duration{Duration: time.Second * 10}, - BootOrder: []vmopv1.VirtualMachineBootOptionsBootableDevice{ - { - Type: vmopv1.VirtualMachineBootOptionsBootableDiskDevice, - Name: "disk-0", - }, - { - Type: vmopv1.VirtualMachineBootOptionsBootableNetworkDevice, - Name: "eth0", - }, - { - Type: vmopv1.VirtualMachineBootOptionsBootableCDRomDevice, - }, - }, - BootRetry: vmopv1.VirtualMachineBootOptionsBootRetryDisabled, - BootRetryDelay: &metav1.Duration{Duration: time.Second * 10}, - EFISecureBoot: vmopv1.VirtualMachineBootOptionsEFISecureBootDisabled, - NetworkBootProtocol: vmopv1.VirtualMachineBootOptionsNetworkBootProtocolIP4, - }, + }, + }, + }, + { + name: "spec.groupName", + hub: &vmopv1.VirtualMachine{ + Spec: vmopv1.VirtualMachineSpec{ + GroupName: "my-group", + }, + }, + }, + { + name: "spec.affinity", + hub: &vmopv1.VirtualMachine{ + Spec: vmopv1.VirtualMachineSpec{ Affinity: &vmopv1.AffinitySpec{ VMAffinity: &vmopv1.VMAffinitySpec{ RequiredDuringSchedulingPreferredDuringExecution: []vmopv1.VMAffinityTerm{ diff --git a/api/test/v1alpha4/virtualmachine_conversion_test.go b/api/test/v1alpha4/virtualmachine_conversion_test.go index 19991ed8f..8479ea644 100644 --- a/api/test/v1alpha4/virtualmachine_conversion_test.go +++ b/api/test/v1alpha4/virtualmachine_conversion_test.go @@ -120,6 +120,7 @@ func TestVirtualMachineConversion(t *testing.T) { }, RawProperties: "my-secret", }, + Disabled: true, }, Network: &vmopv1.VirtualMachineNetworkSpec{ DomainName: "my-domain.com", @@ -327,6 +328,16 @@ func TestVirtualMachineConversion(t *testing.T) { }, }, }, + { + name: "spec.bootstrap.disabled", + hub: &vmopv1.VirtualMachine{ + Spec: vmopv1.VirtualMachineSpec{ + Bootstrap: &vmopv1.VirtualMachineBootstrapSpec{ + Disabled: true, + }, + }, + }, + }, { name: "spec.affinity", hub: &vmopv1.VirtualMachine{ diff --git a/api/v1alpha1/virtualmachine_conversion.go b/api/v1alpha1/virtualmachine_conversion.go index 808046123..efc7fb2ca 100644 --- a/api/v1alpha1/virtualmachine_conversion.go +++ b/api/v1alpha1/virtualmachine_conversion.go @@ -842,12 +842,13 @@ func restore_v1alpha5_VirtualMachineBootstrapSpec( dstBootstrap := dst.Spec.Bootstrap if dstBootstrap == nil { - // v1a1 doesn't have a way to represent standalone LinuxPrep. If we didn't do a - // conversion in convert_v1alpha1_VmMetadata_To_v1alpha5_BootstrapSpec() but we - // saved a LinuxPrep in the conversion annotation, restore that here. - if srcBootstrap.LinuxPrep != nil { + // v1a1 doesn't have a way to represent standalone LinuxPrep or Disabled. + // If we didn't do a conversion in convert_v1alpha1_VmMetadata_To_v1alpha5_BootstrapSpec() + // but we saved a LinuxPrep or Disabled in the conversion annotation, restore that here. + if srcBootstrap.LinuxPrep != nil || srcBootstrap.Disabled { dst.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{ LinuxPrep: srcBootstrap.LinuxPrep, + Disabled: srcBootstrap.Disabled, } } return @@ -907,6 +908,8 @@ func restore_v1alpha5_VirtualMachineBootstrapSpec( dstVAppConfig.RawProperties = srcVAppConfig.RawProperties } } + + dstBootstrap.Disabled = srcBootstrap.Disabled } func restore_v1alpha5_VirtualMachineNetworkSpec( diff --git a/api/v1alpha2/virtualmachine_conversion.go b/api/v1alpha2/virtualmachine_conversion.go index 597ecd518..8c64fe077 100644 --- a/api/v1alpha2/virtualmachine_conversion.go +++ b/api/v1alpha2/virtualmachine_conversion.go @@ -27,6 +27,12 @@ func Convert_v1alpha5_PersistentVolumeClaimVolumeSource_To_v1alpha2_PersistentVo return autoConvert_v1alpha5_PersistentVolumeClaimVolumeSource_To_v1alpha2_PersistentVolumeClaimVolumeSource(in, out, s) } +func Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachineBootstrapSpec( + in *vmopv1.VirtualMachineBootstrapSpec, out *VirtualMachineBootstrapSpec, s apiconversion.Scope) error { + + return autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachineBootstrapSpec(in, out, s) +} + func Convert_v1alpha5_VirtualMachineBootstrapCloudInitSpec_To_v1alpha2_VirtualMachineBootstrapCloudInitSpec( in *vmopv1.VirtualMachineBootstrapCloudInitSpec, out *VirtualMachineBootstrapCloudInitSpec, s apiconversion.Scope) error { @@ -347,6 +353,17 @@ func restore_v1alpha5_VirtualMachineBootstrapSysprep(dst, src *vmopv1.VirtualMac } } +func restore_v1alpha5_VirtualMachineBootstrapDisabled(dst, src *vmopv1.VirtualMachine) { + if bs := src.Spec.Bootstrap; bs != nil { + if bs.Disabled { + if dst.Spec.Bootstrap == nil { + dst.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{} + } + dst.Spec.Bootstrap.Disabled = true + } + } +} + func restore_v1alpha5_VirtualMachineGuestID(dst, src *vmopv1.VirtualMachine) { dst.Spec.GuestID = src.Spec.GuestID } @@ -427,6 +444,7 @@ func (src *VirtualMachine) ConvertTo(dstRaw ctrlconversion.Hub) error { restore_v1alpha5_VirtualMachineBootstrapCloudInitWaitOnNetwork(dst, restored) restore_v1alpha5_VirtualMachineBootstrapLinuxPrep(dst, restored) restore_v1alpha5_VirtualMachineBootstrapSysprep(dst, restored) + restore_v1alpha5_VirtualMachineBootstrapDisabled(dst, restored) restore_v1alpha5_VirtualMachineSpecNetworkDomainName(dst, restored) restore_v1alpha5_VirtualMachineGuestID(dst, restored) restore_v1alpha5_VirtualMachinePromoteDisksMode(dst, restored) diff --git a/api/v1alpha2/zz_generated.conversion.go b/api/v1alpha2/zz_generated.conversion.go index 057f85ae0..a72cf0e54 100644 --- a/api/v1alpha2/zz_generated.conversion.go +++ b/api/v1alpha2/zz_generated.conversion.go @@ -306,11 +306,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1alpha5.VirtualMachineBootstrapSpec)(nil), (*VirtualMachineBootstrapSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachineBootstrapSpec(a.(*v1alpha5.VirtualMachineBootstrapSpec), b.(*VirtualMachineBootstrapSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*VirtualMachineBootstrapSysprepSpec)(nil), (*v1alpha5.VirtualMachineBootstrapSysprepSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha2_VirtualMachineBootstrapSysprepSpec_To_v1alpha5_VirtualMachineBootstrapSysprepSpec(a.(*VirtualMachineBootstrapSysprepSpec), b.(*v1alpha5.VirtualMachineBootstrapSysprepSpec), scope) }); err != nil { @@ -1031,6 +1026,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1alpha5.VirtualMachineBootstrapSpec)(nil), (*VirtualMachineBootstrapSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachineBootstrapSpec(a.(*v1alpha5.VirtualMachineBootstrapSpec), b.(*VirtualMachineBootstrapSpec), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1alpha5.VirtualMachineBootstrapSysprepSpec)(nil), (*VirtualMachineBootstrapSysprepSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha5_VirtualMachineBootstrapSysprepSpec_To_v1alpha2_VirtualMachineBootstrapSysprepSpec(a.(*v1alpha5.VirtualMachineBootstrapSysprepSpec), b.(*VirtualMachineBootstrapSysprepSpec), scope) }); err != nil { @@ -1833,14 +1833,10 @@ func autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachine out.Sysprep = nil } out.VAppConfig = (*VirtualMachineBootstrapVAppConfigSpec)(unsafe.Pointer(in.VAppConfig)) + // WARNING: in.Disabled requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachineBootstrapSpec is an autogenerated conversion function. -func Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachineBootstrapSpec(in *v1alpha5.VirtualMachineBootstrapSpec, out *VirtualMachineBootstrapSpec, s conversion.Scope) error { - return autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha2_VirtualMachineBootstrapSpec(in, out, s) -} - func autoConvert_v1alpha2_VirtualMachineBootstrapSysprepSpec_To_v1alpha5_VirtualMachineBootstrapSysprepSpec(in *VirtualMachineBootstrapSysprepSpec, out *v1alpha5.VirtualMachineBootstrapSysprepSpec, s conversion.Scope) error { if in.Sysprep != nil { in, out := &in.Sysprep, &out.Sysprep diff --git a/api/v1alpha3/virtualmachine_conversion.go b/api/v1alpha3/virtualmachine_conversion.go index 0831aab43..ddfb09ec7 100644 --- a/api/v1alpha3/virtualmachine_conversion.go +++ b/api/v1alpha3/virtualmachine_conversion.go @@ -116,6 +116,12 @@ func Convert_v1alpha5_VirtualMachineCdromSpec_To_v1alpha3_VirtualMachineCdromSpe return autoConvert_v1alpha5_VirtualMachineCdromSpec_To_v1alpha3_VirtualMachineCdromSpec(in, out, s) } +func Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachineBootstrapSpec( + in *vmopv1.VirtualMachineBootstrapSpec, out *VirtualMachineBootstrapSpec, s apiconversion.Scope) error { + + return autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachineBootstrapSpec(in, out, s) +} + func Convert_v1alpha5_VirtualMachineBootstrapCloudInitSpec_To_v1alpha3_VirtualMachineBootstrapCloudInitSpec( in *vmopv1.VirtualMachineBootstrapCloudInitSpec, out *VirtualMachineBootstrapCloudInitSpec, s apiconversion.Scope) error { @@ -325,6 +331,17 @@ func restore_v1alpha5_VirtualMachineBootstrapSysprep(dst, src *vmopv1.VirtualMac } } +func restore_v1alpha5_VirtualMachineBootstrapDisabled(dst, src *vmopv1.VirtualMachine) { + if bs := src.Spec.Bootstrap; bs != nil { + if bs.Disabled { + if dst.Spec.Bootstrap == nil { + dst.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{} + } + dst.Spec.Bootstrap.Disabled = true + } + } +} + func restore_v1alpha5_VirtualMachinePolicies(dst, src *vmopv1.VirtualMachine) { dst.Spec.Policies = slices.Clone(src.Spec.Policies) } @@ -361,6 +378,7 @@ func (src *VirtualMachine) ConvertTo(dstRaw ctrlconversion.Hub) error { restore_v1alpha5_VirtualMachineBootstrapCloudInitWaitOnNetwork(dst, restored) restore_v1alpha5_VirtualMachineBootstrapLinuxPrep(dst, restored) restore_v1alpha5_VirtualMachineBootstrapSysprep(dst, restored) + restore_v1alpha5_VirtualMachineBootstrapDisabled(dst, restored) restore_v1alpha5_VirtualMachinePromoteDisksMode(dst, restored) restore_v1alpha5_VirtualMachineBootOptions(dst, restored) restore_v1alpha5_VirtualMachineVolumes(dst, restored) diff --git a/api/v1alpha3/zz_generated.conversion.go b/api/v1alpha3/zz_generated.conversion.go index 98f517efb..cf68838d4 100644 --- a/api/v1alpha3/zz_generated.conversion.go +++ b/api/v1alpha3/zz_generated.conversion.go @@ -318,11 +318,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1alpha5.VirtualMachineBootstrapSpec)(nil), (*VirtualMachineBootstrapSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachineBootstrapSpec(a.(*v1alpha5.VirtualMachineBootstrapSpec), b.(*VirtualMachineBootstrapSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*VirtualMachineBootstrapSysprepSpec)(nil), (*v1alpha5.VirtualMachineBootstrapSysprepSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha3_VirtualMachineBootstrapSysprepSpec_To_v1alpha5_VirtualMachineBootstrapSysprepSpec(a.(*VirtualMachineBootstrapSysprepSpec), b.(*v1alpha5.VirtualMachineBootstrapSysprepSpec), scope) }); err != nil { @@ -1223,6 +1218,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1alpha5.VirtualMachineBootstrapSpec)(nil), (*VirtualMachineBootstrapSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachineBootstrapSpec(a.(*v1alpha5.VirtualMachineBootstrapSpec), b.(*VirtualMachineBootstrapSpec), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1alpha5.VirtualMachineBootstrapSysprepSpec)(nil), (*VirtualMachineBootstrapSysprepSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha5_VirtualMachineBootstrapSysprepSpec_To_v1alpha3_VirtualMachineBootstrapSysprepSpec(a.(*v1alpha5.VirtualMachineBootstrapSysprepSpec), b.(*VirtualMachineBootstrapSysprepSpec), scope) }); err != nil { @@ -2036,14 +2036,10 @@ func autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachine out.Sysprep = nil } out.VAppConfig = (*VirtualMachineBootstrapVAppConfigSpec)(unsafe.Pointer(in.VAppConfig)) + // WARNING: in.Disabled requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachineBootstrapSpec is an autogenerated conversion function. -func Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachineBootstrapSpec(in *v1alpha5.VirtualMachineBootstrapSpec, out *VirtualMachineBootstrapSpec, s conversion.Scope) error { - return autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha3_VirtualMachineBootstrapSpec(in, out, s) -} - func autoConvert_v1alpha3_VirtualMachineBootstrapSysprepSpec_To_v1alpha5_VirtualMachineBootstrapSysprepSpec(in *VirtualMachineBootstrapSysprepSpec, out *v1alpha5.VirtualMachineBootstrapSysprepSpec, s conversion.Scope) error { if in.Sysprep != nil { in, out := &in.Sysprep, &out.Sysprep diff --git a/api/v1alpha4/virtualmachine_conversion.go b/api/v1alpha4/virtualmachine_conversion.go index 51232cf81..89665448e 100644 --- a/api/v1alpha4/virtualmachine_conversion.go +++ b/api/v1alpha4/virtualmachine_conversion.go @@ -59,6 +59,12 @@ func Convert_v1alpha5_VirtualMachineCdromSpec_To_v1alpha4_VirtualMachineCdromSpe return autoConvert_v1alpha5_VirtualMachineCdromSpec_To_v1alpha4_VirtualMachineCdromSpec(in, out, s) } +func Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachineBootstrapSpec( + in *vmopv1.VirtualMachineBootstrapSpec, out *VirtualMachineBootstrapSpec, s apiconversion.Scope) error { + + return autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachineBootstrapSpec(in, out, s) +} + func Convert_v1alpha5_VirtualMachineBootstrapLinuxPrepSpec_To_v1alpha4_VirtualMachineBootstrapLinuxPrepSpec( in *vmopv1.VirtualMachineBootstrapLinuxPrepSpec, out *VirtualMachineBootstrapLinuxPrepSpec, s apiconversion.Scope) error { @@ -269,6 +275,17 @@ func restore_v1alpha5_VirtualMachineBootstrapSysprep(dst, src *vmopv1.VirtualMac } } +func restore_v1alpha5_VirtualMachineBootstrapDisabled(dst, src *vmopv1.VirtualMachine) { + if bs := src.Spec.Bootstrap; bs != nil { + if bs.Disabled { + if dst.Spec.Bootstrap == nil { + dst.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{} + } + dst.Spec.Bootstrap.Disabled = true + } + } +} + func restore_v1alpha5_VirtualMachineAffinity(dst, src *vmopv1.VirtualMachine) { if src.Spec.Affinity == nil { dst.Spec.Affinity = nil @@ -319,6 +336,7 @@ func (src *VirtualMachine) ConvertTo(dstRaw ctrlconversion.Hub) error { restore_v1alpha5_VirtualMachineBootstrapCloudInitWaitOnNetwork(dst, restored) restore_v1alpha5_VirtualMachineBootstrapLinuxPrep(dst, restored) restore_v1alpha5_VirtualMachineBootstrapSysprep(dst, restored) + restore_v1alpha5_VirtualMachineBootstrapDisabled(dst, restored) restore_v1alpha5_VirtualMachineAffinity(dst, restored) restore_v1alpha5_VirtualMachineVolumes(dst, restored) diff --git a/api/v1alpha4/zz_generated.conversion.go b/api/v1alpha4/zz_generated.conversion.go index cf5df4259..4e97b223f 100644 --- a/api/v1alpha4/zz_generated.conversion.go +++ b/api/v1alpha4/zz_generated.conversion.go @@ -318,11 +318,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1alpha5.VirtualMachineBootstrapSpec)(nil), (*VirtualMachineBootstrapSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachineBootstrapSpec(a.(*v1alpha5.VirtualMachineBootstrapSpec), b.(*VirtualMachineBootstrapSpec), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*VirtualMachineBootstrapSysprepSpec)(nil), (*v1alpha5.VirtualMachineBootstrapSysprepSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha4_VirtualMachineBootstrapSysprepSpec_To_v1alpha5_VirtualMachineBootstrapSysprepSpec(a.(*VirtualMachineBootstrapSysprepSpec), b.(*v1alpha5.VirtualMachineBootstrapSysprepSpec), scope) }); err != nil { @@ -1288,6 +1283,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1alpha5.VirtualMachineBootstrapSpec)(nil), (*VirtualMachineBootstrapSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachineBootstrapSpec(a.(*v1alpha5.VirtualMachineBootstrapSpec), b.(*VirtualMachineBootstrapSpec), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*v1alpha5.VirtualMachineBootstrapSysprepSpec)(nil), (*VirtualMachineBootstrapSysprepSpec)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha5_VirtualMachineBootstrapSysprepSpec_To_v1alpha4_VirtualMachineBootstrapSysprepSpec(a.(*v1alpha5.VirtualMachineBootstrapSysprepSpec), b.(*VirtualMachineBootstrapSysprepSpec), scope) }); err != nil { @@ -2106,14 +2106,10 @@ func autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachine out.Sysprep = nil } out.VAppConfig = (*VirtualMachineBootstrapVAppConfigSpec)(unsafe.Pointer(in.VAppConfig)) + // WARNING: in.Disabled requires manual conversion: does not exist in peer-type return nil } -// Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachineBootstrapSpec is an autogenerated conversion function. -func Convert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachineBootstrapSpec(in *v1alpha5.VirtualMachineBootstrapSpec, out *VirtualMachineBootstrapSpec, s conversion.Scope) error { - return autoConvert_v1alpha5_VirtualMachineBootstrapSpec_To_v1alpha4_VirtualMachineBootstrapSpec(in, out, s) -} - func autoConvert_v1alpha4_VirtualMachineBootstrapSysprepSpec_To_v1alpha5_VirtualMachineBootstrapSysprepSpec(in *VirtualMachineBootstrapSysprepSpec, out *v1alpha5.VirtualMachineBootstrapSysprepSpec, s conversion.Scope) error { if in.Sysprep != nil { in, out := &in.Sysprep, &out.Sysprep diff --git a/api/v1alpha5/virtualmachine_bootstrap_types.go b/api/v1alpha5/virtualmachine_bootstrap_types.go index 216ed37ca..8f0393b77 100644 --- a/api/v1alpha5/virtualmachine_bootstrap_types.go +++ b/api/v1alpha5/virtualmachine_bootstrap_types.go @@ -81,6 +81,16 @@ type VirtualMachineBootstrapSpec struct { // This bootstrap provider may not be used in conjunction with the CloudInit // bootstrap provider. VAppConfig *VirtualMachineBootstrapVAppConfigSpec `json:"vAppConfig,omitempty"` + + // +optional + + // Disabled is a flag that indicates whether or not to disable bootstrap + // for this VM. + // + // When set to true, the bootstrap customization is not applied to the VM, + // even if a bootstrap provider such as CloudInit, LinuxPrep, Sysprep, or + // VAppConfig is specified. + Disabled bool `json:"disabled,omitempty"` } // VirtualMachineBootstrapCloudInitSpec describes the CloudInit configuration diff --git a/config/crd/bases/vmoperator.vmware.com_virtualmachinereplicasets.yaml b/config/crd/bases/vmoperator.vmware.com_virtualmachinereplicasets.yaml index 1c5808738..fbe049a74 100644 --- a/config/crd/bases/vmoperator.vmware.com_virtualmachinereplicasets.yaml +++ b/config/crd/bases/vmoperator.vmware.com_virtualmachinereplicasets.yaml @@ -5695,6 +5695,15 @@ spec: check network status, and repeat until an IPv6 address is available. type: boolean type: object + disabled: + description: |- + Disabled is a flag that indicates whether or not to disable bootstrap + for this VM. + + When set to true, the bootstrap customization is not applied to the VM, + even if a bootstrap provider such as CloudInit, LinuxPrep, Sysprep, or + VAppConfig is specified. + type: boolean linuxPrep: description: |- LinuxPrep may be used to bootstrap Linux guests. diff --git a/config/crd/bases/vmoperator.vmware.com_virtualmachines.yaml b/config/crd/bases/vmoperator.vmware.com_virtualmachines.yaml index cfda2b516..f9807fe19 100644 --- a/config/crd/bases/vmoperator.vmware.com_virtualmachines.yaml +++ b/config/crd/bases/vmoperator.vmware.com_virtualmachines.yaml @@ -10389,6 +10389,15 @@ spec: check network status, and repeat until an IPv6 address is available. type: boolean type: object + disabled: + description: |- + Disabled is a flag that indicates whether or not to disable bootstrap + for this VM. + + When set to true, the bootstrap customization is not applied to the VM, + even if a bootstrap provider such as CloudInit, LinuxPrep, Sysprep, or + VAppConfig is specified. + type: boolean linuxPrep: description: |- LinuxPrep may be used to bootstrap Linux guests. diff --git a/pkg/providers/vsphere/vmlifecycle/bootstrap.go b/pkg/providers/vsphere/vmlifecycle/bootstrap.go index c57d504bc..e3f99979b 100644 --- a/pkg/providers/vsphere/vmlifecycle/bootstrap.go +++ b/pkg/providers/vsphere/vmlifecycle/bootstrap.go @@ -73,7 +73,7 @@ var ( ErrBootstrapCustomize = pkgerr.NoRequeueNoErr("bootstrap customized vm") ) -func DoBootstrap( +func DoBootstrap( //nolint:gocyclo vmCtx pkgctx.VirtualMachineContext, vcVM *object.VirtualMachine, config *vimtypes.VirtualMachineConfigInfo, @@ -105,6 +105,11 @@ func DoBootstrap( } } + if bootstrap.Disabled { + vmCtx.Logger.V(4).Info("Skipping bootstrap since disabled") + return nil + } + var ( cloudInit = bootstrap.CloudInit linuxPrep = bootstrap.LinuxPrep diff --git a/pkg/providers/vsphere/vmlifecycle/bootstrap_test.go b/pkg/providers/vsphere/vmlifecycle/bootstrap_test.go index 2d1ccc307..5a4df25ab 100644 --- a/pkg/providers/vsphere/vmlifecycle/bootstrap_test.go +++ b/pkg/providers/vsphere/vmlifecycle/bootstrap_test.go @@ -309,6 +309,24 @@ var _ = Describe("DoBootstrap", func() { bsArgs = vmlifecycle.BootstrapArgs{} }) + Context("CloudInit", func() { + BeforeEach(func() { + vmCtx.VM.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{ + CloudInit: &vmopv1.VirtualMachineBootstrapCloudInitSpec{}, + } + }) + + When("Disabled is true", func() { + BeforeEach(func() { + vmCtx.VM.Spec.Bootstrap.Disabled = true + }) + + It("Noop", func() { + Expect(bsErr).ToNot(HaveOccurred()) + }) + }) + }) + Context("LinuxPrep", func() { BeforeEach(func() { vmCtx.VM.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{ @@ -316,6 +334,16 @@ var _ = Describe("DoBootstrap", func() { } }) + When("Disabled is true", func() { + BeforeEach(func() { + vmCtx.VM.Spec.Bootstrap.Disabled = true + }) + + It("Noop", func() { + Expect(bsErr).ToNot(HaveOccurred()) + }) + }) + It("Customizes", func() { Expect(bsErr).To(MatchError(vmlifecycle.ErrBootstrapCustomize)) }) @@ -352,6 +380,16 @@ var _ = Describe("DoBootstrap", func() { } }) + When("Disabled is true", func() { + BeforeEach(func() { + vmCtx.VM.Spec.Bootstrap.Disabled = true + }) + + It("Noop", func() { + Expect(bsErr).ToNot(HaveOccurred()) + }) + }) + It("Customizes", func() { Expect(bsErr).To(MatchError(vmlifecycle.ErrBootstrapCustomize)) }) @@ -378,4 +416,22 @@ var _ = Describe("DoBootstrap", func() { }) }) }) + + Context("vAppConfig", func() { + BeforeEach(func() { + vmCtx.VM.Spec.Bootstrap = &vmopv1.VirtualMachineBootstrapSpec{ + VAppConfig: &vmopv1.VirtualMachineBootstrapVAppConfigSpec{}, + } + }) + + When("Disabled is true", func() { + BeforeEach(func() { + vmCtx.VM.Spec.Bootstrap.Disabled = true + }) + + It("Noop", func() { + Expect(bsErr).ToNot(HaveOccurred()) + }) + }) + }) })